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