]> git.ipfire.org Git - people/stevee/pakfire.git/blob - src/libpakfire/build.c
repos: Add convenience function to download packages from cmdline
[people/stevee/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 char** packages = NULL;
1066 int r = 1;
1067
1068 // Fetch configuration
1069 struct pakfire_config* config = pakfire_get_config(build->pakfire);
1070 if (!config) {
1071 ERROR(build->pakfire, "Could not fetch configuration: %m\n");
1072 r = 1;
1073 goto ERROR;
1074 }
1075
1076 // Fetch build environment
1077 const char* requires = pakfire_config_get(config, "build", "requires", NULL);
1078 if (!requires) {
1079 ERROR(build->pakfire, "No build requirements have been defined\n");
1080 goto ERROR;
1081 }
1082
1083 // Split requirements into packages
1084 packages = pakfire_string_split(requires, ',');
1085 if (!packages)
1086 goto ERROR;
1087
1088 int changed = 0;
1089
1090 // Install everything
1091 r = pakfire_install(build->pakfire, 0, 0, (const char**)packages, NULL, 0,
1092 &changed, NULL, NULL);
1093 if (r) {
1094 ERROR(build->pakfire, "Could not install build dependencies: %m\n");
1095 goto ERROR;
1096 }
1097
1098 // Mark snapshot as changed if new packages were installed
1099 if (changed)
1100 *snapshot_needs_update = 1;
1101
1102 // Update everything
1103 r = pakfire_sync(build->pakfire, 0, 0, &changed, NULL, NULL);
1104 if (r) {
1105 ERROR(build->pakfire, "Could not update packages: %m\n");
1106 goto ERROR;
1107 }
1108
1109 // Has anything changed?
1110 if (changed)
1111 *snapshot_needs_update = 1;
1112
1113 // Success
1114 r = 0;
1115
1116 ERROR:
1117 if (config)
1118 pakfire_config_unref(config);
1119
1120 if (packages) {
1121 for (char** package = packages; *package; package++)
1122 free(*package);
1123 free(packages);
1124 }
1125
1126 return r;
1127 }
1128
1129 /*
1130 Initializes the build environment
1131 */
1132 static int pakfire_build_init(struct pakfire_build* build) {
1133 char path[PATH_MAX];
1134 int r;
1135
1136 // Don't do it again
1137 if (build->init) {
1138 DEBUG(build->pakfire, "Build environment has already been initialized\n");
1139 return 0;
1140 }
1141
1142 // Compose path for snapshot
1143 r = pakfire_cache_path(build->pakfire, path, "%s", "snapshot.tar.zst");
1144 if (r) {
1145 ERROR(build->pakfire, "Could not compose snapshot path: %m\n");
1146 return 1;
1147 }
1148
1149 // Tells us whether we need to (re-)create the snapshot
1150 int snapshot_needs_update = 0;
1151
1152 // Extract snapshot
1153 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT)) {
1154 // Try restoring the snapshot
1155 r = pakfire_snapshot_restore(build->pakfire, path);
1156 if (r && errno != ENOENT)
1157 return r;
1158 }
1159
1160 // Install or update any build dependencies
1161 r = pakfire_build_install_packages(build, &snapshot_needs_update);
1162 if (r)
1163 return r;
1164
1165 // Update the snapshot if there were changes
1166 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT) && snapshot_needs_update) {
1167 // Create a new snapshot
1168 r = pakfire_snapshot_create(build->pakfire, path);
1169 if (r)
1170 return r;
1171 }
1172
1173 // Mark as initialized
1174 build->init = 1;
1175
1176 return 0;
1177 }
1178
1179 static int pakfire_build_read_makefile(struct pakfire_build* build,
1180 struct pakfire_parser** parser, struct pakfire_package* package) {
1181 char path[PATH_MAX];
1182 int r;
1183
1184 struct pakfire_parser_error* error = NULL;
1185
1186 const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA);
1187 const char* name = pakfire_package_get_string(package, PAKFIRE_PKG_NAME);
1188
1189 // Compose path to makefile
1190 r = pakfire_path(build->pakfire, path, "/usr/src/packages/%s/%s.nm", nevra, name);
1191 if (r < 0)
1192 return 1;
1193
1194 // Read makefile
1195 r = pakfire_read_makefile(parser, build->pakfire, path, &error);
1196 if (r) {
1197 if (error) {
1198 ERROR(build->pakfire, "Could not parse makefile %s: %s\n", path,
1199 pakfire_parser_error_get_message(error));
1200 } else {
1201 ERROR(build->pakfire, "Could not parse makefile %s: %m\n", path);
1202 }
1203
1204 goto ERROR;
1205 }
1206
1207 // Set BUILDROOT
1208 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
1209 if (buildroot)
1210 pakfire_parser_set(*parser, NULL, "BUILDROOT", buildroot, 0);
1211
1212 ERROR:
1213 if (error)
1214 pakfire_parser_error_unref(error);
1215
1216 return r;
1217 }
1218
1219 static int pakfire_build_perform(struct pakfire_build* build,
1220 struct pakfire_parser* makefile) {
1221 int r;
1222
1223 // Prepare the build
1224 r = pakfire_build_stage(build, makefile, "prepare");
1225 if (r)
1226 goto ERROR;
1227
1228 // Perform the build
1229 r = pakfire_build_stage(build, makefile, "build");
1230 if (r)
1231 goto ERROR;
1232
1233 // Test the build
1234 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_TESTS)) {
1235 r = pakfire_build_stage(build, makefile, "test");
1236 if (r)
1237 goto ERROR;
1238 }
1239
1240 // Install everything
1241 r = pakfire_build_stage(build, makefile, "install");
1242 if (r)
1243 goto ERROR;
1244
1245 // Run post build scripts
1246 r = pakfire_build_run_post_build_scripts(build);
1247 if (r)
1248 goto ERROR;
1249
1250 // Done!
1251 return 0;
1252
1253 ERROR:
1254 // Drop to a shell for debugging
1255 if (pakfire_has_flag(build->pakfire, PAKFIRE_BUILD_INTERACTIVE))
1256 pakfire_build_shell(build);
1257
1258 return r;
1259 }
1260
1261 static int __pakfire_build_unpackaged_file(struct pakfire* pakfire,
1262 struct pakfire_file* file, void* p) {
1263 char* s = pakfire_file_dump(file);
1264 if (s) {
1265 ERROR(pakfire, "%s\n", s);
1266 free(s);
1267 }
1268
1269 return 0;
1270 }
1271
1272 static int pakfire_build_check_unpackaged_files(struct pakfire_build* build) {
1273 struct pakfire_filelist* filelist = NULL;
1274 int r;
1275
1276 // Create a new filelist
1277 r = pakfire_filelist_create(&filelist, build->pakfire);
1278 if (r)
1279 goto ERROR;
1280
1281 // Scan for all files in BUILDROOT
1282 r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL);
1283 if (r)
1284 goto ERROR;
1285
1286 const size_t length = pakfire_filelist_size(filelist);
1287
1288 if (length) {
1289 ERROR(build->pakfire, "Unpackaged files found:\n");
1290
1291 r = pakfire_filelist_walk(filelist, __pakfire_build_unpackaged_file, NULL);
1292 if (r)
1293 goto ERROR;
1294
1295 // Report an error
1296 r = 1;
1297 }
1298
1299 ERROR:
1300 if (filelist)
1301 pakfire_filelist_unref(filelist);
1302
1303 return r;
1304 }
1305
1306 static int pakfire_build_install_package(struct pakfire* pakfire,
1307 struct pakfire_package* pkg, void* p) {
1308 struct pakfire_request* request = (struct pakfire_request*)p;
1309
1310 return pakfire_request_install_package(request, pkg);
1311 }
1312
1313 static int pakfire_build_install_test(struct pakfire_build* build) {
1314 struct pakfire_request* request = NULL;
1315 struct pakfire_problem* problem = NULL;
1316 struct pakfire_solution* solution = NULL;
1317 const char* s = NULL;
1318 int r;
1319
1320 // Create a new request
1321 r = pakfire_request_create(&request, build->pakfire, 0);
1322 if (r)
1323 goto ERROR;
1324
1325 // Add all packages
1326 r = pakfire_packagelist_walk(build->packages, pakfire_build_install_package, request);
1327
1328 // Solve the request
1329 r = pakfire_request_solve(request, NULL);
1330 switch (r) {
1331 // All okay
1332 case 0:
1333 break;
1334
1335 // Dependency Error
1336 case 2:
1337 ERROR(build->pakfire, "Install test failed:\n");
1338
1339 // Walk through all problems
1340 for (;;) {
1341 r = pakfire_request_next_problem(request, &problem);
1342 if (r)
1343 goto ERROR;
1344
1345 // There are no more problems
1346 if (!problem)
1347 break;
1348
1349 // Format the problem into something human-readable
1350 s = pakfire_problem_to_string(problem);
1351 if (!s)
1352 continue;
1353
1354 ERROR(build->pakfire, " * %s\n", s);
1355
1356 // Walk through all solutions
1357 for (;;) {
1358 r = pakfire_problem_next_solution(problem, &solution);
1359 if (r)
1360 goto ERROR;
1361
1362 // There are no more solutions
1363 if (!solution)
1364 break;
1365
1366 // Format the solution into something human-readable
1367 s = pakfire_solution_to_string(solution);
1368 if (!s)
1369 continue;
1370
1371 ERROR(build->pakfire, " * %s\n", s);
1372 }
1373 }
1374
1375 break;
1376
1377 // Any other errors
1378 default:
1379 goto ERROR;
1380 }
1381
1382 ERROR:
1383 if (r)
1384 ERROR(build->pakfire, "Install test failed: %m\n");
1385 if (request)
1386 pakfire_request_unref(request);
1387 if (problem)
1388 pakfire_problem_unref(problem);
1389 if (solution)
1390 pakfire_solution_unref(solution);
1391
1392 return r;
1393 }
1394
1395 static int pakfire_build_post_check(struct pakfire_build* build) {
1396 int r;
1397
1398 // Check for unpackaged files
1399 r = pakfire_build_check_unpackaged_files(build);
1400 if (r)
1401 return r;
1402
1403 // Perform install test
1404 r = pakfire_build_install_test(build);
1405 if (r)
1406 return r;
1407
1408 return 0;
1409 }
1410
1411 static int pakfire_build_copy_package(struct pakfire* pakfire,
1412 struct pakfire_package* pkg, void* p) {
1413 struct pakfire_archive* archive = NULL;
1414 char path[PATH_MAX];
1415 int r;
1416
1417 const char* target = (const char*)p;
1418
1419 if (!target) {
1420 errno = EINVAL;
1421 return 1;
1422 }
1423
1424 // Fetch the package filename
1425 const char* filename = pakfire_package_get_string(pkg, PAKFIRE_PKG_FILENAME);
1426 if (!filename) {
1427 r = 1;
1428 goto ERROR;
1429 }
1430
1431 // Format the destination path
1432 r = pakfire_string_format(path, "%s/%s", target, filename);
1433 if (r)
1434 goto ERROR;
1435
1436 // Open the archive
1437 archive = pakfire_package_get_archive(pkg);
1438 if (!archive) {
1439 r = 1;
1440 goto ERROR;
1441 }
1442
1443 // Copy it to its destination
1444 r = pakfire_archive_copy(archive, path);
1445 if (r)
1446 goto ERROR;
1447
1448 ERROR:
1449 if (archive)
1450 pakfire_archive_unref(archive);
1451
1452 return r;
1453 }
1454
1455 static int pakfire_build_copy_packages(struct pakfire_build* build) {
1456 struct pakfire_repo* local = NULL;
1457 int r = 0;
1458
1459 DEBUG(build->pakfire, "Copying built packages\n");
1460
1461 // Fetch local repository
1462 local = pakfire_get_repo(build->pakfire, PAKFIRE_REPO_LOCAL);
1463
1464 // Copy all packages to the target path
1465 if (*build->target) {
1466 r = pakfire_packagelist_walk(build->packages,
1467 pakfire_build_copy_package, build->target);
1468 if (r)
1469 goto ERROR;
1470 }
1471
1472 // If we have a local repository, we copy all packages to it
1473 if (local) {
1474 const char* path = pakfire_repo_get_path(local);
1475 if (path) {
1476 r = pakfire_packagelist_walk(build->packages,
1477 pakfire_build_copy_package, (void*)path);
1478 if (r)
1479 goto ERROR;
1480 }
1481 }
1482
1483 ERROR:
1484 if (local)
1485 pakfire_repo_unref(local);
1486
1487 return r;
1488 }
1489
1490 PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) {
1491 struct pakfire_package* package = NULL;
1492 struct pakfire_parser* makefile = NULL;
1493 char* buildroot = NULL;
1494 int r;
1495
1496 // Set buildroot
1497 r = pakfire_path(build->pakfire, build->buildroot, "%s", "/var/tmp/.pakfire-buildroot.XXXXXX");
1498 if (r)
1499 goto ERROR;
1500
1501 // Open the source package
1502 r = pakfire_commandline_add(build->pakfire, path, &package);
1503 if (r)
1504 goto ERROR;
1505
1506 const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA);
1507
1508 INFO(build->pakfire, "Building %s...\n", nevra);
1509
1510 // Initialize the build environment
1511 r = pakfire_build_init(build);
1512 if (r)
1513 goto ERROR;
1514
1515 const char* packages[] = {
1516 path, NULL
1517 };
1518
1519 // Install the package into the build environment
1520 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, PAKFIRE_REQUEST_ESSENTIAL,
1521 NULL, NULL, NULL);
1522 if (r) {
1523 ERROR(build->pakfire, "Could not install %s\n", path);
1524 goto ERROR;
1525 }
1526
1527 // Create BUILDROOT
1528 buildroot = pakfire_mkdtemp(build->buildroot);
1529 if (!buildroot) {
1530 ERROR(build->pakfire, "Could not create BUILDROOT: %m\n");
1531 goto ERROR;
1532 }
1533
1534 // Open the makefile
1535 r = pakfire_build_read_makefile(build, &makefile, package);
1536 if (r)
1537 goto ERROR;
1538
1539 // Perform the actual build
1540 r = pakfire_build_perform(build, makefile);
1541 if (r)
1542 goto ERROR;
1543
1544 // Create the packages
1545 r = pakfire_build_packages(build, makefile);
1546 if (r) {
1547 ERROR(build->pakfire, "Could not create packages: %m\n");
1548 goto ERROR;
1549 }
1550
1551 // Perform post build checks
1552 r = pakfire_build_post_check(build);
1553 if (r)
1554 goto ERROR;
1555
1556 // Copy packages to their destination
1557 r = pakfire_build_copy_packages(build);
1558 if (r)
1559 goto ERROR;
1560
1561 ERROR:
1562 if (makefile)
1563 pakfire_parser_unref(makefile);
1564 if (package)
1565 pakfire_package_unref(package);
1566
1567 // Cleanup buildroot
1568 if (buildroot)
1569 pakfire_rmtree(buildroot, 0);
1570
1571 return r;
1572 }
1573
1574 /*
1575 Compatibility function to keep the legacy API.
1576 */
1577 PAKFIRE_EXPORT int pakfire_build(struct pakfire* pakfire, const char* path,
1578 const char* target, const char* id, int flags) {
1579 struct pakfire_build* build = NULL;
1580 int r;
1581
1582 // Check if path is set
1583 if (!path) {
1584 errno = EINVAL;
1585 return 1;
1586 }
1587
1588 // Create a new build environment
1589 r = pakfire_build_create(&build, pakfire, id, flags);
1590 if (r)
1591 goto ERROR;
1592
1593 // Set target
1594 if (target) {
1595 r = pakfire_build_set_target(build, target);
1596 if (r)
1597 goto ERROR;
1598 }
1599
1600 // Run build
1601 r = pakfire_build_exec(build, path);
1602
1603 ERROR:
1604 if (build)
1605 pakfire_build_unref(build);
1606
1607 return r;
1608 }
1609
1610 int pakfire_build_clean(struct pakfire* pakfire, int flags) {
1611 struct pakfire_repo* local = NULL;
1612 int r = 0;
1613
1614 // Fetch local repository
1615 local = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL);
1616 if (!local) {
1617 ERROR(pakfire, "Could not find repository %s: %m\n", PAKFIRE_REPO_LOCAL);
1618 goto ERROR;
1619 }
1620
1621 // Destroy everything in it
1622 r = pakfire_repo_clean(local, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
1623 if (r)
1624 goto ERROR;
1625
1626 ERROR:
1627 if (local)
1628 pakfire_repo_unref(local);
1629
1630 return r;
1631 }
1632
1633 /*
1634 This is a convenience function that sets up a build environment and
1635 then drops the user into an interactive shell.
1636 */
1637 PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages) {
1638 struct pakfire_build* build = NULL;
1639 int r;
1640
1641 // Create a new build environment
1642 r = pakfire_build_create(&build, pakfire, NULL, PAKFIRE_BUILD_INTERACTIVE);
1643 if (r) {
1644 ERROR(pakfire, "Could not create build: %m\n");
1645 goto ERROR;
1646 }
1647
1648 // Initialize the build environment
1649 r = pakfire_build_init(build);
1650 if (r)
1651 goto ERROR;
1652
1653 // Install any additional packages
1654 if (packages) {
1655 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0, NULL, NULL, NULL);
1656 if (r)
1657 return r;
1658 }
1659
1660 // Run shell
1661 r = pakfire_build_shell(build);
1662
1663 ERROR:
1664 if (build)
1665 pakfire_build_unref(build);
1666
1667 return r;
1668 }