]>
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> | |
01b29895 | 22 | #include <glob.h> |
1a276007 | 23 | #include <stdlib.h> |
26452aef | 24 | #include <sys/mount.h> |
cee6363a | 25 | #include <unistd.h> |
57e2cf99 | 26 | #include <uuid/uuid.h> |
1a276007 MT |
27 | |
28 | #include <pakfire/build.h> | |
29 | #include <pakfire/execute.h> | |
30 | #include <pakfire/dist.h> | |
ae7968a7 | 31 | #include <pakfire/file.h> |
1a276007 | 32 | #include <pakfire/logging.h> |
4c07774f | 33 | #include <pakfire/package.h> |
48c6f2e7 | 34 | #include <pakfire/packager.h> |
1a276007 MT |
35 | #include <pakfire/parser.h> |
36 | #include <pakfire/private.h> | |
4651122b | 37 | #include <pakfire/repo.h> |
106d2edd | 38 | #include <pakfire/scriptlet.h> |
26452aef | 39 | #include <pakfire/snapshot.h> |
1a276007 MT |
40 | #include <pakfire/types.h> |
41 | #include <pakfire/util.h> | |
42 | ||
26452aef MT |
43 | #define CCACHE_DIR "/var/cache/ccache" |
44 | ||
1a276007 MT |
45 | static const char* stages[] = { |
46 | "prepare", | |
47 | "build", | |
48 | "test", | |
49 | "install", | |
50 | NULL, | |
51 | }; | |
52 | ||
53 | #define TEMPLATE \ | |
54 | "#!/bin/bash --login\n" \ | |
55 | "\n" \ | |
56 | "set -e\n" \ | |
57 | "set -x\n" \ | |
58 | "\n" \ | |
59 | "%%{_%s}\n" \ | |
60 | "\n" \ | |
61 | "exit 0\n" | |
62 | ||
26452aef | 63 | static int pakfire_build_install_packages(Pakfire pakfire, int* snapshot_needs_update) { |
26452aef MT |
64 | char** packages = NULL; |
65 | int r = 1; | |
66 | ||
67 | struct pakfire_config* config = pakfire_get_config(pakfire); | |
68 | ||
69 | // Fetch build environment | |
70 | const char* requires = pakfire_config_get(config, "build", "requires", NULL); | |
71 | if (!requires) { | |
72 | ERROR(pakfire, "No build requirements have been defined\n"); | |
73 | goto ERROR; | |
74 | } | |
75 | ||
76 | // Split requirements into packages | |
77 | packages = pakfire_split_string(requires, ','); | |
78 | if (!packages) | |
79 | goto ERROR; | |
80 | ||
b1a06c73 MT |
81 | int changed = 0; |
82 | ||
976d4999 | 83 | // Install everything |
cf69c5dc | 84 | r = pakfire_install(pakfire, 0, (const char**)packages, NULL, 0, &changed); |
976d4999 MT |
85 | if (r) { |
86 | ERROR(pakfire, "Could not install build dependencies\n"); | |
26452aef | 87 | goto ERROR; |
26452aef MT |
88 | } |
89 | ||
b1a06c73 MT |
90 | // Mark snapshot as changed if new packages were installed |
91 | if (changed) | |
92 | *snapshot_needs_update = 1; | |
26452aef | 93 | |
0a282d1b | 94 | // Update everything |
cf69c5dc | 95 | r = pakfire_sync(pakfire, 0, 0, &changed); |
0a282d1b MT |
96 | if (r) { |
97 | ERROR(pakfire, "Could not update packages: %m\n"); | |
98 | goto ERROR; | |
99 | } | |
100 | ||
101 | // Has anything changed? | |
102 | if (changed) | |
103 | *snapshot_needs_update = 1; | |
26452aef MT |
104 | |
105 | // Success | |
106 | r = 0; | |
107 | ||
108 | ERROR: | |
26452aef MT |
109 | if (packages) { |
110 | for (char** package = packages; *package; package++) | |
111 | free(*package); | |
112 | free(packages); | |
113 | } | |
114 | if (config) | |
115 | pakfire_config_unref(config); | |
116 | ||
117 | return r; | |
118 | } | |
119 | ||
120 | int pakfire_build_setup(Pakfire pakfire) { | |
121 | char path[PATH_MAX]; | |
122 | int r; | |
123 | ||
124 | // Mount ccache | |
125 | if (!pakfire_has_flag(pakfire, PAKFIRE_FLAGS_DISABLE_CCACHE)) { | |
126 | r = pakfire_make_cache_path(pakfire, path, "%s", "ccache"); | |
127 | if (r < 0) | |
128 | return r; | |
129 | ||
130 | // Ensure path exists | |
131 | r = pakfire_mkdir(path, 0); | |
132 | if (r && errno != EEXIST) { | |
133 | ERROR(pakfire, "Could not create ccache directory %s: %m\n", path); | |
134 | return r; | |
135 | } | |
136 | ||
137 | r = pakfire_bind(pakfire, path, CCACHE_DIR, MS_NOSUID|MS_NOEXEC|MS_NODEV); | |
138 | if (r) { | |
139 | ERROR(pakfire, "Could not mount ccache: %m\n"); | |
140 | return r; | |
141 | } | |
142 | } | |
143 | ||
144 | // Extract snapshot | |
145 | if (!pakfire_has_flag(pakfire, PAKFIRE_FLAGS_DISABLE_SNAPSHOT)) { | |
146 | r = pakfire_make_cache_path(pakfire, path, "%s", "snapshot.tar.zst"); | |
147 | if (r < 0) | |
148 | return r; | |
149 | ||
150 | // Open the snapshot | |
151 | FILE* f = fopen(path, "r"); | |
152 | ||
153 | // Try restoring the snapshot | |
154 | if (f) { | |
155 | r = pakfire_snapshot_restore(pakfire, f); | |
156 | if (r) { | |
157 | fclose(f); | |
158 | return r; | |
159 | } | |
160 | } | |
161 | ||
162 | // Tells us whether we need to (re-)create the snapshot | |
163 | int snapshot_needs_update = 0; | |
164 | ||
165 | // Install or update any build dependencies | |
166 | r = pakfire_build_install_packages(pakfire, &snapshot_needs_update); | |
167 | if (r) | |
168 | return r; | |
169 | ||
170 | if (snapshot_needs_update) { | |
171 | // Open snapshot file for writing | |
172 | f = fopen(path, "w"); | |
173 | if (!f) { | |
174 | ERROR(pakfire, "Could not open snapshot file for writing: %m\n"); | |
175 | return 1; | |
176 | } | |
177 | ||
178 | // Create a new snapshot | |
179 | r = pakfire_snapshot_create(pakfire, f); | |
180 | fclose(f); | |
181 | ||
182 | if (r) | |
183 | return r; | |
184 | } | |
185 | } | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
29f2151d | 190 | static int pakfire_build_run_script(Pakfire pakfire, const char* filename, const char* args[], |
8d0f3a35 MT |
191 | pakfire_execute_logging_callback logging_callback, void* data) { |
192 | char* script = NULL; | |
193 | size_t size = 0; | |
194 | char path[PATH_MAX]; | |
195 | ||
196 | DEBUG(pakfire, "Running build script '%s'...\n", filename); | |
197 | ||
198 | // Make the source path | |
199 | pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename); | |
200 | ||
201 | // Open the source script | |
202 | FILE* f = fopen(path, "r"); | |
203 | if (!f) { | |
b1772bfb | 204 | ERROR(pakfire, "Could not open %s: %m\n", path); |
8d0f3a35 MT |
205 | return 1; |
206 | } | |
207 | ||
208 | // Read the script into memory | |
209 | int r = pakfire_read_file_into_buffer(f, &script, &size); | |
210 | if (r) { | |
b1772bfb | 211 | ERROR(pakfire, "Could not read script into memory: %m\n"); |
8d0f3a35 MT |
212 | goto ERROR; |
213 | } | |
214 | ||
215 | // Execute it | |
29f2151d | 216 | r = pakfire_execute_script(pakfire, script, size, args, NULL, 0, logging_callback, data); |
8d0f3a35 MT |
217 | if (r) { |
218 | ERROR(pakfire, "Script '%s' failed with status %d\n", filename, r); | |
219 | } | |
220 | ||
221 | ERROR: | |
222 | if (script) | |
223 | free(script); | |
224 | ||
225 | return r; | |
226 | } | |
227 | ||
5b0b3dc2 MT |
228 | static int find_dependency(char** haystack, const char* needle) { |
229 | if (!needle) { | |
230 | errno = EINVAL; | |
231 | return -1; | |
232 | } | |
233 | ||
234 | if (!haystack) | |
235 | return 0; | |
236 | ||
237 | for (char** element = haystack; *element; element++) { | |
238 | if (strcmp(needle, *element) == 0) | |
239 | return 1; | |
240 | } | |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
245 | static int pakfire_build_find_dependencies(Pakfire pakfire, | |
31480bee | 246 | struct pakfire_package* pkg, PakfireFilelist filelist, const char* buildroot) { |
5b0b3dc2 MT |
247 | char** provides = NULL; |
248 | char** requires = NULL; | |
249 | char path[PATH_MAX]; | |
250 | ||
251 | // Allocate path to write the filelist to | |
252 | int r = pakfire_make_path(pakfire, path, "tmp/.pakfire-filelist.XXXXXX"); | |
253 | if (r < 0) | |
254 | return 1; | |
255 | ||
256 | // Create a temporary file | |
257 | FILE* f = pakfire_mktemp(path); | |
258 | if (!f) | |
259 | goto ERROR; | |
260 | ||
261 | // Write filelist to the temporary file | |
262 | r = pakfire_filelist_export(filelist, f); | |
263 | fclose(f); | |
264 | if (r) { | |
b1772bfb | 265 | ERROR(pakfire, "Could not export filelist: %m\n"); |
5b0b3dc2 MT |
266 | goto ERROR; |
267 | } | |
268 | ||
269 | const char* root = pakfire_get_path(pakfire); | |
270 | ||
271 | // Pass buildroot and the filelist as arguments | |
272 | const char* args[] = { | |
273 | buildroot, pakfire_path_relpath(root, path), NULL | |
274 | }; | |
275 | ||
276 | // Find all provides | |
277 | r = pakfire_build_run_script(pakfire, "find-provides", args, | |
278 | pakfire_execute_capture_stdout_to_array, &provides); | |
279 | if (r) { | |
280 | ERROR(pakfire, "find-provides returned with error %d\n", r); | |
281 | goto ERROR; | |
282 | } | |
283 | ||
284 | // Find all requires | |
285 | r = pakfire_build_run_script(pakfire, "find-requires", args, | |
286 | pakfire_execute_capture_stdout_to_array, &requires); | |
287 | if (r) { | |
288 | ERROR(pakfire, "find-requires returned with error %d\n", r); | |
289 | goto ERROR; | |
290 | } | |
291 | ||
292 | // Add all provides to the package | |
293 | if (provides) { | |
294 | for (char** element = provides; *element; element++) { | |
295 | DEBUG(pakfire, "Adding provides: %s\n", *element); | |
296 | pakfire_package_add_provides(pkg, *element); | |
297 | } | |
298 | } | |
299 | ||
300 | // Add all requires to the package | |
301 | if (requires) { | |
302 | for (char** element = requires; *element; element++) { | |
303 | // Skip adding this requirement if also provided by this package | |
304 | if (find_dependency(provides, *element)) | |
305 | continue; | |
306 | ||
307 | DEBUG(pakfire, "Adding requires: %s\n", *element); | |
308 | pakfire_package_add_requires(pkg, *element); | |
309 | } | |
310 | } | |
311 | ||
312 | // Success | |
313 | r = 0; | |
314 | ||
315 | ERROR: | |
316 | if (provides) { | |
317 | for (char** element = provides; *element; element++) | |
318 | free(*element); | |
319 | free(provides); | |
320 | } | |
321 | if (requires) { | |
322 | for (char** element = requires; *element; element++) | |
323 | free(*element); | |
324 | free(requires); | |
325 | } | |
326 | unlink(path); | |
327 | ||
328 | return r; | |
329 | } | |
330 | ||
73543ae3 MT |
331 | static int append_to_array(const char*** array, const char* s) { |
332 | unsigned int length = 0; | |
333 | ||
334 | // Determine the length of the existing array | |
335 | if (*array) { | |
336 | for (const char** element = *array; *element; element++) | |
337 | length++; | |
338 | } | |
339 | ||
340 | // Allocate space | |
341 | *array = reallocarray(*array, length + 2, sizeof(**array)); | |
342 | if (!*array) | |
343 | return 1; | |
344 | ||
345 | // Append s and terminate the array | |
346 | (*array)[length] = s; | |
347 | (*array)[length + 1] = NULL; | |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
657a5c72 | 352 | static int pakfire_build_package_add_files(Pakfire pakfire, struct pakfire_parser* makefile, |
31480bee | 353 | const char* buildroot, const char* namespace, struct pakfire_package* pkg, |
5b0b3dc2 | 354 | struct pakfire_packager* packager) { |
73543ae3 MT |
355 | PakfireFilelist filelist = NULL; |
356 | char path[PATH_MAX]; | |
357 | int r = 1; | |
358 | ||
359 | char** files = NULL; | |
360 | const char** includes = NULL; | |
361 | const char** excludes = NULL; | |
362 | ||
363 | // Fetch filelist from makefile | |
364 | files = pakfire_parser_get_split(makefile, namespace, "files", '\n'); | |
365 | ||
366 | // No files to package? | |
367 | if (!files) | |
368 | return 0; | |
369 | ||
73543ae3 MT |
370 | // Convert to absolute path |
371 | pakfire_make_path(pakfire, path, buildroot); | |
372 | ||
373 | // Split into includes and excludes | |
374 | for (char** file = files; *file; file++) { | |
375 | if (**file == '!') | |
376 | r = append_to_array(&excludes, *file); | |
377 | else | |
378 | r = append_to_array(&includes, *file); | |
379 | if (r) | |
380 | goto ERROR; | |
381 | } | |
382 | ||
383 | // Allocate a new filelist | |
384 | r = pakfire_filelist_create(&filelist, pakfire); | |
385 | if (r) | |
386 | goto ERROR; | |
387 | ||
388 | // Scan for files | |
389 | r = pakfire_filelist_scan(filelist, path, includes, excludes); | |
390 | if (r) | |
391 | goto ERROR; | |
392 | ||
ae7968a7 | 393 | const size_t length = pakfire_filelist_size(filelist); |
73543ae3 MT |
394 | DEBUG(pakfire, "%zu file(s) found\n", length); |
395 | ||
84556307 MT |
396 | // Nothing to do if the filelist is empty |
397 | if (!length) | |
398 | goto ERROR; | |
399 | ||
5b0b3dc2 MT |
400 | // Find dependencies |
401 | r = pakfire_build_find_dependencies(pakfire, pkg, filelist, buildroot); | |
402 | if (r) { | |
b1772bfb | 403 | ERROR(pakfire, "Finding dependencies failed: %m\n"); |
5b0b3dc2 MT |
404 | goto ERROR; |
405 | } | |
406 | ||
ae7968a7 MT |
407 | // Add all files to the package |
408 | for (unsigned int i = 0; i < length; i++) { | |
5803b5f6 | 409 | struct pakfire_file* file = pakfire_filelist_get(filelist, i); |
ae7968a7 MT |
410 | |
411 | // Add the file to the package | |
412 | r = pakfire_packager_add( | |
413 | packager, | |
414 | pakfire_file_get_abspath(file), | |
415 | pakfire_file_get_path(file) | |
416 | ); | |
417 | if (r) { | |
418 | pakfire_file_unref(file); | |
419 | goto ERROR; | |
420 | } | |
421 | ||
3fca5032 MT |
422 | // Remove the file after it was packaged |
423 | r = pakfire_file_cleanup(file); | |
424 | if (r) { | |
425 | pakfire_file_unref(file); | |
426 | goto ERROR; | |
427 | } | |
428 | ||
ae7968a7 MT |
429 | pakfire_file_unref(file); |
430 | } | |
73543ae3 MT |
431 | |
432 | ERROR: | |
433 | if (filelist) | |
434 | pakfire_filelist_unref(filelist); | |
435 | if (files) { | |
436 | for (char** file = files; *file; file++) | |
437 | free(*file); | |
438 | free(files); | |
439 | } | |
440 | if (includes) | |
441 | free(includes); | |
442 | if (excludes) | |
443 | free(excludes); | |
73543ae3 MT |
444 | |
445 | return r; | |
446 | } | |
447 | ||
31480bee | 448 | static int pakfire_build_add_scriptlet_requires(Pakfire pakfire, struct pakfire_package* pkg, |
106d2edd MT |
449 | struct pakfire_scriptlet* scriptlet) { |
450 | char** prerequires = NULL; | |
451 | char path[PATH_MAX]; | |
452 | size_t size; | |
453 | int r; | |
454 | ||
455 | const char* root = pakfire_get_path(pakfire); | |
456 | ||
457 | // Make filename | |
458 | r = pakfire_make_path(pakfire, path, "tmp/.pakfire-scriptlet.XXXXXX"); | |
459 | if (r < 0) | |
460 | return r; | |
461 | ||
462 | // Fetch scriptlet payload | |
463 | const char* data = pakfire_scriptlet_get_data(scriptlet, &size); | |
464 | ||
465 | // Create a temporary file | |
466 | FILE* f = pakfire_mktemp(path); | |
467 | if (!f) | |
468 | return 1; | |
469 | ||
470 | // Write scriptlet | |
471 | ssize_t bytes_written = fwrite(data, 1, size, f); | |
472 | if (bytes_written < 0) { | |
473 | ERROR(pakfire, "Could not write to %s: %m\n", path); | |
474 | fclose(f); | |
475 | goto ERROR; | |
476 | } | |
477 | ||
478 | // Close file | |
479 | fclose(f); | |
480 | ||
481 | // Build commandline | |
482 | const char* args[] = { | |
483 | pakfire_path_relpath(root, path), | |
484 | NULL, | |
485 | }; | |
486 | ||
487 | // Find all pre-requires | |
488 | r = pakfire_build_run_script(pakfire, "find-prerequires", args, | |
489 | pakfire_execute_capture_stdout_to_array, &prerequires); | |
490 | if (r) | |
491 | goto ERROR; | |
492 | ||
493 | // Add all pre-requires to the package | |
494 | if (prerequires) { | |
495 | for (char** element = prerequires; *element; element++) { | |
496 | DEBUG(pakfire, "Adding pre-requires: %s\n", *element); | |
497 | pakfire_package_add_prerequires(pkg, *element); | |
498 | } | |
499 | } | |
500 | ||
501 | ERROR: | |
502 | if (r && *path) | |
503 | unlink(path); | |
504 | if (prerequires) { | |
505 | for (char** element = prerequires; *element; element++) | |
506 | free(*element); | |
507 | free(prerequires); | |
508 | } | |
509 | return r; | |
510 | } | |
511 | ||
31480bee | 512 | static int pakfire_build_package_add_scriptlet(Pakfire pakfire, struct pakfire_package* pkg, |
106d2edd MT |
513 | struct pakfire_packager* packager, const char* type, const char* data) { |
514 | struct pakfire_scriptlet* scriptlet = NULL; | |
515 | char* shell = NULL; | |
516 | int r; | |
517 | ||
518 | // Wrap scriptlet into a shell script | |
519 | r = asprintf(&shell, "#!/bin/sh\n\nset -e\n\n%s\n\nexit 0\n", data); | |
520 | if (r < 0) | |
521 | goto ERROR; | |
522 | ||
523 | // Create a scriptlet | |
524 | r = pakfire_scriptlet_create(&scriptlet, pakfire, type, shell, 0); | |
525 | if (r) | |
526 | goto ERROR; | |
527 | ||
528 | // Add it to the package | |
529 | r = pakfire_packager_add_scriptlet(packager, scriptlet); | |
530 | if (r) { | |
531 | ERROR(pakfire, "Could not add scriptlet %s\n", type); | |
532 | goto ERROR; | |
533 | } | |
534 | ||
535 | // Add scriptlet requirements | |
536 | r = pakfire_build_add_scriptlet_requires(pakfire, pkg, scriptlet); | |
537 | if (r) { | |
b1772bfb | 538 | ERROR(pakfire, "Could not add scriptlet requirements: %m\n"); |
106d2edd MT |
539 | goto ERROR; |
540 | } | |
541 | ||
542 | // Success | |
543 | r = 0; | |
544 | ||
545 | ERROR: | |
546 | if (scriptlet) | |
547 | pakfire_scriptlet_unref(scriptlet); | |
548 | if (shell) | |
549 | free(shell); | |
550 | ||
551 | return r; | |
552 | } | |
553 | ||
657a5c72 | 554 | static int pakfire_build_package_add_scriptlets(Pakfire pakfire, struct pakfire_parser* makefile, |
31480bee | 555 | const char* namespace, struct pakfire_package* pkg, struct pakfire_packager* packager) { |
106d2edd MT |
556 | char name[NAME_MAX]; |
557 | int r; | |
558 | ||
559 | for (const char** type = pakfire_scriptlet_types; *type; type++) { | |
560 | r = pakfire_string_format(name, "scriptlet:%s", *type); | |
561 | if (r < 0) | |
562 | return r; | |
563 | ||
564 | // Fetch the scriptlet | |
565 | char* data = pakfire_parser_get(makefile, namespace, name); | |
566 | if (!data) | |
567 | continue; | |
568 | ||
569 | // Add it to the package | |
570 | r = pakfire_build_package_add_scriptlet(pakfire, pkg, packager, *type, data); | |
571 | if (r) { | |
572 | free(data); | |
573 | return r; | |
574 | } | |
575 | ||
576 | free(data); | |
577 | } | |
578 | ||
579 | return 0; | |
580 | } | |
581 | ||
657a5c72 | 582 | static int pakfire_build_package(Pakfire pakfire, struct pakfire_parser* makefile, |
57e2cf99 | 583 | uuid_t* build_id, const char* buildroot, const char* namespace, const char* target) { |
4651122b | 584 | struct pakfire_repo* repo = NULL; |
31480bee | 585 | struct pakfire_package* pkg = NULL; |
48c6f2e7 | 586 | struct pakfire_packager* packager = NULL; |
73543ae3 | 587 | |
a50bde9c MT |
588 | int r = 1; |
589 | ||
590 | // Expand the handle into the package name | |
4c07774f | 591 | char* name = pakfire_parser_expand(makefile, "packages", namespace); |
a50bde9c | 592 | if (!name) { |
b1772bfb | 593 | ERROR(pakfire, "Could not get package name: %m\n"); |
a50bde9c MT |
594 | goto ERROR; |
595 | } | |
596 | ||
597 | INFO(pakfire, "Building package '%s'...\n", name); | |
598 | ||
4c07774f MT |
599 | // Fetch build architecture |
600 | const char* arch = pakfire_get_arch(pakfire); | |
601 | if (!arch) | |
602 | goto ERROR; | |
603 | ||
604 | // Fetch the dummy repository | |
605 | repo = pakfire_get_repo(pakfire, "@dummy"); | |
606 | if (!repo) | |
607 | goto ERROR; | |
608 | ||
609 | // Fetch package from makefile | |
610 | r = pakfire_parser_create_package(makefile, &pkg, repo, namespace, arch); | |
611 | if (r) { | |
b1772bfb | 612 | ERROR(pakfire, "Could not create package from makefile: %m\n"); |
4c07774f MT |
613 | goto ERROR; |
614 | } | |
615 | ||
7996020a | 616 | // Set build ID |
57e2cf99 | 617 | pakfire_package_set_build_id_from_uuid(pkg, build_id); |
7996020a | 618 | |
48c6f2e7 MT |
619 | // Create a packager |
620 | r = pakfire_packager_create(&packager, pkg); | |
621 | if (r) | |
622 | goto ERROR; | |
623 | ||
73543ae3 | 624 | // Add files |
5b0b3dc2 MT |
625 | r = pakfire_build_package_add_files(pakfire, makefile, buildroot, namespace, |
626 | pkg, packager); | |
73543ae3 MT |
627 | if (r) |
628 | goto ERROR; | |
629 | ||
106d2edd MT |
630 | // Add scriptlets |
631 | r = pakfire_build_package_add_scriptlets(pakfire, makefile, namespace, pkg, packager); | |
632 | if (r) | |
633 | goto ERROR; | |
634 | ||
ae7968a7 MT |
635 | // Write the finished package |
636 | r = pakfire_packager_finish_to_directory(packager, target); | |
637 | if (r) { | |
b1772bfb | 638 | ERROR(pakfire, "pakfire_packager_finish() failed: %m\n"); |
ae7968a7 MT |
639 | goto ERROR; |
640 | } | |
641 | ||
4c07774f | 642 | #ifdef ENABLE_DEBUG |
23e22751 | 643 | char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG); |
4c07774f MT |
644 | if (dump) { |
645 | DEBUG(pakfire, "%s\n", dump); | |
646 | free(dump); | |
647 | } | |
648 | #endif | |
a50bde9c MT |
649 | |
650 | // Success | |
651 | r = 0; | |
652 | ||
653 | ERROR: | |
48c6f2e7 MT |
654 | if (packager) |
655 | pakfire_packager_unref(packager); | |
4c07774f MT |
656 | if (repo) |
657 | pakfire_repo_unref(repo); | |
658 | if (pkg) | |
659 | pakfire_package_unref(pkg); | |
a50bde9c MT |
660 | if (name) |
661 | free(name); | |
662 | ||
663 | return r; | |
664 | } | |
665 | ||
657a5c72 | 666 | static int pakfire_build_packages(Pakfire pakfire, struct pakfire_parser* makefile, |
57e2cf99 | 667 | uuid_t* build_id, const char* buildroot, const char* target) { |
a50bde9c MT |
668 | DEBUG(pakfire, "Creating packages..."); |
669 | int r = 1; | |
670 | ||
671 | // Fetch a list all all packages | |
672 | char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*"); | |
673 | if (!packages) { | |
b1772bfb | 674 | ERROR(pakfire, "Could not find any packages: %m\n"); |
a50bde9c MT |
675 | goto ERROR; |
676 | } | |
677 | ||
678 | unsigned int num_packages = 0; | |
679 | ||
680 | // Count how many packages we have | |
681 | for (char** package = packages; *package; package++) | |
682 | num_packages++; | |
683 | ||
684 | DEBUG(pakfire, "Found %d package(s)\n", num_packages); | |
685 | ||
686 | // Build packages in reverse order | |
687 | for (int i = num_packages - 1; i >= 0; i--) { | |
57e2cf99 | 688 | r = pakfire_build_package(pakfire, makefile, build_id, buildroot, packages[i], target); |
a50bde9c MT |
689 | if (r) |
690 | goto ERROR; | |
691 | } | |
692 | ||
693 | // Success | |
694 | r = 0; | |
695 | ||
696 | ERROR: | |
697 | if (packages) | |
698 | free(packages); | |
699 | ||
700 | return r; | |
701 | } | |
702 | ||
657a5c72 | 703 | static int pakfire_build_stage(Pakfire pakfire, struct pakfire_parser* makefile, const char* stage, |
7d999600 | 704 | pakfire_execute_logging_callback logging_callback, void* data) { |
1a276007 MT |
705 | char template[1024]; |
706 | ||
707 | // Prepare template for this stage | |
708 | int r = pakfire_string_format(template, TEMPLATE, stage); | |
709 | if (r < 0) | |
710 | return r; | |
711 | ||
2ffd21f9 MT |
712 | // Fetch the environment |
713 | char** envp = pakfire_parser_make_environ(makefile); | |
714 | ||
1a276007 MT |
715 | // Create the build script |
716 | char* script = pakfire_parser_expand(makefile, "build", template); | |
717 | if (!script) { | |
b1772bfb | 718 | ERROR(pakfire, "Could not generate the build script for stage '%s': %m\n", stage); |
1a276007 MT |
719 | goto ERROR; |
720 | } | |
721 | ||
722 | INFO(pakfire, "Running build stage '%s'\n", stage); | |
723 | ||
2ffd21f9 | 724 | r = pakfire_execute_script(pakfire, script, strlen(script), NULL, envp, 0, |
7d999600 | 725 | logging_callback, data); |
1a276007 MT |
726 | if (r) { |
727 | ERROR(pakfire, "Build stage '%s' failed with status %d\n", stage, r); | |
728 | } | |
729 | ||
730 | ERROR: | |
2ffd21f9 MT |
731 | if (envp) { |
732 | for (char** e = envp; *e; e++) | |
733 | free(*e); | |
734 | free(envp); | |
735 | } | |
1a276007 MT |
736 | if (script) |
737 | free(script); | |
738 | ||
739 | return r; | |
740 | } | |
741 | ||
39f45b30 MT |
742 | static const char* post_build_scripts[] = { |
743 | "remove-static-libs", | |
1f62ffae | 744 | "check-symlinks", |
022b794e | 745 | "check-unsafe-files", |
0b5f0bbc | 746 | "check-libraries", |
16043831 | 747 | "check-rpaths", |
99aee237 | 748 | "check-buildroot", |
7e1fec6f | 749 | "check-include", |
dd864160 | 750 | "check-hardening", |
ae703321 | 751 | "check-interpreters", |
2504194a | 752 | "check-fhs", |
39f45b30 | 753 | "compress-man-pages", |
ffb65de6 | 754 | "strip", |
39f45b30 MT |
755 | NULL, |
756 | }; | |
757 | ||
758 | static int pakfire_build_run_post_build_scripts(Pakfire pakfire, const char* buildroot, | |
759 | pakfire_execute_logging_callback logging_callback, void* data) { | |
760 | // Set default arguments for build scripts | |
761 | const char* args[] = { | |
762 | buildroot, NULL | |
763 | }; | |
764 | ||
765 | // Run them one by one | |
766 | for (const char** script = post_build_scripts; *script; script++) { | |
767 | int r = pakfire_build_run_script(pakfire, *script, args, logging_callback, data); | |
768 | if (r) | |
769 | return r; | |
770 | } | |
771 | ||
772 | return 0; | |
773 | } | |
774 | ||
01b29895 | 775 | static int pakfire_build_makefile(Pakfire pakfire, const char* path, const char* target, |
57e2cf99 MT |
776 | uuid_t* build_id, int flags, |
777 | pakfire_execute_logging_callback logging_callback, void* data) { | |
657a5c72 | 778 | struct pakfire_parser* makefile = NULL; |
3d4011eb | 779 | char buildroot[PATH_MAX]; |
1a276007 | 780 | struct pakfire_parser_error* error = NULL; |
01b29895 | 781 | int r = 1; |
f0893704 | 782 | |
3d4011eb MT |
783 | const char* root = pakfire_get_path(pakfire); |
784 | ||
785 | // Create BUILDROOT | |
786 | pakfire_make_path(pakfire, buildroot, "/tmp/.buildroot.XXXXXX"); | |
787 | if (!pakfire_mkdtemp(buildroot)) | |
f0893704 | 788 | goto ERROR; |
3d4011eb MT |
789 | |
790 | // Make relative BUILDROOT path | |
791 | const char* buildroot_rel = pakfire_path_relpath(root, buildroot); | |
792 | if (!buildroot_rel) | |
f0893704 | 793 | goto ERROR; |
3d4011eb | 794 | |
1a276007 | 795 | // Read makefile |
3d4011eb | 796 | r = pakfire_read_makefile(&makefile, pakfire, path, &error); |
1a276007 MT |
797 | if (r) { |
798 | if (error) { | |
799 | ERROR(pakfire, "Could not parse makefile %s: %s\n", path, | |
800 | pakfire_parser_error_get_message(error)); | |
801 | pakfire_parser_error_unref(error); | |
802 | } else { | |
b1772bfb | 803 | ERROR(pakfire, "Could not parse makefile %s: %m\n", path); |
1a276007 MT |
804 | } |
805 | ||
806 | goto ERROR; | |
807 | } | |
808 | ||
3d4011eb | 809 | // Set BUILDROOT |
fea2e884 | 810 | pakfire_parser_set(makefile, NULL, "BUILDROOT", buildroot_rel, 0); |
3d4011eb | 811 | |
1a276007 MT |
812 | // Run through all build stages |
813 | for (const char** stage = stages; *stage; stage++) { | |
7d999600 | 814 | int r = pakfire_build_stage(pakfire, makefile, *stage, logging_callback, data); |
1a276007 MT |
815 | if (r) { |
816 | // Drop to a shell for debugging | |
817 | if (flags & PAKFIRE_BUILD_INTERACTIVE) | |
818 | pakfire_shell(pakfire); | |
819 | ||
820 | goto ERROR; | |
821 | } | |
822 | } | |
823 | ||
39f45b30 MT |
824 | // Run post build scripts |
825 | r = pakfire_build_run_post_build_scripts(pakfire, buildroot_rel, logging_callback, data); | |
8d0f3a35 MT |
826 | if (r) |
827 | goto ERROR; | |
828 | ||
a50bde9c | 829 | // Create the packages |
57e2cf99 | 830 | r = pakfire_build_packages(pakfire, makefile, build_id, buildroot_rel, target); |
a50bde9c | 831 | if (r) { |
b1772bfb | 832 | ERROR(pakfire, "Could not create packages: %m\n"); |
a50bde9c MT |
833 | goto ERROR; |
834 | } | |
1a276007 MT |
835 | |
836 | ERROR: | |
837 | if (makefile) | |
838 | pakfire_parser_unref(makefile); | |
839 | ||
3d4011eb MT |
840 | // Remove buildroot |
841 | pakfire_rmtree(buildroot, 0); | |
842 | ||
1a276007 MT |
843 | return r; |
844 | } | |
845 | ||
01b29895 MT |
846 | PAKFIRE_EXPORT int pakfire_build(Pakfire pakfire, const char* path, |
847 | const char* target, const char* id, int flags, | |
848 | pakfire_execute_logging_callback logging_callback, void* data) { | |
849 | char makefiles[PATH_MAX]; | |
cee6363a | 850 | char cwd[PATH_MAX]; |
57e2cf99 | 851 | uuid_t build_id; |
01b29895 MT |
852 | glob_t buffer; |
853 | int r = 1; | |
854 | ||
855 | // Check for valid input | |
856 | if (!path) { | |
857 | errno = EINVAL; | |
858 | return 1; | |
859 | } | |
860 | ||
57e2cf99 MT |
861 | // Try parsing the Build ID |
862 | if (id) { | |
863 | r = uuid_parse(id, build_id); | |
864 | if (r) | |
865 | return r; | |
866 | ||
867 | // Otherwise initialize the Build ID with something random | |
868 | } else { | |
869 | uuid_generate_random(build_id); | |
870 | } | |
871 | ||
01b29895 MT |
872 | // The default target is the local repository path |
873 | if (!target) { | |
4651122b | 874 | struct pakfire_repo* repo = pakfire_get_repo(pakfire, "@local"); |
cee6363a MT |
875 | if (repo) { |
876 | target = pakfire_repo_get_path(repo); | |
877 | pakfire_repo_unref(repo); | |
01b29895 | 878 | |
cee6363a MT |
879 | // If the repository could not be found, just write to the cwd |
880 | } else { | |
881 | target = getcwd(cwd, sizeof(cwd)); | |
882 | } | |
01b29895 MT |
883 | } |
884 | ||
01b29895 MT |
885 | const char* packages[] = { |
886 | path, NULL | |
887 | }; | |
888 | ||
889 | // Install the package into the build environment | |
cf69c5dc | 890 | r = pakfire_install(pakfire, 0, packages, NULL, 0, NULL); |
01b29895 MT |
891 | if (r) { |
892 | ERROR(pakfire, "Could not install %s\n", path); | |
893 | goto ERROR; | |
894 | } | |
895 | ||
896 | // Where are the makefiles located? | |
897 | r = pakfire_make_path(pakfire, makefiles, "/usr/src/packages/*/*.nm"); | |
898 | if (r < 0) | |
899 | goto ERROR; | |
900 | ||
901 | // Find all makefiles | |
902 | r = glob(makefiles, 0, NULL, &buffer); | |
903 | if (r) { | |
904 | ERROR(pakfire, "glob() on %s failed: %m\n", makefiles); | |
b631c2f2 | 905 | globfree(&buffer); |
01b29895 MT |
906 | goto ERROR; |
907 | } | |
908 | ||
909 | // Iterate over all makefiles | |
910 | for (unsigned int i = 0; i < buffer.gl_pathc; i++) { | |
57e2cf99 | 911 | r = pakfire_build_makefile(pakfire, buffer.gl_pathv[i], target, &build_id, flags, |
01b29895 | 912 | logging_callback, data); |
b631c2f2 MT |
913 | if (r) { |
914 | ERROR(pakfire, "Could not build %s: %m\n", buffer.gl_pathv[i]); | |
915 | globfree(&buffer); | |
01b29895 | 916 | goto ERROR; |
b631c2f2 | 917 | } |
01b29895 MT |
918 | } |
919 | ||
920 | ERROR: | |
01b29895 MT |
921 | return r; |
922 | } | |
923 | ||
1a276007 MT |
924 | PAKFIRE_EXPORT int pakfire_shell(Pakfire pakfire) { |
925 | const char* argv[] = { | |
926 | "/bin/bash", "--login", NULL, | |
927 | }; | |
928 | ||
929 | const int flags = | |
930 | PAKFIRE_EXECUTE_INTERACTIVE | PAKFIRE_EXECUTE_ENABLE_NETWORK; | |
931 | ||
932 | return pakfire_execute(pakfire, argv, NULL, flags, NULL, NULL); | |
933 | } |