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