]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/libpakfire/build.c
filelist: Change type from PakfireFilelist to struct pakfire_filelist
[people/ms/pakfire.git] / src / libpakfire / build.c
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 <glob.h>
23 #include <stdlib.h>
24 #include <sys/mount.h>
25 #include <unistd.h>
26 #include <uuid/uuid.h>
27
28 #include <pakfire/build.h>
29 #include <pakfire/execute.h>
30 #include <pakfire/dist.h>
31 #include <pakfire/file.h>
32 #include <pakfire/logging.h>
33 #include <pakfire/package.h>
34 #include <pakfire/packager.h>
35 #include <pakfire/parser.h>
36 #include <pakfire/private.h>
37 #include <pakfire/repo.h>
38 #include <pakfire/scriptlet.h>
39 #include <pakfire/snapshot.h>
40 #include <pakfire/types.h>
41 #include <pakfire/util.h>
42
43 #define CCACHE_DIR "/var/cache/ccache"
44
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
63 static int pakfire_build_install_packages(Pakfire pakfire, int* snapshot_needs_update) {
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
81 int changed = 0;
82
83 // Install everything
84 r = pakfire_install(pakfire, 0, (const char**)packages, NULL, 0, &changed);
85 if (r) {
86 ERROR(pakfire, "Could not install build dependencies\n");
87 goto ERROR;
88 }
89
90 // Mark snapshot as changed if new packages were installed
91 if (changed)
92 *snapshot_needs_update = 1;
93
94 // Update everything
95 r = pakfire_sync(pakfire, 0, 0, &changed);
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;
104
105 // Success
106 r = 0;
107
108 ERROR:
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
190 static int pakfire_build_run_script(Pakfire pakfire, const char* filename, const char* args[],
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) {
204 ERROR(pakfire, "Could not open %s: %m\n", path);
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) {
211 ERROR(pakfire, "Could not read script into memory: %m\n");
212 goto ERROR;
213 }
214
215 // Execute it
216 r = pakfire_execute_script(pakfire, script, size, args, NULL, 0, logging_callback, data);
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
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,
246 struct pakfire_package* pkg, struct pakfire_filelist* filelist, const char* buildroot) {
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) {
265 ERROR(pakfire, "Could not export filelist: %m\n");
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
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
352 static int pakfire_build_package_add_files(Pakfire pakfire, struct pakfire_parser* makefile,
353 const char* buildroot, const char* namespace, struct pakfire_package* pkg,
354 struct pakfire_packager* packager) {
355 struct pakfire_filelist* 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
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
393 const size_t length = pakfire_filelist_size(filelist);
394 DEBUG(pakfire, "%zu file(s) found\n", length);
395
396 // Nothing to do if the filelist is empty
397 if (!length)
398 goto ERROR;
399
400 // Find dependencies
401 r = pakfire_build_find_dependencies(pakfire, pkg, filelist, buildroot);
402 if (r) {
403 ERROR(pakfire, "Finding dependencies failed: %m\n");
404 goto ERROR;
405 }
406
407 // Add all files to the package
408 for (unsigned int i = 0; i < length; i++) {
409 struct pakfire_file* file = pakfire_filelist_get(filelist, i);
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
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
429 pakfire_file_unref(file);
430 }
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);
444
445 return r;
446 }
447
448 static int pakfire_build_add_scriptlet_requires(Pakfire pakfire, struct pakfire_package* pkg,
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
512 static int pakfire_build_package_add_scriptlet(Pakfire pakfire, struct pakfire_package* pkg,
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) {
538 ERROR(pakfire, "Could not add scriptlet requirements: %m\n");
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
554 static int pakfire_build_package_add_scriptlets(Pakfire pakfire, struct pakfire_parser* makefile,
555 const char* namespace, struct pakfire_package* pkg, struct pakfire_packager* packager) {
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
582 static int pakfire_build_package(Pakfire pakfire, struct pakfire_parser* makefile,
583 uuid_t* build_id, const char* buildroot, const char* namespace, const char* target) {
584 struct pakfire_repo* repo = NULL;
585 struct pakfire_package* pkg = NULL;
586 struct pakfire_packager* packager = NULL;
587
588 int r = 1;
589
590 // Expand the handle into the package name
591 char* name = pakfire_parser_expand(makefile, "packages", namespace);
592 if (!name) {
593 ERROR(pakfire, "Could not get package name: %m\n");
594 goto ERROR;
595 }
596
597 INFO(pakfire, "Building package '%s'...\n", name);
598
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) {
612 ERROR(pakfire, "Could not create package from makefile: %m\n");
613 goto ERROR;
614 }
615
616 // Set build ID
617 pakfire_package_set_build_id_from_uuid(pkg, build_id);
618
619 // Create a packager
620 r = pakfire_packager_create(&packager, pkg);
621 if (r)
622 goto ERROR;
623
624 // Add files
625 r = pakfire_build_package_add_files(pakfire, makefile, buildroot, namespace,
626 pkg, packager);
627 if (r)
628 goto ERROR;
629
630 // Add scriptlets
631 r = pakfire_build_package_add_scriptlets(pakfire, makefile, namespace, pkg, packager);
632 if (r)
633 goto ERROR;
634
635 // Write the finished package
636 r = pakfire_packager_finish_to_directory(packager, target);
637 if (r) {
638 ERROR(pakfire, "pakfire_packager_finish() failed: %m\n");
639 goto ERROR;
640 }
641
642 #ifdef ENABLE_DEBUG
643 char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
644 if (dump) {
645 DEBUG(pakfire, "%s\n", dump);
646 free(dump);
647 }
648 #endif
649
650 // Success
651 r = 0;
652
653 ERROR:
654 if (packager)
655 pakfire_packager_unref(packager);
656 if (repo)
657 pakfire_repo_unref(repo);
658 if (pkg)
659 pakfire_package_unref(pkg);
660 if (name)
661 free(name);
662
663 return r;
664 }
665
666 static int pakfire_build_packages(Pakfire pakfire, struct pakfire_parser* makefile,
667 uuid_t* build_id, const char* buildroot, const char* target) {
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) {
674 ERROR(pakfire, "Could not find any packages: %m\n");
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--) {
688 r = pakfire_build_package(pakfire, makefile, build_id, buildroot, packages[i], target);
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
703 static int pakfire_build_stage(Pakfire pakfire, struct pakfire_parser* makefile, const char* stage,
704 pakfire_execute_logging_callback logging_callback, void* data) {
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
712 // Fetch the environment
713 char** envp = pakfire_parser_make_environ(makefile);
714
715 // Create the build script
716 char* script = pakfire_parser_expand(makefile, "build", template);
717 if (!script) {
718 ERROR(pakfire, "Could not generate the build script for stage '%s': %m\n", stage);
719 goto ERROR;
720 }
721
722 INFO(pakfire, "Running build stage '%s'\n", stage);
723
724 r = pakfire_execute_script(pakfire, script, strlen(script), NULL, envp, 0,
725 logging_callback, data);
726 if (r) {
727 ERROR(pakfire, "Build stage '%s' failed with status %d\n", stage, r);
728 }
729
730 ERROR:
731 if (envp) {
732 for (char** e = envp; *e; e++)
733 free(*e);
734 free(envp);
735 }
736 if (script)
737 free(script);
738
739 return r;
740 }
741
742 static const char* post_build_scripts[] = {
743 "remove-static-libs",
744 "check-symlinks",
745 "check-unsafe-files",
746 "check-libraries",
747 "check-rpaths",
748 "check-buildroot",
749 "check-include",
750 "check-hardening",
751 "check-interpreters",
752 "check-fhs",
753 "compress-man-pages",
754 "strip",
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
775 static int pakfire_build_makefile(Pakfire pakfire, const char* path, const char* target,
776 uuid_t* build_id, int flags,
777 pakfire_execute_logging_callback logging_callback, void* data) {
778 struct pakfire_parser* makefile = NULL;
779 char buildroot[PATH_MAX];
780 struct pakfire_parser_error* error = NULL;
781 int r = 1;
782
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))
788 goto ERROR;
789
790 // Make relative BUILDROOT path
791 const char* buildroot_rel = pakfire_path_relpath(root, buildroot);
792 if (!buildroot_rel)
793 goto ERROR;
794
795 // Read makefile
796 r = pakfire_read_makefile(&makefile, pakfire, path, &error);
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 {
803 ERROR(pakfire, "Could not parse makefile %s: %m\n", path);
804 }
805
806 goto ERROR;
807 }
808
809 // Set BUILDROOT
810 pakfire_parser_set(makefile, NULL, "BUILDROOT", buildroot_rel, 0);
811
812 // Run through all build stages
813 for (const char** stage = stages; *stage; stage++) {
814 int r = pakfire_build_stage(pakfire, makefile, *stage, logging_callback, data);
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
824 // Run post build scripts
825 r = pakfire_build_run_post_build_scripts(pakfire, buildroot_rel, logging_callback, data);
826 if (r)
827 goto ERROR;
828
829 // Create the packages
830 r = pakfire_build_packages(pakfire, makefile, build_id, buildroot_rel, target);
831 if (r) {
832 ERROR(pakfire, "Could not create packages: %m\n");
833 goto ERROR;
834 }
835
836 ERROR:
837 if (makefile)
838 pakfire_parser_unref(makefile);
839
840 // Remove buildroot
841 pakfire_rmtree(buildroot, 0);
842
843 return r;
844 }
845
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];
850 char cwd[PATH_MAX];
851 uuid_t build_id;
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
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
872 // The default target is the local repository path
873 if (!target) {
874 struct pakfire_repo* repo = pakfire_get_repo(pakfire, "@local");
875 if (repo) {
876 target = pakfire_repo_get_path(repo);
877 pakfire_repo_unref(repo);
878
879 // If the repository could not be found, just write to the cwd
880 } else {
881 target = getcwd(cwd, sizeof(cwd));
882 }
883 }
884
885 const char* packages[] = {
886 path, NULL
887 };
888
889 // Install the package into the build environment
890 r = pakfire_install(pakfire, 0, packages, NULL, 0, NULL);
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);
905 globfree(&buffer);
906 goto ERROR;
907 }
908
909 // Iterate over all makefiles
910 for (unsigned int i = 0; i < buffer.gl_pathc; i++) {
911 r = pakfire_build_makefile(pakfire, buffer.gl_pathv[i], target, &build_id, flags,
912 logging_callback, data);
913 if (r) {
914 ERROR(pakfire, "Could not build %s: %m\n", buffer.gl_pathv[i]);
915 globfree(&buffer);
916 goto ERROR;
917 }
918 }
919
920 ERROR:
921 return r;
922 }
923
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 }