]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/build.c
build: Pass context to pakfire_build_package_add_files
[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>
1a276007 29#include <pakfire/dist.h>
ae7968a7 30#include <pakfire/file.h>
8e99f22d 31#include <pakfire/jail.h>
1a276007 32#include <pakfire/logging.h>
163851bc 33#include <pakfire/mount.h>
4c07774f 34#include <pakfire/package.h>
48c6f2e7 35#include <pakfire/packager.h>
1a276007
MT
36#include <pakfire/parser.h>
37#include <pakfire/private.h>
4651122b 38#include <pakfire/repo.h>
99a56775 39#include <pakfire/request.h>
106d2edd 40#include <pakfire/scriptlet.h>
1a276007
MT
41#include <pakfire/util.h>
42
abbad00b
MT
43struct pakfire_build {
44 struct pakfire* pakfire;
45 int nrefs;
46
47 // Flags
48 int flags;
49
50 // Build ID
51 uuid_t id;
f877fdfa
MT
52 char _id[UUID_STR_LEN];
53
a84624be
MT
54 char target[PATH_MAX];
55
f877fdfa
MT
56 // cgroup
57 struct pakfire_cgroup* cgroup;
753ddf74
MT
58
59 // Jail
60 struct pakfire_jail* jail;
abbad00b
MT
61};
62
1a276007
MT
63static const char* stages[] = {
64 "prepare",
65 "build",
66 "test",
67 "install",
68 NULL,
69};
70
71#define TEMPLATE \
72 "#!/bin/bash --login\n" \
73 "\n" \
74 "set -e\n" \
75 "set -x\n" \
76 "\n" \
77 "%%{_%s}\n" \
78 "\n" \
79 "exit 0\n"
80
8e99f22d
MT
81/*
82 This function creates a new jail which is pre-configured for a build job.
83
84 TODO Add resource limits
85*/
86static struct pakfire_jail* pakfire_build_make_jail(struct pakfire* pakfire) {
87 struct pakfire_jail* jail = NULL;
88 int r;
89
90 // Create a new jail
91 r = pakfire_jail_create(&jail, pakfire, 0);
92 if (r)
93 goto ERROR;
94
95 return jail;
96
97ERROR:
98 if (jail)
99 pakfire_jail_unref(jail);
100
101 return NULL;
102}
103
0de6bb30
MT
104static int pakfire_build_run_script(struct pakfire* pakfire, const char* filename,
105 const char* args[], char*** output) {
8e99f22d 106 struct pakfire_jail* jail = NULL;
8d0f3a35
MT
107 char* script = NULL;
108 size_t size = 0;
109 char path[PATH_MAX];
110
111 DEBUG(pakfire, "Running build script '%s'...\n", filename);
112
113 // Make the source path
114 pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename);
115
116 // Open the source script
117 FILE* f = fopen(path, "r");
118 if (!f) {
b1772bfb 119 ERROR(pakfire, "Could not open %s: %m\n", path);
8d0f3a35
MT
120 return 1;
121 }
122
123 // Read the script into memory
124 int r = pakfire_read_file_into_buffer(f, &script, &size);
125 if (r) {
b1772bfb 126 ERROR(pakfire, "Could not read script into memory: %m\n");
8d0f3a35
MT
127 goto ERROR;
128 }
129
8e99f22d
MT
130 // Create a new jail
131 jail = pakfire_build_make_jail(pakfire);
132 if (!jail)
133 goto ERROR;
134
8e99f22d 135 // Execute the script
0de6bb30 136 r = pakfire_jail_exec_script(jail, script, size, args, output);
8d0f3a35
MT
137 if (r) {
138 ERROR(pakfire, "Script '%s' failed with status %d\n", filename, r);
139 }
140
141ERROR:
8e99f22d
MT
142 if (jail)
143 pakfire_jail_unref(jail);
8d0f3a35
MT
144 if (script)
145 free(script);
146
147 return r;
148}
149
5b0b3dc2
MT
150static int find_dependency(char** haystack, const char* needle) {
151 if (!needle) {
152 errno = EINVAL;
153 return -1;
154 }
155
156 if (!haystack)
157 return 0;
158
159 for (char** element = haystack; *element; element++) {
160 if (strcmp(needle, *element) == 0)
161 return 1;
162 }
163
164 return 0;
165}
166
ac4c607b 167static int pakfire_build_find_dependencies(struct pakfire* pakfire,
1bbbfb9e 168 struct pakfire_package* pkg, struct pakfire_filelist* filelist, const char* buildroot) {
5b0b3dc2
MT
169 char** provides = NULL;
170 char** requires = NULL;
171 char path[PATH_MAX];
172
173 // Allocate path to write the filelist to
174 int r = pakfire_make_path(pakfire, path, "tmp/.pakfire-filelist.XXXXXX");
175 if (r < 0)
176 return 1;
177
178 // Create a temporary file
179 FILE* f = pakfire_mktemp(path);
180 if (!f)
181 goto ERROR;
182
183 // Write filelist to the temporary file
184 r = pakfire_filelist_export(filelist, f);
185 fclose(f);
186 if (r) {
b1772bfb 187 ERROR(pakfire, "Could not export filelist: %m\n");
5b0b3dc2
MT
188 goto ERROR;
189 }
190
191 const char* root = pakfire_get_path(pakfire);
192
193 // Pass buildroot and the filelist as arguments
194 const char* args[] = {
195 buildroot, pakfire_path_relpath(root, path), NULL
196 };
197
198 // Find all provides
0de6bb30 199 r = pakfire_build_run_script(pakfire, "find-provides", args, &provides);
5b0b3dc2
MT
200 if (r) {
201 ERROR(pakfire, "find-provides returned with error %d\n", r);
202 goto ERROR;
203 }
204
205 // Find all requires
0de6bb30 206 r = pakfire_build_run_script(pakfire, "find-requires", args, &requires);
5b0b3dc2
MT
207 if (r) {
208 ERROR(pakfire, "find-requires returned with error %d\n", r);
209 goto ERROR;
210 }
211
212 // Add all provides to the package
213 if (provides) {
214 for (char** element = provides; *element; element++) {
215 DEBUG(pakfire, "Adding provides: %s\n", *element);
216 pakfire_package_add_provides(pkg, *element);
217 }
218 }
219
220 // Add all requires to the package
221 if (requires) {
222 for (char** element = requires; *element; element++) {
223 // Skip adding this requirement if also provided by this package
224 if (find_dependency(provides, *element))
225 continue;
226
227 DEBUG(pakfire, "Adding requires: %s\n", *element);
228 pakfire_package_add_requires(pkg, *element);
229 }
230 }
231
232 // Success
233 r = 0;
234
235ERROR:
236 if (provides) {
237 for (char** element = provides; *element; element++)
238 free(*element);
239 free(provides);
240 }
241 if (requires) {
242 for (char** element = requires; *element; element++)
243 free(*element);
244 free(requires);
245 }
246 unlink(path);
247
248 return r;
249}
250
73543ae3
MT
251static int append_to_array(const char*** array, const char* s) {
252 unsigned int length = 0;
253
254 // Determine the length of the existing array
255 if (*array) {
256 for (const char** element = *array; *element; element++)
257 length++;
258 }
259
260 // Allocate space
261 *array = reallocarray(*array, length + 2, sizeof(**array));
262 if (!*array)
263 return 1;
264
265 // Append s and terminate the array
266 (*array)[length] = s;
267 (*array)[length + 1] = NULL;
268
269 return 0;
270}
271
adfa3573
MT
272static int pakfire_build_package_add_files(struct pakfire_build* build,
273 struct pakfire_parser* makefile, const char* buildroot, const char* namespace,
274 struct pakfire_package* pkg, struct pakfire_packager* packager) {
1bbbfb9e 275 struct pakfire_filelist* filelist = NULL;
73543ae3
MT
276 char path[PATH_MAX];
277 int r = 1;
278
279 char** files = NULL;
280 const char** includes = NULL;
281 const char** excludes = NULL;
282
283 // Fetch filelist from makefile
284 files = pakfire_parser_get_split(makefile, namespace, "files", '\n');
285
286 // No files to package?
287 if (!files)
288 return 0;
289
73543ae3 290 // Convert to absolute path
adfa3573 291 pakfire_make_path(build->pakfire, path, buildroot);
73543ae3
MT
292
293 // Split into includes and excludes
294 for (char** file = files; *file; file++) {
295 if (**file == '!')
296 r = append_to_array(&excludes, *file);
297 else
298 r = append_to_array(&includes, *file);
299 if (r)
300 goto ERROR;
301 }
302
303 // Allocate a new filelist
adfa3573 304 r = pakfire_filelist_create(&filelist, build->pakfire);
73543ae3
MT
305 if (r)
306 goto ERROR;
307
308 // Scan for files
309 r = pakfire_filelist_scan(filelist, path, includes, excludes);
310 if (r)
311 goto ERROR;
312
ae7968a7 313 const size_t length = pakfire_filelist_size(filelist);
adfa3573 314 DEBUG(build->pakfire, "%zu file(s) found\n", length);
73543ae3 315
84556307
MT
316 // Nothing to do if the filelist is empty
317 if (!length)
318 goto ERROR;
319
5b0b3dc2 320 // Find dependencies
adfa3573 321 r = pakfire_build_find_dependencies(build->pakfire, pkg, filelist, buildroot);
5b0b3dc2 322 if (r) {
adfa3573 323 ERROR(build->pakfire, "Finding dependencies failed: %m\n");
5b0b3dc2
MT
324 goto ERROR;
325 }
326
ae7968a7
MT
327 // Add all files to the package
328 for (unsigned int i = 0; i < length; i++) {
5803b5f6 329 struct pakfire_file* file = pakfire_filelist_get(filelist, i);
ae7968a7
MT
330
331 // Add the file to the package
332 r = pakfire_packager_add(
333 packager,
334 pakfire_file_get_abspath(file),
335 pakfire_file_get_path(file)
336 );
337 if (r) {
338 pakfire_file_unref(file);
339 goto ERROR;
340 }
341
3fca5032
MT
342 // Remove the file after it was packaged
343 r = pakfire_file_cleanup(file);
344 if (r) {
345 pakfire_file_unref(file);
346 goto ERROR;
347 }
348
ae7968a7
MT
349 pakfire_file_unref(file);
350 }
73543ae3
MT
351
352ERROR:
353 if (filelist)
354 pakfire_filelist_unref(filelist);
355 if (files) {
356 for (char** file = files; *file; file++)
357 free(*file);
358 free(files);
359 }
360 if (includes)
361 free(includes);
362 if (excludes)
363 free(excludes);
73543ae3
MT
364
365 return r;
366}
367
9df89a8f
MT
368static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build,
369 struct pakfire_package* pkg, struct pakfire_scriptlet* scriptlet) {
106d2edd
MT
370 char** prerequires = NULL;
371 char path[PATH_MAX];
372 size_t size;
373 int r;
374
9df89a8f 375 const char* root = pakfire_get_path(build->pakfire);
106d2edd
MT
376
377 // Make filename
9df89a8f 378 r = pakfire_make_path(build->pakfire, path, "tmp/.pakfire-scriptlet.XXXXXX");
106d2edd
MT
379 if (r < 0)
380 return r;
381
382 // Fetch scriptlet payload
383 const char* data = pakfire_scriptlet_get_data(scriptlet, &size);
384
385 // Create a temporary file
386 FILE* f = pakfire_mktemp(path);
387 if (!f)
388 return 1;
389
390 // Write scriptlet
391 ssize_t bytes_written = fwrite(data, 1, size, f);
392 if (bytes_written < 0) {
9df89a8f 393 ERROR(build->pakfire, "Could not write to %s: %m\n", path);
106d2edd
MT
394 fclose(f);
395 goto ERROR;
396 }
397
398 // Close file
399 fclose(f);
400
401 // Build commandline
402 const char* args[] = {
403 pakfire_path_relpath(root, path),
404 NULL,
405 };
406
407 // Find all pre-requires
9df89a8f 408 r = pakfire_build_run_script(build->pakfire, "find-prerequires", args, &prerequires);
106d2edd
MT
409 if (r)
410 goto ERROR;
411
412 // Add all pre-requires to the package
413 if (prerequires) {
414 for (char** element = prerequires; *element; element++) {
9df89a8f 415 DEBUG(build->pakfire, "Adding pre-requires: %s\n", *element);
106d2edd
MT
416 pakfire_package_add_prerequires(pkg, *element);
417 }
418 }
419
420ERROR:
421 if (r && *path)
422 unlink(path);
423 if (prerequires) {
424 for (char** element = prerequires; *element; element++)
425 free(*element);
426 free(prerequires);
427 }
428 return r;
429}
430
9df89a8f
MT
431static int pakfire_build_package_add_scriptlet(struct pakfire_build* build,
432 struct pakfire_package* pkg, struct pakfire_packager* packager,
433 const char* type, const char* data) {
106d2edd
MT
434 struct pakfire_scriptlet* scriptlet = NULL;
435 char* shell = NULL;
436 int r;
437
438 // Wrap scriptlet into a shell script
439 r = asprintf(&shell, "#!/bin/sh\n\nset -e\n\n%s\n\nexit 0\n", data);
440 if (r < 0)
441 goto ERROR;
442
443 // Create a scriptlet
9df89a8f 444 r = pakfire_scriptlet_create(&scriptlet, build->pakfire, type, shell, 0);
106d2edd
MT
445 if (r)
446 goto ERROR;
447
448 // Add it to the package
449 r = pakfire_packager_add_scriptlet(packager, scriptlet);
450 if (r) {
9df89a8f 451 ERROR(build->pakfire, "Could not add scriptlet %s\n", type);
106d2edd
MT
452 goto ERROR;
453 }
454
455 // Add scriptlet requirements
9df89a8f 456 r = pakfire_build_add_scriptlet_requires(build, pkg, scriptlet);
106d2edd 457 if (r) {
9df89a8f 458 ERROR(build->pakfire, "Could not add scriptlet requirements: %m\n");
106d2edd
MT
459 goto ERROR;
460 }
461
462 // Success
463 r = 0;
464
465ERROR:
466 if (scriptlet)
467 pakfire_scriptlet_unref(scriptlet);
468 if (shell)
469 free(shell);
470
471 return r;
472}
473
9df89a8f
MT
474static int pakfire_build_package_add_scriptlets(struct pakfire_build* build,
475 struct pakfire_parser* makefile, const char* namespace,
476 struct pakfire_package* pkg, struct pakfire_packager* packager) {
106d2edd
MT
477 char name[NAME_MAX];
478 int r;
479
480 for (const char** type = pakfire_scriptlet_types; *type; type++) {
481 r = pakfire_string_format(name, "scriptlet:%s", *type);
482 if (r < 0)
483 return r;
484
485 // Fetch the scriptlet
486 char* data = pakfire_parser_get(makefile, namespace, name);
487 if (!data)
488 continue;
489
490 // Add it to the package
9df89a8f 491 r = pakfire_build_package_add_scriptlet(build, pkg, packager, *type, data);
106d2edd
MT
492 if (r) {
493 free(data);
494 return r;
495 }
496
497 free(data);
498 }
499
500 return 0;
501}
502
0e177be7
MT
503static int pakfire_build_package(struct pakfire_build* build, struct pakfire_parser* makefile,
504 const char* buildroot, const char* namespace) {
31480bee 505 struct pakfire_package* pkg = NULL;
48c6f2e7 506 struct pakfire_packager* packager = NULL;
73543ae3 507
a50bde9c
MT
508 int r = 1;
509
510 // Expand the handle into the package name
4c07774f 511 char* name = pakfire_parser_expand(makefile, "packages", namespace);
a50bde9c 512 if (!name) {
0e177be7 513 ERROR(build->pakfire, "Could not get package name: %m\n");
a50bde9c
MT
514 goto ERROR;
515 }
516
0e177be7 517 INFO(build->pakfire, "Building package '%s'...\n", name);
a50bde9c 518
4c07774f 519 // Fetch build architecture
0e177be7 520 const char* arch = pakfire_get_arch(build->pakfire);
4c07774f
MT
521 if (!arch)
522 goto ERROR;
523
4c07774f 524 // Fetch package from makefile
6ebd55e2 525 r = pakfire_parser_create_package(makefile, &pkg, NULL, namespace, arch);
4c07774f 526 if (r) {
0e177be7 527 ERROR(build->pakfire, "Could not create package from makefile: %m\n");
4c07774f
MT
528 goto ERROR;
529 }
530
ca002cae
MT
531 // Set distribution
532 const char* distribution = pakfire_parser_get(makefile, NULL, "DISTRO_NAME");
533 if (distribution)
534 pakfire_package_set_distribution(pkg, distribution);
535
7996020a 536 // Set build ID
0e177be7 537 pakfire_package_set_build_id_from_uuid(pkg, &build->id);
7996020a 538
571539a7
MT
539 // Set source package
540 const char* source_name = pakfire_parser_get(makefile, NULL, "name");
541 if (source_name)
542 pakfire_package_set_source_name(pkg, source_name);
543
544 // Set source EVR
545 const char* source_evr = pakfire_parser_get(makefile, NULL, "evr");
546 if (source_evr)
547 pakfire_package_set_source_evr(pkg, source_evr);
548
549 // Set source arch
550 pakfire_package_set_source_arch(pkg, "src");
551
48c6f2e7 552 // Create a packager
0e177be7 553 r = pakfire_packager_create(&packager, build->pakfire, pkg);
48c6f2e7
MT
554 if (r)
555 goto ERROR;
556
73543ae3 557 // Add files
adfa3573 558 r = pakfire_build_package_add_files(build, makefile, buildroot, namespace,
5b0b3dc2 559 pkg, packager);
73543ae3
MT
560 if (r)
561 goto ERROR;
562
106d2edd 563 // Add scriptlets
9df89a8f 564 r = pakfire_build_package_add_scriptlets(build, makefile, namespace,
0e177be7 565 pkg, packager);
106d2edd
MT
566 if (r)
567 goto ERROR;
568
ae7968a7 569 // Write the finished package
0e177be7 570 r = pakfire_packager_finish_to_directory(packager, build->target, NULL);
ae7968a7 571 if (r) {
0e177be7 572 ERROR(build->pakfire, "pakfire_packager_finish() failed: %m\n");
ae7968a7
MT
573 goto ERROR;
574 }
575
4c07774f 576#ifdef ENABLE_DEBUG
23e22751 577 char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
4c07774f 578 if (dump) {
0e177be7 579 DEBUG(build->pakfire, "%s\n", dump);
4c07774f
MT
580 free(dump);
581 }
582#endif
a50bde9c
MT
583
584 // Success
585 r = 0;
586
587ERROR:
48c6f2e7
MT
588 if (packager)
589 pakfire_packager_unref(packager);
4c07774f
MT
590 if (pkg)
591 pakfire_package_unref(pkg);
a50bde9c
MT
592 if (name)
593 free(name);
594
595 return r;
596}
597
0e177be7
MT
598static int pakfire_build_packages(struct pakfire_build* build,
599 struct pakfire_parser* makefile, const char* buildroot) {
600 DEBUG(build->pakfire, "Creating packages...");
a50bde9c
MT
601 int r = 1;
602
603 // Fetch a list all all packages
604 char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*");
605 if (!packages) {
0e177be7 606 ERROR(build->pakfire, "Could not find any packages: %m\n");
a50bde9c
MT
607 goto ERROR;
608 }
609
610 unsigned int num_packages = 0;
611
612 // Count how many packages we have
613 for (char** package = packages; *package; package++)
614 num_packages++;
615
0e177be7 616 DEBUG(build->pakfire, "Found %d package(s)\n", num_packages);
a50bde9c
MT
617
618 // Build packages in reverse order
619 for (int i = num_packages - 1; i >= 0; i--) {
0e177be7 620 r = pakfire_build_package(build, makefile, buildroot, packages[i]);
a50bde9c
MT
621 if (r)
622 goto ERROR;
623 }
624
625 // Success
626 r = 0;
627
628ERROR:
629 if (packages)
630 free(packages);
631
632 return r;
633}
634
46748697
MT
635static int pakfire_build_stage(struct pakfire_build* build,
636 struct pakfire_parser* makefile, const char* stage) {
830f5d18 637 struct pakfire_jail* jail = NULL;
1a276007
MT
638 char template[1024];
639
640 // Prepare template for this stage
641 int r = pakfire_string_format(template, TEMPLATE, stage);
642 if (r < 0)
643 return r;
644
2ffd21f9
MT
645 // Fetch the environment
646 char** envp = pakfire_parser_make_environ(makefile);
647
1a276007
MT
648 // Create the build script
649 char* script = pakfire_parser_expand(makefile, "build", template);
650 if (!script) {
46748697
MT
651 ERROR(build->pakfire, "Could not generate the build script for stage '%s': %m\n",
652 stage);
1a276007
MT
653 goto ERROR;
654 }
655
46748697 656 INFO(build->pakfire, "Running build stage '%s'\n", stage);
1a276007 657
830f5d18 658 // Create a new jail
46748697 659 jail = pakfire_build_make_jail(build->pakfire);
830f5d18
MT
660 if (!jail)
661 goto ERROR;
662
663 // Import environment
664 r = pakfire_jail_import_env(jail, (const char**)envp);
665 if (r) {
46748697 666 ERROR(build->pakfire, "Could not import environment: %m\n");
830f5d18
MT
667 goto ERROR;
668 }
669
670 // Run the script
0de6bb30 671 r = pakfire_jail_exec_script(jail, script, strlen(script), NULL, NULL);
1a276007 672 if (r) {
46748697 673 ERROR(build->pakfire, "Build stage '%s' failed with status %d\n", stage, r);
1a276007
MT
674 }
675
676ERROR:
830f5d18
MT
677 if (jail)
678 pakfire_jail_unref(jail);
2ffd21f9
MT
679 if (envp) {
680 for (char** e = envp; *e; e++)
681 free(*e);
682 free(envp);
683 }
1a276007
MT
684 if (script)
685 free(script);
686
687 return r;
688}
689
39f45b30
MT
690static const char* post_build_scripts[] = {
691 "remove-static-libs",
1f62ffae 692 "check-symlinks",
022b794e 693 "check-unsafe-files",
0b5f0bbc 694 "check-libraries",
16043831 695 "check-rpaths",
99aee237 696 "check-buildroot",
7e1fec6f 697 "check-include",
dd864160 698 "check-hardening",
ae703321 699 "check-interpreters",
2504194a 700 "check-fhs",
39f45b30 701 "compress-man-pages",
ffb65de6 702 "strip",
39f45b30
MT
703 NULL,
704};
705
247dd943
MT
706static int pakfire_build_run_post_build_scripts(
707 struct pakfire_build* build, const char* buildroot) {
39f45b30
MT
708 // Set default arguments for build scripts
709 const char* args[] = {
710 buildroot, NULL
711 };
712
713 // Run them one by one
714 for (const char** script = post_build_scripts; *script; script++) {
247dd943 715 int r = pakfire_build_run_script(build->pakfire, *script, args, NULL);
39f45b30
MT
716 if (r)
717 return r;
718 }
719
720 return 0;
721}
722
87bb7749 723static int pakfire_build_makefile(struct pakfire_build* build, const char* path) {
657a5c72 724 struct pakfire_parser* makefile = NULL;
3d4011eb 725 char buildroot[PATH_MAX];
1a276007 726 struct pakfire_parser_error* error = NULL;
01b29895 727 int r = 1;
f0893704 728
87bb7749 729 const char* root = pakfire_get_path(build->pakfire);
3d4011eb
MT
730
731 // Create BUILDROOT
87bb7749 732 pakfire_make_path(build->pakfire, buildroot, "/tmp/.buildroot.XXXXXX");
3d4011eb 733 if (!pakfire_mkdtemp(buildroot))
f0893704 734 goto ERROR;
3d4011eb
MT
735
736 // Make relative BUILDROOT path
737 const char* buildroot_rel = pakfire_path_relpath(root, buildroot);
738 if (!buildroot_rel)
f0893704 739 goto ERROR;
3d4011eb 740
1a276007 741 // Read makefile
87bb7749 742 r = pakfire_read_makefile(&makefile, build->pakfire, path, &error);
1a276007
MT
743 if (r) {
744 if (error) {
87bb7749 745 ERROR(build->pakfire, "Could not parse makefile %s: %s\n", path,
1a276007
MT
746 pakfire_parser_error_get_message(error));
747 pakfire_parser_error_unref(error);
748 } else {
87bb7749 749 ERROR(build->pakfire, "Could not parse makefile %s: %m\n", path);
1a276007
MT
750 }
751
752 goto ERROR;
753 }
754
3d4011eb 755 // Set BUILDROOT
fea2e884 756 pakfire_parser_set(makefile, NULL, "BUILDROOT", buildroot_rel, 0);
3d4011eb 757
1a276007
MT
758 // Run through all build stages
759 for (const char** stage = stages; *stage; stage++) {
46748697 760 r = pakfire_build_stage(build, makefile, *stage);
1a276007
MT
761 if (r) {
762 // Drop to a shell for debugging
87bb7749
MT
763 if (pakfire_has_flag(build->pakfire, PAKFIRE_FLAGS_INTERACTIVE))
764 pakfire_jail_shell(build->pakfire);
1a276007
MT
765
766 goto ERROR;
767 }
768 }
769
39f45b30 770 // Run post build scripts
247dd943 771 r = pakfire_build_run_post_build_scripts(build, buildroot_rel);
8d0f3a35
MT
772 if (r)
773 goto ERROR;
774
a50bde9c 775 // Create the packages
0e177be7 776 r = pakfire_build_packages(build, makefile, buildroot_rel);
a50bde9c 777 if (r) {
87bb7749 778 ERROR(build->pakfire, "Could not create packages: %m\n");
a50bde9c
MT
779 goto ERROR;
780 }
1a276007
MT
781
782ERROR:
783 if (makefile)
784 pakfire_parser_unref(makefile);
785
3d4011eb
MT
786 // Remove buildroot
787 pakfire_rmtree(buildroot, 0);
788
1a276007
MT
789 return r;
790}
791
abbad00b 792static void pakfire_build_free(struct pakfire_build* build) {
753ddf74
MT
793 if (build->jail)
794 pakfire_jail_unref(build->jail);
795
49fd9926
MT
796 if (build->cgroup) {
797 // Destroy the cgroup
798 pakfire_cgroup_destroy(build->cgroup);
799
800 // Free it
801 pakfire_cgroup_unref(build->cgroup);
802 }
803
abbad00b
MT
804 pakfire_unref(build->pakfire);
805 free(build);
806}
807
808static int pakfire_build_parse_id(struct pakfire_build* build, const char* id) {
809 int r;
810
811 // Try parsing the Build ID
812 if (id) {
813 r = uuid_parse(id, build->id);
814 if (r) {
815 ERROR(build->pakfire, "Could not parse build ID '%s'\n", id);
1df321ff 816 errno = EINVAL;
abbad00b
MT
817 return r;
818 }
819
820 // Otherwise initialize the Build ID with something random
821 } else {
822 uuid_generate_random(build->id);
823 }
824
f877fdfa
MT
825 // Store the ID as string, too
826 uuid_unparse_lower(build->id, build->_id);
827
828 return 0;
829}
830
a84624be
MT
831/*
832 Sets the default target
833*/
834static int pakfire_build_set_default_target(struct pakfire_build* build) {
835 // Look for the "local" repository
836 struct pakfire_repo* repo = pakfire_get_repo(build->pakfire, PAKFIRE_REPO_LOCAL);
837 if (repo) {
838 const char* target = pakfire_repo_get_path(repo);
839 if (target)
840 pakfire_string_set(build->target, target);
841
842 pakfire_repo_unref(repo);
843 }
844
845 // Default to the current working directory
846 if (!*build->target) {
847 char* cwd = getcwd(build->target, sizeof(build->target));
848 if (!cwd) {
849 ERROR(build->pakfire, "getcwd() failed: %m\n");
850 return 1;
851 }
852 }
853
854 return 0;
855}
856
f877fdfa
MT
857/*
858 Sets up a new cgroup for this build
859*/
860static int pakfire_build_setup_cgroup(struct pakfire_build* build) {
861 char path[PATH_MAX];
862 int r;
863
864 // Compose path
865 r = pakfire_string_format(path, "pakfire/build-%s", build->_id);
866 if (r < 0) {
867 ERROR(build->pakfire, "Could not compose path for cgroup: %m\n");
868 return 1;
869 }
870
871 // Create a new cgroup
872 r = pakfire_cgroup_open(&build->cgroup, build->pakfire, path,
873 PAKFIRE_CGROUP_ENABLE_ACCOUNTING);
874 if (r) {
875 ERROR(build->pakfire, "Could not create cgroup for build %s: %m\n", build->_id);
876 return r;
877 }
878
879 // Done
abbad00b
MT
880 return 0;
881}
882
753ddf74
MT
883/*
884 Sets up a new jail for this build
885*/
886static int pakfire_build_setup_jail(struct pakfire_build* build) {
887 int r;
888
889 // Create a new jail
890 r = pakfire_jail_create(&build->jail, build->pakfire, 0);
891 if (r) {
892 ERROR(build->pakfire, "Could not create jail for build %s: %m\n", build->_id);
893 return r;
894 }
895
15503538
MT
896 // Connect the jail to our cgroup
897 r = pakfire_jail_set_cgroup(build->jail, build->cgroup);
898 if (r) {
899 ERROR(build->pakfire, "Could not set cgroup for jail: %m\n");
900 return r;
901 }
902
753ddf74
MT
903 // Done
904 return 0;
905}
906
abbad00b
MT
907PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
908 struct pakfire* pakfire, const char* id, int flags) {
909 int r;
910
911 // Allocate build object
912 struct pakfire_build* b = calloc(1, sizeof(*b));
913 if (!b)
914 return 1;
915
916 // Reference pakfire
917 b->pakfire = pakfire_ref(pakfire);
918
919 // Initialize reference counter
920 b->nrefs = 1;
921
922 // Copy flags
923 b->flags = flags;
924
925 // Parse ID
926 r = pakfire_build_parse_id(b, id);
927 if (r)
928 goto ERROR;
929
a84624be
MT
930 // Set target
931 r = pakfire_build_set_default_target(b);
932 if (r)
933 goto ERROR;
934
f877fdfa
MT
935 // Create cgroup
936 r = pakfire_build_setup_cgroup(b);
937 if (r)
938 goto ERROR;
939
753ddf74
MT
940 // Create jail
941 r = pakfire_build_setup_jail(b);
942 if (r)
943 goto ERROR;
944
abbad00b
MT
945 *build = b;
946 return 0;
947
948ERROR:
949 pakfire_build_free(b);
950 return r;
951}
952
77a1964f 953PAKFIRE_EXPORT struct pakfire_build* pakfire_build_ref(struct pakfire_build* build) {
abbad00b
MT
954 ++build->nrefs;
955
956 return build;
957}
958
77a1964f 959PAKFIRE_EXPORT struct pakfire_build* pakfire_build_unref(struct pakfire_build* build) {
abbad00b
MT
960 if (--build->nrefs > 0)
961 return build;
962
963 pakfire_build_free(build);
964 return NULL;
965}
966
ea924657
MT
967PAKFIRE_EXPORT int pakfire_build_set_target(
968 struct pakfire_build* build, const char* target) {
969 pakfire_string_set(build->target, target);
57e2cf99 970
ea924657
MT
971 return 0;
972}
57e2cf99 973
ea924657 974PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) {
33a8ba3c
MT
975 struct pakfire_archive* archive = NULL;
976 struct pakfire_package* package = NULL;
977
978 char makefile[PATH_MAX];
ea924657 979 int r;
01b29895 980
33a8ba3c
MT
981 // Open source archive
982 r = pakfire_archive_open(&archive, build->pakfire, path);
983 if (r) {
984 ERROR(build->pakfire, "Could not open source archive %s: %m\n", path);
985 goto ERROR;
986 }
987
988 // Fetch package metadata
989 r = pakfire_archive_make_package(archive, NULL, &package);
990 if (r) {
991 ERROR(build->pakfire, "Could not read package metadata: %m\n");
992 goto ERROR;
993 }
994
995 // Fetch some information
996 const char* nevra = pakfire_package_get_nevra(package);
997 const char* name = pakfire_package_get_name(package);
998
999 INFO(build->pakfire, "Building %s...\n", nevra);
01b29895 1000
14919022 1001 // Setup build environment
ea924657 1002 r = pakfire_build_setup(build->pakfire);
14919022 1003 if (r)
33a8ba3c 1004 goto ERROR;
14919022 1005
01b29895
MT
1006 const char* packages[] = {
1007 path, NULL
1008 };
1009
1010 // Install the package into the build environment
ea924657 1011 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, PAKFIRE_REQUEST_ESSENTIAL,
99a56775 1012 NULL, NULL, NULL);
01b29895 1013 if (r) {
ea924657 1014 ERROR(build->pakfire, "Could not install %s\n", path);
33a8ba3c 1015 goto ERROR;
01b29895
MT
1016 }
1017
33a8ba3c
MT
1018 // Compose path to makefile
1019 r = pakfire_string_format(makefile, "/usr/src/packages/%s/%s.nm", name, name);
1020 if (r < 0) {
1021 ERROR(build->pakfire, "Could not compose makefile path: %m\n");
01b29895 1022 goto ERROR;
33a8ba3c 1023 }
01b29895 1024
33a8ba3c 1025 // Run build
87bb7749 1026 r = pakfire_build_makefile(build, makefile);
01b29895 1027 if (r) {
33a8ba3c 1028 ERROR(build->pakfire, "Could not build %s: %m\n", nevra);
01b29895
MT
1029 goto ERROR;
1030 }
1031
01b29895 1032ERROR:
33a8ba3c
MT
1033 if (archive)
1034 pakfire_archive_unref(archive);
1035 if (package)
1036 pakfire_package_unref(package);
1037
01b29895
MT
1038 return r;
1039}
1040
ea924657
MT
1041/*
1042 Compatibility function to keep the legacy API.
1043*/
1044PAKFIRE_EXPORT int pakfire_build(struct pakfire* pakfire, const char* path,
1045 const char* target, const char* id, int flags) {
1046 struct pakfire_build* build = NULL;
1047 int r;
1048
1049 // Check if path is set
1050 if (!path) {
1051 errno = EINVAL;
1052 return 1;
1053 }
1054
1055 // Create a new build environment
1056 r = pakfire_build_create(&build, pakfire, id, flags);
1057 if (r)
1058 goto ERROR;
1059
1060 // Set target
1061 if (target) {
1062 r = pakfire_build_set_target(build, target);
1063 if (r)
1064 goto ERROR;
1065 }
1066
1067 // Run build
1068 r = pakfire_build_exec(build, path);
1069
1070ERROR:
1071 if (build)
1072 pakfire_build_unref(build);
1073
1074 return r;
1075}
1076
22a0733e
MT
1077/*
1078 This function enables the local repository so that it can be access by
1079 a pakfire instance running inside the chroot.
1080*/
1081static int pakfire_build_enable_repos(struct pakfire* pakfire) {
1082 char repopath[PATH_MAX];
1083 FILE* f = NULL;
1084 int r = 1;
1085
1086 // Fetch the local repository
1087 struct pakfire_repo* repo = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL);
1088 if (!repo) {
1089 DEBUG(pakfire, "Could not find repository '%s': %m\n", PAKFIRE_REPO_LOCAL);
1090 return 0;
1091 }
1092
1093 // Fetch repository configuration
1094 char* config = pakfire_repo_get_config(repo);
1095 if (!config) {
1096 ERROR(pakfire, "Could not generate repository configuration: %m\n");
1097 goto ERROR;
1098 }
1099
1100 const char* path = pakfire_repo_get_path(repo);
1101
1102 // Make sure the source directory exists
520ce66c 1103 r = pakfire_mkdir(path, 0700);
4e98e6bc 1104 if (r && errno != EEXIST) {
22a0733e
MT
1105 ERROR(pakfire, "Could not create repository directory %s: %m\n", path);
1106 goto ERROR;
1107 }
1108
1109 // Bind-mount the repository data read-only
1110 r = pakfire_bind(pakfire, path, NULL, MS_RDONLY);
1111 if (r) {
1112 ERROR(pakfire, "Could not bind-mount the repository at %s: %m\n", path);
1113 goto ERROR;
1114 }
1115
1116 // Make path for configuration file
1117 r = pakfire_make_path(pakfire, repopath,
3f559dd0 1118 PAKFIRE_CONFIG_DIR "/repos/" PAKFIRE_REPO_LOCAL ".repo");
22a0733e
MT
1119 if (r < 0)
1120 goto ERROR;
1121
1122 // Open configuration file for writing
1123 f = fopen(repopath, "w");
1124 if (!f) {
1125 ERROR(pakfire, "Could not open %s for writing: %m\n", path);
1126 r = 1;
1127 goto ERROR;
1128 }
1129
1130 // Write configuration
1131 size_t bytes_written = fprintf(f, "%s", config);
1132 if (bytes_written < strlen(config)) {
1133 ERROR(pakfire, "Could not write repository configuration: %m\n");
1134 r = 1;
1135 goto ERROR;
1136 }
1137
1138 // Success
1139 r = 0;
1140
1141ERROR:
1142 if (f)
1143 fclose(f);
1144 if (config)
1145 free(config);
1146 pakfire_repo_unref(repo);
1147
1148 return r;
1149}
1150
7f068a08 1151PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages) {
48dc31f7 1152 int r;
1a276007 1153
48dc31f7
MT
1154 // Setup build environment
1155 r = pakfire_build_setup(pakfire);
1156 if (r)
1157 return r;
1a276007 1158
7f068a08
MT
1159 // Install any additional packages
1160 if (packages) {
46f60d93 1161 r = pakfire_install(pakfire, 0, 0, packages, NULL, 0, NULL, NULL, NULL);
7f068a08
MT
1162 if (r)
1163 return r;
1164 }
1165
22a0733e
MT
1166 // Export local repository if running in interactive mode
1167 r = pakfire_build_enable_repos(pakfire);
1168 if (r)
1169 return r;
1170
e43489f7 1171 return pakfire_jail_shell(pakfire);
1a276007 1172}
397371db
MT
1173
1174int pakfire_build_clean(struct pakfire* pakfire, int flags) {
1175 struct pakfire_repo* local = NULL;
1176 int r = 0;
1177
1178 // Fetch local repository
7ccac5e5 1179 local = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL);
397371db 1180 if (!local) {
7ccac5e5 1181 ERROR(pakfire, "Could not find repository %s: %m\n", PAKFIRE_REPO_LOCAL);
397371db
MT
1182 goto ERROR;
1183 }
1184
1185 // Destroy everything in it
1186 r = pakfire_repo_clean(local, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
1187 if (r)
1188 goto ERROR;
1189
1190ERROR:
1191 if (local)
1192 pakfire_repo_unref(local);
1193
1194 return r;
1195}