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