1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2021 Pakfire development team #
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. #
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. #
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/>. #
19 #############################################################################*/
23 #include <sys/mount.h>
25 #include <uuid/uuid.h>
27 #include <pakfire/build.h>
28 #include <pakfire/cgroup.h>
29 #include <pakfire/dependencies.h>
30 #include <pakfire/dist.h>
31 #include <pakfire/file.h>
32 #include <pakfire/i18n.h>
33 #include <pakfire/jail.h>
34 #include <pakfire/logging.h>
35 #include <pakfire/mount.h>
36 #include <pakfire/package.h>
37 #include <pakfire/packager.h>
38 #include <pakfire/parser.h>
39 #include <pakfire/private.h>
40 #include <pakfire/problem.h>
41 #include <pakfire/repo.h>
42 #include <pakfire/request.h>
43 #include <pakfire/scriptlet.h>
44 #include <pakfire/snapshot.h>
45 #include <pakfire/solution.h>
46 #include <pakfire/string.h>
47 #include <pakfire/util.h>
49 #define CCACHE_DIR "/var/cache/ccache"
51 // We guarantee 2 GiB of memory to every build container
52 #define PAKFIRE_BUILD_MEMORY_GUARANTEED (size_t)2 * 1024 * 1024 * 1024
54 // We allow only up to 2048 processes/threads for every build container
55 #define PAKFIRE_BUILD_PID_LIMIT (size_t)2048
57 struct pakfire_build
{
58 struct pakfire
* pakfire
;
66 char _id
[UUID_STR_LEN
];
68 char target
[PATH_MAX
];
71 struct pakfire_cgroup
* cgroup
;
74 struct pakfire_jail
* jail
;
76 // The build repository
77 struct pakfire_repo
* repo
;
79 // A list of all built packages
80 struct pakfire_packagelist
* packages
;
83 char buildroot
[PATH_MAX
];
90 "#!/bin/bash --login\n" \
99 static int pakfire_build_has_flag(struct pakfire_build
* build
, int flag
) {
100 return build
->flags
& flag
;
103 static int __pakfire_build_setup_repo(struct pakfire
* pakfire
,
104 struct pakfire_repo
* repo
, void* p
) {
109 struct pakfire_build
* build
= (struct pakfire_build
*)p
;
111 // Skip processing the installed repository
112 if (pakfire_repo_is_installed_repo(repo
))
115 // Skip processing any other internal repositories
116 if (pakfire_repo_is_internal(repo
))
119 const char* name
= pakfire_repo_get_name(repo
);
121 DEBUG(pakfire
, "Exporting repository configuration for '%s'\n", name
);
123 // Make path for configuration file
124 r
= pakfire_path(build
->pakfire
, path
, PAKFIRE_CONFIG_DIR
"/repos/%s.repo", name
);
126 ERROR(pakfire
, "Could not make repository configuration path for %s: %m\n", name
);
130 // Open the repository configuration
131 f
= fopen(path
, "w");
133 ERROR(pakfire
, "Could not open %s for writing: %m\n", path
);
137 // Write repository configuration
138 r
= pakfire_repo_write_config(repo
, f
);
140 ERROR(pakfire
, "Could not write repository configuration for %s: %m\n", name
);
144 // Bind-mount any local repositories
145 if (pakfire_repo_is_local(repo
)) {
146 const char* _path
= pakfire_repo_get_path(repo
);
148 // Bind-mount the repository data read-only
149 r
= pakfire_jail_bind(build
->jail
, _path
, _path
, MS_RDONLY
);
151 ERROR(pakfire
, "Could not bind-mount the repository at %s: %m\n", _path
);
164 This function enables the local repository so that it can be accessed by
165 a pakfire instance running inside the chroot.
167 static int pakfire_build_enable_repos(struct pakfire_build
* build
) {
168 return pakfire_repo_walk(build
->pakfire
, __pakfire_build_setup_repo
, build
);
172 Drops the user into a shell
174 static int pakfire_build_shell(struct pakfire_build
* build
) {
177 // Export local repository if running in interactive mode
178 r
= pakfire_build_enable_repos(build
);
183 return pakfire_jail_shell(build
->jail
);
186 static int pakfire_build_run_script(struct pakfire_build
* build
, const char* filename
,
187 const char* args
[], char** output
) {
192 DEBUG(build
->pakfire
, "Running build script '%s'...\n", filename
);
194 // Make the source path
195 pakfire_path_join(path
, PAKFIRE_SCRIPTS_DIR
, filename
);
197 // Open the source script
198 FILE* f
= fopen(path
, "r");
200 ERROR(build
->pakfire
, "Could not open %s: %m\n", path
);
204 // Read the script into memory
205 int r
= pakfire_read_file_into_buffer(f
, &script
, &size
);
207 ERROR(build
->pakfire
, "Could not read script into memory: %m\n");
211 // Execute the script
212 r
= pakfire_jail_exec_script(build
->jail
, script
, size
, args
, output
);
214 ERROR(build
->pakfire
, "Script '%s' failed with status %d\n", filename
, r
);
224 static int pakfire_build_find_dependencies(struct pakfire_build
* build
,
225 struct pakfire_package
* pkg
, struct pakfire_filelist
* filelist
, const char* buildroot
) {
226 char* provides
= NULL
;
227 char* requires
= NULL
;
230 // Allocate path to write the filelist to
231 int r
= pakfire_path(build
->pakfire
, path
, "%s", "/var/tmp/.pakfire-filelist.XXXXXX");
235 // Create a temporary file
236 FILE* f
= pakfire_mktemp(path
, 0);
240 // Write filelist to the temporary file
241 r
= pakfire_filelist_export(filelist
, f
);
244 ERROR(build
->pakfire
, "Could not export filelist: %m\n");
248 const char* root
= pakfire_get_path(build
->pakfire
);
250 // Pass buildroot and the filelist as arguments
251 const char* args
[] = {
252 buildroot
, pakfire_path_relpath(root
, path
), NULL
256 r
= pakfire_build_run_script(build
, "find-provides", args
, &provides
);
258 ERROR(build
->pakfire
, "find-provides returned with error %d\n", r
);
263 r
= pakfire_build_run_script(build
, "find-requires", args
, &requires
);
265 ERROR(build
->pakfire
, "find-requires returned with error %d\n", r
);
269 // Add all provides to the package
271 r
= pakfire_str2deps(build
->pakfire
, pkg
, PAKFIRE_PKG_PROVIDES
, provides
);
273 ERROR(build
->pakfire
, "Could not add provides: %m\n");
278 // Add all requires to the package
280 r
= pakfire_str2deps(build
->pakfire
, pkg
, PAKFIRE_PKG_REQUIRES
, requires
);
282 ERROR(build
->pakfire
, "Could not add provides: %m\n");
300 static int append_to_array(const char*** array
, const char* s
) {
301 unsigned int length
= 0;
303 // Determine the length of the existing array
305 for (const char** element
= *array
; *element
; element
++)
310 *array
= reallocarray(*array
, length
+ 2, sizeof(**array
));
314 // Append s and terminate the array
315 (*array
)[length
] = s
;
316 (*array
)[length
+ 1] = NULL
;
321 static int pakfire_build_package_add_files(struct pakfire_build
* build
,
322 struct pakfire_parser
* makefile
, const char* buildroot
, const char* namespace,
323 struct pakfire_package
* pkg
, struct pakfire_packager
* packager
) {
324 struct pakfire_filelist
* filelist
= NULL
;
328 const char** includes
= NULL
;
329 const char** excludes
= NULL
;
331 // Fetch filelist from makefile
332 files
= pakfire_parser_get_split(makefile
, namespace, "files", '\n');
334 // No files to package?
338 // Split into includes and excludes
339 for (char** file
= files
; *file
; file
++) {
341 r
= append_to_array(&excludes
, *file
);
343 r
= append_to_array(&includes
, *file
);
348 // Allocate a new filelist
349 r
= pakfire_filelist_create(&filelist
, build
->pakfire
);
354 r
= pakfire_filelist_scan(filelist
, build
->buildroot
, includes
, excludes
);
358 const size_t length
= pakfire_filelist_size(filelist
);
359 DEBUG(build
->pakfire
, "%zu file(s) found\n", length
);
361 // Nothing to do if the filelist is empty
366 r
= pakfire_build_find_dependencies(build
, pkg
, filelist
, buildroot
);
368 ERROR(build
->pakfire
, "Finding dependencies failed: %m\n");
372 // Add all files to the package
373 r
= pakfire_packager_add_files(packager
, filelist
);
379 pakfire_filelist_unref(filelist
);
381 for (char** file
= files
; *file
; file
++)
393 static int pakfire_build_add_scriptlet_requires(struct pakfire_build
* build
,
394 struct pakfire_package
* pkg
, struct pakfire_scriptlet
* scriptlet
) {
395 char* prerequires
= NULL
;
400 const char* root
= pakfire_get_path(build
->pakfire
);
403 r
= pakfire_path(build
->pakfire
, path
, "%s", "/var/tmp/.pakfire-scriptlet.XXXXXX");
407 // Fetch scriptlet payload
408 const char* data
= pakfire_scriptlet_get_data(scriptlet
, &size
);
410 // Create a temporary file
411 FILE* f
= pakfire_mktemp(path
, 0);
416 ssize_t bytes_written
= fwrite(data
, 1, size
, f
);
417 if (bytes_written
< 0) {
418 ERROR(build
->pakfire
, "Could not write to %s: %m\n", path
);
427 const char* args
[] = {
428 pakfire_path_relpath(root
, path
),
432 // Find all pre-requires
433 r
= pakfire_build_run_script(build
, "find-prerequires", args
, &prerequires
);
437 // Add all pre-requires to the package
439 r
= pakfire_str2deps(build
->pakfire
, pkg
, PAKFIRE_PKG_PREREQUIRES
, prerequires
);
441 ERROR(build
->pakfire
, "Could not add pre-requires: %m\n");
454 static int pakfire_build_package_add_scriptlet(struct pakfire_build
* build
,
455 struct pakfire_package
* pkg
, struct pakfire_packager
* packager
,
456 const char* type
, const char* data
) {
457 struct pakfire_scriptlet
* scriptlet
= NULL
;
461 // Wrap scriptlet into a shell script
462 r
= asprintf(&shell
, "#!/bin/sh\n\nset -e\n\n%s\n\nexit 0\n", data
);
466 // Create a scriptlet
467 r
= pakfire_scriptlet_create(&scriptlet
, build
->pakfire
, type
, shell
, 0);
471 // Add it to the package
472 r
= pakfire_packager_add_scriptlet(packager
, scriptlet
);
474 ERROR(build
->pakfire
, "Could not add scriptlet %s\n", type
);
478 // Add scriptlet requirements
479 r
= pakfire_build_add_scriptlet_requires(build
, pkg
, scriptlet
);
481 ERROR(build
->pakfire
, "Could not add scriptlet requirements: %m\n");
490 pakfire_scriptlet_unref(scriptlet
);
497 static int pakfire_build_package_add_scriptlets(struct pakfire_build
* build
,
498 struct pakfire_parser
* makefile
, const char* namespace,
499 struct pakfire_package
* pkg
, struct pakfire_packager
* packager
) {
503 for (const char** type
= pakfire_scriptlet_types
; *type
; type
++) {
504 r
= pakfire_string_format(name
, "scriptlet:%s", *type
);
508 // Fetch the scriptlet
509 char* data
= pakfire_parser_get(makefile
, namespace, name
);
513 // Add it to the package
514 r
= pakfire_build_package_add_scriptlet(build
, pkg
, packager
, *type
, data
);
526 static int pakfire_build_package(struct pakfire_build
* build
, struct pakfire_parser
* makefile
,
527 const char* buildroot
, const char* namespace) {
528 struct pakfire_package
* pkg
= NULL
;
529 struct pakfire_packager
* packager
= NULL
;
533 // Expand the handle into the package name
534 char* name
= pakfire_parser_expand(makefile
, "packages", namespace);
536 ERROR(build
->pakfire
, "Could not get package name: %m\n");
540 INFO(build
->pakfire
, "Building package '%s'...\n", name
);
541 DEBUG(build
->pakfire
, " buildroot = %s\n", buildroot
);
543 // Fetch build architecture
544 const char* arch
= pakfire_get_arch(build
->pakfire
);
548 // Fetch package from makefile
549 r
= pakfire_parser_create_package(makefile
, &pkg
, NULL
, namespace, arch
);
551 ERROR(build
->pakfire
, "Could not create package from makefile: %m\n");
556 const char* distribution
= pakfire_parser_get(makefile
, NULL
, "DISTRO_NAME");
558 r
= pakfire_package_set_string(pkg
, PAKFIRE_PKG_DISTRO
, distribution
);
564 pakfire_package_set_uuid(pkg
, PAKFIRE_PKG_BUILD_ID
, build
->id
);
566 // Set source package
567 const char* source_name
= pakfire_parser_get(makefile
, NULL
, "name");
569 r
= pakfire_package_set_string(pkg
, PAKFIRE_PKG_SOURCE_NAME
, source_name
);
575 const char* source_evr
= pakfire_parser_get(makefile
, NULL
, "evr");
577 r
= pakfire_package_set_string(pkg
, PAKFIRE_PKG_SOURCE_EVR
, source_evr
);
583 r
= pakfire_package_set_string(pkg
, PAKFIRE_PKG_SOURCE_ARCH
, "src");
588 r
= pakfire_packager_create(&packager
, build
->pakfire
, pkg
);
593 r
= pakfire_build_package_add_files(build
, makefile
, buildroot
, namespace,
599 r
= pakfire_build_package_add_scriptlets(build
, makefile
, namespace,
604 const char* path
= pakfire_repo_get_path(build
->repo
);
606 // Write the finished package
607 r
= pakfire_packager_finish_to_directory(packager
, path
, NULL
);
609 ERROR(build
->pakfire
, "pakfire_packager_finish() failed: %m\n");
613 // Cleanup all packaged files
614 r
= pakfire_packager_cleanup(packager
);
623 pakfire_packager_unref(packager
);
625 pakfire_package_unref(pkg
);
632 static int pakfire_build_package_dump(struct pakfire
* pakfire
,
633 struct pakfire_package
* pkg
, void* p
) {
634 char* dump
= pakfire_package_dump(pkg
, PAKFIRE_PKG_DUMP_LONG
);
638 INFO(pakfire
, "%s\n", dump
);
644 static int pakfire_build_packages(struct pakfire_build
* build
,
645 struct pakfire_parser
* makefile
) {
646 DEBUG(build
->pakfire
, "Creating packages...");
649 const char* buildroot
= pakfire_relpath(build
->pakfire
, build
->buildroot
);
651 // Fetch a list all all packages
652 char** packages
= pakfire_parser_list_namespaces(makefile
, "packages.package:*");
654 ERROR(build
->pakfire
, "Could not find any packages: %m\n");
658 unsigned int num_packages
= 0;
660 // Count how many packages we have
661 for (char** package
= packages
; *package
; package
++)
664 DEBUG(build
->pakfire
, "Found %d package(s)\n", num_packages
);
666 // Build packages in reverse order
667 for (int i
= num_packages
- 1; i
>= 0; i
--) {
668 r
= pakfire_build_package(build
, makefile
, buildroot
, packages
[i
]);
673 // Rescan the build repository to import all packages again
674 r
= pakfire_repo_scan(build
->repo
, 0);
678 // Fetch all packages
679 r
= pakfire_repo_create_packagelist(build
->repo
, &build
->packages
);
684 r
= pakfire_packagelist_walk(build
->packages
, pakfire_build_package_dump
, NULL
);
698 static int pakfire_build_stage(struct pakfire_build
* build
,
699 struct pakfire_parser
* makefile
, const char* stage
) {
702 // Prepare template for this stage
703 int r
= pakfire_string_format(template, TEMPLATE
, stage
);
707 // Fetch the environment
708 char** envp
= pakfire_parser_make_environ(makefile
);
710 // Create the build script
711 char* script
= pakfire_parser_expand(makefile
, "build", template);
713 ERROR(build
->pakfire
, "Could not generate the build script for stage '%s': %m\n",
718 INFO(build
->pakfire
, "Running build stage '%s'\n", stage
);
720 // Import environment
721 // XXX is this a good idea?
722 r
= pakfire_jail_import_env(build
->jail
, (const char**)envp
);
724 ERROR(build
->pakfire
, "Could not import environment: %m\n");
729 r
= pakfire_jail_exec_script(build
->jail
, script
, strlen(script
), NULL
, NULL
);
731 ERROR(build
->pakfire
, "Build stage '%s' failed with status %d\n", stage
, r
);
736 for (char** e
= envp
; *e
; e
++)
746 static const char* post_build_scripts
[] = {
747 "remove-static-libs",
749 "check-unsafe-files",
755 "check-interpreters",
757 "compress-man-pages",
762 static int pakfire_build_run_post_build_scripts(struct pakfire_build
* build
) {
764 const char* buildroot
= pakfire_relpath(build
->pakfire
, build
->buildroot
);
766 // Set default arguments for build scripts
767 const char* args
[] = {
771 // Run them one by one
772 for (const char** script
= post_build_scripts
; *script
; script
++) {
773 int r
= pakfire_build_run_script(build
, *script
, args
, NULL
);
781 static void pakfire_build_free(struct pakfire_build
* build
) {
783 pakfire_packagelist_unref(build
->packages
);
786 pakfire_repo_clean(build
->repo
, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY
);
787 pakfire_repo_unref(build
->repo
);
791 pakfire_jail_unref(build
->jail
);
794 // Destroy the cgroup
795 pakfire_cgroup_destroy(build
->cgroup
);
798 pakfire_cgroup_unref(build
->cgroup
);
801 pakfire_unref(build
->pakfire
);
805 static int pakfire_build_parse_id(struct pakfire_build
* build
, const char* id
) {
808 // Try parsing the Build ID
810 r
= uuid_parse(id
, build
->id
);
812 ERROR(build
->pakfire
, "Could not parse build ID '%s'\n", id
);
817 // Otherwise initialize the Build ID with something random
819 uuid_generate_random(build
->id
);
822 // Store the ID as string, too
823 uuid_unparse_lower(build
->id
, build
->_id
);
829 Sets up a new cgroup for this build
831 static int pakfire_build_setup_cgroup(struct pakfire_build
* build
) {
832 struct pakfire_config
* config
= NULL
;
837 r
= pakfire_string_format(path
, "pakfire/build-%s", build
->_id
);
839 ERROR(build
->pakfire
, "Could not compose path for cgroup: %m\n");
843 // Create a new cgroup
844 r
= pakfire_cgroup_open(&build
->cgroup
, build
->pakfire
, path
,
845 PAKFIRE_CGROUP_ENABLE_ACCOUNTING
);
847 ERROR(build
->pakfire
, "Could not create cgroup for build %s: %m\n", build
->_id
);
852 config
= pakfire_get_config(build
->pakfire
);
856 // Guarantee some minimum memory
857 size_t memory_guaranteed
= pakfire_config_get_bytes(config
, "build",
858 "memory_guaranteed", PAKFIRE_BUILD_MEMORY_GUARANTEED
);
859 if (memory_guaranteed
) {
860 r
= pakfire_cgroup_set_guaranteed_memory(build
->cgroup
, memory_guaranteed
);
866 size_t memory_limit
= pakfire_config_get_bytes(config
, "build", "memory_limit", 0);
868 r
= pakfire_cgroup_set_memory_limit(build
->cgroup
, memory_limit
);
874 size_t pid_limit
= pakfire_config_get_int(config
, "build",
875 "pid_limit", PAKFIRE_BUILD_PID_LIMIT
);
877 r
= pakfire_cgroup_set_pid_limit(build
->cgroup
, pid_limit
);
884 pakfire_config_unref(config
);
890 Sets up a new jail for this build
892 static int pakfire_build_setup_jail(struct pakfire_build
* build
) {
896 r
= pakfire_jail_create(&build
->jail
, build
->pakfire
, 0);
898 ERROR(build
->pakfire
, "Could not create jail for build %s: %m\n", build
->_id
);
902 // Connect the jail to our cgroup
903 r
= pakfire_jail_set_cgroup(build
->jail
, build
->cgroup
);
905 ERROR(build
->pakfire
, "Could not set cgroup for jail: %m\n");
914 Sets up the ccache for this build
916 static int pakfire_build_setup_ccache(struct pakfire_build
* build
) {
920 // Check if we want a ccache
921 if (pakfire_build_has_flag(build
, PAKFIRE_BUILD_DISABLE_CCACHE
)) {
922 DEBUG(build
->pakfire
, "ccache usage has been disabled for this build\n");
924 // Set CCACHE_DISABLE=1 so that if ccache is installed, it will disable itself
925 r
= pakfire_jail_set_env(build
->jail
, "CCACHE_DISABLE", "1");
927 ERROR(build
->pakfire
, "Could not disable ccache: %m\n");
935 r
= pakfire_cache_path(build
->pakfire
, path
, "%s", "ccache");
937 ERROR(build
->pakfire
, "Could not compose ccache path: %m\n");
941 DEBUG(build
->pakfire
, "Mounting ccache from %s\n", path
);
943 // Ensure path exists
944 r
= pakfire_mkdir(path
, 0755);
945 if (r
&& errno
!= EEXIST
) {
946 ERROR(build
->pakfire
, "Could not create ccache directory %s: %m\n", path
);
950 // Bind-mount the directory
951 r
= pakfire_jail_bind(build
->jail
, path
, CCACHE_DIR
, MS_NOSUID
|MS_NOEXEC
|MS_NODEV
);
953 ERROR(build
->pakfire
, "Could not mount ccache: %m\n");
960 static int pakfire_build_setup_repo(struct pakfire_build
* build
) {
961 char path
[PATH_MAX
] = "/var/tmp/.pakfire-build-repo.XXXXXX";
965 // Create a new repository
966 r
= pakfire_repo_create(&build
->repo
, build
->pakfire
, PAKFIRE_REPO_RESULT
);
968 ERROR(build
->pakfire
, "Could not create repository %s: %m", PAKFIRE_REPO_RESULT
);
973 pakfire_repo_set_description(build
->repo
, _("Build Repository"));
975 // Create a temporary directory
976 const char* p
= pakfire_mkdtemp(path
);
978 ERROR(build
->pakfire
, "Could not create a the build repository: %m\n");
983 r
= pakfire_string_format(url
, "file://%s", path
);
988 pakfire_repo_set_baseurl(build
->repo
, url
);
993 PAKFIRE_EXPORT
int pakfire_build_create(struct pakfire_build
** build
,
994 struct pakfire
* pakfire
, const char* id
, int flags
) {
997 // Allocate build object
998 struct pakfire_build
* b
= calloc(1, sizeof(*b
));
1002 // Reference pakfire
1003 b
->pakfire
= pakfire_ref(pakfire
);
1005 // Initialize reference counter
1012 r
= pakfire_build_parse_id(b
, id
);
1017 r
= pakfire_build_setup_repo(b
);
1022 r
= pakfire_build_setup_cgroup(b
);
1027 r
= pakfire_build_setup_jail(b
);
1032 r
= pakfire_build_setup_ccache(b
);
1040 pakfire_build_free(b
);
1044 PAKFIRE_EXPORT
struct pakfire_build
* pakfire_build_ref(struct pakfire_build
* build
) {
1050 PAKFIRE_EXPORT
struct pakfire_build
* pakfire_build_unref(struct pakfire_build
* build
) {
1051 if (--build
->nrefs
> 0)
1054 pakfire_build_free(build
);
1058 PAKFIRE_EXPORT
int pakfire_build_set_target(
1059 struct pakfire_build
* build
, const char* target
) {
1060 return pakfire_string_set(build
->target
, target
);
1063 static int pakfire_build_install_packages(struct pakfire_build
* build
,
1064 int* snapshot_needs_update
) {
1065 char** packages
= NULL
;
1068 // Fetch configuration
1069 struct pakfire_config
* config
= pakfire_get_config(build
->pakfire
);
1071 ERROR(build
->pakfire
, "Could not fetch configuration: %m\n");
1076 // Fetch build environment
1077 const char* requires
= pakfire_config_get(config
, "build", "requires", NULL
);
1079 ERROR(build
->pakfire
, "No build requirements have been defined\n");
1083 // Split requirements into packages
1084 packages
= pakfire_string_split(requires
, ',');
1090 // Install everything
1091 r
= pakfire_install(build
->pakfire
, 0, 0, (const char**)packages
, NULL
, 0,
1092 &changed
, NULL
, NULL
);
1094 ERROR(build
->pakfire
, "Could not install build dependencies: %m\n");
1098 // Mark snapshot as changed if new packages were installed
1100 *snapshot_needs_update
= 1;
1102 // Update everything
1103 r
= pakfire_sync(build
->pakfire
, 0, 0, &changed
, NULL
, NULL
);
1105 ERROR(build
->pakfire
, "Could not update packages: %m\n");
1109 // Has anything changed?
1111 *snapshot_needs_update
= 1;
1118 pakfire_config_unref(config
);
1121 for (char** package
= packages
; *package
; package
++)
1130 Initializes the build environment
1132 static int pakfire_build_init(struct pakfire_build
* build
) {
1133 char path
[PATH_MAX
];
1136 // Don't do it again
1138 DEBUG(build
->pakfire
, "Build environment has already been initialized\n");
1142 // Compose path for snapshot
1143 r
= pakfire_cache_path(build
->pakfire
, path
, "%s", "snapshot.tar.zst");
1145 ERROR(build
->pakfire
, "Could not compose snapshot path: %m\n");
1149 // Tells us whether we need to (re-)create the snapshot
1150 int snapshot_needs_update
= 0;
1153 if (!pakfire_build_has_flag(build
, PAKFIRE_BUILD_DISABLE_SNAPSHOT
)) {
1154 // Try restoring the snapshot
1155 r
= pakfire_snapshot_restore(build
->pakfire
, path
);
1156 if (r
&& errno
!= ENOENT
)
1160 // Install or update any build dependencies
1161 r
= pakfire_build_install_packages(build
, &snapshot_needs_update
);
1165 // Update the snapshot if there were changes
1166 if (!pakfire_build_has_flag(build
, PAKFIRE_BUILD_DISABLE_SNAPSHOT
) && snapshot_needs_update
) {
1167 // Create a new snapshot
1168 r
= pakfire_snapshot_create(build
->pakfire
, path
);
1173 // Mark as initialized
1179 static int pakfire_build_read_makefile(struct pakfire_build
* build
,
1180 struct pakfire_parser
** parser
, struct pakfire_package
* package
) {
1181 char path
[PATH_MAX
];
1184 struct pakfire_parser_error
* error
= NULL
;
1186 const char* nevra
= pakfire_package_get_string(package
, PAKFIRE_PKG_NEVRA
);
1187 const char* name
= pakfire_package_get_string(package
, PAKFIRE_PKG_NAME
);
1189 // Compose path to makefile
1190 r
= pakfire_path(build
->pakfire
, path
, "/usr/src/packages/%s/%s.nm", nevra
, name
);
1195 r
= pakfire_read_makefile(parser
, build
->pakfire
, path
, &error
);
1198 ERROR(build
->pakfire
, "Could not parse makefile %s: %s\n", path
,
1199 pakfire_parser_error_get_message(error
));
1201 ERROR(build
->pakfire
, "Could not parse makefile %s: %m\n", path
);
1208 const char* buildroot
= pakfire_relpath(build
->pakfire
, build
->buildroot
);
1210 pakfire_parser_set(*parser
, NULL
, "BUILDROOT", buildroot
, 0);
1214 pakfire_parser_error_unref(error
);
1219 static int pakfire_build_perform(struct pakfire_build
* build
,
1220 struct pakfire_parser
* makefile
) {
1223 // Prepare the build
1224 r
= pakfire_build_stage(build
, makefile
, "prepare");
1228 // Perform the build
1229 r
= pakfire_build_stage(build
, makefile
, "build");
1234 if (!pakfire_build_has_flag(build
, PAKFIRE_BUILD_DISABLE_TESTS
)) {
1235 r
= pakfire_build_stage(build
, makefile
, "test");
1240 // Install everything
1241 r
= pakfire_build_stage(build
, makefile
, "install");
1245 // Run post build scripts
1246 r
= pakfire_build_run_post_build_scripts(build
);
1254 // Drop to a shell for debugging
1255 if (pakfire_has_flag(build
->pakfire
, PAKFIRE_BUILD_INTERACTIVE
))
1256 pakfire_build_shell(build
);
1261 static int __pakfire_build_unpackaged_file(struct pakfire
* pakfire
,
1262 struct pakfire_file
* file
, void* p
) {
1263 char* s
= pakfire_file_dump(file
);
1265 ERROR(pakfire
, "%s\n", s
);
1272 static int pakfire_build_check_unpackaged_files(struct pakfire_build
* build
) {
1273 struct pakfire_filelist
* filelist
= NULL
;
1276 // Create a new filelist
1277 r
= pakfire_filelist_create(&filelist
, build
->pakfire
);
1281 // Scan for all files in BUILDROOT
1282 r
= pakfire_filelist_scan(filelist
, build
->buildroot
, NULL
, NULL
);
1286 const size_t length
= pakfire_filelist_size(filelist
);
1289 ERROR(build
->pakfire
, "Unpackaged files found:\n");
1291 r
= pakfire_filelist_walk(filelist
, __pakfire_build_unpackaged_file
, NULL
);
1301 pakfire_filelist_unref(filelist
);
1306 static int pakfire_build_install_package(struct pakfire
* pakfire
,
1307 struct pakfire_package
* pkg
, void* p
) {
1308 struct pakfire_request
* request
= (struct pakfire_request
*)p
;
1310 return pakfire_request_install_package(request
, pkg
);
1313 static int pakfire_build_install_test(struct pakfire_build
* build
) {
1314 struct pakfire_request
* request
= NULL
;
1315 struct pakfire_problem
* problem
= NULL
;
1316 struct pakfire_solution
* solution
= NULL
;
1317 const char* s
= NULL
;
1320 // Create a new request
1321 r
= pakfire_request_create(&request
, build
->pakfire
, 0);
1326 r
= pakfire_packagelist_walk(build
->packages
, pakfire_build_install_package
, request
);
1328 // Solve the request
1329 r
= pakfire_request_solve(request
, NULL
);
1337 ERROR(build
->pakfire
, "Install test failed:\n");
1339 // Walk through all problems
1341 r
= pakfire_request_next_problem(request
, &problem
);
1345 // There are no more problems
1349 // Format the problem into something human-readable
1350 s
= pakfire_problem_to_string(problem
);
1354 ERROR(build
->pakfire
, " * %s\n", s
);
1356 // Walk through all solutions
1358 r
= pakfire_problem_next_solution(problem
, &solution
);
1362 // There are no more solutions
1366 // Format the solution into something human-readable
1367 s
= pakfire_solution_to_string(solution
);
1371 ERROR(build
->pakfire
, " * %s\n", s
);
1384 ERROR(build
->pakfire
, "Install test failed: %m\n");
1386 pakfire_request_unref(request
);
1388 pakfire_problem_unref(problem
);
1390 pakfire_solution_unref(solution
);
1395 static int pakfire_build_post_check(struct pakfire_build
* build
) {
1398 // Check for unpackaged files
1399 r
= pakfire_build_check_unpackaged_files(build
);
1403 // Perform install test
1404 r
= pakfire_build_install_test(build
);
1411 static int pakfire_build_copy_package(struct pakfire
* pakfire
,
1412 struct pakfire_package
* pkg
, void* p
) {
1413 struct pakfire_archive
* archive
= NULL
;
1414 char path
[PATH_MAX
];
1417 const char* target
= (const char*)p
;
1424 // Fetch the package filename
1425 const char* filename
= pakfire_package_get_string(pkg
, PAKFIRE_PKG_FILENAME
);
1431 // Format the destination path
1432 r
= pakfire_string_format(path
, "%s/%s", target
, filename
);
1437 archive
= pakfire_package_get_archive(pkg
);
1443 // Copy it to its destination
1444 r
= pakfire_archive_copy(archive
, path
);
1450 pakfire_archive_unref(archive
);
1455 static int pakfire_build_copy_packages(struct pakfire_build
* build
) {
1456 struct pakfire_repo
* local
= NULL
;
1459 DEBUG(build
->pakfire
, "Copying built packages\n");
1461 // Fetch local repository
1462 local
= pakfire_get_repo(build
->pakfire
, PAKFIRE_REPO_LOCAL
);
1464 // Copy all packages to the target path
1465 if (*build
->target
) {
1466 r
= pakfire_packagelist_walk(build
->packages
,
1467 pakfire_build_copy_package
, build
->target
);
1472 // If we have a local repository, we copy all packages to it
1474 const char* path
= pakfire_repo_get_path(local
);
1476 r
= pakfire_packagelist_walk(build
->packages
,
1477 pakfire_build_copy_package
, (void*)path
);
1485 pakfire_repo_unref(local
);
1490 PAKFIRE_EXPORT
int pakfire_build_exec(struct pakfire_build
* build
, const char* path
) {
1491 struct pakfire_package
* package
= NULL
;
1492 struct pakfire_parser
* makefile
= NULL
;
1493 char* buildroot
= NULL
;
1497 r
= pakfire_path(build
->pakfire
, build
->buildroot
, "%s", "/var/tmp/.pakfire-buildroot.XXXXXX");
1501 // Open the source package
1502 r
= pakfire_commandline_add(build
->pakfire
, path
, &package
);
1506 const char* nevra
= pakfire_package_get_string(package
, PAKFIRE_PKG_NEVRA
);
1508 INFO(build
->pakfire
, "Building %s...\n", nevra
);
1510 // Initialize the build environment
1511 r
= pakfire_build_init(build
);
1515 const char* packages
[] = {
1519 // Install the package into the build environment
1520 r
= pakfire_install(build
->pakfire
, 0, 0, packages
, NULL
, PAKFIRE_REQUEST_ESSENTIAL
,
1523 ERROR(build
->pakfire
, "Could not install %s\n", path
);
1528 buildroot
= pakfire_mkdtemp(build
->buildroot
);
1530 ERROR(build
->pakfire
, "Could not create BUILDROOT: %m\n");
1534 // Open the makefile
1535 r
= pakfire_build_read_makefile(build
, &makefile
, package
);
1539 // Perform the actual build
1540 r
= pakfire_build_perform(build
, makefile
);
1544 // Create the packages
1545 r
= pakfire_build_packages(build
, makefile
);
1547 ERROR(build
->pakfire
, "Could not create packages: %m\n");
1551 // Perform post build checks
1552 r
= pakfire_build_post_check(build
);
1556 // Copy packages to their destination
1557 r
= pakfire_build_copy_packages(build
);
1563 pakfire_parser_unref(makefile
);
1565 pakfire_package_unref(package
);
1567 // Cleanup buildroot
1569 pakfire_rmtree(buildroot
, 0);
1575 Compatibility function to keep the legacy API.
1577 PAKFIRE_EXPORT
int pakfire_build(struct pakfire
* pakfire
, const char* path
,
1578 const char* target
, const char* id
, int flags
) {
1579 struct pakfire_build
* build
= NULL
;
1582 // Check if path is set
1588 // Create a new build environment
1589 r
= pakfire_build_create(&build
, pakfire
, id
, flags
);
1595 r
= pakfire_build_set_target(build
, target
);
1601 r
= pakfire_build_exec(build
, path
);
1605 pakfire_build_unref(build
);
1610 int pakfire_build_clean(struct pakfire
* pakfire
, int flags
) {
1611 struct pakfire_repo
* local
= NULL
;
1614 // Fetch local repository
1615 local
= pakfire_get_repo(pakfire
, PAKFIRE_REPO_LOCAL
);
1617 ERROR(pakfire
, "Could not find repository %s: %m\n", PAKFIRE_REPO_LOCAL
);
1621 // Destroy everything in it
1622 r
= pakfire_repo_clean(local
, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY
);
1628 pakfire_repo_unref(local
);
1634 This is a convenience function that sets up a build environment and
1635 then drops the user into an interactive shell.
1637 PAKFIRE_EXPORT
int pakfire_shell(struct pakfire
* pakfire
, const char** packages
) {
1638 struct pakfire_build
* build
= NULL
;
1641 // Create a new build environment
1642 r
= pakfire_build_create(&build
, pakfire
, NULL
, PAKFIRE_BUILD_INTERACTIVE
);
1644 ERROR(pakfire
, "Could not create build: %m\n");
1648 // Initialize the build environment
1649 r
= pakfire_build_init(build
);
1653 // Install any additional packages
1655 r
= pakfire_install(build
->pakfire
, 0, 0, packages
, NULL
, 0, NULL
, NULL
, NULL
);
1661 r
= pakfire_build_shell(build
);
1665 pakfire_build_unref(build
);