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