]>
Commit | Line | Data |
---|---|---|
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> | |
22a0733e | 23 | #include <sys/mount.h> |
cee6363a | 24 | #include <unistd.h> |
57e2cf99 | 25 | #include <uuid/uuid.h> |
1a276007 MT |
26 | |
27 | #include <pakfire/build.h> | |
f877fdfa | 28 | #include <pakfire/cgroup.h> |
2a623cf8 | 29 | #include <pakfire/dependencies.h> |
1a276007 | 30 | #include <pakfire/dist.h> |
ae7968a7 | 31 | #include <pakfire/file.h> |
e545f6ec | 32 | #include <pakfire/i18n.h> |
8e99f22d | 33 | #include <pakfire/jail.h> |
1a276007 | 34 | #include <pakfire/logging.h> |
163851bc | 35 | #include <pakfire/mount.h> |
4c07774f | 36 | #include <pakfire/package.h> |
48c6f2e7 | 37 | #include <pakfire/packager.h> |
1a276007 MT |
38 | #include <pakfire/parser.h> |
39 | #include <pakfire/private.h> | |
4651122b | 40 | #include <pakfire/repo.h> |
99a56775 | 41 | #include <pakfire/request.h> |
106d2edd | 42 | #include <pakfire/scriptlet.h> |
c0f2502a | 43 | #include <pakfire/snapshot.h> |
d973a13d | 44 | #include <pakfire/string.h> |
1a276007 MT |
45 | #include <pakfire/util.h> |
46 | ||
5a06668c MT |
47 | #define CCACHE_DIR "/var/cache/ccache" |
48 | ||
882ae03b | 49 | // We guarantee 2 GiB of memory to every build container |
0a1284ff | 50 | #define PAKFIRE_BUILD_MEMORY_GUARANTEED (size_t)2 * 1024 * 1024 * 1024 |
882ae03b | 51 | |
de4c8fe6 MT |
52 | // We allow only up to 2048 processes/threads for every build container |
53 | #define PAKFIRE_BUILD_PID_LIMIT (size_t)2048 | |
54 | ||
abbad00b MT |
55 | struct pakfire_build { |
56 | struct pakfire* pakfire; | |
57 | int nrefs; | |
58 | ||
59 | // Flags | |
60 | int flags; | |
61 | ||
62 | // Build ID | |
63 | uuid_t id; | |
f877fdfa MT |
64 | char _id[UUID_STR_LEN]; |
65 | ||
a84624be MT |
66 | char target[PATH_MAX]; |
67 | ||
f877fdfa MT |
68 | // cgroup |
69 | struct pakfire_cgroup* cgroup; | |
753ddf74 MT |
70 | |
71 | // Jail | |
72 | struct pakfire_jail* jail; | |
e545f6ec | 73 | |
d6451238 MT |
74 | // The result repository |
75 | struct pakfire_repo* result; | |
76 | ||
6fc3956f MT |
77 | // Buildroot |
78 | char buildroot[PATH_MAX]; | |
79 | ||
8dbb69f9 MT |
80 | // States |
81 | int init:1; | |
abbad00b MT |
82 | }; |
83 | ||
1a276007 MT |
84 | #define TEMPLATE \ |
85 | "#!/bin/bash --login\n" \ | |
86 | "\n" \ | |
87 | "set -e\n" \ | |
88 | "set -x\n" \ | |
89 | "\n" \ | |
90 | "%%{_%s}\n" \ | |
91 | "\n" \ | |
92 | "exit 0\n" | |
93 | ||
c0f2502a MT |
94 | static int pakfire_build_has_flag(struct pakfire_build* build, int flag) { |
95 | return build->flags & flag; | |
96 | } | |
97 | ||
5f6e42a2 | 98 | /* |
7a347de7 | 99 | This function enables the local repository so that it can be accessed by |
5f6e42a2 MT |
100 | a pakfire instance running inside the chroot. |
101 | */ | |
102 | static int pakfire_build_enable_repos(struct pakfire_build* build) { | |
d8c8cdc4 | 103 | return 0; |
5f6e42a2 MT |
104 | } |
105 | ||
106 | /* | |
107 | Drops the user into a shell | |
108 | */ | |
109 | static int pakfire_build_shell(struct pakfire_build* build) { | |
110 | int r; | |
111 | ||
112 | // Export local repository if running in interactive mode | |
113 | r = pakfire_build_enable_repos(build); | |
114 | if (r) | |
115 | return r; | |
116 | ||
117 | // Run shell | |
118 | return pakfire_jail_shell(build->jail); | |
119 | } | |
120 | ||
779e16de | 121 | static int pakfire_build_run_script(struct pakfire_build* build, const char* filename, |
12b9b39f | 122 | const char* args[], char** output) { |
8d0f3a35 MT |
123 | char* script = NULL; |
124 | size_t size = 0; | |
125 | char path[PATH_MAX]; | |
126 | ||
779e16de | 127 | DEBUG(build->pakfire, "Running build script '%s'...\n", filename); |
8d0f3a35 MT |
128 | |
129 | // Make the source path | |
130 | pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename); | |
131 | ||
132 | // Open the source script | |
133 | FILE* f = fopen(path, "r"); | |
134 | if (!f) { | |
779e16de | 135 | ERROR(build->pakfire, "Could not open %s: %m\n", path); |
8d0f3a35 MT |
136 | return 1; |
137 | } | |
138 | ||
139 | // Read the script into memory | |
140 | int r = pakfire_read_file_into_buffer(f, &script, &size); | |
141 | if (r) { | |
779e16de | 142 | ERROR(build->pakfire, "Could not read script into memory: %m\n"); |
8d0f3a35 MT |
143 | goto ERROR; |
144 | } | |
145 | ||
8e99f22d | 146 | // Execute the script |
001bcbd7 | 147 | r = pakfire_jail_exec_script(build->jail, script, size, args, output); |
8d0f3a35 | 148 | if (r) { |
779e16de | 149 | ERROR(build->pakfire, "Script '%s' failed with status %d\n", filename, r); |
8d0f3a35 MT |
150 | } |
151 | ||
152 | ERROR: | |
153 | if (script) | |
154 | free(script); | |
155 | ||
156 | return r; | |
157 | } | |
158 | ||
bd95a433 | 159 | static int pakfire_build_find_dependencies(struct pakfire_build* build, |
1bbbfb9e | 160 | struct pakfire_package* pkg, struct pakfire_filelist* filelist, const char* buildroot) { |
12b9b39f MT |
161 | char* provides = NULL; |
162 | char* requires = NULL; | |
5b0b3dc2 MT |
163 | char path[PATH_MAX]; |
164 | ||
165 | // Allocate path to write the filelist to | |
77e26129 MT |
166 | int r = pakfire_path(build->pakfire, path, "%s", "/var/tmp/.pakfire-filelist.XXXXXX"); |
167 | if (r) | |
5b0b3dc2 MT |
168 | return 1; |
169 | ||
170 | // Create a temporary file | |
171 | FILE* f = pakfire_mktemp(path); | |
172 | if (!f) | |
173 | goto ERROR; | |
174 | ||
175 | // Write filelist to the temporary file | |
176 | r = pakfire_filelist_export(filelist, f); | |
177 | fclose(f); | |
178 | if (r) { | |
bd95a433 | 179 | ERROR(build->pakfire, "Could not export filelist: %m\n"); |
5b0b3dc2 MT |
180 | goto ERROR; |
181 | } | |
182 | ||
bd95a433 | 183 | const char* root = pakfire_get_path(build->pakfire); |
5b0b3dc2 MT |
184 | |
185 | // Pass buildroot and the filelist as arguments | |
186 | const char* args[] = { | |
187 | buildroot, pakfire_path_relpath(root, path), NULL | |
188 | }; | |
189 | ||
190 | // Find all provides | |
779e16de | 191 | r = pakfire_build_run_script(build, "find-provides", args, &provides); |
5b0b3dc2 | 192 | if (r) { |
bd95a433 | 193 | ERROR(build->pakfire, "find-provides returned with error %d\n", r); |
5b0b3dc2 MT |
194 | goto ERROR; |
195 | } | |
196 | ||
197 | // Find all requires | |
779e16de | 198 | r = pakfire_build_run_script(build, "find-requires", args, &requires); |
5b0b3dc2 | 199 | if (r) { |
bd95a433 | 200 | ERROR(build->pakfire, "find-requires returned with error %d\n", r); |
5b0b3dc2 MT |
201 | goto ERROR; |
202 | } | |
203 | ||
204 | // Add all provides to the package | |
205 | if (provides) { | |
6f3fad3b | 206 | r = pakfire_str2deps(build->pakfire, pkg, PAKFIRE_PKG_PROVIDES, provides); |
83d93f33 MT |
207 | if (r) { |
208 | ERROR(build->pakfire, "Could not add provides: %m\n"); | |
209 | goto ERROR; | |
5b0b3dc2 MT |
210 | } |
211 | } | |
212 | ||
213 | // Add all requires to the package | |
214 | if (requires) { | |
6f3fad3b | 215 | r = pakfire_str2deps(build->pakfire, pkg, PAKFIRE_PKG_REQUIRES, requires); |
83d93f33 MT |
216 | if (r) { |
217 | ERROR(build->pakfire, "Could not add provides: %m\n"); | |
218 | goto ERROR; | |
5b0b3dc2 MT |
219 | } |
220 | } | |
221 | ||
222 | // Success | |
223 | r = 0; | |
224 | ||
225 | ERROR: | |
12b9b39f | 226 | if (provides) |
5b0b3dc2 | 227 | free(provides); |
12b9b39f | 228 | if (requires) |
5b0b3dc2 | 229 | free(requires); |
5b0b3dc2 MT |
230 | unlink(path); |
231 | ||
232 | return r; | |
233 | } | |
234 | ||
73543ae3 MT |
235 | static int append_to_array(const char*** array, const char* s) { |
236 | unsigned int length = 0; | |
237 | ||
238 | // Determine the length of the existing array | |
239 | if (*array) { | |
240 | for (const char** element = *array; *element; element++) | |
241 | length++; | |
242 | } | |
243 | ||
244 | // Allocate space | |
245 | *array = reallocarray(*array, length + 2, sizeof(**array)); | |
246 | if (!*array) | |
247 | return 1; | |
248 | ||
249 | // Append s and terminate the array | |
250 | (*array)[length] = s; | |
251 | (*array)[length + 1] = NULL; | |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
adfa3573 MT |
256 | static int pakfire_build_package_add_files(struct pakfire_build* build, |
257 | struct pakfire_parser* makefile, const char* buildroot, const char* namespace, | |
258 | struct pakfire_package* pkg, struct pakfire_packager* packager) { | |
1bbbfb9e | 259 | struct pakfire_filelist* filelist = NULL; |
73543ae3 MT |
260 | int r = 1; |
261 | ||
262 | char** files = NULL; | |
263 | const char** includes = NULL; | |
264 | const char** excludes = NULL; | |
265 | ||
266 | // Fetch filelist from makefile | |
267 | files = pakfire_parser_get_split(makefile, namespace, "files", '\n'); | |
268 | ||
269 | // No files to package? | |
270 | if (!files) | |
271 | return 0; | |
272 | ||
73543ae3 MT |
273 | // Split into includes and excludes |
274 | for (char** file = files; *file; file++) { | |
275 | if (**file == '!') | |
276 | r = append_to_array(&excludes, *file); | |
277 | else | |
278 | r = append_to_array(&includes, *file); | |
279 | if (r) | |
280 | goto ERROR; | |
281 | } | |
282 | ||
283 | // Allocate a new filelist | |
adfa3573 | 284 | r = pakfire_filelist_create(&filelist, build->pakfire); |
73543ae3 MT |
285 | if (r) |
286 | goto ERROR; | |
287 | ||
288 | // Scan for files | |
77e26129 | 289 | r = pakfire_filelist_scan(filelist, build->buildroot, includes, excludes); |
73543ae3 MT |
290 | if (r) |
291 | goto ERROR; | |
292 | ||
ae7968a7 | 293 | const size_t length = pakfire_filelist_size(filelist); |
adfa3573 | 294 | DEBUG(build->pakfire, "%zu file(s) found\n", length); |
73543ae3 | 295 | |
84556307 MT |
296 | // Nothing to do if the filelist is empty |
297 | if (!length) | |
298 | goto ERROR; | |
299 | ||
5b0b3dc2 | 300 | // Find dependencies |
bd95a433 | 301 | r = pakfire_build_find_dependencies(build, pkg, filelist, buildroot); |
5b0b3dc2 | 302 | if (r) { |
adfa3573 | 303 | ERROR(build->pakfire, "Finding dependencies failed: %m\n"); |
5b0b3dc2 MT |
304 | goto ERROR; |
305 | } | |
306 | ||
ae7968a7 | 307 | // Add all files to the package |
5d5da764 | 308 | r = pakfire_packager_add_files(packager, filelist); |
814b7fee MT |
309 | if (r) |
310 | goto ERROR; | |
73543ae3 MT |
311 | |
312 | ERROR: | |
313 | if (filelist) | |
314 | pakfire_filelist_unref(filelist); | |
315 | if (files) { | |
316 | for (char** file = files; *file; file++) | |
317 | free(*file); | |
318 | free(files); | |
319 | } | |
320 | if (includes) | |
321 | free(includes); | |
322 | if (excludes) | |
323 | free(excludes); | |
73543ae3 MT |
324 | |
325 | return r; | |
326 | } | |
327 | ||
9df89a8f MT |
328 | static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build, |
329 | struct pakfire_package* pkg, struct pakfire_scriptlet* scriptlet) { | |
12b9b39f | 330 | char* prerequires = NULL; |
106d2edd MT |
331 | char path[PATH_MAX]; |
332 | size_t size; | |
333 | int r; | |
334 | ||
9df89a8f | 335 | const char* root = pakfire_get_path(build->pakfire); |
106d2edd MT |
336 | |
337 | // Make filename | |
77e26129 MT |
338 | r = pakfire_path(build->pakfire, path, "%s", "/var/tmp/.pakfire-scriptlet.XXXXXX"); |
339 | if (r) | |
106d2edd MT |
340 | return r; |
341 | ||
342 | // Fetch scriptlet payload | |
343 | const char* data = pakfire_scriptlet_get_data(scriptlet, &size); | |
344 | ||
345 | // Create a temporary file | |
346 | FILE* f = pakfire_mktemp(path); | |
347 | if (!f) | |
348 | return 1; | |
349 | ||
350 | // Write scriptlet | |
351 | ssize_t bytes_written = fwrite(data, 1, size, f); | |
352 | if (bytes_written < 0) { | |
9df89a8f | 353 | ERROR(build->pakfire, "Could not write to %s: %m\n", path); |
106d2edd MT |
354 | fclose(f); |
355 | goto ERROR; | |
356 | } | |
357 | ||
358 | // Close file | |
359 | fclose(f); | |
360 | ||
361 | // Build commandline | |
362 | const char* args[] = { | |
363 | pakfire_path_relpath(root, path), | |
364 | NULL, | |
365 | }; | |
366 | ||
367 | // Find all pre-requires | |
779e16de | 368 | r = pakfire_build_run_script(build, "find-prerequires", args, &prerequires); |
106d2edd MT |
369 | if (r) |
370 | goto ERROR; | |
371 | ||
372 | // Add all pre-requires to the package | |
373 | if (prerequires) { | |
6f3fad3b | 374 | r = pakfire_str2deps(build->pakfire, pkg, PAKFIRE_PKG_PREREQUIRES, prerequires); |
83d93f33 MT |
375 | if (r) { |
376 | ERROR(build->pakfire, "Could not add pre-requires: %m\n"); | |
377 | goto ERROR; | |
106d2edd MT |
378 | } |
379 | } | |
380 | ||
381 | ERROR: | |
382 | if (r && *path) | |
383 | unlink(path); | |
12b9b39f | 384 | if (prerequires) |
106d2edd | 385 | free(prerequires); |
106d2edd MT |
386 | return r; |
387 | } | |
388 | ||
9df89a8f MT |
389 | static int pakfire_build_package_add_scriptlet(struct pakfire_build* build, |
390 | struct pakfire_package* pkg, struct pakfire_packager* packager, | |
391 | const char* type, const char* data) { | |
106d2edd MT |
392 | struct pakfire_scriptlet* scriptlet = NULL; |
393 | char* shell = NULL; | |
394 | int r; | |
395 | ||
396 | // Wrap scriptlet into a shell script | |
397 | r = asprintf(&shell, "#!/bin/sh\n\nset -e\n\n%s\n\nexit 0\n", data); | |
398 | if (r < 0) | |
399 | goto ERROR; | |
400 | ||
401 | // Create a scriptlet | |
9df89a8f | 402 | r = pakfire_scriptlet_create(&scriptlet, build->pakfire, type, shell, 0); |
106d2edd MT |
403 | if (r) |
404 | goto ERROR; | |
405 | ||
406 | // Add it to the package | |
407 | r = pakfire_packager_add_scriptlet(packager, scriptlet); | |
408 | if (r) { | |
9df89a8f | 409 | ERROR(build->pakfire, "Could not add scriptlet %s\n", type); |
106d2edd MT |
410 | goto ERROR; |
411 | } | |
412 | ||
413 | // Add scriptlet requirements | |
9df89a8f | 414 | r = pakfire_build_add_scriptlet_requires(build, pkg, scriptlet); |
106d2edd | 415 | if (r) { |
9df89a8f | 416 | ERROR(build->pakfire, "Could not add scriptlet requirements: %m\n"); |
106d2edd MT |
417 | goto ERROR; |
418 | } | |
419 | ||
420 | // Success | |
421 | r = 0; | |
422 | ||
423 | ERROR: | |
424 | if (scriptlet) | |
425 | pakfire_scriptlet_unref(scriptlet); | |
426 | if (shell) | |
427 | free(shell); | |
428 | ||
429 | return r; | |
430 | } | |
431 | ||
9df89a8f MT |
432 | static int pakfire_build_package_add_scriptlets(struct pakfire_build* build, |
433 | struct pakfire_parser* makefile, const char* namespace, | |
434 | struct pakfire_package* pkg, struct pakfire_packager* packager) { | |
106d2edd MT |
435 | char name[NAME_MAX]; |
436 | int r; | |
437 | ||
438 | for (const char** type = pakfire_scriptlet_types; *type; type++) { | |
439 | r = pakfire_string_format(name, "scriptlet:%s", *type); | |
a60955af | 440 | if (r) |
106d2edd MT |
441 | return r; |
442 | ||
443 | // Fetch the scriptlet | |
444 | char* data = pakfire_parser_get(makefile, namespace, name); | |
445 | if (!data) | |
446 | continue; | |
447 | ||
448 | // Add it to the package | |
9df89a8f | 449 | r = pakfire_build_package_add_scriptlet(build, pkg, packager, *type, data); |
106d2edd MT |
450 | if (r) { |
451 | free(data); | |
452 | return r; | |
453 | } | |
454 | ||
455 | free(data); | |
456 | } | |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
0e177be7 MT |
461 | static int pakfire_build_package(struct pakfire_build* build, struct pakfire_parser* makefile, |
462 | const char* buildroot, const char* namespace) { | |
31480bee | 463 | struct pakfire_package* pkg = NULL; |
48c6f2e7 | 464 | struct pakfire_packager* packager = NULL; |
73543ae3 | 465 | |
a50bde9c MT |
466 | int r = 1; |
467 | ||
468 | // Expand the handle into the package name | |
4c07774f | 469 | char* name = pakfire_parser_expand(makefile, "packages", namespace); |
a50bde9c | 470 | if (!name) { |
0e177be7 | 471 | ERROR(build->pakfire, "Could not get package name: %m\n"); |
a50bde9c MT |
472 | goto ERROR; |
473 | } | |
474 | ||
0e177be7 | 475 | INFO(build->pakfire, "Building package '%s'...\n", name); |
0f9bb14d | 476 | DEBUG(build->pakfire, " buildroot = %s\n", buildroot); |
a50bde9c | 477 | |
4c07774f | 478 | // Fetch build architecture |
0e177be7 | 479 | const char* arch = pakfire_get_arch(build->pakfire); |
4c07774f MT |
480 | if (!arch) |
481 | goto ERROR; | |
482 | ||
4c07774f | 483 | // Fetch package from makefile |
6ebd55e2 | 484 | r = pakfire_parser_create_package(makefile, &pkg, NULL, namespace, arch); |
4c07774f | 485 | if (r) { |
0e177be7 | 486 | ERROR(build->pakfire, "Could not create package from makefile: %m\n"); |
4c07774f MT |
487 | goto ERROR; |
488 | } | |
489 | ||
ca002cae MT |
490 | // Set distribution |
491 | const char* distribution = pakfire_parser_get(makefile, NULL, "DISTRO_NAME"); | |
74468e4f MT |
492 | if (distribution) { |
493 | r = pakfire_package_set_string(pkg, PAKFIRE_PKG_DISTRO, distribution); | |
494 | if (r) | |
495 | goto ERROR; | |
496 | } | |
ca002cae | 497 | |
7996020a | 498 | // Set build ID |
3bea955d | 499 | pakfire_package_set_uuid(pkg, PAKFIRE_PKG_BUILD_ID, build->id); |
7996020a | 500 | |
571539a7 MT |
501 | // Set source package |
502 | const char* source_name = pakfire_parser_get(makefile, NULL, "name"); | |
988ce1bc MT |
503 | if (source_name) { |
504 | r = pakfire_package_set_string(pkg, PAKFIRE_PKG_SOURCE_NAME, source_name); | |
505 | if (r) | |
506 | goto ERROR; | |
507 | } | |
571539a7 MT |
508 | |
509 | // Set source EVR | |
510 | const char* source_evr = pakfire_parser_get(makefile, NULL, "evr"); | |
988ce1bc MT |
511 | if (source_evr) { |
512 | r = pakfire_package_set_string(pkg, PAKFIRE_PKG_SOURCE_EVR, source_evr); | |
513 | if (r) | |
514 | goto ERROR; | |
515 | } | |
571539a7 MT |
516 | |
517 | // Set source arch | |
988ce1bc MT |
518 | r = pakfire_package_set_string(pkg, PAKFIRE_PKG_SOURCE_ARCH, "src"); |
519 | if (r) | |
520 | goto ERROR; | |
571539a7 | 521 | |
48c6f2e7 | 522 | // Create a packager |
0e177be7 | 523 | r = pakfire_packager_create(&packager, build->pakfire, pkg); |
48c6f2e7 MT |
524 | if (r) |
525 | goto ERROR; | |
526 | ||
73543ae3 | 527 | // Add files |
adfa3573 | 528 | r = pakfire_build_package_add_files(build, makefile, buildroot, namespace, |
5b0b3dc2 | 529 | pkg, packager); |
73543ae3 MT |
530 | if (r) |
531 | goto ERROR; | |
532 | ||
106d2edd | 533 | // Add scriptlets |
9df89a8f | 534 | r = pakfire_build_package_add_scriptlets(build, makefile, namespace, |
0e177be7 | 535 | pkg, packager); |
106d2edd MT |
536 | if (r) |
537 | goto ERROR; | |
538 | ||
733efe16 MT |
539 | const char* path = pakfire_repo_get_path(build->result); |
540 | ||
ae7968a7 | 541 | // Write the finished package |
733efe16 | 542 | r = pakfire_packager_finish_to_directory(packager, path, NULL); |
ae7968a7 | 543 | if (r) { |
0e177be7 | 544 | ERROR(build->pakfire, "pakfire_packager_finish() failed: %m\n"); |
ae7968a7 MT |
545 | goto ERROR; |
546 | } | |
547 | ||
4c07774f | 548 | #ifdef ENABLE_DEBUG |
23e22751 | 549 | char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG); |
4c07774f | 550 | if (dump) { |
0e177be7 | 551 | DEBUG(build->pakfire, "%s\n", dump); |
4c07774f MT |
552 | free(dump); |
553 | } | |
554 | #endif | |
a50bde9c | 555 | |
2a838122 MT |
556 | // Cleanup all packaged files |
557 | r = pakfire_packager_cleanup(packager); | |
558 | if (r) | |
559 | goto ERROR; | |
560 | ||
a50bde9c MT |
561 | // Success |
562 | r = 0; | |
563 | ||
564 | ERROR: | |
48c6f2e7 MT |
565 | if (packager) |
566 | pakfire_packager_unref(packager); | |
4c07774f MT |
567 | if (pkg) |
568 | pakfire_package_unref(pkg); | |
a50bde9c MT |
569 | if (name) |
570 | free(name); | |
571 | ||
572 | return r; | |
573 | } | |
574 | ||
0e177be7 | 575 | static int pakfire_build_packages(struct pakfire_build* build, |
03dc1d52 | 576 | struct pakfire_parser* makefile) { |
0e177be7 | 577 | DEBUG(build->pakfire, "Creating packages..."); |
a50bde9c MT |
578 | int r = 1; |
579 | ||
03dc1d52 MT |
580 | const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot); |
581 | ||
a50bde9c MT |
582 | // Fetch a list all all packages |
583 | char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*"); | |
584 | if (!packages) { | |
0e177be7 | 585 | ERROR(build->pakfire, "Could not find any packages: %m\n"); |
a50bde9c MT |
586 | goto ERROR; |
587 | } | |
588 | ||
589 | unsigned int num_packages = 0; | |
590 | ||
591 | // Count how many packages we have | |
592 | for (char** package = packages; *package; package++) | |
593 | num_packages++; | |
594 | ||
0e177be7 | 595 | DEBUG(build->pakfire, "Found %d package(s)\n", num_packages); |
a50bde9c MT |
596 | |
597 | // Build packages in reverse order | |
598 | for (int i = num_packages - 1; i >= 0; i--) { | |
0e177be7 | 599 | r = pakfire_build_package(build, makefile, buildroot, packages[i]); |
a50bde9c MT |
600 | if (r) |
601 | goto ERROR; | |
602 | } | |
603 | ||
d6451238 MT |
604 | // Rescan the build repository to import all packages again |
605 | r = pakfire_repo_scan(build->result, 0); | |
606 | if (r) | |
607 | goto ERROR; | |
608 | ||
a50bde9c MT |
609 | // Success |
610 | r = 0; | |
611 | ||
612 | ERROR: | |
613 | if (packages) | |
614 | free(packages); | |
615 | ||
616 | return r; | |
617 | } | |
618 | ||
46748697 MT |
619 | static int pakfire_build_stage(struct pakfire_build* build, |
620 | struct pakfire_parser* makefile, const char* stage) { | |
1a276007 MT |
621 | char template[1024]; |
622 | ||
623 | // Prepare template for this stage | |
624 | int r = pakfire_string_format(template, TEMPLATE, stage); | |
a60955af | 625 | if (r) |
1a276007 MT |
626 | return r; |
627 | ||
2ffd21f9 MT |
628 | // Fetch the environment |
629 | char** envp = pakfire_parser_make_environ(makefile); | |
630 | ||
1a276007 MT |
631 | // Create the build script |
632 | char* script = pakfire_parser_expand(makefile, "build", template); | |
633 | if (!script) { | |
46748697 MT |
634 | ERROR(build->pakfire, "Could not generate the build script for stage '%s': %m\n", |
635 | stage); | |
1a276007 MT |
636 | goto ERROR; |
637 | } | |
638 | ||
46748697 | 639 | INFO(build->pakfire, "Running build stage '%s'\n", stage); |
1a276007 | 640 | |
830f5d18 | 641 | // Import environment |
44d5ebfd MT |
642 | // XXX is this a good idea? |
643 | r = pakfire_jail_import_env(build->jail, (const char**)envp); | |
830f5d18 | 644 | if (r) { |
46748697 | 645 | ERROR(build->pakfire, "Could not import environment: %m\n"); |
830f5d18 MT |
646 | goto ERROR; |
647 | } | |
648 | ||
649 | // Run the script | |
44d5ebfd | 650 | r = pakfire_jail_exec_script(build->jail, script, strlen(script), NULL, NULL); |
1a276007 | 651 | if (r) { |
46748697 | 652 | ERROR(build->pakfire, "Build stage '%s' failed with status %d\n", stage, r); |
1a276007 MT |
653 | } |
654 | ||
655 | ERROR: | |
2ffd21f9 MT |
656 | if (envp) { |
657 | for (char** e = envp; *e; e++) | |
658 | free(*e); | |
659 | free(envp); | |
660 | } | |
1a276007 MT |
661 | if (script) |
662 | free(script); | |
663 | ||
664 | return r; | |
665 | } | |
666 | ||
39f45b30 MT |
667 | static const char* post_build_scripts[] = { |
668 | "remove-static-libs", | |
1f62ffae | 669 | "check-symlinks", |
022b794e | 670 | "check-unsafe-files", |
0b5f0bbc | 671 | "check-libraries", |
16043831 | 672 | "check-rpaths", |
99aee237 | 673 | "check-buildroot", |
7e1fec6f | 674 | "check-include", |
dd864160 | 675 | "check-hardening", |
ae703321 | 676 | "check-interpreters", |
2504194a | 677 | "check-fhs", |
39f45b30 | 678 | "compress-man-pages", |
ffb65de6 | 679 | "strip", |
39f45b30 MT |
680 | NULL, |
681 | }; | |
682 | ||
03dc1d52 MT |
683 | static int pakfire_build_run_post_build_scripts(struct pakfire_build* build) { |
684 | // Fetch buildroot | |
685 | const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot); | |
686 | ||
39f45b30 MT |
687 | // Set default arguments for build scripts |
688 | const char* args[] = { | |
689 | buildroot, NULL | |
690 | }; | |
691 | ||
692 | // Run them one by one | |
693 | for (const char** script = post_build_scripts; *script; script++) { | |
779e16de | 694 | int r = pakfire_build_run_script(build, *script, args, NULL); |
39f45b30 MT |
695 | if (r) |
696 | return r; | |
697 | } | |
698 | ||
699 | return 0; | |
700 | } | |
701 | ||
abbad00b | 702 | static void pakfire_build_free(struct pakfire_build* build) { |
d6451238 MT |
703 | if (build->result) { |
704 | pakfire_repo_clean(build->result, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY); | |
705 | pakfire_repo_unref(build->result); | |
706 | } | |
707 | ||
753ddf74 MT |
708 | if (build->jail) |
709 | pakfire_jail_unref(build->jail); | |
710 | ||
49fd9926 MT |
711 | if (build->cgroup) { |
712 | // Destroy the cgroup | |
713 | pakfire_cgroup_destroy(build->cgroup); | |
714 | ||
715 | // Free it | |
716 | pakfire_cgroup_unref(build->cgroup); | |
717 | } | |
718 | ||
abbad00b MT |
719 | pakfire_unref(build->pakfire); |
720 | free(build); | |
721 | } | |
722 | ||
723 | static int pakfire_build_parse_id(struct pakfire_build* build, const char* id) { | |
724 | int r; | |
725 | ||
726 | // Try parsing the Build ID | |
727 | if (id) { | |
728 | r = uuid_parse(id, build->id); | |
729 | if (r) { | |
730 | ERROR(build->pakfire, "Could not parse build ID '%s'\n", id); | |
1df321ff | 731 | errno = EINVAL; |
abbad00b MT |
732 | return r; |
733 | } | |
734 | ||
735 | // Otherwise initialize the Build ID with something random | |
736 | } else { | |
737 | uuid_generate_random(build->id); | |
738 | } | |
739 | ||
f877fdfa MT |
740 | // Store the ID as string, too |
741 | uuid_unparse_lower(build->id, build->_id); | |
742 | ||
743 | return 0; | |
744 | } | |
745 | ||
a84624be MT |
746 | /* |
747 | Sets the default target | |
748 | */ | |
749 | static int pakfire_build_set_default_target(struct pakfire_build* build) { | |
a84624be MT |
750 | // Default to the current working directory |
751 | if (!*build->target) { | |
752 | char* cwd = getcwd(build->target, sizeof(build->target)); | |
753 | if (!cwd) { | |
754 | ERROR(build->pakfire, "getcwd() failed: %m\n"); | |
755 | return 1; | |
756 | } | |
757 | } | |
758 | ||
759 | return 0; | |
760 | } | |
761 | ||
f877fdfa MT |
762 | /* |
763 | Sets up a new cgroup for this build | |
764 | */ | |
765 | static int pakfire_build_setup_cgroup(struct pakfire_build* build) { | |
0a1284ff | 766 | struct pakfire_config* config = NULL; |
f877fdfa MT |
767 | char path[PATH_MAX]; |
768 | int r; | |
769 | ||
770 | // Compose path | |
771 | r = pakfire_string_format(path, "pakfire/build-%s", build->_id); | |
a60955af | 772 | if (r) { |
f877fdfa | 773 | ERROR(build->pakfire, "Could not compose path for cgroup: %m\n"); |
0a1284ff | 774 | goto ERROR; |
f877fdfa MT |
775 | } |
776 | ||
777 | // Create a new cgroup | |
778 | r = pakfire_cgroup_open(&build->cgroup, build->pakfire, path, | |
779 | PAKFIRE_CGROUP_ENABLE_ACCOUNTING); | |
780 | if (r) { | |
781 | ERROR(build->pakfire, "Could not create cgroup for build %s: %m\n", build->_id); | |
0a1284ff | 782 | goto ERROR; |
f877fdfa MT |
783 | } |
784 | ||
0a1284ff MT |
785 | // Fetch config |
786 | config = pakfire_get_config(build->pakfire); | |
787 | if (!config) | |
788 | goto ERROR; | |
789 | ||
882ae03b | 790 | // Guarantee some minimum memory |
0a1284ff MT |
791 | size_t memory_guaranteed = pakfire_config_get_bytes(config, "build", |
792 | "memory_guaranteed", PAKFIRE_BUILD_MEMORY_GUARANTEED); | |
793 | if (memory_guaranteed) { | |
794 | r = pakfire_cgroup_set_guaranteed_memory(build->cgroup, memory_guaranteed); | |
795 | if (r) | |
796 | goto ERROR; | |
797 | } | |
882ae03b | 798 | |
5b877e84 MT |
799 | // Limit memory |
800 | size_t memory_limit = pakfire_config_get_bytes(config, "build", "memory_limit", 0); | |
801 | if (memory_limit) { | |
802 | r = pakfire_cgroup_set_memory_limit(build->cgroup, memory_limit); | |
803 | if (r) | |
804 | goto ERROR; | |
805 | } | |
806 | ||
de4c8fe6 | 807 | // Set PID limit |
0a1284ff MT |
808 | size_t pid_limit = pakfire_config_get_int(config, "build", |
809 | "pid_limit", PAKFIRE_BUILD_PID_LIMIT); | |
810 | if (pid_limit) { | |
811 | r = pakfire_cgroup_set_pid_limit(build->cgroup, pid_limit); | |
812 | if (r) | |
813 | goto ERROR; | |
814 | } | |
de4c8fe6 | 815 | |
0a1284ff MT |
816 | ERROR: |
817 | if (config) | |
818 | pakfire_config_unref(config); | |
819 | ||
820 | return r; | |
abbad00b MT |
821 | } |
822 | ||
753ddf74 MT |
823 | /* |
824 | Sets up a new jail for this build | |
825 | */ | |
826 | static int pakfire_build_setup_jail(struct pakfire_build* build) { | |
827 | int r; | |
828 | ||
829 | // Create a new jail | |
830 | r = pakfire_jail_create(&build->jail, build->pakfire, 0); | |
831 | if (r) { | |
832 | ERROR(build->pakfire, "Could not create jail for build %s: %m\n", build->_id); | |
833 | return r; | |
834 | } | |
835 | ||
15503538 MT |
836 | // Connect the jail to our cgroup |
837 | r = pakfire_jail_set_cgroup(build->jail, build->cgroup); | |
838 | if (r) { | |
839 | ERROR(build->pakfire, "Could not set cgroup for jail: %m\n"); | |
840 | return r; | |
841 | } | |
842 | ||
753ddf74 MT |
843 | // Done |
844 | return 0; | |
845 | } | |
846 | ||
5a06668c MT |
847 | /* |
848 | Sets up the ccache for this build | |
849 | */ | |
850 | static int pakfire_build_setup_ccache(struct pakfire_build* build) { | |
851 | char path[PATH_MAX]; | |
852 | int r; | |
853 | ||
854 | // Check if we want a ccache | |
855 | if (pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_CCACHE)) { | |
856 | DEBUG(build->pakfire, "ccache usage has been disabled for this build\n"); | |
0570ccd2 MT |
857 | |
858 | // Set CCACHE_DISABLE=1 so that if ccache is installed, it will disable itself | |
859 | r = pakfire_jail_set_env(build->jail, "CCACHE_DISABLE", "1"); | |
860 | if (r) { | |
861 | ERROR(build->pakfire, "Could not disable ccache: %m\n"); | |
862 | return r; | |
863 | } | |
864 | ||
5a06668c MT |
865 | return 0; |
866 | } | |
867 | ||
868 | // Compose path | |
df1409ef MT |
869 | r = pakfire_cache_path(build->pakfire, path, "%s", "ccache"); |
870 | if (r) { | |
5a06668c MT |
871 | ERROR(build->pakfire, "Could not compose ccache path: %m\n"); |
872 | return 1; | |
873 | } | |
874 | ||
875 | DEBUG(build->pakfire, "Mounting ccache from %s\n", path); | |
876 | ||
877 | // Ensure path exists | |
878 | r = pakfire_mkdir(path, 0755); | |
879 | if (r && errno != EEXIST) { | |
880 | ERROR(build->pakfire, "Could not create ccache directory %s: %m\n", path); | |
881 | return r; | |
882 | } | |
883 | ||
884 | // Bind-mount the directory | |
885 | r = pakfire_jail_bind(build->jail, path, CCACHE_DIR, MS_NOSUID|MS_NOEXEC|MS_NODEV); | |
886 | if (r) { | |
887 | ERROR(build->pakfire, "Could not mount ccache: %m\n"); | |
888 | return r; | |
889 | } | |
890 | ||
891 | return 0; | |
892 | } | |
893 | ||
d8c8cdc4 | 894 | static int pakfire_build_setup_repo(struct pakfire_build* build) { |
d6451238 MT |
895 | char path[PATH_MAX] = "/var/tmp/.pakfire-build-repo.XXXXXX"; |
896 | char url[PATH_MAX]; | |
897 | int r; | |
898 | ||
899 | // Create a new repository | |
900 | r = pakfire_repo_create(&build->result, build->pakfire, PAKFIRE_REPO_RESULT); | |
901 | if (r) { | |
902 | ERROR(build->pakfire, "Could not create repository %s: %m", PAKFIRE_REPO_RESULT); | |
903 | return r; | |
904 | } | |
905 | ||
906 | // Set description | |
907 | pakfire_repo_set_description(build->result, _("Build Repository")); | |
908 | ||
909 | // Create a temporary directory | |
910 | const char* p = pakfire_mkdtemp(path); | |
911 | if (!p) { | |
912 | ERROR(build->pakfire, "Could not create a the build repository: %m\n"); | |
913 | return 1; | |
914 | } | |
915 | ||
916 | // Format the URL | |
917 | r = pakfire_string_format(url, "file://%s", path); | |
918 | if (r) | |
919 | return r; | |
920 | ||
921 | // Set the URL | |
922 | pakfire_repo_set_baseurl(build->result, url); | |
923 | ||
924 | return r; | |
925 | } | |
926 | ||
abbad00b MT |
927 | PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build, |
928 | struct pakfire* pakfire, const char* id, int flags) { | |
929 | int r; | |
930 | ||
931 | // Allocate build object | |
932 | struct pakfire_build* b = calloc(1, sizeof(*b)); | |
933 | if (!b) | |
934 | return 1; | |
935 | ||
936 | // Reference pakfire | |
937 | b->pakfire = pakfire_ref(pakfire); | |
938 | ||
939 | // Initialize reference counter | |
940 | b->nrefs = 1; | |
941 | ||
942 | // Copy flags | |
943 | b->flags = flags; | |
944 | ||
945 | // Parse ID | |
946 | r = pakfire_build_parse_id(b, id); | |
947 | if (r) | |
948 | goto ERROR; | |
949 | ||
d8c8cdc4 MT |
950 | // Setup repo |
951 | r = pakfire_build_setup_repo(b); | |
e545f6ec MT |
952 | if (r) |
953 | goto ERROR; | |
954 | ||
a84624be MT |
955 | // Set target |
956 | r = pakfire_build_set_default_target(b); | |
957 | if (r) | |
958 | goto ERROR; | |
959 | ||
f877fdfa MT |
960 | // Create cgroup |
961 | r = pakfire_build_setup_cgroup(b); | |
962 | if (r) | |
963 | goto ERROR; | |
964 | ||
753ddf74 MT |
965 | // Create jail |
966 | r = pakfire_build_setup_jail(b); | |
967 | if (r) | |
968 | goto ERROR; | |
969 | ||
5a06668c MT |
970 | // Setup ccache |
971 | r = pakfire_build_setup_ccache(b); | |
972 | if (r) | |
973 | goto ERROR; | |
974 | ||
abbad00b MT |
975 | *build = b; |
976 | return 0; | |
977 | ||
978 | ERROR: | |
979 | pakfire_build_free(b); | |
980 | return r; | |
981 | } | |
982 | ||
77a1964f | 983 | PAKFIRE_EXPORT struct pakfire_build* pakfire_build_ref(struct pakfire_build* build) { |
abbad00b MT |
984 | ++build->nrefs; |
985 | ||
986 | return build; | |
987 | } | |
988 | ||
77a1964f | 989 | PAKFIRE_EXPORT struct pakfire_build* pakfire_build_unref(struct pakfire_build* build) { |
abbad00b MT |
990 | if (--build->nrefs > 0) |
991 | return build; | |
992 | ||
993 | pakfire_build_free(build); | |
994 | return NULL; | |
995 | } | |
996 | ||
ea924657 MT |
997 | PAKFIRE_EXPORT int pakfire_build_set_target( |
998 | struct pakfire_build* build, const char* target) { | |
a60955af | 999 | return pakfire_string_set(build->target, target); |
ea924657 | 1000 | } |
57e2cf99 | 1001 | |
c0f2502a MT |
1002 | static int pakfire_build_install_packages(struct pakfire_build* build, |
1003 | int* snapshot_needs_update) { | |
1004 | char** packages = NULL; | |
1005 | int r = 1; | |
1006 | ||
1007 | // Fetch configuration | |
1008 | struct pakfire_config* config = pakfire_get_config(build->pakfire); | |
1009 | if (!config) { | |
1010 | ERROR(build->pakfire, "Could not fetch configuration: %m\n"); | |
1011 | r = 1; | |
1012 | goto ERROR; | |
1013 | } | |
1014 | ||
1015 | // Fetch build environment | |
1016 | const char* requires = pakfire_config_get(config, "build", "requires", NULL); | |
1017 | if (!requires) { | |
1018 | ERROR(build->pakfire, "No build requirements have been defined\n"); | |
1019 | goto ERROR; | |
1020 | } | |
1021 | ||
1022 | // Split requirements into packages | |
d973a13d | 1023 | packages = pakfire_string_split(requires, ','); |
c0f2502a MT |
1024 | if (!packages) |
1025 | goto ERROR; | |
1026 | ||
1027 | int changed = 0; | |
1028 | ||
1029 | // Install everything | |
1030 | r = pakfire_install(build->pakfire, 0, 0, (const char**)packages, NULL, 0, | |
1031 | &changed, NULL, NULL); | |
1032 | if (r) { | |
1033 | ERROR(build->pakfire, "Could not install build dependencies: %m\n"); | |
1034 | goto ERROR; | |
1035 | } | |
1036 | ||
1037 | // Mark snapshot as changed if new packages were installed | |
1038 | if (changed) | |
1039 | *snapshot_needs_update = 1; | |
1040 | ||
1041 | // Update everything | |
1042 | r = pakfire_sync(build->pakfire, 0, 0, &changed, NULL, NULL); | |
1043 | if (r) { | |
1044 | ERROR(build->pakfire, "Could not update packages: %m\n"); | |
1045 | goto ERROR; | |
1046 | } | |
1047 | ||
1048 | // Has anything changed? | |
1049 | if (changed) | |
1050 | *snapshot_needs_update = 1; | |
1051 | ||
1052 | // Success | |
1053 | r = 0; | |
1054 | ||
1055 | ERROR: | |
1056 | if (config) | |
1057 | pakfire_config_unref(config); | |
1058 | ||
1059 | if (packages) { | |
1060 | for (char** package = packages; *package; package++) | |
1061 | free(*package); | |
1062 | free(packages); | |
1063 | } | |
1064 | ||
1065 | return r; | |
1066 | } | |
1067 | ||
8dbb69f9 MT |
1068 | /* |
1069 | Initializes the build environment | |
1070 | */ | |
1071 | static int pakfire_build_init(struct pakfire_build* build) { | |
c0f2502a MT |
1072 | char path[PATH_MAX]; |
1073 | int r; | |
1074 | ||
8dbb69f9 MT |
1075 | // Don't do it again |
1076 | if (build->init) { | |
1077 | DEBUG(build->pakfire, "Build environment has already been initialized\n"); | |
1078 | return 0; | |
1079 | } | |
1080 | ||
1ea7b360 | 1081 | // Compose path for snapshot |
df1409ef MT |
1082 | r = pakfire_cache_path(build->pakfire, path, "%s", "snapshot.tar.zst"); |
1083 | if (r) { | |
1ea7b360 MT |
1084 | ERROR(build->pakfire, "Could not compose snapshot path: %m\n"); |
1085 | return 1; | |
1086 | } | |
1087 | ||
ffc2630d MT |
1088 | // Tells us whether we need to (re-)create the snapshot |
1089 | int snapshot_needs_update = 0; | |
c0f2502a | 1090 | |
1ea7b360 | 1091 | // Extract snapshot |
ffc2630d | 1092 | if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT)) { |
ffc2630d | 1093 | // Try restoring the snapshot |
4667a2ca MT |
1094 | r = pakfire_snapshot_restore(build->pakfire, path); |
1095 | if (r && errno != ENOENT) | |
1096 | return r; | |
c0f2502a MT |
1097 | } |
1098 | ||
c0f2502a MT |
1099 | // Install or update any build dependencies |
1100 | r = pakfire_build_install_packages(build, &snapshot_needs_update); | |
1101 | if (r) | |
1102 | return r; | |
1103 | ||
ffc2630d MT |
1104 | // Update the snapshot if there were changes |
1105 | if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT) && snapshot_needs_update) { | |
c0f2502a | 1106 | // Create a new snapshot |
4667a2ca | 1107 | r = pakfire_snapshot_create(build->pakfire, path); |
c0f2502a MT |
1108 | if (r) |
1109 | return r; | |
1110 | } | |
1111 | ||
8dbb69f9 MT |
1112 | // Mark as initialized |
1113 | build->init = 1; | |
1114 | ||
c0f2502a MT |
1115 | return 0; |
1116 | } | |
1117 | ||
e57188c0 MT |
1118 | static int pakfire_build_read_makefile(struct pakfire_build* build, |
1119 | struct pakfire_parser** parser, struct pakfire_package* package) { | |
e57188c0 MT |
1120 | char path[PATH_MAX]; |
1121 | int r; | |
1122 | ||
1123 | struct pakfire_parser_error* error = NULL; | |
1124 | ||
74468e4f MT |
1125 | const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA); |
1126 | const char* name = pakfire_package_get_string(package, PAKFIRE_PKG_NAME); | |
e57188c0 MT |
1127 | |
1128 | // Compose path to makefile | |
77e26129 | 1129 | r = pakfire_path(build->pakfire, path, "/usr/src/packages/%s/%s.nm", nevra, name); |
e57188c0 MT |
1130 | if (r < 0) |
1131 | return 1; | |
1132 | ||
1133 | // Read makefile | |
1134 | r = pakfire_read_makefile(parser, build->pakfire, path, &error); | |
1135 | if (r) { | |
1136 | if (error) { | |
1137 | ERROR(build->pakfire, "Could not parse makefile %s: %s\n", path, | |
1138 | pakfire_parser_error_get_message(error)); | |
1139 | } else { | |
1140 | ERROR(build->pakfire, "Could not parse makefile %s: %m\n", path); | |
1141 | } | |
1142 | ||
1143 | goto ERROR; | |
1144 | } | |
1145 | ||
6fc3956f | 1146 | // Set BUILDROOT |
5caf06e3 | 1147 | const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot); |
6fc3956f MT |
1148 | if (buildroot) |
1149 | pakfire_parser_set(*parser, NULL, "BUILDROOT", buildroot, 0); | |
1150 | ||
e57188c0 MT |
1151 | ERROR: |
1152 | if (error) | |
1153 | pakfire_parser_error_unref(error); | |
1154 | ||
1155 | return r; | |
1156 | } | |
1157 | ||
03dc1d52 MT |
1158 | static int pakfire_build_perform(struct pakfire_build* build, |
1159 | struct pakfire_parser* makefile) { | |
1160 | int r; | |
1161 | ||
b8786cdb MT |
1162 | // Prepare the build |
1163 | r = pakfire_build_stage(build, makefile, "prepare"); | |
1164 | if (r) | |
1165 | goto ERROR; | |
03dc1d52 | 1166 | |
b8786cdb MT |
1167 | // Perform the build |
1168 | r = pakfire_build_stage(build, makefile, "build"); | |
1169 | if (r) | |
1170 | goto ERROR; | |
1171 | ||
1172 | // Test the build | |
9605d535 MT |
1173 | if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_TESTS)) { |
1174 | r = pakfire_build_stage(build, makefile, "test"); | |
1175 | if (r) | |
1176 | goto ERROR; | |
1177 | } | |
b8786cdb MT |
1178 | |
1179 | // Install everything | |
1180 | r = pakfire_build_stage(build, makefile, "install"); | |
1181 | if (r) | |
1182 | goto ERROR; | |
03dc1d52 MT |
1183 | |
1184 | // Run post build scripts | |
b8786cdb MT |
1185 | r = pakfire_build_run_post_build_scripts(build); |
1186 | if (r) | |
1187 | goto ERROR; | |
1188 | ||
1189 | // Done! | |
1190 | return 0; | |
1191 | ||
1192 | ERROR: | |
1193 | // Drop to a shell for debugging | |
1194 | if (pakfire_has_flag(build->pakfire, PAKFIRE_BUILD_INTERACTIVE)) | |
1195 | pakfire_build_shell(build); | |
1196 | ||
1197 | return r; | |
03dc1d52 MT |
1198 | } |
1199 | ||
0f9bb14d MT |
1200 | static int __pakfire_build_unpackaged_file(struct pakfire* pakfire, |
1201 | struct pakfire_file* file, void* p) { | |
1202 | char* s = pakfire_file_dump(file); | |
6cb8b2b5 | 1203 | if (s) { |
0f9bb14d | 1204 | ERROR(pakfire, "%s\n", s); |
6cb8b2b5 MT |
1205 | free(s); |
1206 | } | |
0f9bb14d MT |
1207 | |
1208 | return 0; | |
1209 | } | |
1210 | ||
1211 | static int pakfire_build_check_unpackaged_files(struct pakfire_build* build) { | |
1212 | struct pakfire_filelist* filelist = NULL; | |
1213 | int r; | |
1214 | ||
1215 | // Create a new filelist | |
1216 | r = pakfire_filelist_create(&filelist, build->pakfire); | |
1217 | if (r) | |
1218 | goto ERROR; | |
1219 | ||
1220 | // Scan for all files in BUILDROOT | |
1221 | r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL); | |
1222 | if (r) | |
1223 | goto ERROR; | |
1224 | ||
1225 | const size_t length = pakfire_filelist_size(filelist); | |
1226 | ||
1227 | if (length) { | |
1228 | ERROR(build->pakfire, "Unpackaged files found:\n"); | |
1229 | ||
1230 | r = pakfire_filelist_walk(filelist, __pakfire_build_unpackaged_file, NULL); | |
1231 | if (r) | |
1232 | goto ERROR; | |
1233 | ||
1234 | // Report an error | |
1235 | r = 1; | |
1236 | } | |
1237 | ||
1238 | ERROR: | |
1239 | if (filelist) | |
1240 | pakfire_filelist_unref(filelist); | |
1241 | ||
1242 | return r; | |
1243 | } | |
1244 | ||
1245 | static int pakfire_build_post_check(struct pakfire_build* build) { | |
1246 | int r; | |
1247 | ||
1248 | // Check for unpackaged files | |
1249 | r = pakfire_build_check_unpackaged_files(build); | |
1250 | if (r) | |
1251 | return r; | |
1252 | ||
1253 | return 0; | |
1254 | } | |
1255 | ||
ea924657 | 1256 | PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) { |
33a8ba3c MT |
1257 | struct pakfire_archive* archive = NULL; |
1258 | struct pakfire_package* package = NULL; | |
e57188c0 | 1259 | struct pakfire_parser* makefile = NULL; |
6fc3956f | 1260 | char* buildroot = NULL; |
ea924657 | 1261 | int r; |
01b29895 | 1262 | |
6fc3956f | 1263 | // Set buildroot |
77e26129 MT |
1264 | r = pakfire_path(build->pakfire, build->buildroot, "%s", "/var/tmp/.pakfire-buildroot.XXXXXX"); |
1265 | if (r) | |
1266 | goto ERROR; | |
6fc3956f | 1267 | |
33a8ba3c MT |
1268 | // Open source archive |
1269 | r = pakfire_archive_open(&archive, build->pakfire, path); | |
1270 | if (r) { | |
1271 | ERROR(build->pakfire, "Could not open source archive %s: %m\n", path); | |
1272 | goto ERROR; | |
1273 | } | |
1274 | ||
1275 | // Fetch package metadata | |
1276 | r = pakfire_archive_make_package(archive, NULL, &package); | |
1277 | if (r) { | |
1278 | ERROR(build->pakfire, "Could not read package metadata: %m\n"); | |
1279 | goto ERROR; | |
1280 | } | |
1281 | ||
74468e4f | 1282 | const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA); |
33a8ba3c MT |
1283 | |
1284 | INFO(build->pakfire, "Building %s...\n", nevra); | |
01b29895 | 1285 | |
8dbb69f9 MT |
1286 | // Initialize the build environment |
1287 | r = pakfire_build_init(build); | |
1288 | if (r) | |
c0f2502a | 1289 | goto ERROR; |
c0f2502a | 1290 | |
01b29895 MT |
1291 | const char* packages[] = { |
1292 | path, NULL | |
1293 | }; | |
1294 | ||
1295 | // Install the package into the build environment | |
ea924657 | 1296 | r = pakfire_install(build->pakfire, 0, 0, packages, NULL, PAKFIRE_REQUEST_ESSENTIAL, |
99a56775 | 1297 | NULL, NULL, NULL); |
01b29895 | 1298 | if (r) { |
ea924657 | 1299 | ERROR(build->pakfire, "Could not install %s\n", path); |
33a8ba3c | 1300 | goto ERROR; |
01b29895 MT |
1301 | } |
1302 | ||
6fc3956f MT |
1303 | // Create BUILDROOT |
1304 | buildroot = pakfire_mkdtemp(build->buildroot); | |
1305 | if (!buildroot) { | |
1306 | ERROR(build->pakfire, "Could not create BUILDROOT: %m\n"); | |
1307 | goto ERROR; | |
1308 | } | |
1309 | ||
e57188c0 MT |
1310 | // Open the makefile |
1311 | r = pakfire_build_read_makefile(build, &makefile, package); | |
1312 | if (r) | |
01b29895 MT |
1313 | goto ERROR; |
1314 | ||
03dc1d52 MT |
1315 | // Perform the actual build |
1316 | r = pakfire_build_perform(build, makefile); | |
1317 | if (r) | |
1318 | goto ERROR; | |
1319 | ||
1320 | // Create the packages | |
1321 | r = pakfire_build_packages(build, makefile); | |
01b29895 | 1322 | if (r) { |
03dc1d52 | 1323 | ERROR(build->pakfire, "Could not create packages: %m\n"); |
01b29895 MT |
1324 | goto ERROR; |
1325 | } | |
1326 | ||
0f9bb14d MT |
1327 | // Perform post build checks |
1328 | r = pakfire_build_post_check(build); | |
1329 | if (r) | |
1330 | goto ERROR; | |
1331 | ||
01b29895 | 1332 | ERROR: |
e57188c0 MT |
1333 | if (makefile) |
1334 | pakfire_parser_unref(makefile); | |
33a8ba3c MT |
1335 | if (archive) |
1336 | pakfire_archive_unref(archive); | |
1337 | if (package) | |
1338 | pakfire_package_unref(package); | |
1339 | ||
6fc3956f MT |
1340 | // Cleanup buildroot |
1341 | if (buildroot) | |
1342 | pakfire_rmtree(buildroot, 0); | |
1343 | ||
01b29895 MT |
1344 | return r; |
1345 | } | |
1346 | ||
ea924657 MT |
1347 | /* |
1348 | Compatibility function to keep the legacy API. | |
1349 | */ | |
1350 | PAKFIRE_EXPORT int pakfire_build(struct pakfire* pakfire, const char* path, | |
1351 | const char* target, const char* id, int flags) { | |
1352 | struct pakfire_build* build = NULL; | |
1353 | int r; | |
1354 | ||
1355 | // Check if path is set | |
1356 | if (!path) { | |
1357 | errno = EINVAL; | |
1358 | return 1; | |
1359 | } | |
1360 | ||
1361 | // Create a new build environment | |
1362 | r = pakfire_build_create(&build, pakfire, id, flags); | |
1363 | if (r) | |
1364 | goto ERROR; | |
1365 | ||
1366 | // Set target | |
1367 | if (target) { | |
1368 | r = pakfire_build_set_target(build, target); | |
1369 | if (r) | |
1370 | goto ERROR; | |
1371 | } | |
1372 | ||
1373 | // Run build | |
1374 | r = pakfire_build_exec(build, path); | |
1375 | ||
1376 | ERROR: | |
1377 | if (build) | |
1378 | pakfire_build_unref(build); | |
1379 | ||
1380 | return r; | |
1381 | } | |
1382 | ||
5f6e42a2 MT |
1383 | int pakfire_build_clean(struct pakfire* pakfire, int flags) { |
1384 | struct pakfire_repo* local = NULL; | |
1385 | int r = 0; | |
22a0733e | 1386 | |
5f6e42a2 MT |
1387 | // Fetch local repository |
1388 | local = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL); | |
1389 | if (!local) { | |
1390 | ERROR(pakfire, "Could not find repository %s: %m\n", PAKFIRE_REPO_LOCAL); | |
22a0733e MT |
1391 | goto ERROR; |
1392 | } | |
1393 | ||
5f6e42a2 MT |
1394 | // Destroy everything in it |
1395 | r = pakfire_repo_clean(local, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY); | |
1396 | if (r) | |
22a0733e | 1397 | goto ERROR; |
22a0733e MT |
1398 | |
1399 | ERROR: | |
5f6e42a2 MT |
1400 | if (local) |
1401 | pakfire_repo_unref(local); | |
22a0733e MT |
1402 | |
1403 | return r; | |
1404 | } | |
1405 | ||
5f6e42a2 MT |
1406 | /* |
1407 | This is a convenience function that sets up a build environment and | |
1408 | then drops the user into an interactive shell. | |
1409 | */ | |
7f068a08 | 1410 | PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages) { |
5f6e42a2 | 1411 | struct pakfire_build* build = NULL; |
48dc31f7 | 1412 | int r; |
1a276007 | 1413 | |
5f6e42a2 | 1414 | // Create a new build environment |
db4f234f | 1415 | r = pakfire_build_create(&build, pakfire, NULL, PAKFIRE_BUILD_INTERACTIVE); |
5f6e42a2 MT |
1416 | if (r) { |
1417 | ERROR(pakfire, "Could not create build: %m\n"); | |
1418 | goto ERROR; | |
7f068a08 MT |
1419 | } |
1420 | ||
8dbb69f9 MT |
1421 | // Initialize the build environment |
1422 | r = pakfire_build_init(build); | |
1423 | if (r) | |
1424 | goto ERROR; | |
22a0733e | 1425 | |
5f6e42a2 MT |
1426 | // Install any additional packages |
1427 | if (packages) { | |
1428 | r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0, NULL, NULL, NULL); | |
1429 | if (r) | |
1430 | return r; | |
397371db MT |
1431 | } |
1432 | ||
5f6e42a2 MT |
1433 | // Run shell |
1434 | r = pakfire_build_shell(build); | |
397371db MT |
1435 | |
1436 | ERROR: | |
5f6e42a2 MT |
1437 | if (build) |
1438 | pakfire_build_unref(build); | |
397371db MT |
1439 | |
1440 | return r; | |
1441 | } |