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