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