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