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