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