]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/build.c
Drop unused chroot-shell script
[people/stevee/pakfire.git] / src / libpakfire / build.c
CommitLineData
1a276007
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2021 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
21#include <errno.h>
22#include <stdlib.h>
23
24#include <pakfire/build.h>
25#include <pakfire/execute.h>
26#include <pakfire/dist.h>
27#include <pakfire/logging.h>
4c07774f 28#include <pakfire/package.h>
48c6f2e7 29#include <pakfire/packager.h>
1a276007
MT
30#include <pakfire/parser.h>
31#include <pakfire/private.h>
32#include <pakfire/types.h>
33#include <pakfire/util.h>
34
35static const char* stages[] = {
36 "prepare",
37 "build",
38 "test",
39 "install",
40 NULL,
41};
42
43#define TEMPLATE \
44 "#!/bin/bash --login\n" \
45 "\n" \
46 "set -e\n" \
47 "set -x\n" \
48 "\n" \
49 "%%{_%s}\n" \
50 "\n" \
51 "exit 0\n"
52
73543ae3
MT
53static int append_to_array(const char*** array, const char* s) {
54 unsigned int length = 0;
55
56 // Determine the length of the existing array
57 if (*array) {
58 for (const char** element = *array; *element; element++)
59 length++;
60 }
61
62 // Allocate space
63 *array = reallocarray(*array, length + 2, sizeof(**array));
64 if (!*array)
65 return 1;
66
67 // Append s and terminate the array
68 (*array)[length] = s;
69 (*array)[length + 1] = NULL;
70
71 return 0;
72}
73
74static int pakfire_build_package_add_files(Pakfire pakfire, PakfireParser makefile,
75 const char* namespace, struct pakfire_packager* packager) {
76 PakfireFilelist filelist = NULL;
77 char path[PATH_MAX];
78 int r = 1;
79
80 char** files = NULL;
81 const char** includes = NULL;
82 const char** excludes = NULL;
83
84 // Fetch filelist from makefile
85 files = pakfire_parser_get_split(makefile, namespace, "files", '\n');
86
87 // No files to package?
88 if (!files)
89 return 0;
90
91 // Fetch buildroot
92 char* buildroot = pakfire_parser_get(makefile, NULL, "BUILDROOT");
93 if (!buildroot)
94 goto ERROR;
95
96 // Convert to absolute path
97 pakfire_make_path(pakfire, path, buildroot);
98
99 // Split into includes and excludes
100 for (char** file = files; *file; file++) {
101 if (**file == '!')
102 r = append_to_array(&excludes, *file);
103 else
104 r = append_to_array(&includes, *file);
105 if (r)
106 goto ERROR;
107 }
108
109 // Allocate a new filelist
110 r = pakfire_filelist_create(&filelist, pakfire);
111 if (r)
112 goto ERROR;
113
114 // Scan for files
115 r = pakfire_filelist_scan(filelist, path, includes, excludes);
116 if (r)
117 goto ERROR;
118
119 size_t length = pakfire_filelist_size(filelist);
120 DEBUG(pakfire, "%zu file(s) found\n", length);
121
122 // XXX package everything
123
124ERROR:
125 if (filelist)
126 pakfire_filelist_unref(filelist);
127 if (files) {
128 for (char** file = files; *file; file++)
129 free(*file);
130 free(files);
131 }
132 if (includes)
133 free(includes);
134 if (excludes)
135 free(excludes);
136 if (buildroot)
137 free(buildroot);
138
139 return r;
140}
141
48c6f2e7
MT
142static int pakfire_build_package(Pakfire pakfire, PakfireParser makefile,
143 const char* namespace, const char* target) {
4c07774f
MT
144 PakfireRepo repo = NULL;
145 PakfirePackage pkg = NULL;
48c6f2e7 146 struct pakfire_packager* packager = NULL;
73543ae3 147
a50bde9c
MT
148 int r = 1;
149
150 // Expand the handle into the package name
4c07774f 151 char* name = pakfire_parser_expand(makefile, "packages", namespace);
a50bde9c
MT
152 if (!name) {
153 ERROR(pakfire, "Could not get package name: %s\n", strerror(errno));
154 goto ERROR;
155 }
156
157 INFO(pakfire, "Building package '%s'...\n", name);
158
4c07774f
MT
159 // Fetch build architecture
160 const char* arch = pakfire_get_arch(pakfire);
161 if (!arch)
162 goto ERROR;
163
164 // Fetch the dummy repository
165 repo = pakfire_get_repo(pakfire, "@dummy");
166 if (!repo)
167 goto ERROR;
168
169 // Fetch package from makefile
170 r = pakfire_parser_create_package(makefile, &pkg, repo, namespace, arch);
171 if (r) {
172 ERROR(pakfire, "Could not create package from makefile: %s\n", strerror(errno));
173 goto ERROR;
174 }
175
48c6f2e7
MT
176 // Create a packager
177 r = pakfire_packager_create(&packager, pkg);
178 if (r)
179 goto ERROR;
180
73543ae3
MT
181 // Add files
182 r = pakfire_build_package_add_files(pakfire, makefile, namespace, packager);
183 if (r)
184 goto ERROR;
185
4c07774f 186#ifdef ENABLE_DEBUG
23e22751 187 char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
4c07774f
MT
188 if (dump) {
189 DEBUG(pakfire, "%s\n", dump);
190 free(dump);
191 }
192#endif
a50bde9c 193
48c6f2e7
MT
194 // Write the finished package
195 r = pakfire_packager_finish_to_directory(packager, target);
196 if (r) {
197 ERROR(pakfire, "pakfire_packager_finish() failed: %s\n", strerror(errno));
198 goto ERROR;
199 }
200
a50bde9c
MT
201 // Success
202 r = 0;
203
204ERROR:
48c6f2e7
MT
205 if (packager)
206 pakfire_packager_unref(packager);
4c07774f
MT
207 if (repo)
208 pakfire_repo_unref(repo);
209 if (pkg)
210 pakfire_package_unref(pkg);
a50bde9c
MT
211 if (name)
212 free(name);
213
214 return r;
215}
216
48c6f2e7 217static int pakfire_build_packages(Pakfire pakfire, PakfireParser makefile, const char* target) {
a50bde9c
MT
218 DEBUG(pakfire, "Creating packages...");
219 int r = 1;
220
221 // Fetch a list all all packages
222 char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*");
223 if (!packages) {
224 ERROR(pakfire, "Could not find any packages: %s\n", strerror(errno));
225 goto ERROR;
226 }
227
228 unsigned int num_packages = 0;
229
230 // Count how many packages we have
231 for (char** package = packages; *package; package++)
232 num_packages++;
233
234 DEBUG(pakfire, "Found %d package(s)\n", num_packages);
235
236 // Build packages in reverse order
237 for (int i = num_packages - 1; i >= 0; i--) {
48c6f2e7 238 r = pakfire_build_package(pakfire, makefile, packages[i], target);
a50bde9c
MT
239 if (r)
240 goto ERROR;
241 }
242
243 // Success
244 r = 0;
245
246ERROR:
247 if (packages)
248 free(packages);
249
250 return r;
251}
252
7d999600
MT
253static int pakfire_build_stage(Pakfire pakfire, PakfireParser makefile, const char* stage,
254 pakfire_execute_logging_callback logging_callback, void* data) {
1a276007
MT
255 char template[1024];
256
257 // Prepare template for this stage
258 int r = pakfire_string_format(template, TEMPLATE, stage);
259 if (r < 0)
260 return r;
261
262 // Create the build script
263 char* script = pakfire_parser_expand(makefile, "build", template);
264 if (!script) {
265 ERROR(pakfire, "Could not generate the build script for stage '%s': %s\n",
266 stage, strerror(errno));
267 goto ERROR;
268 }
269
270 INFO(pakfire, "Running build stage '%s'\n", stage);
271
7d999600
MT
272 r = pakfire_execute_script(pakfire, script, strlen(script), NULL, 0,
273 logging_callback, data);
1a276007
MT
274 if (r) {
275 ERROR(pakfire, "Build stage '%s' failed with status %d\n", stage, r);
276 }
277
278ERROR:
279 if (script)
280 free(script);
281
282 return r;
283}
284
285PAKFIRE_EXPORT int pakfire_build(Pakfire pakfire, const char* path,
7d999600
MT
286 const char* target, int flags,
287 pakfire_execute_logging_callback logging_callback, void* data) {
1a276007
MT
288 PakfireParser makefile = NULL;
289 struct pakfire_parser_error* error = NULL;
290
48c6f2e7
MT
291 // Check for valid input
292 if (!path) {
293 errno = EINVAL;
294 return 1;
295 }
296
297 // The default target is the local repository path
298 if (!target) {
299 PakfireRepo repo = pakfire_get_repo(pakfire, "local");
300 if (!repo) {
301 errno = EINVAL;
302 return 1;
303 }
304
305 target = pakfire_repo_get_path(repo);
306 pakfire_repo_unref(repo);
307 }
308
1a276007
MT
309 // Read makefile
310 int r = pakfire_read_makefile(&makefile, pakfire, path, &error);
311 if (r) {
312 if (error) {
313 ERROR(pakfire, "Could not parse makefile %s: %s\n", path,
314 pakfire_parser_error_get_message(error));
315 pakfire_parser_error_unref(error);
316 } else {
317 ERROR(pakfire, "Could not parse makefile %s: %s\n", path,
318 strerror(errno));
319 }
320
321 goto ERROR;
322 }
323
324 // Run through all build stages
325 for (const char** stage = stages; *stage; stage++) {
7d999600 326 int r = pakfire_build_stage(pakfire, makefile, *stage, logging_callback, data);
1a276007
MT
327 if (r) {
328 // Drop to a shell for debugging
329 if (flags & PAKFIRE_BUILD_INTERACTIVE)
330 pakfire_shell(pakfire);
331
332 goto ERROR;
333 }
334 }
335
a50bde9c 336 // Create the packages
48c6f2e7 337 r = pakfire_build_packages(pakfire, makefile, target);
a50bde9c
MT
338 if (r) {
339 ERROR(pakfire, "Could not create packages: %s\n", strerror(errno));
340 goto ERROR;
341 }
1a276007
MT
342
343ERROR:
344 if (makefile)
345 pakfire_parser_unref(makefile);
346
347 return r;
348}
349
350PAKFIRE_EXPORT int pakfire_shell(Pakfire pakfire) {
351 const char* argv[] = {
352 "/bin/bash", "--login", NULL,
353 };
354
355 const int flags =
356 PAKFIRE_EXECUTE_INTERACTIVE | PAKFIRE_EXECUTE_ENABLE_NETWORK;
357
358 return pakfire_execute(pakfire, argv, NULL, flags, NULL, NULL);
359}