]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/build.c
build: Pass context to pakfire_build_package(s)
[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
ac4c607b 272static int pakfire_build_package_add_files(struct pakfire* pakfire, struct pakfire_parser* makefile,
31480bee 273 const char* buildroot, const char* namespace, struct pakfire_package* pkg,
5b0b3dc2 274 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
MT
290 // Convert to absolute path
291 pakfire_make_path(pakfire, path, buildroot);
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
304 r = pakfire_filelist_create(&filelist, pakfire);
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);
73543ae3
MT
314 DEBUG(pakfire, "%zu file(s) found\n", length);
315
84556307
MT
316 // Nothing to do if the filelist is empty
317 if (!length)
318 goto ERROR;
319
5b0b3dc2
MT
320 // Find dependencies
321 r = pakfire_build_find_dependencies(pakfire, pkg, filelist, buildroot);
322 if (r) {
b1772bfb 323 ERROR(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
ac4c607b 368static int pakfire_build_add_scriptlet_requires(struct pakfire* pakfire, struct pakfire_package* pkg,
106d2edd
MT
369 struct pakfire_scriptlet* scriptlet) {
370 char** prerequires = NULL;
371 char path[PATH_MAX];
372 size_t size;
373 int r;
374
375 const char* root = pakfire_get_path(pakfire);
376
377 // Make filename
378 r = pakfire_make_path(pakfire, path, "tmp/.pakfire-scriptlet.XXXXXX");
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) {
393 ERROR(pakfire, "Could not write to %s: %m\n", path);
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
0de6bb30 408 r = pakfire_build_run_script(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++) {
415 DEBUG(pakfire, "Adding pre-requires: %s\n", *element);
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
ac4c607b 431static int pakfire_build_package_add_scriptlet(struct pakfire* pakfire, struct pakfire_package* pkg,
106d2edd
MT
432 struct pakfire_packager* packager, const char* type, const char* data) {
433 struct pakfire_scriptlet* scriptlet = NULL;
434 char* shell = NULL;
435 int r;
436
437 // Wrap scriptlet into a shell script
438 r = asprintf(&shell, "#!/bin/sh\n\nset -e\n\n%s\n\nexit 0\n", data);
439 if (r < 0)
440 goto ERROR;
441
442 // Create a scriptlet
443 r = pakfire_scriptlet_create(&scriptlet, pakfire, type, shell, 0);
444 if (r)
445 goto ERROR;
446
447 // Add it to the package
448 r = pakfire_packager_add_scriptlet(packager, scriptlet);
449 if (r) {
450 ERROR(pakfire, "Could not add scriptlet %s\n", type);
451 goto ERROR;
452 }
453
454 // Add scriptlet requirements
455 r = pakfire_build_add_scriptlet_requires(pakfire, pkg, scriptlet);
456 if (r) {
b1772bfb 457 ERROR(pakfire, "Could not add scriptlet requirements: %m\n");
106d2edd
MT
458 goto ERROR;
459 }
460
461 // Success
462 r = 0;
463
464ERROR:
465 if (scriptlet)
466 pakfire_scriptlet_unref(scriptlet);
467 if (shell)
468 free(shell);
469
470 return r;
471}
472
ac4c607b 473static int pakfire_build_package_add_scriptlets(struct pakfire* pakfire, struct pakfire_parser* makefile,
31480bee 474 const char* namespace, struct pakfire_package* pkg, struct pakfire_packager* packager) {
106d2edd
MT
475 char name[NAME_MAX];
476 int r;
477
478 for (const char** type = pakfire_scriptlet_types; *type; type++) {
479 r = pakfire_string_format(name, "scriptlet:%s", *type);
480 if (r < 0)
481 return r;
482
483 // Fetch the scriptlet
484 char* data = pakfire_parser_get(makefile, namespace, name);
485 if (!data)
486 continue;
487
488 // Add it to the package
489 r = pakfire_build_package_add_scriptlet(pakfire, pkg, packager, *type, data);
490 if (r) {
491 free(data);
492 return r;
493 }
494
495 free(data);
496 }
497
498 return 0;
499}
500
0e177be7
MT
501static int pakfire_build_package(struct pakfire_build* build, struct pakfire_parser* makefile,
502 const char* buildroot, const char* namespace) {
31480bee 503 struct pakfire_package* pkg = NULL;
48c6f2e7 504 struct pakfire_packager* packager = NULL;
73543ae3 505
a50bde9c
MT
506 int r = 1;
507
508 // Expand the handle into the package name
4c07774f 509 char* name = pakfire_parser_expand(makefile, "packages", namespace);
a50bde9c 510 if (!name) {
0e177be7 511 ERROR(build->pakfire, "Could not get package name: %m\n");
a50bde9c
MT
512 goto ERROR;
513 }
514
0e177be7 515 INFO(build->pakfire, "Building package '%s'...\n", name);
a50bde9c 516
4c07774f 517 // Fetch build architecture
0e177be7 518 const char* arch = pakfire_get_arch(build->pakfire);
4c07774f
MT
519 if (!arch)
520 goto ERROR;
521
4c07774f 522 // Fetch package from makefile
6ebd55e2 523 r = pakfire_parser_create_package(makefile, &pkg, NULL, namespace, arch);
4c07774f 524 if (r) {
0e177be7 525 ERROR(build->pakfire, "Could not create package from makefile: %m\n");
4c07774f
MT
526 goto ERROR;
527 }
528
ca002cae
MT
529 // Set distribution
530 const char* distribution = pakfire_parser_get(makefile, NULL, "DISTRO_NAME");
531 if (distribution)
532 pakfire_package_set_distribution(pkg, distribution);
533
7996020a 534 // Set build ID
0e177be7 535 pakfire_package_set_build_id_from_uuid(pkg, &build->id);
7996020a 536
571539a7
MT
537 // Set source package
538 const char* source_name = pakfire_parser_get(makefile, NULL, "name");
539 if (source_name)
540 pakfire_package_set_source_name(pkg, source_name);
541
542 // Set source EVR
543 const char* source_evr = pakfire_parser_get(makefile, NULL, "evr");
544 if (source_evr)
545 pakfire_package_set_source_evr(pkg, source_evr);
546
547 // Set source arch
548 pakfire_package_set_source_arch(pkg, "src");
549
48c6f2e7 550 // Create a packager
0e177be7 551 r = pakfire_packager_create(&packager, build->pakfire, pkg);
48c6f2e7
MT
552 if (r)
553 goto ERROR;
554
73543ae3 555 // Add files
0e177be7 556 r = pakfire_build_package_add_files(build->pakfire, makefile, buildroot, namespace,
5b0b3dc2 557 pkg, packager);
73543ae3
MT
558 if (r)
559 goto ERROR;
560
106d2edd 561 // Add scriptlets
0e177be7
MT
562 r = pakfire_build_package_add_scriptlets(build->pakfire, makefile, namespace,
563 pkg, packager);
106d2edd
MT
564 if (r)
565 goto ERROR;
566
ae7968a7 567 // Write the finished package
0e177be7 568 r = pakfire_packager_finish_to_directory(packager, build->target, NULL);
ae7968a7 569 if (r) {
0e177be7 570 ERROR(build->pakfire, "pakfire_packager_finish() failed: %m\n");
ae7968a7
MT
571 goto ERROR;
572 }
573
4c07774f 574#ifdef ENABLE_DEBUG
23e22751 575 char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
4c07774f 576 if (dump) {
0e177be7 577 DEBUG(build->pakfire, "%s\n", dump);
4c07774f
MT
578 free(dump);
579 }
580#endif
a50bde9c
MT
581
582 // Success
583 r = 0;
584
585ERROR:
48c6f2e7
MT
586 if (packager)
587 pakfire_packager_unref(packager);
4c07774f
MT
588 if (pkg)
589 pakfire_package_unref(pkg);
a50bde9c
MT
590 if (name)
591 free(name);
592
593 return r;
594}
595
0e177be7
MT
596static int pakfire_build_packages(struct pakfire_build* build,
597 struct pakfire_parser* makefile, const char* buildroot) {
598 DEBUG(build->pakfire, "Creating packages...");
a50bde9c
MT
599 int r = 1;
600
601 // Fetch a list all all packages
602 char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*");
603 if (!packages) {
0e177be7 604 ERROR(build->pakfire, "Could not find any packages: %m\n");
a50bde9c
MT
605 goto ERROR;
606 }
607
608 unsigned int num_packages = 0;
609
610 // Count how many packages we have
611 for (char** package = packages; *package; package++)
612 num_packages++;
613
0e177be7 614 DEBUG(build->pakfire, "Found %d package(s)\n", num_packages);
a50bde9c
MT
615
616 // Build packages in reverse order
617 for (int i = num_packages - 1; i >= 0; i--) {
0e177be7 618 r = pakfire_build_package(build, makefile, buildroot, packages[i]);
a50bde9c
MT
619 if (r)
620 goto ERROR;
621 }
622
623 // Success
624 r = 0;
625
626ERROR:
627 if (packages)
628 free(packages);
629
630 return r;
631}
632
46748697
MT
633static int pakfire_build_stage(struct pakfire_build* build,
634 struct pakfire_parser* makefile, const char* stage) {
830f5d18 635 struct pakfire_jail* jail = NULL;
1a276007
MT
636 char template[1024];
637
638 // Prepare template for this stage
639 int r = pakfire_string_format(template, TEMPLATE, stage);
640 if (r < 0)
641 return r;
642
2ffd21f9
MT
643 // Fetch the environment
644 char** envp = pakfire_parser_make_environ(makefile);
645
1a276007
MT
646 // Create the build script
647 char* script = pakfire_parser_expand(makefile, "build", template);
648 if (!script) {
46748697
MT
649 ERROR(build->pakfire, "Could not generate the build script for stage '%s': %m\n",
650 stage);
1a276007
MT
651 goto ERROR;
652 }
653
46748697 654 INFO(build->pakfire, "Running build stage '%s'\n", stage);
1a276007 655
830f5d18 656 // Create a new jail
46748697 657 jail = pakfire_build_make_jail(build->pakfire);
830f5d18
MT
658 if (!jail)
659 goto ERROR;
660
661 // Import environment
662 r = pakfire_jail_import_env(jail, (const char**)envp);
663 if (r) {
46748697 664 ERROR(build->pakfire, "Could not import environment: %m\n");
830f5d18
MT
665 goto ERROR;
666 }
667
668 // Run the script
0de6bb30 669 r = pakfire_jail_exec_script(jail, script, strlen(script), NULL, NULL);
1a276007 670 if (r) {
46748697 671 ERROR(build->pakfire, "Build stage '%s' failed with status %d\n", stage, r);
1a276007
MT
672 }
673
674ERROR:
830f5d18
MT
675 if (jail)
676 pakfire_jail_unref(jail);
2ffd21f9
MT
677 if (envp) {
678 for (char** e = envp; *e; e++)
679 free(*e);
680 free(envp);
681 }
1a276007
MT
682 if (script)
683 free(script);
684
685 return r;
686}
687
39f45b30
MT
688static const char* post_build_scripts[] = {
689 "remove-static-libs",
1f62ffae 690 "check-symlinks",
022b794e 691 "check-unsafe-files",
0b5f0bbc 692 "check-libraries",
16043831 693 "check-rpaths",
99aee237 694 "check-buildroot",
7e1fec6f 695 "check-include",
dd864160 696 "check-hardening",
ae703321 697 "check-interpreters",
2504194a 698 "check-fhs",
39f45b30 699 "compress-man-pages",
ffb65de6 700 "strip",
39f45b30
MT
701 NULL,
702};
703
247dd943
MT
704static int pakfire_build_run_post_build_scripts(
705 struct pakfire_build* build, const char* buildroot) {
39f45b30
MT
706 // Set default arguments for build scripts
707 const char* args[] = {
708 buildroot, NULL
709 };
710
711 // Run them one by one
712 for (const char** script = post_build_scripts; *script; script++) {
247dd943 713 int r = pakfire_build_run_script(build->pakfire, *script, args, NULL);
39f45b30
MT
714 if (r)
715 return r;
716 }
717
718 return 0;
719}
720
87bb7749 721static int pakfire_build_makefile(struct pakfire_build* build, const char* path) {
657a5c72 722 struct pakfire_parser* makefile = NULL;
3d4011eb 723 char buildroot[PATH_MAX];
1a276007 724 struct pakfire_parser_error* error = NULL;
01b29895 725 int r = 1;
f0893704 726
87bb7749 727 const char* root = pakfire_get_path(build->pakfire);
3d4011eb
MT
728
729 // Create BUILDROOT
87bb7749 730 pakfire_make_path(build->pakfire, buildroot, "/tmp/.buildroot.XXXXXX");
3d4011eb 731 if (!pakfire_mkdtemp(buildroot))
f0893704 732 goto ERROR;
3d4011eb
MT
733
734 // Make relative BUILDROOT path
735 const char* buildroot_rel = pakfire_path_relpath(root, buildroot);
736 if (!buildroot_rel)
f0893704 737 goto ERROR;
3d4011eb 738
1a276007 739 // Read makefile
87bb7749 740 r = pakfire_read_makefile(&makefile, build->pakfire, path, &error);
1a276007
MT
741 if (r) {
742 if (error) {
87bb7749 743 ERROR(build->pakfire, "Could not parse makefile %s: %s\n", path,
1a276007
MT
744 pakfire_parser_error_get_message(error));
745 pakfire_parser_error_unref(error);
746 } else {
87bb7749 747 ERROR(build->pakfire, "Could not parse makefile %s: %m\n", path);
1a276007
MT
748 }
749
750 goto ERROR;
751 }
752
3d4011eb 753 // Set BUILDROOT
fea2e884 754 pakfire_parser_set(makefile, NULL, "BUILDROOT", buildroot_rel, 0);
3d4011eb 755
1a276007
MT
756 // Run through all build stages
757 for (const char** stage = stages; *stage; stage++) {
46748697 758 r = pakfire_build_stage(build, makefile, *stage);
1a276007
MT
759 if (r) {
760 // Drop to a shell for debugging
87bb7749
MT
761 if (pakfire_has_flag(build->pakfire, PAKFIRE_FLAGS_INTERACTIVE))
762 pakfire_jail_shell(build->pakfire);
1a276007
MT
763
764 goto ERROR;
765 }
766 }
767
39f45b30 768 // Run post build scripts
247dd943 769 r = pakfire_build_run_post_build_scripts(build, buildroot_rel);
8d0f3a35
MT
770 if (r)
771 goto ERROR;
772
a50bde9c 773 // Create the packages
0e177be7 774 r = pakfire_build_packages(build, makefile, buildroot_rel);
a50bde9c 775 if (r) {
87bb7749 776 ERROR(build->pakfire, "Could not create packages: %m\n");
a50bde9c
MT
777 goto ERROR;
778 }
1a276007
MT
779
780ERROR:
781 if (makefile)
782 pakfire_parser_unref(makefile);
783
3d4011eb
MT
784 // Remove buildroot
785 pakfire_rmtree(buildroot, 0);
786
1a276007
MT
787 return r;
788}
789
abbad00b 790static void pakfire_build_free(struct pakfire_build* build) {
753ddf74
MT
791 if (build->jail)
792 pakfire_jail_unref(build->jail);
793
49fd9926
MT
794 if (build->cgroup) {
795 // Destroy the cgroup
796 pakfire_cgroup_destroy(build->cgroup);
797
798 // Free it
799 pakfire_cgroup_unref(build->cgroup);
800 }
801
abbad00b
MT
802 pakfire_unref(build->pakfire);
803 free(build);
804}
805
806static int pakfire_build_parse_id(struct pakfire_build* build, const char* id) {
807 int r;
808
809 // Try parsing the Build ID
810 if (id) {
811 r = uuid_parse(id, build->id);
812 if (r) {
813 ERROR(build->pakfire, "Could not parse build ID '%s'\n", id);
1df321ff 814 errno = EINVAL;
abbad00b
MT
815 return r;
816 }
817
818 // Otherwise initialize the Build ID with something random
819 } else {
820 uuid_generate_random(build->id);
821 }
822
f877fdfa
MT
823 // Store the ID as string, too
824 uuid_unparse_lower(build->id, build->_id);
825
826 return 0;
827}
828
a84624be
MT
829/*
830 Sets the default target
831*/
832static int pakfire_build_set_default_target(struct pakfire_build* build) {
833 // Look for the "local" repository
834 struct pakfire_repo* repo = pakfire_get_repo(build->pakfire, PAKFIRE_REPO_LOCAL);
835 if (repo) {
836 const char* target = pakfire_repo_get_path(repo);
837 if (target)
838 pakfire_string_set(build->target, target);
839
840 pakfire_repo_unref(repo);
841 }
842
843 // Default to the current working directory
844 if (!*build->target) {
845 char* cwd = getcwd(build->target, sizeof(build->target));
846 if (!cwd) {
847 ERROR(build->pakfire, "getcwd() failed: %m\n");
848 return 1;
849 }
850 }
851
852 return 0;
853}
854
f877fdfa
MT
855/*
856 Sets up a new cgroup for this build
857*/
858static int pakfire_build_setup_cgroup(struct pakfire_build* build) {
859 char path[PATH_MAX];
860 int r;
861
862 // Compose path
863 r = pakfire_string_format(path, "pakfire/build-%s", build->_id);
864 if (r < 0) {
865 ERROR(build->pakfire, "Could not compose path for cgroup: %m\n");
866 return 1;
867 }
868
869 // Create a new cgroup
870 r = pakfire_cgroup_open(&build->cgroup, build->pakfire, path,
871 PAKFIRE_CGROUP_ENABLE_ACCOUNTING);
872 if (r) {
873 ERROR(build->pakfire, "Could not create cgroup for build %s: %m\n", build->_id);
874 return r;
875 }
876
877 // Done
abbad00b
MT
878 return 0;
879}
880
753ddf74
MT
881/*
882 Sets up a new jail for this build
883*/
884static int pakfire_build_setup_jail(struct pakfire_build* build) {
885 int r;
886
887 // Create a new jail
888 r = pakfire_jail_create(&build->jail, build->pakfire, 0);
889 if (r) {
890 ERROR(build->pakfire, "Could not create jail for build %s: %m\n", build->_id);
891 return r;
892 }
893
15503538
MT
894 // Connect the jail to our cgroup
895 r = pakfire_jail_set_cgroup(build->jail, build->cgroup);
896 if (r) {
897 ERROR(build->pakfire, "Could not set cgroup for jail: %m\n");
898 return r;
899 }
900
753ddf74
MT
901 // Done
902 return 0;
903}
904
abbad00b
MT
905PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
906 struct pakfire* pakfire, const char* id, int flags) {
907 int r;
908
909 // Allocate build object
910 struct pakfire_build* b = calloc(1, sizeof(*b));
911 if (!b)
912 return 1;
913
914 // Reference pakfire
915 b->pakfire = pakfire_ref(pakfire);
916
917 // Initialize reference counter
918 b->nrefs = 1;
919
920 // Copy flags
921 b->flags = flags;
922
923 // Parse ID
924 r = pakfire_build_parse_id(b, id);
925 if (r)
926 goto ERROR;
927
a84624be
MT
928 // Set target
929 r = pakfire_build_set_default_target(b);
930 if (r)
931 goto ERROR;
932
f877fdfa
MT
933 // Create cgroup
934 r = pakfire_build_setup_cgroup(b);
935 if (r)
936 goto ERROR;
937
753ddf74
MT
938 // Create jail
939 r = pakfire_build_setup_jail(b);
940 if (r)
941 goto ERROR;
942
abbad00b
MT
943 *build = b;
944 return 0;
945
946ERROR:
947 pakfire_build_free(b);
948 return r;
949}
950
77a1964f 951PAKFIRE_EXPORT struct pakfire_build* pakfire_build_ref(struct pakfire_build* build) {
abbad00b
MT
952 ++build->nrefs;
953
954 return build;
955}
956
77a1964f 957PAKFIRE_EXPORT struct pakfire_build* pakfire_build_unref(struct pakfire_build* build) {
abbad00b
MT
958 if (--build->nrefs > 0)
959 return build;
960
961 pakfire_build_free(build);
962 return NULL;
963}
964
ea924657
MT
965PAKFIRE_EXPORT int pakfire_build_set_target(
966 struct pakfire_build* build, const char* target) {
967 pakfire_string_set(build->target, target);
57e2cf99 968
ea924657
MT
969 return 0;
970}
57e2cf99 971
ea924657 972PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) {
33a8ba3c
MT
973 struct pakfire_archive* archive = NULL;
974 struct pakfire_package* package = NULL;
975
976 char makefile[PATH_MAX];
ea924657 977 int r;
01b29895 978
33a8ba3c
MT
979 // Open source archive
980 r = pakfire_archive_open(&archive, build->pakfire, path);
981 if (r) {
982 ERROR(build->pakfire, "Could not open source archive %s: %m\n", path);
983 goto ERROR;
984 }
985
986 // Fetch package metadata
987 r = pakfire_archive_make_package(archive, NULL, &package);
988 if (r) {
989 ERROR(build->pakfire, "Could not read package metadata: %m\n");
990 goto ERROR;
991 }
992
993 // Fetch some information
994 const char* nevra = pakfire_package_get_nevra(package);
995 const char* name = pakfire_package_get_name(package);
996
997 INFO(build->pakfire, "Building %s...\n", nevra);
01b29895 998
14919022 999 // Setup build environment
ea924657 1000 r = pakfire_build_setup(build->pakfire);
14919022 1001 if (r)
33a8ba3c 1002 goto ERROR;
14919022 1003
01b29895
MT
1004 const char* packages[] = {
1005 path, NULL
1006 };
1007
1008 // Install the package into the build environment
ea924657 1009 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, PAKFIRE_REQUEST_ESSENTIAL,
99a56775 1010 NULL, NULL, NULL);
01b29895 1011 if (r) {
ea924657 1012 ERROR(build->pakfire, "Could not install %s\n", path);
33a8ba3c 1013 goto ERROR;
01b29895
MT
1014 }
1015
33a8ba3c
MT
1016 // Compose path to makefile
1017 r = pakfire_string_format(makefile, "/usr/src/packages/%s/%s.nm", name, name);
1018 if (r < 0) {
1019 ERROR(build->pakfire, "Could not compose makefile path: %m\n");
01b29895 1020 goto ERROR;
33a8ba3c 1021 }
01b29895 1022
33a8ba3c 1023 // Run build
87bb7749 1024 r = pakfire_build_makefile(build, makefile);
01b29895 1025 if (r) {
33a8ba3c 1026 ERROR(build->pakfire, "Could not build %s: %m\n", nevra);
01b29895
MT
1027 goto ERROR;
1028 }
1029
01b29895 1030ERROR:
33a8ba3c
MT
1031 if (archive)
1032 pakfire_archive_unref(archive);
1033 if (package)
1034 pakfire_package_unref(package);
1035
01b29895
MT
1036 return r;
1037}
1038
ea924657
MT
1039/*
1040 Compatibility function to keep the legacy API.
1041*/
1042PAKFIRE_EXPORT int pakfire_build(struct pakfire* pakfire, const char* path,
1043 const char* target, const char* id, int flags) {
1044 struct pakfire_build* build = NULL;
1045 int r;
1046
1047 // Check if path is set
1048 if (!path) {
1049 errno = EINVAL;
1050 return 1;
1051 }
1052
1053 // Create a new build environment
1054 r = pakfire_build_create(&build, pakfire, id, flags);
1055 if (r)
1056 goto ERROR;
1057
1058 // Set target
1059 if (target) {
1060 r = pakfire_build_set_target(build, target);
1061 if (r)
1062 goto ERROR;
1063 }
1064
1065 // Run build
1066 r = pakfire_build_exec(build, path);
1067
1068ERROR:
1069 if (build)
1070 pakfire_build_unref(build);
1071
1072 return r;
1073}
1074
22a0733e
MT
1075/*
1076 This function enables the local repository so that it can be access by
1077 a pakfire instance running inside the chroot.
1078*/
1079static int pakfire_build_enable_repos(struct pakfire* pakfire) {
1080 char repopath[PATH_MAX];
1081 FILE* f = NULL;
1082 int r = 1;
1083
1084 // Fetch the local repository
1085 struct pakfire_repo* repo = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL);
1086 if (!repo) {
1087 DEBUG(pakfire, "Could not find repository '%s': %m\n", PAKFIRE_REPO_LOCAL);
1088 return 0;
1089 }
1090
1091 // Fetch repository configuration
1092 char* config = pakfire_repo_get_config(repo);
1093 if (!config) {
1094 ERROR(pakfire, "Could not generate repository configuration: %m\n");
1095 goto ERROR;
1096 }
1097
1098 const char* path = pakfire_repo_get_path(repo);
1099
1100 // Make sure the source directory exists
520ce66c 1101 r = pakfire_mkdir(path, 0700);
4e98e6bc 1102 if (r && errno != EEXIST) {
22a0733e
MT
1103 ERROR(pakfire, "Could not create repository directory %s: %m\n", path);
1104 goto ERROR;
1105 }
1106
1107 // Bind-mount the repository data read-only
1108 r = pakfire_bind(pakfire, path, NULL, MS_RDONLY);
1109 if (r) {
1110 ERROR(pakfire, "Could not bind-mount the repository at %s: %m\n", path);
1111 goto ERROR;
1112 }
1113
1114 // Make path for configuration file
1115 r = pakfire_make_path(pakfire, repopath,
3f559dd0 1116 PAKFIRE_CONFIG_DIR "/repos/" PAKFIRE_REPO_LOCAL ".repo");
22a0733e
MT
1117 if (r < 0)
1118 goto ERROR;
1119
1120 // Open configuration file for writing
1121 f = fopen(repopath, "w");
1122 if (!f) {
1123 ERROR(pakfire, "Could not open %s for writing: %m\n", path);
1124 r = 1;
1125 goto ERROR;
1126 }
1127
1128 // Write configuration
1129 size_t bytes_written = fprintf(f, "%s", config);
1130 if (bytes_written < strlen(config)) {
1131 ERROR(pakfire, "Could not write repository configuration: %m\n");
1132 r = 1;
1133 goto ERROR;
1134 }
1135
1136 // Success
1137 r = 0;
1138
1139ERROR:
1140 if (f)
1141 fclose(f);
1142 if (config)
1143 free(config);
1144 pakfire_repo_unref(repo);
1145
1146 return r;
1147}
1148
7f068a08 1149PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages) {
48dc31f7 1150 int r;
1a276007 1151
48dc31f7
MT
1152 // Setup build environment
1153 r = pakfire_build_setup(pakfire);
1154 if (r)
1155 return r;
1a276007 1156
7f068a08
MT
1157 // Install any additional packages
1158 if (packages) {
46f60d93 1159 r = pakfire_install(pakfire, 0, 0, packages, NULL, 0, NULL, NULL, NULL);
7f068a08
MT
1160 if (r)
1161 return r;
1162 }
1163
22a0733e
MT
1164 // Export local repository if running in interactive mode
1165 r = pakfire_build_enable_repos(pakfire);
1166 if (r)
1167 return r;
1168
e43489f7 1169 return pakfire_jail_shell(pakfire);
1a276007 1170}
397371db
MT
1171
1172int pakfire_build_clean(struct pakfire* pakfire, int flags) {
1173 struct pakfire_repo* local = NULL;
1174 int r = 0;
1175
1176 // Fetch local repository
7ccac5e5 1177 local = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL);
397371db 1178 if (!local) {
7ccac5e5 1179 ERROR(pakfire, "Could not find repository %s: %m\n", PAKFIRE_REPO_LOCAL);
397371db
MT
1180 goto ERROR;
1181 }
1182
1183 // Destroy everything in it
1184 r = pakfire_repo_clean(local, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
1185 if (r)
1186 goto ERROR;
1187
1188ERROR:
1189 if (local)
1190 pakfire_repo_unref(local);
1191
1192 return r;
1193}