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