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