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