]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/build.c
archive: Change type from PakfireArchive to struct pakfire_archive
[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>
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
45static 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 63static 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
108ERROR:
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
120int 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 190static 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
221ERROR:
222 if (script)
223 free(script);
224
225 return r;
226}
227
5b0b3dc2
MT
228static 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
245static 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
315ERROR:
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
331static 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 352static 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
432ERROR:
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 448static 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
501ERROR:
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 512static 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
545ERROR:
546 if (scriptlet)
547 pakfire_scriptlet_unref(scriptlet);
548 if (shell)
549 free(shell);
550
551 return r;
552}
553
657a5c72 554static 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 582static 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
653ERROR:
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 666static 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
696ERROR:
697 if (packages)
698 free(packages);
699
700 return r;
701}
702
657a5c72 703static 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
730ERROR:
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
742static 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
758static 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 775static 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
836ERROR:
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
846PAKFIRE_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
920ERROR:
01b29895
MT
921 return r;
922}
923
1a276007
MT
924PAKFIRE_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}