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