]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/build.c
pakfire_install/erase/update: Indicate how many packages changed
[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>
26452aef 23#include <sys/mount.h>
1a276007
MT
24
25#include <pakfire/build.h>
26#include <pakfire/execute.h>
27#include <pakfire/dist.h>
ae7968a7 28#include <pakfire/file.h>
1a276007 29#include <pakfire/logging.h>
4c07774f 30#include <pakfire/package.h>
48c6f2e7 31#include <pakfire/packager.h>
1a276007
MT
32#include <pakfire/parser.h>
33#include <pakfire/private.h>
106d2edd 34#include <pakfire/scriptlet.h>
26452aef 35#include <pakfire/snapshot.h>
1a276007
MT
36#include <pakfire/types.h>
37#include <pakfire/util.h>
38
26452aef
MT
39#define CCACHE_DIR "/var/cache/ccache"
40
1a276007
MT
41static const char* stages[] = {
42 "prepare",
43 "build",
44 "test",
45 "install",
46 NULL,
47};
48
49#define TEMPLATE \
50 "#!/bin/bash --login\n" \
51 "\n" \
52 "set -e\n" \
53 "set -x\n" \
54 "\n" \
55 "%%{_%s}\n" \
56 "\n" \
57 "exit 0\n"
58
26452aef 59static int pakfire_build_install_packages(Pakfire pakfire, int* snapshot_needs_update) {
26452aef
MT
60 char** packages = NULL;
61 int r = 1;
62
63 struct pakfire_config* config = pakfire_get_config(pakfire);
64
65 // Fetch build environment
66 const char* requires = pakfire_config_get(config, "build", "requires", NULL);
67 if (!requires) {
68 ERROR(pakfire, "No build requirements have been defined\n");
69 goto ERROR;
70 }
71
72 // Split requirements into packages
73 packages = pakfire_split_string(requires, ',');
74 if (!packages)
75 goto ERROR;
76
b1a06c73
MT
77 int changed = 0;
78
976d4999 79 // Install everything
b1a06c73 80 r = pakfire_install(pakfire, (const char**)packages, 0, &changed);
976d4999
MT
81 if (r) {
82 ERROR(pakfire, "Could not install build dependencies\n");
26452aef 83 goto ERROR;
26452aef
MT
84 }
85
b1a06c73
MT
86 // Mark snapshot as changed if new packages were installed
87 if (changed)
88 *snapshot_needs_update = 1;
26452aef 89
b1a06c73 90 // XXX Update everything
26452aef
MT
91
92 // Success
93 r = 0;
94
95ERROR:
26452aef
MT
96 if (packages) {
97 for (char** package = packages; *package; package++)
98 free(*package);
99 free(packages);
100 }
101 if (config)
102 pakfire_config_unref(config);
103
104 return r;
105}
106
107int pakfire_build_setup(Pakfire pakfire) {
108 char path[PATH_MAX];
109 int r;
110
111 // Mount ccache
112 if (!pakfire_has_flag(pakfire, PAKFIRE_FLAGS_DISABLE_CCACHE)) {
113 r = pakfire_make_cache_path(pakfire, path, "%s", "ccache");
114 if (r < 0)
115 return r;
116
117 // Ensure path exists
118 r = pakfire_mkdir(path, 0);
119 if (r && errno != EEXIST) {
120 ERROR(pakfire, "Could not create ccache directory %s: %m\n", path);
121 return r;
122 }
123
124 r = pakfire_bind(pakfire, path, CCACHE_DIR, MS_NOSUID|MS_NOEXEC|MS_NODEV);
125 if (r) {
126 ERROR(pakfire, "Could not mount ccache: %m\n");
127 return r;
128 }
129 }
130
131 // Extract snapshot
132 if (!pakfire_has_flag(pakfire, PAKFIRE_FLAGS_DISABLE_SNAPSHOT)) {
133 r = pakfire_make_cache_path(pakfire, path, "%s", "snapshot.tar.zst");
134 if (r < 0)
135 return r;
136
137 // Open the snapshot
138 FILE* f = fopen(path, "r");
139
140 // Try restoring the snapshot
141 if (f) {
142 r = pakfire_snapshot_restore(pakfire, f);
143 if (r) {
144 fclose(f);
145 return r;
146 }
147 }
148
149 // Tells us whether we need to (re-)create the snapshot
150 int snapshot_needs_update = 0;
151
152 // Install or update any build dependencies
153 r = pakfire_build_install_packages(pakfire, &snapshot_needs_update);
154 if (r)
155 return r;
156
157 if (snapshot_needs_update) {
158 // Open snapshot file for writing
159 f = fopen(path, "w");
160 if (!f) {
161 ERROR(pakfire, "Could not open snapshot file for writing: %m\n");
162 return 1;
163 }
164
165 // Create a new snapshot
166 r = pakfire_snapshot_create(pakfire, f);
167 fclose(f);
168
169 if (r)
170 return r;
171 }
172 }
173
174 return 0;
175}
176
29f2151d 177static int pakfire_build_run_script(Pakfire pakfire, const char* filename, const char* args[],
8d0f3a35
MT
178 pakfire_execute_logging_callback logging_callback, void* data) {
179 char* script = NULL;
180 size_t size = 0;
181 char path[PATH_MAX];
182
183 DEBUG(pakfire, "Running build script '%s'...\n", filename);
184
185 // Make the source path
186 pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename);
187
188 // Open the source script
189 FILE* f = fopen(path, "r");
190 if (!f) {
191 ERROR(pakfire, "Could not open %s: %s\n", path, strerror(errno));
192 return 1;
193 }
194
195 // Read the script into memory
196 int r = pakfire_read_file_into_buffer(f, &script, &size);
197 if (r) {
198 ERROR(pakfire, "Could not read script into memory: %s\n", strerror(errno));
199 goto ERROR;
200 }
201
202 // Execute it
29f2151d 203 r = pakfire_execute_script(pakfire, script, size, args, NULL, 0, logging_callback, data);
8d0f3a35
MT
204 if (r) {
205 ERROR(pakfire, "Script '%s' failed with status %d\n", filename, r);
206 }
207
208ERROR:
209 if (script)
210 free(script);
211
212 return r;
213}
214
5b0b3dc2
MT
215static int find_dependency(char** haystack, const char* needle) {
216 if (!needle) {
217 errno = EINVAL;
218 return -1;
219 }
220
221 if (!haystack)
222 return 0;
223
224 for (char** element = haystack; *element; element++) {
225 if (strcmp(needle, *element) == 0)
226 return 1;
227 }
228
229 return 0;
230}
231
232static int pakfire_build_find_dependencies(Pakfire pakfire,
233 PakfirePackage pkg, PakfireFilelist filelist, const char* buildroot) {
234 char** provides = NULL;
235 char** requires = NULL;
236 char path[PATH_MAX];
237
238 // Allocate path to write the filelist to
239 int r = pakfire_make_path(pakfire, path, "tmp/.pakfire-filelist.XXXXXX");
240 if (r < 0)
241 return 1;
242
243 // Create a temporary file
244 FILE* f = pakfire_mktemp(path);
245 if (!f)
246 goto ERROR;
247
248 // Write filelist to the temporary file
249 r = pakfire_filelist_export(filelist, f);
250 fclose(f);
251 if (r) {
252 ERROR(pakfire, "Could not export filelist: %s\n", strerror(errno));
253 goto ERROR;
254 }
255
256 const char* root = pakfire_get_path(pakfire);
257
258 // Pass buildroot and the filelist as arguments
259 const char* args[] = {
260 buildroot, pakfire_path_relpath(root, path), NULL
261 };
262
263 // Find all provides
264 r = pakfire_build_run_script(pakfire, "find-provides", args,
265 pakfire_execute_capture_stdout_to_array, &provides);
266 if (r) {
267 ERROR(pakfire, "find-provides returned with error %d\n", r);
268 goto ERROR;
269 }
270
271 // Find all requires
272 r = pakfire_build_run_script(pakfire, "find-requires", args,
273 pakfire_execute_capture_stdout_to_array, &requires);
274 if (r) {
275 ERROR(pakfire, "find-requires returned with error %d\n", r);
276 goto ERROR;
277 }
278
279 // Add all provides to the package
280 if (provides) {
281 for (char** element = provides; *element; element++) {
282 DEBUG(pakfire, "Adding provides: %s\n", *element);
283 pakfire_package_add_provides(pkg, *element);
284 }
285 }
286
287 // Add all requires to the package
288 if (requires) {
289 for (char** element = requires; *element; element++) {
290 // Skip adding this requirement if also provided by this package
291 if (find_dependency(provides, *element))
292 continue;
293
294 DEBUG(pakfire, "Adding requires: %s\n", *element);
295 pakfire_package_add_requires(pkg, *element);
296 }
297 }
298
299 // Success
300 r = 0;
301
302ERROR:
303 if (provides) {
304 for (char** element = provides; *element; element++)
305 free(*element);
306 free(provides);
307 }
308 if (requires) {
309 for (char** element = requires; *element; element++)
310 free(*element);
311 free(requires);
312 }
313 unlink(path);
314
315 return r;
316}
317
73543ae3
MT
318static int append_to_array(const char*** array, const char* s) {
319 unsigned int length = 0;
320
321 // Determine the length of the existing array
322 if (*array) {
323 for (const char** element = *array; *element; element++)
324 length++;
325 }
326
327 // Allocate space
328 *array = reallocarray(*array, length + 2, sizeof(**array));
329 if (!*array)
330 return 1;
331
332 // Append s and terminate the array
333 (*array)[length] = s;
334 (*array)[length + 1] = NULL;
335
336 return 0;
337}
338
339static int pakfire_build_package_add_files(Pakfire pakfire, PakfireParser makefile,
5b0b3dc2
MT
340 const char* buildroot, const char* namespace, PakfirePackage pkg,
341 struct pakfire_packager* packager) {
73543ae3
MT
342 PakfireFilelist filelist = NULL;
343 char path[PATH_MAX];
344 int r = 1;
345
346 char** files = NULL;
347 const char** includes = NULL;
348 const char** excludes = NULL;
349
350 // Fetch filelist from makefile
351 files = pakfire_parser_get_split(makefile, namespace, "files", '\n');
352
353 // No files to package?
354 if (!files)
355 return 0;
356
73543ae3
MT
357 // Convert to absolute path
358 pakfire_make_path(pakfire, path, buildroot);
359
360 // Split into includes and excludes
361 for (char** file = files; *file; file++) {
362 if (**file == '!')
363 r = append_to_array(&excludes, *file);
364 else
365 r = append_to_array(&includes, *file);
366 if (r)
367 goto ERROR;
368 }
369
370 // Allocate a new filelist
371 r = pakfire_filelist_create(&filelist, pakfire);
372 if (r)
373 goto ERROR;
374
375 // Scan for files
376 r = pakfire_filelist_scan(filelist, path, includes, excludes);
377 if (r)
378 goto ERROR;
379
ae7968a7 380 const size_t length = pakfire_filelist_size(filelist);
73543ae3
MT
381 DEBUG(pakfire, "%zu file(s) found\n", length);
382
84556307
MT
383 // Nothing to do if the filelist is empty
384 if (!length)
385 goto ERROR;
386
5b0b3dc2
MT
387 // Find dependencies
388 r = pakfire_build_find_dependencies(pakfire, pkg, filelist, buildroot);
389 if (r) {
390 ERROR(pakfire, "Finding dependencies failed: %s\n", strerror(errno));
391 goto ERROR;
392 }
393
ae7968a7
MT
394 // Add all files to the package
395 for (unsigned int i = 0; i < length; i++) {
5803b5f6 396 struct pakfire_file* file = pakfire_filelist_get(filelist, i);
ae7968a7
MT
397
398 // Add the file to the package
399 r = pakfire_packager_add(
400 packager,
401 pakfire_file_get_abspath(file),
402 pakfire_file_get_path(file)
403 );
404 if (r) {
405 pakfire_file_unref(file);
406 goto ERROR;
407 }
408
3fca5032
MT
409 // Remove the file after it was packaged
410 r = pakfire_file_cleanup(file);
411 if (r) {
412 pakfire_file_unref(file);
413 goto ERROR;
414 }
415
ae7968a7
MT
416 pakfire_file_unref(file);
417 }
73543ae3
MT
418
419ERROR:
420 if (filelist)
421 pakfire_filelist_unref(filelist);
422 if (files) {
423 for (char** file = files; *file; file++)
424 free(*file);
425 free(files);
426 }
427 if (includes)
428 free(includes);
429 if (excludes)
430 free(excludes);
73543ae3
MT
431
432 return r;
433}
434
106d2edd
MT
435static int pakfire_build_add_scriptlet_requires(Pakfire pakfire, PakfirePackage pkg,
436 struct pakfire_scriptlet* scriptlet) {
437 char** prerequires = NULL;
438 char path[PATH_MAX];
439 size_t size;
440 int r;
441
442 const char* root = pakfire_get_path(pakfire);
443
444 // Make filename
445 r = pakfire_make_path(pakfire, path, "tmp/.pakfire-scriptlet.XXXXXX");
446 if (r < 0)
447 return r;
448
449 // Fetch scriptlet payload
450 const char* data = pakfire_scriptlet_get_data(scriptlet, &size);
451
452 // Create a temporary file
453 FILE* f = pakfire_mktemp(path);
454 if (!f)
455 return 1;
456
457 // Write scriptlet
458 ssize_t bytes_written = fwrite(data, 1, size, f);
459 if (bytes_written < 0) {
460 ERROR(pakfire, "Could not write to %s: %m\n", path);
461 fclose(f);
462 goto ERROR;
463 }
464
465 // Close file
466 fclose(f);
467
468 // Build commandline
469 const char* args[] = {
470 pakfire_path_relpath(root, path),
471 NULL,
472 };
473
474 // Find all pre-requires
475 r = pakfire_build_run_script(pakfire, "find-prerequires", args,
476 pakfire_execute_capture_stdout_to_array, &prerequires);
477 if (r)
478 goto ERROR;
479
480 // Add all pre-requires to the package
481 if (prerequires) {
482 for (char** element = prerequires; *element; element++) {
483 DEBUG(pakfire, "Adding pre-requires: %s\n", *element);
484 pakfire_package_add_prerequires(pkg, *element);
485 }
486 }
487
488ERROR:
489 if (r && *path)
490 unlink(path);
491 if (prerequires) {
492 for (char** element = prerequires; *element; element++)
493 free(*element);
494 free(prerequires);
495 }
496 return r;
497}
498
499static int pakfire_build_package_add_scriptlet(Pakfire pakfire, PakfirePackage pkg,
500 struct pakfire_packager* packager, const char* type, const char* data) {
501 struct pakfire_scriptlet* scriptlet = NULL;
502 char* shell = NULL;
503 int r;
504
505 // Wrap scriptlet into a shell script
506 r = asprintf(&shell, "#!/bin/sh\n\nset -e\n\n%s\n\nexit 0\n", data);
507 if (r < 0)
508 goto ERROR;
509
510 // Create a scriptlet
511 r = pakfire_scriptlet_create(&scriptlet, pakfire, type, shell, 0);
512 if (r)
513 goto ERROR;
514
515 // Add it to the package
516 r = pakfire_packager_add_scriptlet(packager, scriptlet);
517 if (r) {
518 ERROR(pakfire, "Could not add scriptlet %s\n", type);
519 goto ERROR;
520 }
521
522 // Add scriptlet requirements
523 r = pakfire_build_add_scriptlet_requires(pakfire, pkg, scriptlet);
524 if (r) {
525 ERROR(pakfire, "Could not add scriptlet requirements: %s\n", strerror(errno));
526 goto ERROR;
527 }
528
529 // Success
530 r = 0;
531
532ERROR:
533 if (scriptlet)
534 pakfire_scriptlet_unref(scriptlet);
535 if (shell)
536 free(shell);
537
538 return r;
539}
540
541static int pakfire_build_package_add_scriptlets(Pakfire pakfire, PakfireParser makefile,
542 const char* namespace, PakfirePackage pkg, struct pakfire_packager* packager) {
543 char name[NAME_MAX];
544 int r;
545
546 for (const char** type = pakfire_scriptlet_types; *type; type++) {
547 r = pakfire_string_format(name, "scriptlet:%s", *type);
548 if (r < 0)
549 return r;
550
551 // Fetch the scriptlet
552 char* data = pakfire_parser_get(makefile, namespace, name);
553 if (!data)
554 continue;
555
556 // Add it to the package
557 r = pakfire_build_package_add_scriptlet(pakfire, pkg, packager, *type, data);
558 if (r) {
559 free(data);
560 return r;
561 }
562
563 free(data);
564 }
565
566 return 0;
567}
568
48c6f2e7 569static int pakfire_build_package(Pakfire pakfire, PakfireParser makefile,
7996020a 570 const char* id, const char* buildroot, const char* namespace, const char* target) {
4c07774f
MT
571 PakfireRepo repo = NULL;
572 PakfirePackage pkg = NULL;
48c6f2e7 573 struct pakfire_packager* packager = NULL;
73543ae3 574
a50bde9c
MT
575 int r = 1;
576
577 // Expand the handle into the package name
4c07774f 578 char* name = pakfire_parser_expand(makefile, "packages", namespace);
a50bde9c
MT
579 if (!name) {
580 ERROR(pakfire, "Could not get package name: %s\n", strerror(errno));
581 goto ERROR;
582 }
583
584 INFO(pakfire, "Building package '%s'...\n", name);
585
4c07774f
MT
586 // Fetch build architecture
587 const char* arch = pakfire_get_arch(pakfire);
588 if (!arch)
589 goto ERROR;
590
591 // Fetch the dummy repository
592 repo = pakfire_get_repo(pakfire, "@dummy");
593 if (!repo)
594 goto ERROR;
595
596 // Fetch package from makefile
597 r = pakfire_parser_create_package(makefile, &pkg, repo, namespace, arch);
598 if (r) {
599 ERROR(pakfire, "Could not create package from makefile: %s\n", strerror(errno));
600 goto ERROR;
601 }
602
7996020a
MT
603 // Set build ID
604 if (id)
605 pakfire_package_set_build_id(pkg, id);
606
48c6f2e7
MT
607 // Create a packager
608 r = pakfire_packager_create(&packager, pkg);
609 if (r)
610 goto ERROR;
611
73543ae3 612 // Add files
5b0b3dc2
MT
613 r = pakfire_build_package_add_files(pakfire, makefile, buildroot, namespace,
614 pkg, packager);
73543ae3
MT
615 if (r)
616 goto ERROR;
617
106d2edd
MT
618 // Add scriptlets
619 r = pakfire_build_package_add_scriptlets(pakfire, makefile, namespace, pkg, packager);
620 if (r)
621 goto ERROR;
622
ae7968a7
MT
623 // Write the finished package
624 r = pakfire_packager_finish_to_directory(packager, target);
625 if (r) {
626 ERROR(pakfire, "pakfire_packager_finish() failed: %s\n", strerror(errno));
627 goto ERROR;
628 }
629
4c07774f 630#ifdef ENABLE_DEBUG
23e22751 631 char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
4c07774f
MT
632 if (dump) {
633 DEBUG(pakfire, "%s\n", dump);
634 free(dump);
635 }
636#endif
a50bde9c
MT
637
638 // Success
639 r = 0;
640
641ERROR:
48c6f2e7
MT
642 if (packager)
643 pakfire_packager_unref(packager);
4c07774f
MT
644 if (repo)
645 pakfire_repo_unref(repo);
646 if (pkg)
647 pakfire_package_unref(pkg);
a50bde9c
MT
648 if (name)
649 free(name);
650
651 return r;
652}
653
5b0b3dc2 654static int pakfire_build_packages(Pakfire pakfire, PakfireParser makefile,
7996020a 655 const char* id, const char* buildroot, const char* target) {
a50bde9c
MT
656 DEBUG(pakfire, "Creating packages...");
657 int r = 1;
658
659 // Fetch a list all all packages
660 char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*");
661 if (!packages) {
662 ERROR(pakfire, "Could not find any packages: %s\n", strerror(errno));
663 goto ERROR;
664 }
665
666 unsigned int num_packages = 0;
667
668 // Count how many packages we have
669 for (char** package = packages; *package; package++)
670 num_packages++;
671
672 DEBUG(pakfire, "Found %d package(s)\n", num_packages);
673
674 // Build packages in reverse order
675 for (int i = num_packages - 1; i >= 0; i--) {
7996020a 676 r = pakfire_build_package(pakfire, makefile, id, buildroot, packages[i], target);
a50bde9c
MT
677 if (r)
678 goto ERROR;
679 }
680
681 // Success
682 r = 0;
683
684ERROR:
685 if (packages)
686 free(packages);
687
688 return r;
689}
690
7d999600
MT
691static int pakfire_build_stage(Pakfire pakfire, PakfireParser makefile, const char* stage,
692 pakfire_execute_logging_callback logging_callback, void* data) {
1a276007
MT
693 char template[1024];
694
695 // Prepare template for this stage
696 int r = pakfire_string_format(template, TEMPLATE, stage);
697 if (r < 0)
698 return r;
699
2ffd21f9
MT
700 // Fetch the environment
701 char** envp = pakfire_parser_make_environ(makefile);
702
1a276007
MT
703 // Create the build script
704 char* script = pakfire_parser_expand(makefile, "build", template);
705 if (!script) {
706 ERROR(pakfire, "Could not generate the build script for stage '%s': %s\n",
707 stage, strerror(errno));
708 goto ERROR;
709 }
710
711 INFO(pakfire, "Running build stage '%s'\n", stage);
712
2ffd21f9 713 r = pakfire_execute_script(pakfire, script, strlen(script), NULL, envp, 0,
7d999600 714 logging_callback, data);
1a276007
MT
715 if (r) {
716 ERROR(pakfire, "Build stage '%s' failed with status %d\n", stage, r);
717 }
718
719ERROR:
2ffd21f9
MT
720 if (envp) {
721 for (char** e = envp; *e; e++)
722 free(*e);
723 free(envp);
724 }
1a276007
MT
725 if (script)
726 free(script);
727
728 return r;
729}
730
39f45b30
MT
731static const char* post_build_scripts[] = {
732 "remove-static-libs",
1f62ffae 733 "check-symlinks",
022b794e 734 "check-unsafe-files",
0b5f0bbc 735 "check-libraries",
16043831 736 "check-rpaths",
99aee237 737 "check-buildroot",
7e1fec6f 738 "check-include",
dd864160 739 "check-hardening",
ae703321 740 "check-interpreters",
2504194a 741 "check-fhs",
39f45b30 742 "compress-man-pages",
ffb65de6 743 "strip",
39f45b30
MT
744 NULL,
745};
746
747static int pakfire_build_run_post_build_scripts(Pakfire pakfire, const char* buildroot,
748 pakfire_execute_logging_callback logging_callback, void* data) {
749 // Set default arguments for build scripts
750 const char* args[] = {
751 buildroot, NULL
752 };
753
754 // Run them one by one
755 for (const char** script = post_build_scripts; *script; script++) {
756 int r = pakfire_build_run_script(pakfire, *script, args, logging_callback, data);
757 if (r)
758 return r;
759 }
760
761 return 0;
762}
763
1a276007 764PAKFIRE_EXPORT int pakfire_build(Pakfire pakfire, const char* path,
7996020a 765 const char* target, const char* id, int flags,
7d999600 766 pakfire_execute_logging_callback logging_callback, void* data) {
1a276007 767 PakfireParser makefile = NULL;
3d4011eb 768 char buildroot[PATH_MAX];
1a276007 769 struct pakfire_parser_error* error = NULL;
7996020a 770 char* generated_id = NULL;
3d4011eb 771 int r;
1a276007 772
48c6f2e7
MT
773 // Check for valid input
774 if (!path) {
775 errno = EINVAL;
776 return 1;
777 }
778
779 // The default target is the local repository path
780 if (!target) {
781 PakfireRepo repo = pakfire_get_repo(pakfire, "local");
782 if (!repo) {
783 errno = EINVAL;
784 return 1;
785 }
786
787 target = pakfire_repo_get_path(repo);
788 pakfire_repo_unref(repo);
789 }
790
f0893704
MT
791 const char* packages[] = {
792 path, NULL
793 };
794
795 // Install the package into the build environment
b1a06c73 796 r = pakfire_install(pakfire, packages, 0, NULL);
f0893704
MT
797 if (r) {
798 ERROR(pakfire, "Could not install %s\n", path);
799 goto ERROR;
800 }
801
3d4011eb
MT
802 const char* root = pakfire_get_path(pakfire);
803
804 // Create BUILDROOT
805 pakfire_make_path(pakfire, buildroot, "/tmp/.buildroot.XXXXXX");
806 if (!pakfire_mkdtemp(buildroot))
f0893704 807 goto ERROR;
3d4011eb
MT
808
809 // Make relative BUILDROOT path
810 const char* buildroot_rel = pakfire_path_relpath(root, buildroot);
811 if (!buildroot_rel)
f0893704 812 goto ERROR;
3d4011eb 813
1a276007 814 // Read makefile
3d4011eb 815 r = pakfire_read_makefile(&makefile, pakfire, path, &error);
1a276007
MT
816 if (r) {
817 if (error) {
818 ERROR(pakfire, "Could not parse makefile %s: %s\n", path,
819 pakfire_parser_error_get_message(error));
820 pakfire_parser_error_unref(error);
821 } else {
822 ERROR(pakfire, "Could not parse makefile %s: %s\n", path,
823 strerror(errno));
824 }
825
826 goto ERROR;
827 }
828
7996020a
MT
829 // If no build ID was passed, we generate a random one
830 if (!id) {
831 generated_id = pakfire_generate_uuid();
832 if (!generated_id)
833 goto ERROR;
834
835 id = generated_id;
836 }
837
3d4011eb 838 // Set BUILDROOT
fea2e884 839 pakfire_parser_set(makefile, NULL, "BUILDROOT", buildroot_rel, 0);
3d4011eb 840
1a276007
MT
841 // Run through all build stages
842 for (const char** stage = stages; *stage; stage++) {
7d999600 843 int r = pakfire_build_stage(pakfire, makefile, *stage, logging_callback, data);
1a276007
MT
844 if (r) {
845 // Drop to a shell for debugging
846 if (flags & PAKFIRE_BUILD_INTERACTIVE)
847 pakfire_shell(pakfire);
848
849 goto ERROR;
850 }
851 }
852
39f45b30
MT
853 // Run post build scripts
854 r = pakfire_build_run_post_build_scripts(pakfire, buildroot_rel, logging_callback, data);
8d0f3a35
MT
855 if (r)
856 goto ERROR;
857
a50bde9c 858 // Create the packages
7996020a 859 r = pakfire_build_packages(pakfire, makefile, id, buildroot_rel, target);
a50bde9c
MT
860 if (r) {
861 ERROR(pakfire, "Could not create packages: %s\n", strerror(errno));
862 goto ERROR;
863 }
1a276007
MT
864
865ERROR:
7996020a
MT
866 if (generated_id)
867 free(generated_id);
1a276007
MT
868 if (makefile)
869 pakfire_parser_unref(makefile);
870
3d4011eb
MT
871 // Remove buildroot
872 pakfire_rmtree(buildroot, 0);
873
1a276007
MT
874 return r;
875}
876
877PAKFIRE_EXPORT int pakfire_shell(Pakfire pakfire) {
878 const char* argv[] = {
879 "/bin/bash", "--login", NULL,
880 };
881
882 const int flags =
883 PAKFIRE_EXECUTE_INTERACTIVE | PAKFIRE_EXECUTE_ENABLE_NETWORK;
884
885 return pakfire_execute(pakfire, argv, NULL, flags, NULL, NULL);
886}