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