]> git.ipfire.org Git - people/stevee/pakfire.git/blobdiff - src/libpakfire/build.c
build: Remove any *.a and *.la files internally instead of using a script
[people/stevee/pakfire.git] / src / libpakfire / build.c
index 08977a61fb0021f654364d55f0fa2d0423e7aeda..1d06bf26ac356e189ff8d82f19df73e0f2499abb 100644 (file)
@@ -24,6 +24,9 @@
 #include <unistd.h>
 #include <uuid/uuid.h>
 
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
+
 #include <pakfire/build.h>
 #include <pakfire/cgroup.h>
 #include <pakfire/dependencies.h>
@@ -100,12 +103,77 @@ static int pakfire_build_has_flag(struct pakfire_build* build, int flag) {
        return build->flags & flag;
 }
 
+static int __pakfire_build_setup_repo(struct pakfire* pakfire,
+               struct pakfire_repo* repo, void* p) {
+       char path[PATH_MAX];
+       FILE* f = NULL;
+       int r;
+
+       struct pakfire_build* build = (struct pakfire_build*)p;
+
+       // Skip processing the installed repository
+       if (pakfire_repo_is_installed_repo(repo))
+               return 0;
+
+       // Skip processing any other internal repositories
+       if (pakfire_repo_is_internal(repo))
+               return 0;
+
+       const char* name = pakfire_repo_get_name(repo);
+
+       DEBUG(pakfire, "Exporting repository configuration for '%s'\n", name);
+
+       // Make path for configuration file
+       r = pakfire_path(build->pakfire, path, PAKFIRE_CONFIG_DIR "/repos/%s.repo", name);
+       if (r) {
+               ERROR(pakfire, "Could not make repository configuration path for %s: %m\n", name);
+               goto ERROR;
+       }
+
+       // Create the parent directory
+       r = pakfire_mkparentdir(path, 0755);
+       if (r)
+               goto ERROR;
+
+       // Open the repository configuration
+       f = fopen(path, "w");
+       if (!f) {
+               ERROR(pakfire, "Could not open %s for writing: %m\n", path);
+               goto ERROR;
+       }
+
+       // Write repository configuration
+       r = pakfire_repo_write_config(repo, f);
+       if (r) {
+               ERROR(pakfire, "Could not write repository configuration for %s: %m\n", name);
+               goto ERROR;
+       }
+
+       // Bind-mount any local repositories
+       if (pakfire_repo_is_local(repo)) {
+               const char* _path = pakfire_repo_get_path(repo);
+
+               // Bind-mount the repository data read-only
+               r = pakfire_jail_bind(build->jail, _path, _path, MS_RDONLY);
+               if (r) {
+                       ERROR(pakfire, "Could not bind-mount the repository at %s: %m\n", _path);
+                       goto ERROR;
+               }
+       }
+
+ERROR:
+       if (f)
+               fclose(f);
+
+       return r;
+}
+
 /*
        This function enables the local repository so that it can be accessed by
        a pakfire instance running inside the chroot.
 */
 static int pakfire_build_enable_repos(struct pakfire_build* build) {
-       return 0;
+       return pakfire_repo_walk(build->pakfire, __pakfire_build_setup_repo, build);
 }
 
 /*
@@ -123,126 +191,366 @@ static int pakfire_build_shell(struct pakfire_build* build) {
        return pakfire_jail_shell(build->jail);
 }
 
-static int pakfire_build_run_script(struct pakfire_build* build, const char* filename,
-               const char* args[], char** output) {
-       char* script = NULL;
-       size_t size = 0;
+static int pakfire_build_read_script(struct pakfire_build* build,
+               const char* filename, char** buffer, size_t* length) {
        char path[PATH_MAX];
+       FILE* f = NULL;
+       int r;
 
-       DEBUG(build->pakfire, "Running build script '%s'...\n", filename);
+       // Compose the source path
+       r = pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename);
+       if (r) {
+               ERROR(build->pakfire, "Could not compose path for script '%s': %m\n", filename);
+               goto ERROR;
+       }
 
-       // Make the source path
-       pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename);
+       DEBUG(build->pakfire, "Reading script from %s...\n", path);
 
-       // Open the source script
-       FILE* f = fopen(path, "r");
+       // Open the file
+       f = fopen(path, "r");
        if (!f) {
-               ERROR(build->pakfire, "Could not open %s: %m\n", path);
-               return 1;
+               ERROR(build->pakfire, "Could not open script %s: %m\n", path);
+               goto ERROR;
        }
 
-       // Read the script into memory
-       int r = pakfire_read_file_into_buffer(f, &script, &size);
+       // Read the file into a the buffer
+       r = pakfire_read_file_into_buffer(f, buffer, length);
        if (r) {
-               ERROR(build->pakfire, "Could not read script into memory: %m\n");
+               ERROR(build->pakfire, "Could not read script: %m\n");
                goto ERROR;
        }
 
+ERROR:
+       if (f)
+               fclose(f);
+
+       return r;
+}
+
+static int pakfire_build_run_script(
+               struct pakfire_build* build,
+               const char* filename,
+               const char* args[],
+               pakfire_jail_communicate_in communicate_in,
+               pakfire_jail_communicate_out communicate_out,
+               void* data) {
+       int r;
+
+       char* script = NULL;
+       size_t length = 0;
+
+       DEBUG(build->pakfire, "Running build script '%s'...\n", filename);
+
+       // Read the script
+       r = pakfire_build_read_script(build, filename, &script, &length);
+       if (r) {
+               ERROR(build->pakfire, "Could not read script %s: %m\n", filename);
+               return r;
+       }
+
        // Execute the script
-       r = pakfire_jail_exec_script(build->jail, script, size, args, output);
+       r = pakfire_jail_exec_script(build->jail, script, length, args,
+                       communicate_in, communicate_out, data);
        if (r) {
                ERROR(build->pakfire, "Script '%s' failed with status %d\n", filename, r);
        }
 
-ERROR:
        if (script)
                free(script);
 
        return r;
 }
 
-static int pakfire_build_find_dependencies(struct pakfire_build* build,
-               struct pakfire_package* pkg, struct pakfire_filelist* filelist, const char* buildroot) {
-       char* provides = NULL;
-       char* requires = NULL;
-       char path[PATH_MAX];
+struct pakfire_find_deps_ctx {
+       struct pakfire_package* pkg;
+       int dep;
+       struct pakfire_scriptlet* scriptlet;
+       int class;
+       const pcre2_code* filter;
+
+       struct pakfire_filelist* filelist;
+       unsigned int i;
+};
+
+static int pakfire_build_make_filter(struct pakfire_build* build, pcre2_code** regex,
+               struct pakfire_parser* makefile, const char* namespace, const char* filter) {
+       char* pattern = NULL;
+       int r = 0;
+
+       // Fetch the pattern
+       pattern = pakfire_parser_get(makefile, namespace, filter);
+
+       // Nothing if to if there is no or an empty pattern
+       if (!pattern || !*pattern)
+               goto ERROR;
+
+       DEBUG(build->pakfire, "Found filter for %s: %s\n", filter, pattern);
 
-       // Allocate path to write the filelist to
-       int r = pakfire_path(build->pakfire, path, "%s", "/var/tmp/.pakfire-filelist.XXXXXX");
+       // Compile the regular expression
+       r = pakfire_compile_regex(build->pakfire, regex, pattern);
        if (r)
-               return 1;
+               goto ERROR;
+
+ERROR:
+       if (pattern)
+               free(pattern);
+
+       return r;
+}
 
-       // Create a temporary file
-       FILE* f = pakfire_mktemp(path);
-       if (!f)
+static int pakfire_build_send_filelist(struct pakfire* pakfire, void* data, int fd) {
+       struct pakfire_find_deps_ctx* ctx = (struct pakfire_find_deps_ctx*)data;
+       struct pakfire_file* file = NULL;
+       int r = 0;
+
+       const size_t length = pakfire_filelist_size(ctx->filelist);
+
+       // Check if we have reached the end of the filelist
+       if (ctx->i >= length)
+               return EOF;
+
+       // Fetch the next file
+       file = pakfire_filelist_get(ctx->filelist, ctx->i);
+       if (!file) {
+               DEBUG(pakfire, "Could not fetch file %d: %m\n", ctx->i);
+               r = 1;
                goto ERROR;
+       }
 
-       // Write filelist to the temporary file
-       r = pakfire_filelist_export(filelist, f);
-       fclose(f);
-       if (r) {
-               ERROR(build->pakfire, "Could not export filelist: %m\n");
+       // Fetch the path of the file
+       const char* path = pakfire_file_get_path(file);
+       if (!path) {
+               ERROR(pakfire, "Received a file with an empty path\n");
+               r = 1;
                goto ERROR;
        }
 
-       const char* root = pakfire_get_path(build->pakfire);
+       // Skip files that don't match what we are looking for
+       if (ctx->class && !pakfire_file_matches_class(file, ctx->class))
+               goto SKIP;
 
-       // Pass buildroot and the filelist as arguments
+       // Write path to stdin
+       r = dprintf(fd, "%s\n", path);
+       if (r < 0)
+               return r;
+
+SKIP:
+       // Move on to the next file
+       ctx->i++;
+
+       // Success
+       r = 0;
+
+ERROR:
+       if (file)
+               pakfire_file_unref(file);
+
+       return r;
+}
+
+static int pakfire_build_process_deps(struct pakfire* pakfire,
+               void* data, int priority, const char* buffer, const size_t length) {
+       const struct pakfire_find_deps_ctx* ctx = (struct pakfire_find_deps_ctx*)data;
+       char dep[PATH_MAX];
+       pcre2_match_data* match = NULL;
+       int r = 0;
+
+       // Nothing to do for an empty buffer
+       if (!buffer || !*buffer)
+               return 0;
+
+       switch (priority) {
+               // Add every dependency that we have received
+               case LOG_INFO:
+                       // Copy the dependency to the stack (and remove the trailing newline)
+                       r = pakfire_string_format(dep, "%.*s", (int)length - 1, buffer);
+                       if (r)
+                               return r;
+
+                       DEBUG(pakfire, "Processing dependency: %s\n", dep);
+
+                       // Filter out any dependencies that are provided by this package
+                       if (ctx->dep == PAKFIRE_PKG_REQUIRES) {
+                               // If this is a file, we check if it is on the filelist
+                               if (pakfire_filelist_contains(ctx->filelist, dep))
+                                       goto SKIP;
+
+                               // Otherwise check if this dependency is provided by this package
+                               else if (pakfire_package_matches_dep(ctx->pkg, PAKFIRE_PKG_PROVIDES, dep))
+                                       goto SKIP;
+                       }
+
+                       // Check if this dependency should be filtered
+                       if (ctx->filter) {
+                               match = pcre2_match_data_create_from_pattern(ctx->filter, NULL);
+                               if (!match) {
+                                       ERROR(pakfire, "Could not allocate PCRE match data: %m\n");
+                                       goto ERROR;
+                               }
+
+                               // Perform matching
+                               r = pcre2_jit_match(ctx->filter, (PCRE2_SPTR)dep, length - 1,
+                                       0, 0, match, NULL);
+
+                               // No match
+                               if (r == PCRE2_ERROR_NOMATCH) {
+                                       // Fall through...
+
+                               // Handle any errors
+                               } else if (r < 0) {
+                                       char error[120];
+
+                                       // Fetch the error message
+                                       r = pcre2_get_error_message(r, (PCRE2_UCHAR*)error, sizeof(error));
+                                       if (r < 0) {
+                                               ERROR(pakfire, "Could not fetch PCRE error message: %m\n");
+                                               r = 1;
+                                               goto ERROR;
+                                       }
+
+                                       ERROR(pakfire, "Could not match the filter: %s\n", error);
+                                       r = 1;
+                                       goto ERROR;
+
+                               // Match!
+                               } else {
+                                       DEBUG(pakfire, "Skipping dependency that has been filtered: %s\n", dep);
+                                       r = 0;
+                                       goto ERROR;
+                               }
+                       }
+
+                       // Add dependency
+                       r = pakfire_package_add_dep(ctx->pkg, ctx->dep, buffer);
+                       if (r) {
+                               ERROR(pakfire, "Could not process dependency '%s': %m\n", buffer);
+                               return r;
+                       }
+                       break;
+
+               // Send everything else to the default logger
+               default:
+                       ERROR(pakfire, "%s\n", buffer);
+                       break;
+       }
+
+       goto ERROR;
+
+SKIP:
+       DEBUG(pakfire, "Skipping dependency that is provided by the package itself: %s\n", dep);
+
+ERROR:
+       if (match)
+               pcre2_match_data_free(match);
+
+       return r;
+}
+
+/*
+       This function is a special way to run a script.
+
+       It will pipe the filelist into the standard input of the called script
+       and will read any dependencies from the standard output.
+*/
+static int pakfire_build_find_deps(struct pakfire_build* build,
+               struct pakfire_package* pkg, int dep, const char* script,
+               struct pakfire_filelist* filelist, int class, const pcre2_code* filter) {
+       // Construct the context
+       struct pakfire_find_deps_ctx ctx = {
+               .pkg      = pkg,
+               .dep      = dep,
+               .class    = class,
+               .filter   = filter,
+
+               // Filelist
+               .filelist = filelist,
+               .i        = 0,
+       };
+       int r;
+
+       // Skip calling the script if class doesn't match
+       if (class && !pakfire_filelist_matches_class(filelist, class)) {
+               DEBUG(build->pakfire, "Skipping calling %s as class does not match\n", script);
+               return 0;
+       }
+
+       // Pass the buildroot as first argument
        const char* args[] = {
-               buildroot, pakfire_path_relpath(root, path), NULL
+               pakfire_relpath(build->pakfire, build->buildroot),
+               NULL,
        };
 
-       // Find all provides
-       r = pakfire_build_run_script(build, "find-provides", args, &provides);
+       // Run the script
+       r = pakfire_build_run_script(build, script, args,
+               pakfire_build_send_filelist, pakfire_build_process_deps, &ctx);
+       if (r)
+               ERROR(build->pakfire, "%s returned with error %d\n", script, r);
+
+       return r;
+}
+
+static int pakfire_build_find_dependencies(struct pakfire_build* build,
+               struct pakfire_parser* makefile, const char* namespace,
+               struct pakfire_package* pkg, struct pakfire_filelist* filelist) {
+       pcre2_code* filter_provides = NULL;
+       pcre2_code* filter_requires = NULL;
+       int r;
+
+       // Fetch the provides filter
+       r = pakfire_build_make_filter(build, &filter_provides,
+               makefile, namespace, "filter_provides");
        if (r) {
-               ERROR(build->pakfire, "find-provides returned with error %d\n", r);
+               ERROR(build->pakfire, "Provides filter is broken: %m\n");
                goto ERROR;
        }
 
-       // Find all requires
-       r = pakfire_build_run_script(build, "find-requires", args, &requires);
+       // Fetch the requires filter
+       r = pakfire_build_make_filter(build, &filter_requires,
+               makefile, namespace, "filter_requires");
        if (r) {
-               ERROR(build->pakfire, "find-requires returned with error %d\n", r);
+               ERROR(build->pakfire, "Requires filter is broken: %m\n");
                goto ERROR;
        }
 
-       // Add all provides to the package
-       if (provides) {
-               r = pakfire_str2deps(build->pakfire, pkg, PAKFIRE_PKG_PROVIDES, provides);
-               if (r) {
-                       ERROR(build->pakfire, "Could not add provides: %m\n");
-                       goto ERROR;
-               }
-       }
+       // Find all provides
+       r = pakfire_build_find_deps(build, pkg,
+               PAKFIRE_PKG_PROVIDES, "find-provides", filelist, 0, filter_provides);
+       if (r)
+               goto ERROR;
 
-       // Add all requires to the package
-       if (requires) {
-               r = pakfire_str2deps(build->pakfire, pkg, PAKFIRE_PKG_REQUIRES, requires);
-               if (r) {
-                       ERROR(build->pakfire, "Could not add provides: %m\n");
-                       goto ERROR;
-               }
-       }
+       // Find all Perl provides
+       r = pakfire_build_find_deps(build, pkg,
+               PAKFIRE_PKG_PROVIDES, "perl.prov", filelist, PAKFIRE_FILE_PERL, filter_provides);
+       if (r)
+               goto ERROR;
 
-       // Success
-       r = 0;
+       // Find all requires
+       r = pakfire_build_find_deps(build, pkg,
+               PAKFIRE_PKG_REQUIRES, "find-requires", filelist, 0, filter_requires);
+       if (r)
+               goto ERROR;
+
+       // Find all Perl requires
+       r = pakfire_build_find_deps(build, pkg,
+               PAKFIRE_PKG_REQUIRES, "perl.req", filelist, PAKFIRE_FILE_PERL, filter_requires);
+       if (r)
+               goto ERROR;
 
 ERROR:
-       if (provides)
-               free(provides);
-       if (requires)
-               free(requires);
-       unlink(path);
+       if (filter_provides)
+               pcre2_code_free(filter_provides);
+       if (filter_requires)
+               pcre2_code_free(filter_requires);
 
        return r;
 }
 
-static int append_to_array(const char*** array, const char* s) {
+static int append_to_array(char*** array, const char* s) {
        unsigned int length = 0;
 
        // Determine the length of the existing array
        if (*array) {
-               for (const char** element = *array; *element; element++)
+               for (char** element = *array; *element; element++)
                        length++;
        }
 
@@ -251,8 +559,13 @@ static int append_to_array(const char*** array, const char* s) {
        if (!*array)
                return 1;
 
-       // Append s and terminate the array
-       (*array)[length] = s;
+       // Copy the string to the heap
+       char* p = strdup(s);
+       if (!p)
+               return 1;
+
+       // Append p and terminate the array
+       (*array)[length] = p;
        (*array)[length + 1] = NULL;
 
        return 0;
@@ -264,25 +577,30 @@ static int pakfire_build_package_add_files(struct pakfire_build* build,
        struct pakfire_filelist* filelist = NULL;
        int r = 1;
 
-       char** files = NULL;
-       const char** includes = NULL;
-       const char** excludes = NULL;
+       char** includes = NULL;
+       char** excludes = NULL;
+       char* p = NULL;
 
        // Fetch filelist from makefile
-       files = pakfire_parser_get_split(makefile, namespace, "files", '\n');
+       char* files = pakfire_parser_get(makefile, namespace, "files");
 
        // No files to package?
        if (!files)
                return 0;
 
+       const char* file = strtok_r(files, " \n", &p);
+
        // Split into includes and excludes
-       for (char** file = files; *file; file++) {
-               if (**file == '!')
-                       r = append_to_array(&excludes, *file);
+       while (file) {
+               if (*file == '!')
+                       r = append_to_array(&excludes, file + 1);
                else
-                       r = append_to_array(&includes, *file);
+                       r = append_to_array(&includes, file);
                if (r)
                        goto ERROR;
+
+               // Move on to the next token
+               file = strtok_r(NULL, " \n", &p);
        }
 
        // Allocate a new filelist
@@ -291,7 +609,8 @@ static int pakfire_build_package_add_files(struct pakfire_build* build,
                goto ERROR;
 
        // Scan for files
-       r = pakfire_filelist_scan(filelist, build->buildroot, includes, excludes);
+       r = pakfire_filelist_scan(filelist, build->buildroot,
+               (const char**)includes, (const char**)excludes);
        if (r)
                goto ERROR;
 
@@ -302,8 +621,11 @@ static int pakfire_build_package_add_files(struct pakfire_build* build,
        if (!length)
                goto ERROR;
 
+       // Dump the filelist
+       pakfire_filelist_dump(filelist, 1);
+
        // Find dependencies
-       r = pakfire_build_find_dependencies(build, pkg, filelist, buildroot);
+       r = pakfire_build_find_dependencies(build, makefile, namespace, pkg, filelist);
        if (r) {
                ERROR(build->pakfire, "Finding dependencies failed: %m\n");
                goto ERROR;
@@ -317,77 +639,60 @@ static int pakfire_build_package_add_files(struct pakfire_build* build,
 ERROR:
        if (filelist)
                pakfire_filelist_unref(filelist);
-       if (files) {
-               for (char** file = files; *file; file++)
-                       free(*file);
+       if (files)
                free(files);
-       }
-       if (includes)
+       if (includes) {
+               for (char** include = includes; *include; include++)
+                       free(*include);
                free(includes);
-       if (excludes)
+       }
+       if (excludes) {
+               for (char** exclude = excludes; *exclude; exclude++)
+                       free(*exclude);
                free(excludes);
+       }
 
        return r;
 }
 
-static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build,
-               struct pakfire_package* pkg, struct pakfire_scriptlet* scriptlet) {
-       char* prerequires = NULL;
-       char path[PATH_MAX];
-       size_t size;
-       int r;
+static int pakfire_build_send_scriptlet(struct pakfire* pakfire, void* data, int fd) {
+       const struct pakfire_find_deps_ctx* ctx = (struct pakfire_find_deps_ctx*)data;
+       size_t length = 0;
 
-       const char* root = pakfire_get_path(build->pakfire);
-
-       // Make filename
-       r = pakfire_path(build->pakfire, path, "%s", "/var/tmp/.pakfire-scriptlet.XXXXXX");
-       if (r)
-               return r;
-
-       // Fetch scriptlet payload
-       const char* data = pakfire_scriptlet_get_data(scriptlet, &size);
-
-       // Create a temporary file
-       FILE* f = pakfire_mktemp(path);
-       if (!f)
+       // Fetch the scriptlet
+       const char* p = pakfire_scriptlet_get_data(ctx->scriptlet, &length);
+       if (!p) {
+               ERROR(pakfire, "Could not fetch scriptlet: %m\n");
                return 1;
+       }
 
-       // Write scriptlet
-       ssize_t bytes_written = fwrite(data, 1, size, f);
+       // Write it into the pipe
+       ssize_t bytes_written = write(fd, p, length);
        if (bytes_written < 0) {
-               ERROR(build->pakfire, "Could not write to %s: %m\n", path);
-               fclose(f);
-               goto ERROR;
+               ERROR(pakfire, "Could not send scriptlet: %m\n");
+               return 1;
        }
 
-       // Close file
-       fclose(f);
+       return EOF;
+}
+
+static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build,
+               struct pakfire_package* pkg, struct pakfire_scriptlet* scriptlet) {
+       int r;
 
-       // Build commandline
-       const char* args[] = {
-               pakfire_path_relpath(root, path),
-               NULL,
+       struct pakfire_find_deps_ctx ctx = {
+               .pkg       = pkg,
+               .dep       = PAKFIRE_PKG_PREREQUIRES,
+               .scriptlet = scriptlet,
        };
 
        // Find all pre-requires
-       r = pakfire_build_run_script(build, "find-prerequires", args, &prerequires);
+       r = pakfire_build_run_script(build, "find-prerequires", NULL,
+               pakfire_build_send_scriptlet, pakfire_build_process_deps, &ctx);
        if (r)
                goto ERROR;
 
-       // Add all pre-requires to the package
-       if (prerequires) {
-               r = pakfire_str2deps(build->pakfire, pkg, PAKFIRE_PKG_PREREQUIRES, prerequires);
-               if (r) {
-                       ERROR(build->pakfire, "Could not add pre-requires: %m\n");
-                       goto ERROR;
-               }
-       }
-
 ERROR:
-       if (r && *path)
-               unlink(path);
-       if (prerequires)
-               free(prerequires);
        return r;
 }
 
@@ -666,7 +971,7 @@ static int pakfire_build_stage(struct pakfire_build* build,
        }
 
        // Run the script
-       r = pakfire_jail_exec_script(build->jail, script, strlen(script), NULL, NULL);
+       r = pakfire_jail_exec_script(build->jail, script, strlen(script), NULL, NULL, NULL, NULL);
        if (r) {
                ERROR(build->pakfire, "Build stage '%s' failed with status %d\n", stage, r);
        }
@@ -683,8 +988,128 @@ ERROR:
        return r;
 }
 
+/*
+       This helper function takes a callback which is expected to add any files
+       to the given filelist which will be all removed after.
+*/
+static int pakfire_build_post_remove_files(struct pakfire_build* build,
+               struct pakfire_filelist* filelist, const char* description,
+               int (*callback)(struct pakfire* pakfire, struct pakfire_file* file, void* data)) {
+       struct pakfire_filelist* removees = NULL;
+       int r;
+
+       // Create a filelist with objects that need to be removed
+       r = pakfire_filelist_create(&removees, build->pakfire);
+       if (r)
+               goto ERROR;
+
+       // Find all files that need to be removed
+       r = pakfire_filelist_walk(filelist, callback, removees);
+       if (r)
+               goto ERROR;
+
+       if (!pakfire_filelist_is_empty(removees)) {
+               if (description)
+                       INFO(build->pakfire, "%s\n", description);
+
+               // Show all files which will be removed
+               pakfire_filelist_dump(removees, 0);
+
+               // Remove all files on the removee list
+               r = pakfire_filelist_cleanup(removees);
+               if (r)
+                       goto ERROR;
+
+               // Remove all files from the filelist
+               r = pakfire_filelist_remove_all(filelist, removees);
+               if (r)
+                       goto ERROR;
+       }
+
+ERROR:
+       if (removees)
+               pakfire_filelist_unref(removees);
+
+       return r;
+}
+
+static int __pakfire_build_remove_static_libraries(
+               struct pakfire* pakfire, struct pakfire_file* file, void* data) {
+       struct pakfire_filelist* removees = (struct pakfire_filelist*)data;
+
+       // Find all static libraries
+       if (pakfire_file_matches_class(file, PAKFIRE_FILE_STATIC_LIBRARY))
+               return pakfire_filelist_append(removees, file);
+
+       return 0;
+}
+
+static int pakfire_build_post_remove_static_libraries(
+               struct pakfire_build* build, struct pakfire_filelist* filelist) {
+       return pakfire_build_post_remove_files(build, filelist,
+               "Removing static libaries...", __pakfire_build_remove_static_libraries);
+}
+
+static int __pakfire_build_remove_libtool_archives(
+               struct pakfire* pakfire, struct pakfire_file* file, void* data) {
+       struct pakfire_filelist* removees = (struct pakfire_filelist*)data;
+
+       // Find all libtool archive files
+       if (pakfire_file_matches_class(file, PAKFIRE_FILE_LIBTOOL_ARCHIVE))
+               return pakfire_filelist_append(removees, file);
+
+       return 0;
+}
+
+static int pakfire_build_post_remove_libtool_archives(
+               struct pakfire_build* build, struct pakfire_filelist* filelist) {
+       return pakfire_build_post_remove_files(build, filelist,
+               "Removing libtool archives...", __pakfire_build_remove_libtool_archives);
+}
+
+static int pakfire_build_run_post_build_checks(struct pakfire_build* build) {
+       struct pakfire_filelist* filelist = NULL;
+       int r;
+
+       // Create a filelist of all files in the build
+       r = pakfire_filelist_create(&filelist, build->pakfire);
+       if (r) {
+               ERROR(build->pakfire, "Could not create filelist: %m\n");
+               goto ERROR;
+       }
+
+       // Scan for all files in BUILDROOT
+       r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL);
+       if (r)
+               goto ERROR;
+
+       const size_t length = pakfire_filelist_size(filelist);
+
+       // If the filelist is empty, we can are done
+       if (length == 0) {
+               DEBUG(build->pakfire, "Empty BUILDROOT. Skipping post build checks...\n");
+               r = 0;
+               goto ERROR;
+       }
+
+       // Remove any static libraries
+       r = pakfire_build_post_remove_static_libraries(build, filelist);
+       if (r)
+               goto ERROR;
+
+       // Remove any libtool archives
+       r = pakfire_build_post_remove_libtool_archives(build, filelist);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       if (filelist)
+               pakfire_filelist_unref(filelist);
+
+       return r;
+}
+
 static const char* post_build_scripts[] = {
-       "remove-static-libs",
        "check-symlinks",
        "check-unsafe-files",
        "check-libraries",
@@ -710,7 +1135,7 @@ static int pakfire_build_run_post_build_scripts(struct pakfire_build* build) {
 
        // Run them one by one
        for (const char** script = post_build_scripts; *script; script++) {
-               int r = pakfire_build_run_script(build, *script, args, NULL);
+               int r = pakfire_build_run_script(build, *script, args, NULL, NULL, NULL);
                if (r)
                        return r;
        }
@@ -898,7 +1323,7 @@ static int pakfire_build_setup_ccache(struct pakfire_build* build) {
 }
 
 static int pakfire_build_setup_repo(struct pakfire_build* build) {
-       char path[PATH_MAX] = "/var/tmp/.pakfire-build-repo.XXXXXX";
+       char path[PATH_MAX] = PAKFIRE_TMP_DIR "/pakfire-build-repo.XXXXXX";
        char url[PATH_MAX];
        int r;
 
@@ -1002,37 +1427,21 @@ PAKFIRE_EXPORT int pakfire_build_set_target(
 
 static int pakfire_build_install_packages(struct pakfire_build* build,
                int* snapshot_needs_update) {
-       char** packages = NULL;
-       int r = 1;
-
-       // Fetch configuration
-       struct pakfire_config* config = pakfire_get_config(build->pakfire);
-       if (!config) {
-               ERROR(build->pakfire, "Could not fetch configuration: %m\n");
-               r = 1;
-               goto ERROR;
-       }
-
-       // Fetch build environment
-       const char* requires = pakfire_config_get(config, "build", "requires", NULL);
-       if (!requires) {
-               ERROR(build->pakfire, "No build requirements have been defined\n");
-               goto ERROR;
-       }
+       int r;
 
-       // Split requirements into packages
-       packages = pakfire_string_split(requires, ',');
-       if (!packages)
-               goto ERROR;
+       const char* packages[] = {
+               "build-essential",
+               NULL,
+       };
 
        int changed = 0;
 
        // Install everything
-       r = pakfire_install(build->pakfire, 0, 0, (const char**)packages, NULL, 0,
+       r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0,
                &changed, NULL, NULL);
        if (r) {
                ERROR(build->pakfire, "Could not install build dependencies: %m\n");
-               goto ERROR;
+               return r;
        }
 
        // Mark snapshot as changed if new packages were installed
@@ -1043,27 +1452,14 @@ static int pakfire_build_install_packages(struct pakfire_build* build,
        r = pakfire_sync(build->pakfire, 0, 0, &changed, NULL, NULL);
        if (r) {
                ERROR(build->pakfire, "Could not update packages: %m\n");
-               goto ERROR;
+               return r;
        }
 
        // Has anything changed?
        if (changed)
                *snapshot_needs_update = 1;
 
-       // Success
-       r = 0;
-
-ERROR:
-       if (config)
-               pakfire_config_unref(config);
-
-       if (packages) {
-               for (char** package = packages; *package; package++)
-                       free(*package);
-               free(packages);
-       }
-
-       return r;
+       return 0;
 }
 
 /*
@@ -1182,6 +1578,11 @@ static int pakfire_build_perform(struct pakfire_build* build,
        if (r)
                goto ERROR;
 
+       // Run post build checks
+       r = pakfire_build_run_post_build_checks(build);
+       if (r)
+               goto ERROR;
+
        // Run post build scripts
        r = pakfire_build_run_post_build_scripts(build);
        if (r)
@@ -1192,7 +1593,7 @@ static int pakfire_build_perform(struct pakfire_build* build,
 
 ERROR:
        // Drop to a shell for debugging
-       if (pakfire_has_flag(build->pakfire, PAKFIRE_BUILD_INTERACTIVE))
+       if (pakfire_build_has_flag(build, PAKFIRE_BUILD_INTERACTIVE))
                pakfire_build_shell(build);
 
        return r;
@@ -1247,7 +1648,8 @@ static int pakfire_build_install_package(struct pakfire* pakfire,
                struct pakfire_package* pkg, void* p) {
        struct pakfire_request* request = (struct pakfire_request*)p;
 
-       return pakfire_request_install_package(request, pkg);
+       return pakfire_request_add_package(request, PAKFIRE_REQ_INSTALL, pkg,
+               PAKFIRE_REQUEST_ESSENTIAL);
 }
 
 static int pakfire_build_install_test(struct pakfire_build* build) {
@@ -1266,7 +1668,7 @@ static int pakfire_build_install_test(struct pakfire_build* build) {
        r = pakfire_packagelist_walk(build->packages, pakfire_build_install_package, request);
 
        // Solve the request
-       r = pakfire_request_solve(request, NULL, NULL);
+       r = pakfire_request_solve(request);
        switch (r) {
                // All okay
                case 0:
@@ -1427,31 +1829,73 @@ ERROR:
        return r;
 }
 
+static int pakfire_build_install_source_package(
+               struct pakfire_build* build, struct pakfire_package* package) {
+       struct pakfire_request* request = NULL;
+       struct pakfire_transaction* transaction = NULL;
+       int r;
+
+       // Create a new request
+       r = pakfire_request_create(&request, build->pakfire, 0);
+       if (r)
+               goto ERROR;
+
+       // Add the package
+       r = pakfire_request_add_package(request, PAKFIRE_REQ_INSTALL, package,
+               PAKFIRE_REQUEST_ESSENTIAL);
+       if (r)
+               goto ERROR;
+
+       // Solve the request
+       r = pakfire_request_solve(request);
+       if (r)
+               goto ERROR;
+
+       // Fetch the transaction
+       r = pakfire_request_get_transaction(request, &transaction);
+       if (r)
+               goto ERROR;
+
+       // Set how many packages have been changed
+       const size_t changes = pakfire_transaction_count(transaction);
+
+       // Sanity check to see if we actually try to install anything
+       if (!changes) {
+               ERROR(build->pakfire, "The source package did not get installed\n");
+               r = 1;
+               goto ERROR;
+       }
+
+       // Run the transaction
+       r = pakfire_transaction_run(transaction, 0);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       if (transaction)
+               pakfire_transaction_unref(transaction);
+       if (request)
+               pakfire_request_unref(request);
+
+       return r;
+}
+
 PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) {
-       struct pakfire_archive* archive = NULL;
        struct pakfire_package* package = NULL;
        struct pakfire_parser* makefile = NULL;
        char* buildroot = NULL;
        int r;
 
        // Set buildroot
-       r = pakfire_path(build->pakfire, build->buildroot, "%s", "/var/tmp/.pakfire-buildroot.XXXXXX");
+       r = pakfire_path(build->pakfire, build->buildroot, "%s",
+               PAKFIRE_TMP_DIR "/pakfire-buildroot.XXXXXX");
        if (r)
                goto ERROR;
 
-       // Open source archive
-       r = pakfire_archive_open(&archive, build->pakfire, path);
-       if (r) {
-               ERROR(build->pakfire, "Could not open source archive %s: %m\n", path);
-               goto ERROR;
-       }
-
-       // Fetch package metadata
-       r = pakfire_archive_make_package(archive, NULL, &package);
-       if (r) {
-               ERROR(build->pakfire, "Could not read package metadata: %m\n");
+       // Open the source package
+       r = pakfire_commandline_add(build->pakfire, path, &package);
+       if (r)
                goto ERROR;
-       }
 
        const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA);
 
@@ -1462,15 +1906,10 @@ PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* p
        if (r)
                goto ERROR;
 
-       const char* packages[] = {
-               path, NULL
-       };
-
-       // Install the package into the build environment
-       r = pakfire_install(build->pakfire, 0, 0, packages, NULL, PAKFIRE_REQUEST_ESSENTIAL,
-               NULL, NULL, NULL);
+       // Install the source package
+       r = pakfire_build_install_source_package(build, package);
        if (r) {
-               ERROR(build->pakfire, "Could not install %s\n", path);
+               ERROR(build->pakfire, "Could not install the source package: %m\n");
                goto ERROR;
        }
 
@@ -1511,8 +1950,6 @@ PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* p
 ERROR:
        if (makefile)
                pakfire_parser_unref(makefile);
-       if (archive)
-               pakfire_archive_unref(archive);
        if (package)
                pakfire_package_unref(package);
 
@@ -1586,12 +2023,15 @@ ERROR:
        This is a convenience function that sets up a build environment and
        then drops the user into an interactive shell.
 */
-PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages) {
+PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages, int flags) {
        struct pakfire_build* build = NULL;
        int r;
 
+       // Shells are always interactive
+       flags |= PAKFIRE_BUILD_INTERACTIVE;
+
        // Create a new build environment
-       r = pakfire_build_create(&build, pakfire, NULL, PAKFIRE_BUILD_INTERACTIVE);
+       r = pakfire_build_create(&build, pakfire, NULL, flags);
        if (r) {
                ERROR(pakfire, "Could not create build: %m\n");
                goto ERROR;