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