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