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