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