]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/build.c
filelist: Add flags argument to walk function
[people/ms/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),
1025};
1026
45dd1960
MT
1027/*
1028 This helper function takes a callback which is expected to add any files
868cd7a5 1029 to the given filelist which will optionally be all removed after.
45dd1960 1030*/
868cd7a5 1031static int pakfire_build_post_process_files(struct pakfire_build* build,
45dd1960 1032 struct pakfire_filelist* filelist, const char* description,
868cd7a5
MT
1033 int (*callback)(struct pakfire* pakfire, struct pakfire_file* file, void* data),
1034 int flags) {
45dd1960
MT
1035 struct pakfire_filelist* removees = NULL;
1036 int r;
1037
1038 // Create a filelist with objects that need to be removed
1039 r = pakfire_filelist_create(&removees, build->pakfire);
1040 if (r)
1041 goto ERROR;
1042
1043 // Find all files that need to be removed
a3f3c077 1044 r = pakfire_filelist_walk(filelist, callback, removees, 0);
45dd1960
MT
1045 if (r)
1046 goto ERROR;
1047
1048 if (!pakfire_filelist_is_empty(removees)) {
1049 if (description)
1050 INFO(build->pakfire, "%s\n", description);
1051
1052 // Show all files which will be removed
28886e21 1053 pakfire_filelist_dump(removees, PAKFIRE_FILE_DUMP_FULL|PAKFIRE_FILE_DUMP_ISSUES);
45dd1960
MT
1054
1055 // Remove all files on the removee list
868cd7a5
MT
1056 if (flags & PAKFIRE_BUILD_CLEANUP_FILES) {
1057 r = pakfire_filelist_cleanup(removees);
1058 if (r)
1059 goto ERROR;
45dd1960 1060
868cd7a5
MT
1061 // Remove all files from the filelist
1062 r = pakfire_filelist_remove_all(filelist, removees);
1063 if (r)
1064 goto ERROR;
1065 }
1066
1067 // Report an error if any files have been found
1068 if (flags & PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY)
1069 r = 1;
45dd1960
MT
1070 }
1071
1072ERROR:
1073 if (removees)
1074 pakfire_filelist_unref(removees);
1075
1076 return r;
1077}
1078
1079static int __pakfire_build_remove_static_libraries(
1080 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1081 struct pakfire_filelist* removees = (struct pakfire_filelist*)data;
add08b7d
MT
1082 char path[PATH_MAX];
1083 int r;
45dd1960
MT
1084
1085 // Find all static libraries
add08b7d
MT
1086 if (pakfire_file_matches_class(file, PAKFIRE_FILE_STATIC_LIBRARY)) {
1087 // Copy the filename
1088 r = pakfire_string_set(path, pakfire_file_get_abspath(file));
1089 if (r)
1090 return -1;
1091
1092 // Remove the extension
1093 r = pakfire_path_replace_extension(path, "so");
1094 if (r)
1095 return -1;
1096
1097 // Only delete if there is a shared object with the same name
1098 if (pakfire_path_exists(path))
1099 return pakfire_filelist_add(removees, file);
1100 }
45dd1960
MT
1101
1102 return 0;
1103}
1104
1105static int pakfire_build_post_remove_static_libraries(
1106 struct pakfire_build* build, struct pakfire_filelist* filelist) {
868cd7a5
MT
1107 return pakfire_build_post_process_files(build, filelist,
1108 "Removing static libaries...",
1109 __pakfire_build_remove_static_libraries,
1110 PAKFIRE_BUILD_CLEANUP_FILES);
45dd1960
MT
1111}
1112
1113static int __pakfire_build_remove_libtool_archives(
1114 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1115 struct pakfire_filelist* removees = (struct pakfire_filelist*)data;
1116
1117 // Find all libtool archive files
1118 if (pakfire_file_matches_class(file, PAKFIRE_FILE_LIBTOOL_ARCHIVE))
2f88682d 1119 return pakfire_filelist_add(removees, file);
45dd1960
MT
1120
1121 return 0;
1122}
1123
1124static int pakfire_build_post_remove_libtool_archives(
1125 struct pakfire_build* build, struct pakfire_filelist* filelist) {
868cd7a5
MT
1126 return pakfire_build_post_process_files(build, filelist,
1127 "Removing libtool archives...",
1128 __pakfire_build_remove_libtool_archives,
1129 PAKFIRE_BUILD_CLEANUP_FILES);
45dd1960
MT
1130}
1131
7434fa90
MT
1132static int __pakfire_build_check_broken_symlinks(
1133 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1134 struct pakfire_filelist* broken = (struct pakfire_filelist*)data;
1135 int r;
1136
1137 // Ignore anything that isn't a symlink
1138 switch (pakfire_file_get_type(file)) {
1139 case S_IFLNK:
1140 if (!pakfire_file_symlink_target_exists(file)) {
1141 r = pakfire_filelist_add(broken, file);
1142 if (r)
1143 return r;
1144 }
1145
1146 break;
1147
1148 // Ignore anything that isn't a symlink
1149 default:
1150 break;
1151 }
1152
1153 return 0;
1154}
1155
1156static int pakfire_build_post_check_broken_symlinks(
1157 struct pakfire_build* build, struct pakfire_filelist* filelist) {
868cd7a5
MT
1158 return pakfire_build_post_process_files(build, filelist,
1159 "Broken symlinks have been found:",
1160 __pakfire_build_check_broken_symlinks,
1161 PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
1162}
7434fa90 1163
fabc09a9
MT
1164/*
1165 BUILDROOT Check
1166*/
1167static int pakfire_build_post_check_buildroot(
1168 struct pakfire_build* build, struct pakfire_filelist* filelist) {
1169 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
1170
1171 // Nested function to keep a reference to buildroot
1172 int __pakfire_build_post_check_buildroot(
1173 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1174 struct pakfire_filelist* matches = (struct pakfire_filelist*)data;
1175 int r;
1176
1177 if (pakfire_file_payload_matches(file, buildroot, strlen(buildroot))) {
1178 r = pakfire_filelist_add(matches, file);
1179 if (r)
1180 return r;
1181 }
1182
1183 return 0;
1184 }
1185
1186 return pakfire_build_post_process_files(
1187 build, filelist, "Files containing BUILDROOT:",
1188 __pakfire_build_post_check_buildroot, PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
1189}
1190
2a67d72c 1191/*
28886e21 1192 File Issues
2a67d72c
MT
1193*/
1194
28886e21 1195static int __pakfire_build_post_check_files(
2dcb43e7
MT
1196 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1197 struct pakfire_filelist* broken = (struct pakfire_filelist*)data;
f7f44921 1198 int issues = 0;
2dcb43e7
MT
1199 int r;
1200
28886e21
MT
1201 // Check file for issues
1202 r = pakfire_file_check(file, &issues);
0dbb8605 1203 if (r) {
28886e21 1204 ERROR(pakfire, "%s: File Check failed: %m\n", pakfire_file_get_path(file));
f7f44921 1205 return r;
0dbb8605 1206 }
2a67d72c 1207
f7f44921
MT
1208 // If any issues have been found, consider this file to be on the list
1209 if (issues) {
2a67d72c
MT
1210 r = pakfire_filelist_add(broken, file);
1211 if (r)
1212 return r;
1213 }
1214
1215 return 0;
1216}
1217
28886e21 1218static int pakfire_build_post_check_files(
2dcb43e7 1219 struct pakfire_build* build, struct pakfire_filelist* filelist) {
f7f44921
MT
1220 return pakfire_build_post_process_files(
1221 build,
1222 filelist,
28886e21
MT
1223 "File Issues:",
1224 __pakfire_build_post_check_files,
f7f44921 1225 PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
7434fa90
MT
1226}
1227
45dd1960
MT
1228static int pakfire_build_run_post_build_checks(struct pakfire_build* build) {
1229 struct pakfire_filelist* filelist = NULL;
1230 int r;
1231
1232 // Create a filelist of all files in the build
1233 r = pakfire_filelist_create(&filelist, build->pakfire);
1234 if (r) {
1235 ERROR(build->pakfire, "Could not create filelist: %m\n");
1236 goto ERROR;
1237 }
1238
1239 // Scan for all files in BUILDROOT
1240 r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL);
1241 if (r)
1242 goto ERROR;
1243
45dd1960 1244 // If the filelist is empty, we can are done
94ff3014 1245 if (pakfire_filelist_is_empty(filelist)) {
45dd1960
MT
1246 DEBUG(build->pakfire, "Empty BUILDROOT. Skipping post build checks...\n");
1247 r = 0;
1248 goto ERROR;
1249 }
1250
1251 // Remove any static libraries
1252 r = pakfire_build_post_remove_static_libraries(build, filelist);
1253 if (r)
1254 goto ERROR;
1255
1256 // Remove any libtool archives
1257 r = pakfire_build_post_remove_libtool_archives(build, filelist);
1258 if (r)
1259 goto ERROR;
1260
7434fa90
MT
1261 // Check for any broken symlinks
1262 r = pakfire_build_post_check_broken_symlinks(build, filelist);
1263 if (r)
1264 goto ERROR;
1265
fabc09a9
MT
1266 // Check for BUILDROOT
1267 r = pakfire_build_post_check_buildroot(build, filelist);
1268 if (r)
1269 goto ERROR;
1270
28886e21
MT
1271 // Check files
1272 r = pakfire_build_post_check_files(build, filelist);
2dcb43e7
MT
1273 if (r)
1274 goto ERROR;
1275
45dd1960
MT
1276ERROR:
1277 if (filelist)
1278 pakfire_filelist_unref(filelist);
1279
1280 return r;
1281}
1282
39f45b30 1283static const char* post_build_scripts[] = {
16043831 1284 "check-rpaths",
dd864160 1285 "check-hardening",
ae703321 1286 "check-interpreters",
39f45b30 1287 "compress-man-pages",
ffb65de6 1288 "strip",
39f45b30
MT
1289 NULL,
1290};
1291
03dc1d52
MT
1292static int pakfire_build_run_post_build_scripts(struct pakfire_build* build) {
1293 // Fetch buildroot
1294 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
1295
39f45b30
MT
1296 // Set default arguments for build scripts
1297 const char* args[] = {
1298 buildroot, NULL
1299 };
1300
1301 // Run them one by one
1302 for (const char** script = post_build_scripts; *script; script++) {
ccdd2e95 1303 int r = pakfire_build_run_script(build, *script, args, NULL, NULL, NULL);
39f45b30
MT
1304 if (r)
1305 return r;
1306 }
1307
1308 return 0;
1309}
1310
abbad00b 1311static void pakfire_build_free(struct pakfire_build* build) {
6ed07bc4
MT
1312 if (build->packages)
1313 pakfire_packagelist_unref(build->packages);
1314
a6144133
MT
1315 if (build->repo) {
1316 pakfire_repo_clean(build->repo, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
1317 pakfire_repo_unref(build->repo);
d6451238
MT
1318 }
1319
753ddf74
MT
1320 if (build->jail)
1321 pakfire_jail_unref(build->jail);
1322
49fd9926
MT
1323 if (build->cgroup) {
1324 // Destroy the cgroup
1325 pakfire_cgroup_destroy(build->cgroup);
1326
1327 // Free it
1328 pakfire_cgroup_unref(build->cgroup);
1329 }
1330
abbad00b
MT
1331 pakfire_unref(build->pakfire);
1332 free(build);
1333}
1334
1335static int pakfire_build_parse_id(struct pakfire_build* build, const char* id) {
1336 int r;
1337
1338 // Try parsing the Build ID
1339 if (id) {
1340 r = uuid_parse(id, build->id);
1341 if (r) {
1342 ERROR(build->pakfire, "Could not parse build ID '%s'\n", id);
1df321ff 1343 errno = EINVAL;
abbad00b
MT
1344 return r;
1345 }
1346
1347 // Otherwise initialize the Build ID with something random
1348 } else {
1349 uuid_generate_random(build->id);
1350 }
1351
f877fdfa
MT
1352 // Store the ID as string, too
1353 uuid_unparse_lower(build->id, build->_id);
1354
1355 return 0;
1356}
1357
1358/*
1359 Sets up a new cgroup for this build
1360*/
1361static int pakfire_build_setup_cgroup(struct pakfire_build* build) {
0a1284ff 1362 struct pakfire_config* config = NULL;
f877fdfa
MT
1363 char path[PATH_MAX];
1364 int r;
1365
1366 // Compose path
1367 r = pakfire_string_format(path, "pakfire/build-%s", build->_id);
a60955af 1368 if (r) {
f877fdfa 1369 ERROR(build->pakfire, "Could not compose path for cgroup: %m\n");
0a1284ff 1370 goto ERROR;
f877fdfa
MT
1371 }
1372
1373 // Create a new cgroup
1374 r = pakfire_cgroup_open(&build->cgroup, build->pakfire, path,
1375 PAKFIRE_CGROUP_ENABLE_ACCOUNTING);
1376 if (r) {
1377 ERROR(build->pakfire, "Could not create cgroup for build %s: %m\n", build->_id);
0a1284ff 1378 goto ERROR;
f877fdfa
MT
1379 }
1380
0a1284ff
MT
1381 // Fetch config
1382 config = pakfire_get_config(build->pakfire);
1383 if (!config)
1384 goto ERROR;
1385
882ae03b 1386 // Guarantee some minimum memory
0a1284ff
MT
1387 size_t memory_guaranteed = pakfire_config_get_bytes(config, "build",
1388 "memory_guaranteed", PAKFIRE_BUILD_MEMORY_GUARANTEED);
1389 if (memory_guaranteed) {
1390 r = pakfire_cgroup_set_guaranteed_memory(build->cgroup, memory_guaranteed);
1391 if (r)
1392 goto ERROR;
1393 }
882ae03b 1394
5b877e84
MT
1395 // Limit memory
1396 size_t memory_limit = pakfire_config_get_bytes(config, "build", "memory_limit", 0);
1397 if (memory_limit) {
1398 r = pakfire_cgroup_set_memory_limit(build->cgroup, memory_limit);
1399 if (r)
1400 goto ERROR;
1401 }
1402
de4c8fe6 1403 // Set PID limit
0a1284ff
MT
1404 size_t pid_limit = pakfire_config_get_int(config, "build",
1405 "pid_limit", PAKFIRE_BUILD_PID_LIMIT);
1406 if (pid_limit) {
1407 r = pakfire_cgroup_set_pid_limit(build->cgroup, pid_limit);
1408 if (r)
1409 goto ERROR;
1410 }
de4c8fe6 1411
0a1284ff
MT
1412ERROR:
1413 if (config)
1414 pakfire_config_unref(config);
1415
1416 return r;
abbad00b
MT
1417}
1418
753ddf74
MT
1419/*
1420 Sets up a new jail for this build
1421*/
1422static int pakfire_build_setup_jail(struct pakfire_build* build) {
1423 int r;
1424
1425 // Create a new jail
1426 r = pakfire_jail_create(&build->jail, build->pakfire, 0);
1427 if (r) {
1428 ERROR(build->pakfire, "Could not create jail for build %s: %m\n", build->_id);
1429 return r;
1430 }
1431
15503538
MT
1432 // Connect the jail to our cgroup
1433 r = pakfire_jail_set_cgroup(build->jail, build->cgroup);
1434 if (r) {
1435 ERROR(build->pakfire, "Could not set cgroup for jail: %m\n");
1436 return r;
1437 }
1438
753ddf74
MT
1439 // Done
1440 return 0;
1441}
1442
5a06668c
MT
1443/*
1444 Sets up the ccache for this build
1445*/
1446static int pakfire_build_setup_ccache(struct pakfire_build* build) {
1447 char path[PATH_MAX];
1448 int r;
1449
1450 // Check if we want a ccache
1451 if (pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_CCACHE)) {
1452 DEBUG(build->pakfire, "ccache usage has been disabled for this build\n");
0570ccd2
MT
1453
1454 // Set CCACHE_DISABLE=1 so that if ccache is installed, it will disable itself
1455 r = pakfire_jail_set_env(build->jail, "CCACHE_DISABLE", "1");
1456 if (r) {
1457 ERROR(build->pakfire, "Could not disable ccache: %m\n");
1458 return r;
1459 }
1460
5a06668c
MT
1461 return 0;
1462 }
1463
1464 // Compose path
df1409ef
MT
1465 r = pakfire_cache_path(build->pakfire, path, "%s", "ccache");
1466 if (r) {
5a06668c
MT
1467 ERROR(build->pakfire, "Could not compose ccache path: %m\n");
1468 return 1;
1469 }
1470
1471 DEBUG(build->pakfire, "Mounting ccache from %s\n", path);
1472
1473 // Ensure path exists
1474 r = pakfire_mkdir(path, 0755);
1475 if (r && errno != EEXIST) {
1476 ERROR(build->pakfire, "Could not create ccache directory %s: %m\n", path);
1477 return r;
1478 }
1479
1480 // Bind-mount the directory
1481 r = pakfire_jail_bind(build->jail, path, CCACHE_DIR, MS_NOSUID|MS_NOEXEC|MS_NODEV);
1482 if (r) {
1483 ERROR(build->pakfire, "Could not mount ccache: %m\n");
1484 return r;
1485 }
1486
1487 return 0;
1488}
1489
d8c8cdc4 1490static int pakfire_build_setup_repo(struct pakfire_build* build) {
eb3e6c00 1491 char path[PATH_MAX] = PAKFIRE_TMP_DIR "/pakfire-build-repo.XXXXXX";
d6451238
MT
1492 char url[PATH_MAX];
1493 int r;
1494
1495 // Create a new repository
a6144133 1496 r = pakfire_repo_create(&build->repo, build->pakfire, PAKFIRE_REPO_RESULT);
d6451238
MT
1497 if (r) {
1498 ERROR(build->pakfire, "Could not create repository %s: %m", PAKFIRE_REPO_RESULT);
1499 return r;
1500 }
1501
1502 // Set description
a6144133 1503 pakfire_repo_set_description(build->repo, _("Build Repository"));
d6451238
MT
1504
1505 // Create a temporary directory
1506 const char* p = pakfire_mkdtemp(path);
1507 if (!p) {
1508 ERROR(build->pakfire, "Could not create a the build repository: %m\n");
1509 return 1;
1510 }
1511
1512 // Format the URL
1513 r = pakfire_string_format(url, "file://%s", path);
1514 if (r)
1515 return r;
1516
1517 // Set the URL
a6144133 1518 pakfire_repo_set_baseurl(build->repo, url);
d6451238
MT
1519
1520 return r;
1521}
1522
f9a4c0b7
MT
1523static int pakfire_build_set_time_start(struct pakfire_build* build) {
1524 const time_t now = time(NULL);
1525
1526 if (now < 0) {
1527 ERROR(build->pakfire, "Could not fetch start time: %m\n");
1528 return 1;
1529 }
1530
1531 build->time_start = now;
1532
1533 return 0;
1534}
1535
abbad00b
MT
1536PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
1537 struct pakfire* pakfire, const char* id, int flags) {
1538 int r;
1539
1540 // Allocate build object
1541 struct pakfire_build* b = calloc(1, sizeof(*b));
1542 if (!b)
1543 return 1;
1544
1545 // Reference pakfire
1546 b->pakfire = pakfire_ref(pakfire);
1547
1548 // Initialize reference counter
1549 b->nrefs = 1;
1550
1551 // Copy flags
1552 b->flags = flags;
1553
f9a4c0b7
MT
1554 // Store start time
1555 r = pakfire_build_set_time_start(b);
1556 if (r)
1557 goto ERROR;
1558
abbad00b
MT
1559 // Parse ID
1560 r = pakfire_build_parse_id(b, id);
1561 if (r)
1562 goto ERROR;
1563
d8c8cdc4
MT
1564 // Setup repo
1565 r = pakfire_build_setup_repo(b);
e545f6ec
MT
1566 if (r)
1567 goto ERROR;
1568
f877fdfa
MT
1569 // Create cgroup
1570 r = pakfire_build_setup_cgroup(b);
1571 if (r)
1572 goto ERROR;
1573
753ddf74
MT
1574 // Create jail
1575 r = pakfire_build_setup_jail(b);
1576 if (r)
1577 goto ERROR;
1578
5a06668c
MT
1579 // Setup ccache
1580 r = pakfire_build_setup_ccache(b);
1581 if (r)
1582 goto ERROR;
1583
abbad00b
MT
1584 *build = b;
1585 return 0;
1586
1587ERROR:
1588 pakfire_build_free(b);
1589 return r;
1590}
1591
77a1964f 1592PAKFIRE_EXPORT struct pakfire_build* pakfire_build_ref(struct pakfire_build* build) {
abbad00b
MT
1593 ++build->nrefs;
1594
1595 return build;
1596}
1597
77a1964f 1598PAKFIRE_EXPORT struct pakfire_build* pakfire_build_unref(struct pakfire_build* build) {
abbad00b
MT
1599 if (--build->nrefs > 0)
1600 return build;
1601
1602 pakfire_build_free(build);
1603 return NULL;
1604}
1605
ea924657
MT
1606PAKFIRE_EXPORT int pakfire_build_set_target(
1607 struct pakfire_build* build, const char* target) {
a60955af 1608 return pakfire_string_set(build->target, target);
ea924657 1609}
57e2cf99 1610
c0f2502a
MT
1611static int pakfire_build_install_packages(struct pakfire_build* build,
1612 int* snapshot_needs_update) {
57185995 1613 int r;
c0f2502a 1614
57185995
MT
1615 const char* packages[] = {
1616 "build-essential",
1617 NULL,
1618 };
c0f2502a
MT
1619
1620 int changed = 0;
1621
1622 // Install everything
57185995 1623 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0,
c0f2502a
MT
1624 &changed, NULL, NULL);
1625 if (r) {
1626 ERROR(build->pakfire, "Could not install build dependencies: %m\n");
57185995 1627 return r;
c0f2502a
MT
1628 }
1629
1630 // Mark snapshot as changed if new packages were installed
1631 if (changed)
1632 *snapshot_needs_update = 1;
1633
1634 // Update everything
1635 r = pakfire_sync(build->pakfire, 0, 0, &changed, NULL, NULL);
1636 if (r) {
1637 ERROR(build->pakfire, "Could not update packages: %m\n");
57185995 1638 return r;
c0f2502a
MT
1639 }
1640
1641 // Has anything changed?
1642 if (changed)
1643 *snapshot_needs_update = 1;
1644
57185995 1645 return 0;
c0f2502a
MT
1646}
1647
8dbb69f9
MT
1648/*
1649 Initializes the build environment
1650*/
1651static int pakfire_build_init(struct pakfire_build* build) {
c0f2502a
MT
1652 int r;
1653
8dbb69f9
MT
1654 // Don't do it again
1655 if (build->init) {
1656 DEBUG(build->pakfire, "Build environment has already been initialized\n");
1657 return 0;
1658 }
1659
ffc2630d
MT
1660 // Tells us whether we need to (re-)create the snapshot
1661 int snapshot_needs_update = 0;
c0f2502a 1662
1ea7b360 1663 // Extract snapshot
ffc2630d 1664 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT)) {
549c98ad
MT
1665 r = pakfire_snapshot_restore(build->pakfire);
1666 if (r)
1667 return r;
c0f2502a
MT
1668 }
1669
c0f2502a
MT
1670 // Install or update any build dependencies
1671 r = pakfire_build_install_packages(build, &snapshot_needs_update);
1672 if (r)
1673 return r;
1674
ffc2630d
MT
1675 // Update the snapshot if there were changes
1676 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT) && snapshot_needs_update) {
356d6811
MT
1677 // Store the snapshot
1678 r = pakfire_snapshot_store(build->pakfire);
c0f2502a
MT
1679 if (r)
1680 return r;
1681 }
1682
8dbb69f9
MT
1683 // Mark as initialized
1684 build->init = 1;
1685
c0f2502a
MT
1686 return 0;
1687}
1688
e57188c0
MT
1689static int pakfire_build_read_makefile(struct pakfire_build* build,
1690 struct pakfire_parser** parser, struct pakfire_package* package) {
e57188c0
MT
1691 char path[PATH_MAX];
1692 int r;
1693
1694 struct pakfire_parser_error* error = NULL;
1695
74468e4f
MT
1696 const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA);
1697 const char* name = pakfire_package_get_string(package, PAKFIRE_PKG_NAME);
e57188c0
MT
1698
1699 // Compose path to makefile
77e26129 1700 r = pakfire_path(build->pakfire, path, "/usr/src/packages/%s/%s.nm", nevra, name);
e57188c0
MT
1701 if (r < 0)
1702 return 1;
1703
1704 // Read makefile
1705 r = pakfire_read_makefile(parser, build->pakfire, path, &error);
1706 if (r) {
1707 if (error) {
1708 ERROR(build->pakfire, "Could not parse makefile %s: %s\n", path,
1709 pakfire_parser_error_get_message(error));
1710 } else {
1711 ERROR(build->pakfire, "Could not parse makefile %s: %m\n", path);
1712 }
1713
1714 goto ERROR;
1715 }
1716
6fc3956f 1717 // Set BUILDROOT
5caf06e3 1718 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
6fc3956f
MT
1719 if (buildroot)
1720 pakfire_parser_set(*parser, NULL, "BUILDROOT", buildroot, 0);
1721
e57188c0
MT
1722ERROR:
1723 if (error)
1724 pakfire_parser_error_unref(error);
1725
1726 return r;
1727}
1728
03dc1d52
MT
1729static int pakfire_build_perform(struct pakfire_build* build,
1730 struct pakfire_parser* makefile) {
1731 int r;
1732
b8786cdb
MT
1733 // Prepare the build
1734 r = pakfire_build_stage(build, makefile, "prepare");
1735 if (r)
1736 goto ERROR;
03dc1d52 1737
b8786cdb
MT
1738 // Perform the build
1739 r = pakfire_build_stage(build, makefile, "build");
1740 if (r)
1741 goto ERROR;
1742
1743 // Test the build
9605d535
MT
1744 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_TESTS)) {
1745 r = pakfire_build_stage(build, makefile, "test");
1746 if (r)
1747 goto ERROR;
1748 }
b8786cdb
MT
1749
1750 // Install everything
1751 r = pakfire_build_stage(build, makefile, "install");
1752 if (r)
1753 goto ERROR;
03dc1d52 1754
45dd1960
MT
1755 // Run post build checks
1756 r = pakfire_build_run_post_build_checks(build);
8e8de3c1
MT
1757 if (r) {
1758 ERROR(build->pakfire, "Post build checks failed\n");
45dd1960 1759 goto ERROR;
8e8de3c1 1760 }
45dd1960 1761
03dc1d52 1762 // Run post build scripts
b8786cdb
MT
1763 r = pakfire_build_run_post_build_scripts(build);
1764 if (r)
1765 goto ERROR;
1766
1767 // Done!
1768 return 0;
1769
1770ERROR:
1771 // Drop to a shell for debugging
cebfd3f6 1772 if (pakfire_build_has_flag(build, PAKFIRE_BUILD_INTERACTIVE))
b8786cdb
MT
1773 pakfire_build_shell(build);
1774
1775 return r;
03dc1d52
MT
1776}
1777
0f9bb14d
MT
1778static int __pakfire_build_unpackaged_file(struct pakfire* pakfire,
1779 struct pakfire_file* file, void* p) {
f7f44921 1780 char* s = pakfire_file_dump(file, PAKFIRE_FILE_DUMP_FULL);
6cb8b2b5 1781 if (s) {
0f9bb14d 1782 ERROR(pakfire, "%s\n", s);
6cb8b2b5
MT
1783 free(s);
1784 }
0f9bb14d
MT
1785
1786 return 0;
1787}
1788
1789static int pakfire_build_check_unpackaged_files(struct pakfire_build* build) {
1790 struct pakfire_filelist* filelist = NULL;
1791 int r;
1792
1793 // Create a new filelist
1794 r = pakfire_filelist_create(&filelist, build->pakfire);
1795 if (r)
1796 goto ERROR;
1797
1798 // Scan for all files in BUILDROOT
1799 r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL);
1800 if (r)
1801 goto ERROR;
1802
94ff3014 1803 if (!pakfire_filelist_is_empty(filelist)) {
0f9bb14d
MT
1804 ERROR(build->pakfire, "Unpackaged files found:\n");
1805
a3f3c077 1806 r = pakfire_filelist_walk(filelist, __pakfire_build_unpackaged_file, NULL, 0);
0f9bb14d
MT
1807 if (r)
1808 goto ERROR;
1809
1810 // Report an error
1811 r = 1;
1812 }
1813
1814ERROR:
1815 if (filelist)
1816 pakfire_filelist_unref(filelist);
1817
1818 return r;
1819}
1820
8ad8d09b
MT
1821static int pakfire_build_install_package(struct pakfire* pakfire,
1822 struct pakfire_package* pkg, void* p) {
1823 struct pakfire_request* request = (struct pakfire_request*)p;
1824
37dade64
MT
1825 return pakfire_request_add_package(request, PAKFIRE_REQ_INSTALL, pkg,
1826 PAKFIRE_REQUEST_ESSENTIAL);
8ad8d09b
MT
1827}
1828
1829static int pakfire_build_install_test(struct pakfire_build* build) {
1830 struct pakfire_request* request = NULL;
1831 struct pakfire_problem* problem = NULL;
1832 struct pakfire_solution* solution = NULL;
1833 const char* s = NULL;
1834 int r;
1835
1836 // Create a new request
1837 r = pakfire_request_create(&request, build->pakfire, 0);
1838 if (r)
1839 goto ERROR;
1840
1841 // Add all packages
1842 r = pakfire_packagelist_walk(build->packages, pakfire_build_install_package, request);
1843
1844 // Solve the request
b3e36a6d 1845 r = pakfire_request_solve(request);
8ad8d09b
MT
1846 switch (r) {
1847 // All okay
1848 case 0:
1849 break;
1850
1851 // Dependency Error
1852 case 2:
1853 ERROR(build->pakfire, "Install test failed:\n");
1854
1855 // Walk through all problems
1856 for (;;) {
1857 r = pakfire_request_next_problem(request, &problem);
1858 if (r)
1859 goto ERROR;
1860
1861 // There are no more problems
1862 if (!problem)
1863 break;
1864
1865 // Format the problem into something human-readable
1866 s = pakfire_problem_to_string(problem);
1867 if (!s)
1868 continue;
1869
1870 ERROR(build->pakfire, " * %s\n", s);
1871
1872 // Walk through all solutions
1873 for (;;) {
1874 r = pakfire_problem_next_solution(problem, &solution);
1875 if (r)
1876 goto ERROR;
1877
1878 // There are no more solutions
1879 if (!solution)
1880 break;
1881
1882 // Format the solution into something human-readable
1883 s = pakfire_solution_to_string(solution);
1884 if (!s)
1885 continue;
1886
1887 ERROR(build->pakfire, " * %s\n", s);
1888 }
1889 }
1890
1891 break;
1892
1893 // Any other errors
1894 default:
1895 goto ERROR;
1896 }
1897
1898ERROR:
1899 if (r)
1900 ERROR(build->pakfire, "Install test failed: %m\n");
1901 if (request)
1902 pakfire_request_unref(request);
1903 if (problem)
1904 pakfire_problem_unref(problem);
1905 if (solution)
1906 pakfire_solution_unref(solution);
1907
1908 return r;
1909}
1910
0f9bb14d
MT
1911static int pakfire_build_post_check(struct pakfire_build* build) {
1912 int r;
1913
1914 // Check for unpackaged files
1915 r = pakfire_build_check_unpackaged_files(build);
1916 if (r)
1917 return r;
1918
62bca61d
MT
1919 // Perform install test
1920 r = pakfire_build_install_test(build);
1921 if (r)
1922 return r;
62bca61d 1923
0f9bb14d
MT
1924 return 0;
1925}
1926
62bca61d
MT
1927static int pakfire_build_copy_package(struct pakfire* pakfire,
1928 struct pakfire_package* pkg, void* p) {
1929 struct pakfire_archive* archive = NULL;
1930 char path[PATH_MAX];
1931 int r;
1932
1933 const char* target = (const char*)p;
1934
1935 if (!target) {
1936 errno = EINVAL;
1937 return 1;
1938 }
1939
1940 // Fetch the package filename
1941 const char* filename = pakfire_package_get_string(pkg, PAKFIRE_PKG_FILENAME);
1942 if (!filename) {
1943 r = 1;
1944 goto ERROR;
1945 }
1946
1947 // Format the destination path
1948 r = pakfire_string_format(path, "%s/%s", target, filename);
1949 if (r)
1950 goto ERROR;
1951
1952 // Open the archive
1953 archive = pakfire_package_get_archive(pkg);
1954 if (!archive) {
1955 r = 1;
1956 goto ERROR;
1957 }
1958
1959 // Copy it to its destination
1960 r = pakfire_archive_copy(archive, path);
1961 if (r)
1962 goto ERROR;
1963
1964ERROR:
1965 if (archive)
1966 pakfire_archive_unref(archive);
1967
1968 return r;
1969}
1970
1971static int pakfire_build_copy_packages(struct pakfire_build* build) {
1972 struct pakfire_repo* local = NULL;
1973 int r = 0;
1974
1975 DEBUG(build->pakfire, "Copying built packages\n");
1976
1977 // Fetch local repository
1978 local = pakfire_get_repo(build->pakfire, PAKFIRE_REPO_LOCAL);
1979
1980 // Copy all packages to the target path
1981 if (*build->target) {
1982 r = pakfire_packagelist_walk(build->packages,
1983 pakfire_build_copy_package, build->target);
1984 if (r)
1985 goto ERROR;
1986 }
1987
1988 // If we have a local repository, we copy all packages to it
1989 if (local) {
1990 const char* path = pakfire_repo_get_path(local);
1991 if (path) {
1992 r = pakfire_packagelist_walk(build->packages,
1993 pakfire_build_copy_package, (void*)path);
1994 if (r)
1995 goto ERROR;
1996 }
1997 }
1998
1999ERROR:
2000 if (local)
2001 pakfire_repo_unref(local);
2002
2003 return r;
2004}
2005
fcc68dff
MT
2006static int pakfire_build_install_source_package(
2007 struct pakfire_build* build, struct pakfire_package* package) {
2008 struct pakfire_request* request = NULL;
2009 struct pakfire_transaction* transaction = NULL;
2010 int r;
2011
2012 // Create a new request
2013 r = pakfire_request_create(&request, build->pakfire, 0);
2014 if (r)
2015 goto ERROR;
2016
2017 // Add the package
2018 r = pakfire_request_add_package(request, PAKFIRE_REQ_INSTALL, package,
2019 PAKFIRE_REQUEST_ESSENTIAL);
2020 if (r)
2021 goto ERROR;
2022
2023 // Solve the request
2024 r = pakfire_request_solve(request);
2025 if (r)
2026 goto ERROR;
2027
2028 // Fetch the transaction
2029 r = pakfire_request_get_transaction(request, &transaction);
2030 if (r)
2031 goto ERROR;
2032
2033 // Set how many packages have been changed
2034 const size_t changes = pakfire_transaction_count(transaction);
2035
2036 // Sanity check to see if we actually try to install anything
2037 if (!changes) {
2038 ERROR(build->pakfire, "The source package did not get installed\n");
2039 r = 1;
2040 goto ERROR;
2041 }
2042
2043 // Run the transaction
2044 r = pakfire_transaction_run(transaction, 0);
2045 if (r)
2046 goto ERROR;
2047
2048ERROR:
2049 if (transaction)
2050 pakfire_transaction_unref(transaction);
2051 if (request)
2052 pakfire_request_unref(request);
2053
2054 return r;
2055}
2056
ea924657 2057PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) {
33a8ba3c 2058 struct pakfire_package* package = NULL;
e57188c0 2059 struct pakfire_parser* makefile = NULL;
6fc3956f 2060 char* buildroot = NULL;
f9a4c0b7 2061 char duration[TIME_STRING_MAX];
ea924657 2062 int r;
01b29895 2063
6fc3956f 2064 // Set buildroot
eb3e6c00
MT
2065 r = pakfire_path(build->pakfire, build->buildroot, "%s",
2066 PAKFIRE_TMP_DIR "/pakfire-buildroot.XXXXXX");
77e26129
MT
2067 if (r)
2068 goto ERROR;
6fc3956f 2069
bf9b62e6
MT
2070 // Open the source package
2071 r = pakfire_commandline_add(build->pakfire, path, &package);
2072 if (r)
33a8ba3c 2073 goto ERROR;
33a8ba3c 2074
74468e4f 2075 const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA);
33a8ba3c
MT
2076
2077 INFO(build->pakfire, "Building %s...\n", nevra);
01b29895 2078
8dbb69f9
MT
2079 // Initialize the build environment
2080 r = pakfire_build_init(build);
2081 if (r)
c0f2502a 2082 goto ERROR;
c0f2502a 2083
fcc68dff
MT
2084 // Install the source package
2085 r = pakfire_build_install_source_package(build, package);
01b29895 2086 if (r) {
fcc68dff 2087 ERROR(build->pakfire, "Could not install the source package: %m\n");
33a8ba3c 2088 goto ERROR;
01b29895
MT
2089 }
2090
6fc3956f
MT
2091 // Create BUILDROOT
2092 buildroot = pakfire_mkdtemp(build->buildroot);
2093 if (!buildroot) {
2094 ERROR(build->pakfire, "Could not create BUILDROOT: %m\n");
2095 goto ERROR;
2096 }
2097
e57188c0
MT
2098 // Open the makefile
2099 r = pakfire_build_read_makefile(build, &makefile, package);
2100 if (r)
01b29895
MT
2101 goto ERROR;
2102
03dc1d52
MT
2103 // Perform the actual build
2104 r = pakfire_build_perform(build, makefile);
2105 if (r)
2106 goto ERROR;
2107
2108 // Create the packages
2109 r = pakfire_build_packages(build, makefile);
01b29895 2110 if (r) {
03dc1d52 2111 ERROR(build->pakfire, "Could not create packages: %m\n");
01b29895
MT
2112 goto ERROR;
2113 }
2114
0f9bb14d
MT
2115 // Perform post build checks
2116 r = pakfire_build_post_check(build);
2117 if (r)
2118 goto ERROR;
2119
62bca61d
MT
2120 // Copy packages to their destination
2121 r = pakfire_build_copy_packages(build);
2122 if (r)
2123 goto ERROR;
2124
f9a4c0b7
MT
2125 // Log duration
2126 r = pakfire_format_time(duration, pakfire_build_duration(build));
2127 if (r)
2128 goto ERROR;
2129
2130 INFO(build->pakfire, "Build successfully completed in %s\n", duration);
2131
01b29895 2132ERROR:
e57188c0
MT
2133 if (makefile)
2134 pakfire_parser_unref(makefile);
33a8ba3c
MT
2135 if (package)
2136 pakfire_package_unref(package);
2137
6fc3956f
MT
2138 // Cleanup buildroot
2139 if (buildroot)
2140 pakfire_rmtree(buildroot, 0);
2141
01b29895
MT
2142 return r;
2143}
2144
ea924657
MT
2145/*
2146 Compatibility function to keep the legacy API.
2147*/
2148PAKFIRE_EXPORT int pakfire_build(struct pakfire* pakfire, const char* path,
2149 const char* target, const char* id, int flags) {
2150 struct pakfire_build* build = NULL;
2151 int r;
2152
2153 // Check if path is set
2154 if (!path) {
2155 errno = EINVAL;
2156 return 1;
2157 }
2158
2159 // Create a new build environment
2160 r = pakfire_build_create(&build, pakfire, id, flags);
2161 if (r)
2162 goto ERROR;
2163
2164 // Set target
2165 if (target) {
2166 r = pakfire_build_set_target(build, target);
2167 if (r)
2168 goto ERROR;
2169 }
2170
2171 // Run build
2172 r = pakfire_build_exec(build, path);
2173
2174ERROR:
2175 if (build)
2176 pakfire_build_unref(build);
2177
2178 return r;
2179}
2180
5f6e42a2
MT
2181int pakfire_build_clean(struct pakfire* pakfire, int flags) {
2182 struct pakfire_repo* local = NULL;
2183 int r = 0;
22a0733e 2184
5f6e42a2
MT
2185 // Fetch local repository
2186 local = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL);
2187 if (!local) {
2188 ERROR(pakfire, "Could not find repository %s: %m\n", PAKFIRE_REPO_LOCAL);
22a0733e
MT
2189 goto ERROR;
2190 }
2191
5f6e42a2
MT
2192 // Destroy everything in it
2193 r = pakfire_repo_clean(local, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
2194 if (r)
22a0733e 2195 goto ERROR;
22a0733e
MT
2196
2197ERROR:
5f6e42a2
MT
2198 if (local)
2199 pakfire_repo_unref(local);
22a0733e
MT
2200
2201 return r;
2202}
2203
5f6e42a2
MT
2204/*
2205 This is a convenience function that sets up a build environment and
2206 then drops the user into an interactive shell.
2207*/
51840d97 2208PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages, int flags) {
5f6e42a2 2209 struct pakfire_build* build = NULL;
48dc31f7 2210 int r;
1a276007 2211
51840d97
MT
2212 // Shells are always interactive
2213 flags |= PAKFIRE_BUILD_INTERACTIVE;
2214
5f6e42a2 2215 // Create a new build environment
51840d97 2216 r = pakfire_build_create(&build, pakfire, NULL, flags);
5f6e42a2
MT
2217 if (r) {
2218 ERROR(pakfire, "Could not create build: %m\n");
2219 goto ERROR;
7f068a08
MT
2220 }
2221
8dbb69f9
MT
2222 // Initialize the build environment
2223 r = pakfire_build_init(build);
2224 if (r)
2225 goto ERROR;
22a0733e 2226
5f6e42a2
MT
2227 // Install any additional packages
2228 if (packages) {
2229 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0, NULL, NULL, NULL);
2230 if (r)
2231 return r;
397371db
MT
2232 }
2233
5f6e42a2
MT
2234 // Run shell
2235 r = pakfire_build_shell(build);
397371db
MT
2236
2237ERROR:
5f6e42a2
MT
2238 if (build)
2239 pakfire_build_unref(build);
397371db
MT
2240
2241 return r;
2242}