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