]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/build.c
build: Abort if there are any unpackaged files
[people/stevee/pakfire.git] / src / libpakfire / build.c
CommitLineData
1a276007
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2021 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
21#include <errno.h>
22#include <stdlib.h>
22a0733e 23#include <sys/mount.h>
cee6363a 24#include <unistd.h>
57e2cf99 25#include <uuid/uuid.h>
1a276007
MT
26
27#include <pakfire/build.h>
f877fdfa 28#include <pakfire/cgroup.h>
2a623cf8 29#include <pakfire/dependencies.h>
1a276007 30#include <pakfire/dist.h>
ae7968a7 31#include <pakfire/file.h>
e545f6ec 32#include <pakfire/i18n.h>
8e99f22d 33#include <pakfire/jail.h>
1a276007 34#include <pakfire/logging.h>
163851bc 35#include <pakfire/mount.h>
4c07774f 36#include <pakfire/package.h>
48c6f2e7 37#include <pakfire/packager.h>
1a276007
MT
38#include <pakfire/parser.h>
39#include <pakfire/private.h>
4651122b 40#include <pakfire/repo.h>
99a56775 41#include <pakfire/request.h>
106d2edd 42#include <pakfire/scriptlet.h>
c0f2502a 43#include <pakfire/snapshot.h>
d973a13d 44#include <pakfire/string.h>
1a276007
MT
45#include <pakfire/util.h>
46
5a06668c
MT
47#define CCACHE_DIR "/var/cache/ccache"
48
882ae03b 49// We guarantee 2 GiB of memory to every build container
0a1284ff 50#define PAKFIRE_BUILD_MEMORY_GUARANTEED (size_t)2 * 1024 * 1024 * 1024
882ae03b 51
de4c8fe6
MT
52// We allow only up to 2048 processes/threads for every build container
53#define PAKFIRE_BUILD_PID_LIMIT (size_t)2048
54
abbad00b
MT
55struct pakfire_build {
56 struct pakfire* pakfire;
57 int nrefs;
58
59 // Flags
60 int flags;
61
62 // Build ID
63 uuid_t id;
f877fdfa
MT
64 char _id[UUID_STR_LEN];
65
a84624be
MT
66 char target[PATH_MAX];
67
f877fdfa
MT
68 // cgroup
69 struct pakfire_cgroup* cgroup;
753ddf74
MT
70
71 // Jail
72 struct pakfire_jail* jail;
e545f6ec
MT
73
74 // Local build repo
75 struct pakfire_repo* repo;
8dbb69f9 76
6fc3956f
MT
77 // Buildroot
78 char buildroot[PATH_MAX];
79
8dbb69f9
MT
80 // States
81 int init:1;
abbad00b
MT
82};
83
1a276007
MT
84#define TEMPLATE \
85 "#!/bin/bash --login\n" \
86 "\n" \
87 "set -e\n" \
88 "set -x\n" \
89 "\n" \
90 "%%{_%s}\n" \
91 "\n" \
92 "exit 0\n"
93
c0f2502a
MT
94static int pakfire_build_has_flag(struct pakfire_build* build, int flag) {
95 return build->flags & flag;
96}
97
5f6e42a2 98/*
7a347de7 99 This function enables the local repository so that it can be accessed by
5f6e42a2
MT
100 a pakfire instance running inside the chroot.
101*/
102static int pakfire_build_enable_repos(struct pakfire_build* build) {
103 char repopath[PATH_MAX];
5f6e42a2
MT
104 int r = 1;
105
5f6e42a2 106 // Fetch repository configuration
e545f6ec 107 char* config = pakfire_repo_get_config(build->repo);
5f6e42a2
MT
108 if (!config) {
109 ERROR(build->pakfire, "Could not generate repository configuration: %m\n");
110 goto ERROR;
111 }
112
e545f6ec 113 const char* path = pakfire_repo_get_path(build->repo);
5f6e42a2
MT
114
115 // Make sure the source directory exists
116 r = pakfire_mkdir(path, 0700);
117 if (r && errno != EEXIST) {
118 ERROR(build->pakfire, "Could not create repository directory %s: %m\n", path);
119 goto ERROR;
120 }
121
122 // Bind-mount the repository data read-only
7a347de7 123 r = pakfire_jail_bind(build->jail, path, path, MS_RDONLY);
5f6e42a2
MT
124 if (r) {
125 ERROR(build->pakfire, "Could not bind-mount the repository at %s: %m\n", path);
126 goto ERROR;
127 }
128
129 // Make path for configuration file
77e26129
MT
130 r = pakfire_path(build->pakfire, repopath,
131 "%s", PAKFIRE_CONFIG_DIR "/repos/" PAKFIRE_REPO_LOCAL ".repo");
132 if (r)
5f6e42a2
MT
133 goto ERROR;
134
c2803561
MT
135 // Write repository configuration
136 r = pakfire_file_write(build->pakfire, repopath, 0, 0, 0644, "%s", config);
137 if (r) {
5f6e42a2 138 ERROR(build->pakfire, "Could not write repository configuration: %m\n");
5f6e42a2
MT
139 goto ERROR;
140 }
141
5f6e42a2 142ERROR:
5f6e42a2
MT
143 if (config)
144 free(config);
5f6e42a2
MT
145
146 return r;
147}
148
149/*
150 Drops the user into a shell
151*/
152static int pakfire_build_shell(struct pakfire_build* build) {
153 int r;
154
155 // Export local repository if running in interactive mode
156 r = pakfire_build_enable_repos(build);
157 if (r)
158 return r;
159
160 // Run shell
161 return pakfire_jail_shell(build->jail);
162}
163
779e16de 164static int pakfire_build_run_script(struct pakfire_build* build, const char* filename,
12b9b39f 165 const char* args[], char** output) {
8d0f3a35
MT
166 char* script = NULL;
167 size_t size = 0;
168 char path[PATH_MAX];
169
779e16de 170 DEBUG(build->pakfire, "Running build script '%s'...\n", filename);
8d0f3a35
MT
171
172 // Make the source path
173 pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename);
174
175 // Open the source script
176 FILE* f = fopen(path, "r");
177 if (!f) {
779e16de 178 ERROR(build->pakfire, "Could not open %s: %m\n", path);
8d0f3a35
MT
179 return 1;
180 }
181
182 // Read the script into memory
183 int r = pakfire_read_file_into_buffer(f, &script, &size);
184 if (r) {
779e16de 185 ERROR(build->pakfire, "Could not read script into memory: %m\n");
8d0f3a35
MT
186 goto ERROR;
187 }
188
8e99f22d 189 // Execute the script
001bcbd7 190 r = pakfire_jail_exec_script(build->jail, script, size, args, output);
8d0f3a35 191 if (r) {
779e16de 192 ERROR(build->pakfire, "Script '%s' failed with status %d\n", filename, r);
8d0f3a35
MT
193 }
194
195ERROR:
196 if (script)
197 free(script);
198
199 return r;
200}
201
bd95a433 202static int pakfire_build_find_dependencies(struct pakfire_build* build,
1bbbfb9e 203 struct pakfire_package* pkg, struct pakfire_filelist* filelist, const char* buildroot) {
12b9b39f
MT
204 char* provides = NULL;
205 char* requires = NULL;
5b0b3dc2
MT
206 char path[PATH_MAX];
207
208 // Allocate path to write the filelist to
77e26129
MT
209 int r = pakfire_path(build->pakfire, path, "%s", "/var/tmp/.pakfire-filelist.XXXXXX");
210 if (r)
5b0b3dc2
MT
211 return 1;
212
213 // Create a temporary file
214 FILE* f = pakfire_mktemp(path);
215 if (!f)
216 goto ERROR;
217
218 // Write filelist to the temporary file
219 r = pakfire_filelist_export(filelist, f);
220 fclose(f);
221 if (r) {
bd95a433 222 ERROR(build->pakfire, "Could not export filelist: %m\n");
5b0b3dc2
MT
223 goto ERROR;
224 }
225
bd95a433 226 const char* root = pakfire_get_path(build->pakfire);
5b0b3dc2
MT
227
228 // Pass buildroot and the filelist as arguments
229 const char* args[] = {
230 buildroot, pakfire_path_relpath(root, path), NULL
231 };
232
233 // Find all provides
779e16de 234 r = pakfire_build_run_script(build, "find-provides", args, &provides);
5b0b3dc2 235 if (r) {
bd95a433 236 ERROR(build->pakfire, "find-provides returned with error %d\n", r);
5b0b3dc2
MT
237 goto ERROR;
238 }
239
240 // Find all requires
779e16de 241 r = pakfire_build_run_script(build, "find-requires", args, &requires);
5b0b3dc2 242 if (r) {
bd95a433 243 ERROR(build->pakfire, "find-requires returned with error %d\n", r);
5b0b3dc2
MT
244 goto ERROR;
245 }
246
247 // Add all provides to the package
248 if (provides) {
83d93f33
MT
249 r = pakfire_str2deps(build->pakfire, pkg,
250 pakfire_package_add_provides, provides);
251 if (r) {
252 ERROR(build->pakfire, "Could not add provides: %m\n");
253 goto ERROR;
5b0b3dc2
MT
254 }
255 }
256
257 // Add all requires to the package
258 if (requires) {
83d93f33
MT
259 r = pakfire_str2deps(build->pakfire, pkg,
260 pakfire_package_add_requires, requires);
261 if (r) {
262 ERROR(build->pakfire, "Could not add provides: %m\n");
263 goto ERROR;
5b0b3dc2
MT
264 }
265 }
266
267 // Success
268 r = 0;
269
270ERROR:
12b9b39f 271 if (provides)
5b0b3dc2 272 free(provides);
12b9b39f 273 if (requires)
5b0b3dc2 274 free(requires);
5b0b3dc2
MT
275 unlink(path);
276
277 return r;
278}
279
73543ae3
MT
280static int append_to_array(const char*** array, const char* s) {
281 unsigned int length = 0;
282
283 // Determine the length of the existing array
284 if (*array) {
285 for (const char** element = *array; *element; element++)
286 length++;
287 }
288
289 // Allocate space
290 *array = reallocarray(*array, length + 2, sizeof(**array));
291 if (!*array)
292 return 1;
293
294 // Append s and terminate the array
295 (*array)[length] = s;
296 (*array)[length + 1] = NULL;
297
298 return 0;
299}
300
adfa3573
MT
301static int pakfire_build_package_add_files(struct pakfire_build* build,
302 struct pakfire_parser* makefile, const char* buildroot, const char* namespace,
303 struct pakfire_package* pkg, struct pakfire_packager* packager) {
1bbbfb9e 304 struct pakfire_filelist* filelist = NULL;
73543ae3
MT
305 int r = 1;
306
307 char** files = NULL;
308 const char** includes = NULL;
309 const char** excludes = NULL;
310
311 // Fetch filelist from makefile
312 files = pakfire_parser_get_split(makefile, namespace, "files", '\n');
313
314 // No files to package?
315 if (!files)
316 return 0;
317
73543ae3
MT
318 // Split into includes and excludes
319 for (char** file = files; *file; file++) {
320 if (**file == '!')
321 r = append_to_array(&excludes, *file);
322 else
323 r = append_to_array(&includes, *file);
324 if (r)
325 goto ERROR;
326 }
327
328 // Allocate a new filelist
adfa3573 329 r = pakfire_filelist_create(&filelist, build->pakfire);
73543ae3
MT
330 if (r)
331 goto ERROR;
332
333 // Scan for files
77e26129 334 r = pakfire_filelist_scan(filelist, build->buildroot, includes, excludes);
73543ae3
MT
335 if (r)
336 goto ERROR;
337
ae7968a7 338 const size_t length = pakfire_filelist_size(filelist);
adfa3573 339 DEBUG(build->pakfire, "%zu file(s) found\n", length);
73543ae3 340
84556307
MT
341 // Nothing to do if the filelist is empty
342 if (!length)
343 goto ERROR;
344
5b0b3dc2 345 // Find dependencies
bd95a433 346 r = pakfire_build_find_dependencies(build, pkg, filelist, buildroot);
5b0b3dc2 347 if (r) {
adfa3573 348 ERROR(build->pakfire, "Finding dependencies failed: %m\n");
5b0b3dc2
MT
349 goto ERROR;
350 }
351
ae7968a7 352 // Add all files to the package
5d5da764 353 r = pakfire_packager_add_files(packager, filelist);
814b7fee
MT
354 if (r)
355 goto ERROR;
73543ae3
MT
356
357ERROR:
358 if (filelist)
359 pakfire_filelist_unref(filelist);
360 if (files) {
361 for (char** file = files; *file; file++)
362 free(*file);
363 free(files);
364 }
365 if (includes)
366 free(includes);
367 if (excludes)
368 free(excludes);
73543ae3
MT
369
370 return r;
371}
372
9df89a8f
MT
373static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build,
374 struct pakfire_package* pkg, struct pakfire_scriptlet* scriptlet) {
12b9b39f 375 char* prerequires = NULL;
106d2edd
MT
376 char path[PATH_MAX];
377 size_t size;
378 int r;
379
9df89a8f 380 const char* root = pakfire_get_path(build->pakfire);
106d2edd
MT
381
382 // Make filename
77e26129
MT
383 r = pakfire_path(build->pakfire, path, "%s", "/var/tmp/.pakfire-scriptlet.XXXXXX");
384 if (r)
106d2edd
MT
385 return r;
386
387 // Fetch scriptlet payload
388 const char* data = pakfire_scriptlet_get_data(scriptlet, &size);
389
390 // Create a temporary file
391 FILE* f = pakfire_mktemp(path);
392 if (!f)
393 return 1;
394
395 // Write scriptlet
396 ssize_t bytes_written = fwrite(data, 1, size, f);
397 if (bytes_written < 0) {
9df89a8f 398 ERROR(build->pakfire, "Could not write to %s: %m\n", path);
106d2edd
MT
399 fclose(f);
400 goto ERROR;
401 }
402
403 // Close file
404 fclose(f);
405
406 // Build commandline
407 const char* args[] = {
408 pakfire_path_relpath(root, path),
409 NULL,
410 };
411
412 // Find all pre-requires
779e16de 413 r = pakfire_build_run_script(build, "find-prerequires", args, &prerequires);
106d2edd
MT
414 if (r)
415 goto ERROR;
416
417 // Add all pre-requires to the package
418 if (prerequires) {
83d93f33
MT
419 r = pakfire_str2deps(build->pakfire, pkg,
420 pakfire_package_add_prerequires, prerequires);
421 if (r) {
422 ERROR(build->pakfire, "Could not add pre-requires: %m\n");
423 goto ERROR;
106d2edd
MT
424 }
425 }
426
427ERROR:
428 if (r && *path)
429 unlink(path);
12b9b39f 430 if (prerequires)
106d2edd 431 free(prerequires);
106d2edd
MT
432 return r;
433}
434
9df89a8f
MT
435static int pakfire_build_package_add_scriptlet(struct pakfire_build* build,
436 struct pakfire_package* pkg, struct pakfire_packager* packager,
437 const char* type, const char* data) {
106d2edd
MT
438 struct pakfire_scriptlet* scriptlet = NULL;
439 char* shell = NULL;
440 int r;
441
442 // Wrap scriptlet into a shell script
443 r = asprintf(&shell, "#!/bin/sh\n\nset -e\n\n%s\n\nexit 0\n", data);
444 if (r < 0)
445 goto ERROR;
446
447 // Create a scriptlet
9df89a8f 448 r = pakfire_scriptlet_create(&scriptlet, build->pakfire, type, shell, 0);
106d2edd
MT
449 if (r)
450 goto ERROR;
451
452 // Add it to the package
453 r = pakfire_packager_add_scriptlet(packager, scriptlet);
454 if (r) {
9df89a8f 455 ERROR(build->pakfire, "Could not add scriptlet %s\n", type);
106d2edd
MT
456 goto ERROR;
457 }
458
459 // Add scriptlet requirements
9df89a8f 460 r = pakfire_build_add_scriptlet_requires(build, pkg, scriptlet);
106d2edd 461 if (r) {
9df89a8f 462 ERROR(build->pakfire, "Could not add scriptlet requirements: %m\n");
106d2edd
MT
463 goto ERROR;
464 }
465
466 // Success
467 r = 0;
468
469ERROR:
470 if (scriptlet)
471 pakfire_scriptlet_unref(scriptlet);
472 if (shell)
473 free(shell);
474
475 return r;
476}
477
9df89a8f
MT
478static int pakfire_build_package_add_scriptlets(struct pakfire_build* build,
479 struct pakfire_parser* makefile, const char* namespace,
480 struct pakfire_package* pkg, struct pakfire_packager* packager) {
106d2edd
MT
481 char name[NAME_MAX];
482 int r;
483
484 for (const char** type = pakfire_scriptlet_types; *type; type++) {
485 r = pakfire_string_format(name, "scriptlet:%s", *type);
a60955af 486 if (r)
106d2edd
MT
487 return r;
488
489 // Fetch the scriptlet
490 char* data = pakfire_parser_get(makefile, namespace, name);
491 if (!data)
492 continue;
493
494 // Add it to the package
9df89a8f 495 r = pakfire_build_package_add_scriptlet(build, pkg, packager, *type, data);
106d2edd
MT
496 if (r) {
497 free(data);
498 return r;
499 }
500
501 free(data);
502 }
503
504 return 0;
505}
506
0e177be7
MT
507static int pakfire_build_package(struct pakfire_build* build, struct pakfire_parser* makefile,
508 const char* buildroot, const char* namespace) {
31480bee 509 struct pakfire_package* pkg = NULL;
48c6f2e7 510 struct pakfire_packager* packager = NULL;
73543ae3 511
a50bde9c
MT
512 int r = 1;
513
514 // Expand the handle into the package name
4c07774f 515 char* name = pakfire_parser_expand(makefile, "packages", namespace);
a50bde9c 516 if (!name) {
0e177be7 517 ERROR(build->pakfire, "Could not get package name: %m\n");
a50bde9c
MT
518 goto ERROR;
519 }
520
0e177be7 521 INFO(build->pakfire, "Building package '%s'...\n", name);
0f9bb14d 522 DEBUG(build->pakfire, " buildroot = %s\n", buildroot);
a50bde9c 523
4c07774f 524 // Fetch build architecture
0e177be7 525 const char* arch = pakfire_get_arch(build->pakfire);
4c07774f
MT
526 if (!arch)
527 goto ERROR;
528
4c07774f 529 // Fetch package from makefile
6ebd55e2 530 r = pakfire_parser_create_package(makefile, &pkg, NULL, namespace, arch);
4c07774f 531 if (r) {
0e177be7 532 ERROR(build->pakfire, "Could not create package from makefile: %m\n");
4c07774f
MT
533 goto ERROR;
534 }
535
ca002cae
MT
536 // Set distribution
537 const char* distribution = pakfire_parser_get(makefile, NULL, "DISTRO_NAME");
538 if (distribution)
539 pakfire_package_set_distribution(pkg, distribution);
540
7996020a 541 // Set build ID
0e177be7 542 pakfire_package_set_build_id_from_uuid(pkg, &build->id);
7996020a 543
571539a7
MT
544 // Set source package
545 const char* source_name = pakfire_parser_get(makefile, NULL, "name");
546 if (source_name)
547 pakfire_package_set_source_name(pkg, source_name);
548
549 // Set source EVR
550 const char* source_evr = pakfire_parser_get(makefile, NULL, "evr");
551 if (source_evr)
552 pakfire_package_set_source_evr(pkg, source_evr);
553
554 // Set source arch
555 pakfire_package_set_source_arch(pkg, "src");
556
48c6f2e7 557 // Create a packager
0e177be7 558 r = pakfire_packager_create(&packager, build->pakfire, pkg);
48c6f2e7
MT
559 if (r)
560 goto ERROR;
561
73543ae3 562 // Add files
adfa3573 563 r = pakfire_build_package_add_files(build, makefile, buildroot, namespace,
5b0b3dc2 564 pkg, packager);
73543ae3
MT
565 if (r)
566 goto ERROR;
567
106d2edd 568 // Add scriptlets
9df89a8f 569 r = pakfire_build_package_add_scriptlets(build, makefile, namespace,
0e177be7 570 pkg, packager);
106d2edd
MT
571 if (r)
572 goto ERROR;
573
ae7968a7 574 // Write the finished package
0e177be7 575 r = pakfire_packager_finish_to_directory(packager, build->target, NULL);
ae7968a7 576 if (r) {
0e177be7 577 ERROR(build->pakfire, "pakfire_packager_finish() failed: %m\n");
ae7968a7
MT
578 goto ERROR;
579 }
580
4c07774f 581#ifdef ENABLE_DEBUG
23e22751 582 char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
4c07774f 583 if (dump) {
0e177be7 584 DEBUG(build->pakfire, "%s\n", dump);
4c07774f
MT
585 free(dump);
586 }
587#endif
a50bde9c
MT
588
589 // Success
590 r = 0;
591
592ERROR:
48c6f2e7
MT
593 if (packager)
594 pakfire_packager_unref(packager);
4c07774f
MT
595 if (pkg)
596 pakfire_package_unref(pkg);
a50bde9c
MT
597 if (name)
598 free(name);
599
600 return r;
601}
602
0e177be7 603static int pakfire_build_packages(struct pakfire_build* build,
03dc1d52 604 struct pakfire_parser* makefile) {
0e177be7 605 DEBUG(build->pakfire, "Creating packages...");
a50bde9c
MT
606 int r = 1;
607
03dc1d52
MT
608 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
609
a50bde9c
MT
610 // Fetch a list all all packages
611 char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*");
612 if (!packages) {
0e177be7 613 ERROR(build->pakfire, "Could not find any packages: %m\n");
a50bde9c
MT
614 goto ERROR;
615 }
616
617 unsigned int num_packages = 0;
618
619 // Count how many packages we have
620 for (char** package = packages; *package; package++)
621 num_packages++;
622
0e177be7 623 DEBUG(build->pakfire, "Found %d package(s)\n", num_packages);
a50bde9c
MT
624
625 // Build packages in reverse order
626 for (int i = num_packages - 1; i >= 0; i--) {
0e177be7 627 r = pakfire_build_package(build, makefile, buildroot, packages[i]);
a50bde9c
MT
628 if (r)
629 goto ERROR;
630 }
631
632 // Success
633 r = 0;
634
635ERROR:
636 if (packages)
637 free(packages);
638
639 return r;
640}
641
46748697
MT
642static int pakfire_build_stage(struct pakfire_build* build,
643 struct pakfire_parser* makefile, const char* stage) {
1a276007
MT
644 char template[1024];
645
646 // Prepare template for this stage
647 int r = pakfire_string_format(template, TEMPLATE, stage);
a60955af 648 if (r)
1a276007
MT
649 return r;
650
2ffd21f9
MT
651 // Fetch the environment
652 char** envp = pakfire_parser_make_environ(makefile);
653
1a276007
MT
654 // Create the build script
655 char* script = pakfire_parser_expand(makefile, "build", template);
656 if (!script) {
46748697
MT
657 ERROR(build->pakfire, "Could not generate the build script for stage '%s': %m\n",
658 stage);
1a276007
MT
659 goto ERROR;
660 }
661
46748697 662 INFO(build->pakfire, "Running build stage '%s'\n", stage);
1a276007 663
830f5d18 664 // Import environment
44d5ebfd
MT
665 // XXX is this a good idea?
666 r = pakfire_jail_import_env(build->jail, (const char**)envp);
830f5d18 667 if (r) {
46748697 668 ERROR(build->pakfire, "Could not import environment: %m\n");
830f5d18
MT
669 goto ERROR;
670 }
671
672 // Run the script
44d5ebfd 673 r = pakfire_jail_exec_script(build->jail, script, strlen(script), NULL, NULL);
1a276007 674 if (r) {
46748697 675 ERROR(build->pakfire, "Build stage '%s' failed with status %d\n", stage, r);
1a276007
MT
676 }
677
678ERROR:
2ffd21f9
MT
679 if (envp) {
680 for (char** e = envp; *e; e++)
681 free(*e);
682 free(envp);
683 }
1a276007
MT
684 if (script)
685 free(script);
686
687 return r;
688}
689
39f45b30
MT
690static const char* post_build_scripts[] = {
691 "remove-static-libs",
1f62ffae 692 "check-symlinks",
022b794e 693 "check-unsafe-files",
0b5f0bbc 694 "check-libraries",
16043831 695 "check-rpaths",
99aee237 696 "check-buildroot",
7e1fec6f 697 "check-include",
dd864160 698 "check-hardening",
ae703321 699 "check-interpreters",
2504194a 700 "check-fhs",
39f45b30 701 "compress-man-pages",
ffb65de6 702 "strip",
39f45b30
MT
703 NULL,
704};
705
03dc1d52
MT
706static int pakfire_build_run_post_build_scripts(struct pakfire_build* build) {
707 // Fetch buildroot
708 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
709
39f45b30
MT
710 // Set default arguments for build scripts
711 const char* args[] = {
712 buildroot, NULL
713 };
714
715 // Run them one by one
716 for (const char** script = post_build_scripts; *script; script++) {
779e16de 717 int r = pakfire_build_run_script(build, *script, args, NULL);
39f45b30
MT
718 if (r)
719 return r;
720 }
721
722 return 0;
723}
724
abbad00b 725static void pakfire_build_free(struct pakfire_build* build) {
e545f6ec
MT
726 if (build->repo)
727 pakfire_repo_unref(build->repo);
728
753ddf74
MT
729 if (build->jail)
730 pakfire_jail_unref(build->jail);
731
49fd9926
MT
732 if (build->cgroup) {
733 // Destroy the cgroup
734 pakfire_cgroup_destroy(build->cgroup);
735
736 // Free it
737 pakfire_cgroup_unref(build->cgroup);
738 }
739
abbad00b
MT
740 pakfire_unref(build->pakfire);
741 free(build);
742}
743
744static int pakfire_build_parse_id(struct pakfire_build* build, const char* id) {
745 int r;
746
747 // Try parsing the Build ID
748 if (id) {
749 r = uuid_parse(id, build->id);
750 if (r) {
751 ERROR(build->pakfire, "Could not parse build ID '%s'\n", id);
1df321ff 752 errno = EINVAL;
abbad00b
MT
753 return r;
754 }
755
756 // Otherwise initialize the Build ID with something random
757 } else {
758 uuid_generate_random(build->id);
759 }
760
f877fdfa
MT
761 // Store the ID as string, too
762 uuid_unparse_lower(build->id, build->_id);
763
764 return 0;
765}
766
a84624be
MT
767/*
768 Sets the default target
769*/
770static int pakfire_build_set_default_target(struct pakfire_build* build) {
771 // Look for the "local" repository
e545f6ec
MT
772 if (build->repo) {
773 const char* target = pakfire_repo_get_path(build->repo);
a84624be
MT
774 if (target)
775 pakfire_string_set(build->target, target);
a84624be
MT
776 }
777
778 // Default to the current working directory
779 if (!*build->target) {
780 char* cwd = getcwd(build->target, sizeof(build->target));
781 if (!cwd) {
782 ERROR(build->pakfire, "getcwd() failed: %m\n");
783 return 1;
784 }
785 }
786
787 return 0;
788}
789
f877fdfa
MT
790/*
791 Sets up a new cgroup for this build
792*/
793static int pakfire_build_setup_cgroup(struct pakfire_build* build) {
0a1284ff 794 struct pakfire_config* config = NULL;
f877fdfa
MT
795 char path[PATH_MAX];
796 int r;
797
798 // Compose path
799 r = pakfire_string_format(path, "pakfire/build-%s", build->_id);
a60955af 800 if (r) {
f877fdfa 801 ERROR(build->pakfire, "Could not compose path for cgroup: %m\n");
0a1284ff 802 goto ERROR;
f877fdfa
MT
803 }
804
805 // Create a new cgroup
806 r = pakfire_cgroup_open(&build->cgroup, build->pakfire, path,
807 PAKFIRE_CGROUP_ENABLE_ACCOUNTING);
808 if (r) {
809 ERROR(build->pakfire, "Could not create cgroup for build %s: %m\n", build->_id);
0a1284ff 810 goto ERROR;
f877fdfa
MT
811 }
812
0a1284ff
MT
813 // Fetch config
814 config = pakfire_get_config(build->pakfire);
815 if (!config)
816 goto ERROR;
817
882ae03b 818 // Guarantee some minimum memory
0a1284ff
MT
819 size_t memory_guaranteed = pakfire_config_get_bytes(config, "build",
820 "memory_guaranteed", PAKFIRE_BUILD_MEMORY_GUARANTEED);
821 if (memory_guaranteed) {
822 r = pakfire_cgroup_set_guaranteed_memory(build->cgroup, memory_guaranteed);
823 if (r)
824 goto ERROR;
825 }
882ae03b 826
5b877e84
MT
827 // Limit memory
828 size_t memory_limit = pakfire_config_get_bytes(config, "build", "memory_limit", 0);
829 if (memory_limit) {
830 r = pakfire_cgroup_set_memory_limit(build->cgroup, memory_limit);
831 if (r)
832 goto ERROR;
833 }
834
de4c8fe6 835 // Set PID limit
0a1284ff
MT
836 size_t pid_limit = pakfire_config_get_int(config, "build",
837 "pid_limit", PAKFIRE_BUILD_PID_LIMIT);
838 if (pid_limit) {
839 r = pakfire_cgroup_set_pid_limit(build->cgroup, pid_limit);
840 if (r)
841 goto ERROR;
842 }
de4c8fe6 843
0a1284ff
MT
844ERROR:
845 if (config)
846 pakfire_config_unref(config);
847
848 return r;
abbad00b
MT
849}
850
753ddf74
MT
851/*
852 Sets up a new jail for this build
853*/
854static int pakfire_build_setup_jail(struct pakfire_build* build) {
855 int r;
856
857 // Create a new jail
858 r = pakfire_jail_create(&build->jail, build->pakfire, 0);
859 if (r) {
860 ERROR(build->pakfire, "Could not create jail for build %s: %m\n", build->_id);
861 return r;
862 }
863
15503538
MT
864 // Connect the jail to our cgroup
865 r = pakfire_jail_set_cgroup(build->jail, build->cgroup);
866 if (r) {
867 ERROR(build->pakfire, "Could not set cgroup for jail: %m\n");
868 return r;
869 }
870
753ddf74
MT
871 // Done
872 return 0;
873}
874
5a06668c
MT
875/*
876 Sets up the ccache for this build
877*/
878static int pakfire_build_setup_ccache(struct pakfire_build* build) {
879 char path[PATH_MAX];
880 int r;
881
882 // Check if we want a ccache
883 if (pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_CCACHE)) {
884 DEBUG(build->pakfire, "ccache usage has been disabled for this build\n");
0570ccd2
MT
885
886 // Set CCACHE_DISABLE=1 so that if ccache is installed, it will disable itself
887 r = pakfire_jail_set_env(build->jail, "CCACHE_DISABLE", "1");
888 if (r) {
889 ERROR(build->pakfire, "Could not disable ccache: %m\n");
890 return r;
891 }
892
5a06668c
MT
893 return 0;
894 }
895
896 // Compose path
df1409ef
MT
897 r = pakfire_cache_path(build->pakfire, path, "%s", "ccache");
898 if (r) {
5a06668c
MT
899 ERROR(build->pakfire, "Could not compose ccache path: %m\n");
900 return 1;
901 }
902
903 DEBUG(build->pakfire, "Mounting ccache from %s\n", path);
904
905 // Ensure path exists
906 r = pakfire_mkdir(path, 0755);
907 if (r && errno != EEXIST) {
908 ERROR(build->pakfire, "Could not create ccache directory %s: %m\n", path);
909 return r;
910 }
911
912 // Bind-mount the directory
913 r = pakfire_jail_bind(build->jail, path, CCACHE_DIR, MS_NOSUID|MS_NOEXEC|MS_NODEV);
914 if (r) {
915 ERROR(build->pakfire, "Could not mount ccache: %m\n");
916 return r;
917 }
918
919 return 0;
920}
921
e545f6ec
MT
922static int pakfire_build_setup_repos(struct pakfire_build* build) {
923 int r;
924
925 // Create a new repository
926 r = pakfire_repo_create(&build->repo, build->pakfire, PAKFIRE_REPO_LOCAL);
927 if (r) {
928 ERROR(build->pakfire, "Could not create repository %s: %m\n", PAKFIRE_REPO_LOCAL);
929 return 1;
930 }
931
932 // Set description
933 pakfire_repo_set_description(build->repo, _("Locally Built Packages"));
934
935 // Set path
936 pakfire_repo_set_baseurl(build->repo, PAKFIRE_REPO_LOCAL_PATH);
937
938 // Set priority
939 pakfire_repo_set_priority(build->repo, PAKFIRE_REPO_LOCAL_PRIORITY);
940
941 return 0;
942}
943
abbad00b
MT
944PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
945 struct pakfire* pakfire, const char* id, int flags) {
946 int r;
947
948 // Allocate build object
949 struct pakfire_build* b = calloc(1, sizeof(*b));
950 if (!b)
951 return 1;
952
953 // Reference pakfire
954 b->pakfire = pakfire_ref(pakfire);
955
956 // Initialize reference counter
957 b->nrefs = 1;
958
959 // Copy flags
960 b->flags = flags;
961
962 // Parse ID
963 r = pakfire_build_parse_id(b, id);
964 if (r)
965 goto ERROR;
966
e545f6ec
MT
967 // Setup repos
968 r = pakfire_build_setup_repos(b);
969 if (r)
970 goto ERROR;
971
a84624be
MT
972 // Set target
973 r = pakfire_build_set_default_target(b);
974 if (r)
975 goto ERROR;
976
f877fdfa
MT
977 // Create cgroup
978 r = pakfire_build_setup_cgroup(b);
979 if (r)
980 goto ERROR;
981
753ddf74
MT
982 // Create jail
983 r = pakfire_build_setup_jail(b);
984 if (r)
985 goto ERROR;
986
5a06668c
MT
987 // Setup ccache
988 r = pakfire_build_setup_ccache(b);
989 if (r)
990 goto ERROR;
991
abbad00b
MT
992 *build = b;
993 return 0;
994
995ERROR:
996 pakfire_build_free(b);
997 return r;
998}
999
77a1964f 1000PAKFIRE_EXPORT struct pakfire_build* pakfire_build_ref(struct pakfire_build* build) {
abbad00b
MT
1001 ++build->nrefs;
1002
1003 return build;
1004}
1005
77a1964f 1006PAKFIRE_EXPORT struct pakfire_build* pakfire_build_unref(struct pakfire_build* build) {
abbad00b
MT
1007 if (--build->nrefs > 0)
1008 return build;
1009
1010 pakfire_build_free(build);
1011 return NULL;
1012}
1013
ea924657
MT
1014PAKFIRE_EXPORT int pakfire_build_set_target(
1015 struct pakfire_build* build, const char* target) {
a60955af 1016 return pakfire_string_set(build->target, target);
ea924657 1017}
57e2cf99 1018
c0f2502a
MT
1019static int pakfire_build_install_packages(struct pakfire_build* build,
1020 int* snapshot_needs_update) {
1021 char** packages = NULL;
1022 int r = 1;
1023
1024 // Fetch configuration
1025 struct pakfire_config* config = pakfire_get_config(build->pakfire);
1026 if (!config) {
1027 ERROR(build->pakfire, "Could not fetch configuration: %m\n");
1028 r = 1;
1029 goto ERROR;
1030 }
1031
1032 // Fetch build environment
1033 const char* requires = pakfire_config_get(config, "build", "requires", NULL);
1034 if (!requires) {
1035 ERROR(build->pakfire, "No build requirements have been defined\n");
1036 goto ERROR;
1037 }
1038
1039 // Split requirements into packages
d973a13d 1040 packages = pakfire_string_split(requires, ',');
c0f2502a
MT
1041 if (!packages)
1042 goto ERROR;
1043
1044 int changed = 0;
1045
1046 // Install everything
1047 r = pakfire_install(build->pakfire, 0, 0, (const char**)packages, NULL, 0,
1048 &changed, NULL, NULL);
1049 if (r) {
1050 ERROR(build->pakfire, "Could not install build dependencies: %m\n");
1051 goto ERROR;
1052 }
1053
1054 // Mark snapshot as changed if new packages were installed
1055 if (changed)
1056 *snapshot_needs_update = 1;
1057
1058 // Update everything
1059 r = pakfire_sync(build->pakfire, 0, 0, &changed, NULL, NULL);
1060 if (r) {
1061 ERROR(build->pakfire, "Could not update packages: %m\n");
1062 goto ERROR;
1063 }
1064
1065 // Has anything changed?
1066 if (changed)
1067 *snapshot_needs_update = 1;
1068
1069 // Success
1070 r = 0;
1071
1072ERROR:
1073 if (config)
1074 pakfire_config_unref(config);
1075
1076 if (packages) {
1077 for (char** package = packages; *package; package++)
1078 free(*package);
1079 free(packages);
1080 }
1081
1082 return r;
1083}
1084
8dbb69f9
MT
1085/*
1086 Initializes the build environment
1087*/
1088static int pakfire_build_init(struct pakfire_build* build) {
c0f2502a
MT
1089 char path[PATH_MAX];
1090 int r;
1091
8dbb69f9
MT
1092 // Don't do it again
1093 if (build->init) {
1094 DEBUG(build->pakfire, "Build environment has already been initialized\n");
1095 return 0;
1096 }
1097
e545f6ec
MT
1098 // Refresh the local repository
1099 if (build->repo) {
f3c4b36a 1100 r = pakfire_repo_refresh(build->repo, 0);
e545f6ec
MT
1101 if (r) {
1102 ERROR(build->pakfire, "Could not refresh the local repository: %m\n");
1103 return 1;
1104 }
1105 }
1106
1ea7b360 1107 // Compose path for snapshot
df1409ef
MT
1108 r = pakfire_cache_path(build->pakfire, path, "%s", "snapshot.tar.zst");
1109 if (r) {
1ea7b360
MT
1110 ERROR(build->pakfire, "Could not compose snapshot path: %m\n");
1111 return 1;
1112 }
1113
ffc2630d
MT
1114 // Tells us whether we need to (re-)create the snapshot
1115 int snapshot_needs_update = 0;
c0f2502a 1116
1ea7b360 1117 // Extract snapshot
ffc2630d 1118 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT)) {
ffc2630d 1119 // Try restoring the snapshot
4667a2ca
MT
1120 r = pakfire_snapshot_restore(build->pakfire, path);
1121 if (r && errno != ENOENT)
1122 return r;
c0f2502a
MT
1123 }
1124
c0f2502a
MT
1125 // Install or update any build dependencies
1126 r = pakfire_build_install_packages(build, &snapshot_needs_update);
1127 if (r)
1128 return r;
1129
ffc2630d
MT
1130 // Update the snapshot if there were changes
1131 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT) && snapshot_needs_update) {
c0f2502a 1132 // Create a new snapshot
4667a2ca 1133 r = pakfire_snapshot_create(build->pakfire, path);
c0f2502a
MT
1134 if (r)
1135 return r;
1136 }
1137
8dbb69f9
MT
1138 // Mark as initialized
1139 build->init = 1;
1140
c0f2502a
MT
1141 return 0;
1142}
1143
e57188c0
MT
1144static int pakfire_build_read_makefile(struct pakfire_build* build,
1145 struct pakfire_parser** parser, struct pakfire_package* package) {
e57188c0
MT
1146 char path[PATH_MAX];
1147 int r;
1148
1149 struct pakfire_parser_error* error = NULL;
1150
1151 const char* nevra = pakfire_package_get_nevra(package);
1152 const char* name = pakfire_package_get_name(package);
1153
1154 // Compose path to makefile
77e26129 1155 r = pakfire_path(build->pakfire, path, "/usr/src/packages/%s/%s.nm", nevra, name);
e57188c0
MT
1156 if (r < 0)
1157 return 1;
1158
1159 // Read makefile
1160 r = pakfire_read_makefile(parser, build->pakfire, path, &error);
1161 if (r) {
1162 if (error) {
1163 ERROR(build->pakfire, "Could not parse makefile %s: %s\n", path,
1164 pakfire_parser_error_get_message(error));
1165 } else {
1166 ERROR(build->pakfire, "Could not parse makefile %s: %m\n", path);
1167 }
1168
1169 goto ERROR;
1170 }
1171
6fc3956f 1172 // Set BUILDROOT
5caf06e3 1173 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
6fc3956f
MT
1174 if (buildroot)
1175 pakfire_parser_set(*parser, NULL, "BUILDROOT", buildroot, 0);
1176
e57188c0
MT
1177ERROR:
1178 if (error)
1179 pakfire_parser_error_unref(error);
1180
1181 return r;
1182}
1183
03dc1d52
MT
1184static int pakfire_build_perform(struct pakfire_build* build,
1185 struct pakfire_parser* makefile) {
1186 int r;
1187
b8786cdb
MT
1188 // Prepare the build
1189 r = pakfire_build_stage(build, makefile, "prepare");
1190 if (r)
1191 goto ERROR;
03dc1d52 1192
b8786cdb
MT
1193 // Perform the build
1194 r = pakfire_build_stage(build, makefile, "build");
1195 if (r)
1196 goto ERROR;
1197
1198 // Test the build
1199 r = pakfire_build_stage(build, makefile, "test");
1200 if (r)
1201 goto ERROR;
1202
1203 // Install everything
1204 r = pakfire_build_stage(build, makefile, "install");
1205 if (r)
1206 goto ERROR;
03dc1d52
MT
1207
1208 // Run post build scripts
b8786cdb
MT
1209 r = pakfire_build_run_post_build_scripts(build);
1210 if (r)
1211 goto ERROR;
1212
1213 // Done!
1214 return 0;
1215
1216ERROR:
1217 // Drop to a shell for debugging
1218 if (pakfire_has_flag(build->pakfire, PAKFIRE_BUILD_INTERACTIVE))
1219 pakfire_build_shell(build);
1220
1221 return r;
03dc1d52
MT
1222}
1223
0f9bb14d
MT
1224static int __pakfire_build_unpackaged_file(struct pakfire* pakfire,
1225 struct pakfire_file* file, void* p) {
1226 char* s = pakfire_file_dump(file);
1227 if (s)
1228 ERROR(pakfire, "%s\n", s);
1229
1230 return 0;
1231}
1232
1233static int pakfire_build_check_unpackaged_files(struct pakfire_build* build) {
1234 struct pakfire_filelist* filelist = NULL;
1235 int r;
1236
1237 // Create a new filelist
1238 r = pakfire_filelist_create(&filelist, build->pakfire);
1239 if (r)
1240 goto ERROR;
1241
1242 // Scan for all files in BUILDROOT
1243 r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL);
1244 if (r)
1245 goto ERROR;
1246
1247 const size_t length = pakfire_filelist_size(filelist);
1248
1249 if (length) {
1250 ERROR(build->pakfire, "Unpackaged files found:\n");
1251
1252 r = pakfire_filelist_walk(filelist, __pakfire_build_unpackaged_file, NULL);
1253 if (r)
1254 goto ERROR;
1255
1256 // Report an error
1257 r = 1;
1258 }
1259
1260ERROR:
1261 if (filelist)
1262 pakfire_filelist_unref(filelist);
1263
1264 return r;
1265}
1266
1267static int pakfire_build_post_check(struct pakfire_build* build) {
1268 int r;
1269
1270 // Check for unpackaged files
1271 r = pakfire_build_check_unpackaged_files(build);
1272 if (r)
1273 return r;
1274
1275 return 0;
1276}
1277
ea924657 1278PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) {
33a8ba3c
MT
1279 struct pakfire_archive* archive = NULL;
1280 struct pakfire_package* package = NULL;
e57188c0 1281 struct pakfire_parser* makefile = NULL;
6fc3956f 1282 char* buildroot = NULL;
ea924657 1283 int r;
01b29895 1284
6fc3956f 1285 // Set buildroot
77e26129
MT
1286 r = pakfire_path(build->pakfire, build->buildroot, "%s", "/var/tmp/.pakfire-buildroot.XXXXXX");
1287 if (r)
1288 goto ERROR;
6fc3956f 1289
33a8ba3c
MT
1290 // Open source archive
1291 r = pakfire_archive_open(&archive, build->pakfire, path);
1292 if (r) {
1293 ERROR(build->pakfire, "Could not open source archive %s: %m\n", path);
1294 goto ERROR;
1295 }
1296
1297 // Fetch package metadata
1298 r = pakfire_archive_make_package(archive, NULL, &package);
1299 if (r) {
1300 ERROR(build->pakfire, "Could not read package metadata: %m\n");
1301 goto ERROR;
1302 }
1303
33a8ba3c 1304 const char* nevra = pakfire_package_get_nevra(package);
33a8ba3c
MT
1305
1306 INFO(build->pakfire, "Building %s...\n", nevra);
01b29895 1307
8dbb69f9
MT
1308 // Initialize the build environment
1309 r = pakfire_build_init(build);
1310 if (r)
c0f2502a 1311 goto ERROR;
c0f2502a 1312
01b29895
MT
1313 const char* packages[] = {
1314 path, NULL
1315 };
1316
1317 // Install the package into the build environment
ea924657 1318 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, PAKFIRE_REQUEST_ESSENTIAL,
99a56775 1319 NULL, NULL, NULL);
01b29895 1320 if (r) {
ea924657 1321 ERROR(build->pakfire, "Could not install %s\n", path);
33a8ba3c 1322 goto ERROR;
01b29895
MT
1323 }
1324
6fc3956f
MT
1325 // Create BUILDROOT
1326 buildroot = pakfire_mkdtemp(build->buildroot);
1327 if (!buildroot) {
1328 ERROR(build->pakfire, "Could not create BUILDROOT: %m\n");
1329 goto ERROR;
1330 }
1331
e57188c0
MT
1332 // Open the makefile
1333 r = pakfire_build_read_makefile(build, &makefile, package);
1334 if (r)
01b29895
MT
1335 goto ERROR;
1336
03dc1d52
MT
1337 // Perform the actual build
1338 r = pakfire_build_perform(build, makefile);
1339 if (r)
1340 goto ERROR;
1341
1342 // Create the packages
1343 r = pakfire_build_packages(build, makefile);
01b29895 1344 if (r) {
03dc1d52 1345 ERROR(build->pakfire, "Could not create packages: %m\n");
01b29895
MT
1346 goto ERROR;
1347 }
1348
0f9bb14d
MT
1349 // Perform post build checks
1350 r = pakfire_build_post_check(build);
1351 if (r)
1352 goto ERROR;
1353
01b29895 1354ERROR:
e57188c0
MT
1355 if (makefile)
1356 pakfire_parser_unref(makefile);
33a8ba3c
MT
1357 if (archive)
1358 pakfire_archive_unref(archive);
1359 if (package)
1360 pakfire_package_unref(package);
1361
6fc3956f
MT
1362 // Cleanup buildroot
1363 if (buildroot)
1364 pakfire_rmtree(buildroot, 0);
1365
01b29895
MT
1366 return r;
1367}
1368
ea924657
MT
1369/*
1370 Compatibility function to keep the legacy API.
1371*/
1372PAKFIRE_EXPORT int pakfire_build(struct pakfire* pakfire, const char* path,
1373 const char* target, const char* id, int flags) {
1374 struct pakfire_build* build = NULL;
1375 int r;
1376
1377 // Check if path is set
1378 if (!path) {
1379 errno = EINVAL;
1380 return 1;
1381 }
1382
1383 // Create a new build environment
1384 r = pakfire_build_create(&build, pakfire, id, flags);
1385 if (r)
1386 goto ERROR;
1387
1388 // Set target
1389 if (target) {
1390 r = pakfire_build_set_target(build, target);
1391 if (r)
1392 goto ERROR;
1393 }
1394
1395 // Run build
1396 r = pakfire_build_exec(build, path);
1397
1398ERROR:
1399 if (build)
1400 pakfire_build_unref(build);
1401
1402 return r;
1403}
1404
5f6e42a2
MT
1405int pakfire_build_clean(struct pakfire* pakfire, int flags) {
1406 struct pakfire_repo* local = NULL;
1407 int r = 0;
22a0733e 1408
5f6e42a2
MT
1409 // Fetch local repository
1410 local = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL);
1411 if (!local) {
1412 ERROR(pakfire, "Could not find repository %s: %m\n", PAKFIRE_REPO_LOCAL);
22a0733e
MT
1413 goto ERROR;
1414 }
1415
5f6e42a2
MT
1416 // Destroy everything in it
1417 r = pakfire_repo_clean(local, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
1418 if (r)
22a0733e 1419 goto ERROR;
22a0733e
MT
1420
1421ERROR:
5f6e42a2
MT
1422 if (local)
1423 pakfire_repo_unref(local);
22a0733e
MT
1424
1425 return r;
1426}
1427
5f6e42a2
MT
1428/*
1429 This is a convenience function that sets up a build environment and
1430 then drops the user into an interactive shell.
1431*/
7f068a08 1432PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages) {
5f6e42a2 1433 struct pakfire_build* build = NULL;
48dc31f7 1434 int r;
1a276007 1435
5f6e42a2 1436 // Create a new build environment
db4f234f 1437 r = pakfire_build_create(&build, pakfire, NULL, PAKFIRE_BUILD_INTERACTIVE);
5f6e42a2
MT
1438 if (r) {
1439 ERROR(pakfire, "Could not create build: %m\n");
1440 goto ERROR;
7f068a08
MT
1441 }
1442
8dbb69f9
MT
1443 // Initialize the build environment
1444 r = pakfire_build_init(build);
1445 if (r)
1446 goto ERROR;
22a0733e 1447
5f6e42a2
MT
1448 // Install any additional packages
1449 if (packages) {
1450 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0, NULL, NULL, NULL);
1451 if (r)
1452 return r;
397371db
MT
1453 }
1454
5f6e42a2
MT
1455 // Run shell
1456 r = pakfire_build_shell(build);
397371db
MT
1457
1458ERROR:
5f6e42a2
MT
1459 if (build)
1460 pakfire_build_unref(build);
397371db
MT
1461
1462 return r;
1463}