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