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