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