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