]> 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 b24d7732768a29672d3dc05d74e63de7e3c62af6..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>
 #include <pakfire/packager.h>
 #include <pakfire/parser.h>
 #include <pakfire/private.h>
+#include <pakfire/problem.h>
 #include <pakfire/repo.h>
 #include <pakfire/request.h>
 #include <pakfire/scriptlet.h>
 #include <pakfire/snapshot.h>
+#include <pakfire/solution.h>
 #include <pakfire/string.h>
 #include <pakfire/util.h>
 
@@ -71,9 +76,12 @@ struct pakfire_build {
        // Jail
        struct pakfire_jail* jail;
 
-       // Local build repo
+       // The build repository
        struct pakfire_repo* repo;
 
+       // A list of all built packages
+       struct pakfire_packagelist* packages;
+
        // Buildroot
        char buildroot[PATH_MAX];
 
@@ -95,57 +103,79 @@ static int pakfire_build_has_flag(struct pakfire_build* build, int flag) {
        return build->flags & flag;
 }
 
-/*
-       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) {
-       char repopath[PATH_MAX];
-       int r = 1;
+static int __pakfire_build_setup_repo(struct pakfire* pakfire,
+               struct pakfire_repo* repo, void* p) {
+       char path[PATH_MAX];
+       FILE* f = NULL;
+       int r;
 
-       // Fetch repository configuration
-       char* config = pakfire_repo_get_config(build->repo);
-       if (!config) {
-               ERROR(build->pakfire, "Could not generate repository configuration: %m\n");
-               goto ERROR;
-       }
+       struct pakfire_build* build = (struct pakfire_build*)p;
 
-       const char* path = pakfire_repo_get_path(build->repo);
+       // Skip processing the installed repository
+       if (pakfire_repo_is_installed_repo(repo))
+               return 0;
 
-       // Make sure the source directory exists
-       r = pakfire_mkdir(path, 0700);
-       if (r && errno != EEXIST) {
-               ERROR(build->pakfire, "Could not create repository directory %s: %m\n", path);
-               goto ERROR;
-       }
+       // 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);
 
-       // Bind-mount the repository data read-only
-       r = pakfire_jail_bind(build->jail, path, path, MS_RDONLY);
+       // Make path for configuration file
+       r = pakfire_path(build->pakfire, path, PAKFIRE_CONFIG_DIR "/repos/%s.repo", name);
        if (r) {
-               ERROR(build->pakfire, "Could not bind-mount the repository at %s: %m\n", path);
+               ERROR(pakfire, "Could not make repository configuration path for %s: %m\n", name);
                goto ERROR;
        }
 
-       // Make path for configuration file
-       r = pakfire_path(build->pakfire, repopath,
-               "%s", PAKFIRE_CONFIG_DIR "/repos/" PAKFIRE_REPO_LOCAL ".repo");
+       // 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_file_write(build->pakfire, repopath, 0, 0, 0644, "%s", config);
+       r = pakfire_repo_write_config(repo, f);
        if (r) {
-               ERROR(build->pakfire, "Could not write repository configuration: %m\n");
+               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 (config)
-               free(config);
+       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 pakfire_repo_walk(build->pakfire, __pakfire_build_setup_repo, build);
+}
+
 /*
        Drops the user into a shell
 */
@@ -161,128 +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;
 
-       // Allocate path to write the filelist to
-       int r = pakfire_path(build->pakfire, path, "%s", "/var/tmp/.pakfire-filelist.XXXXXX");
+       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);
+
+       // 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;
+}
+
+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;
 
-       // Create a temporary file
-       FILE* f = pakfire_mktemp(path);
-       if (!f)
+       // 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_package_add_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_package_add_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++;
        }
 
@@ -291,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;
@@ -304,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
@@ -331,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;
 
@@ -342,113 +621,78 @@ 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;
        }
 
        // Add all files to the package
-       for (unsigned int i = 0; i < length; i++) {
-               struct pakfire_file* file = pakfire_filelist_get(filelist, i);
-
-               // Add the file to the package
-               r = pakfire_packager_add(
-                       packager,
-                       pakfire_file_get_abspath(file),
-                       pakfire_file_get_path(file)
-               );
-               if (r) {
-                       pakfire_file_unref(file);
-                       goto ERROR;
-               }
-
-               // Remove the file after it was packaged
-               r = pakfire_file_cleanup(file);
-               if (r) {
-                       pakfire_file_unref(file);
-                       goto ERROR;
-               }
-
-               pakfire_file_unref(file);
-       }
+       r = pakfire_packager_add_files(packager, filelist);
+       if (r)
+               goto ERROR;
 
 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;
-
-       const char* root = pakfire_get_path(build->pakfire);
+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;
 
-       // 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;
+}
 
-       // Build commandline
-       const char* args[] = {
-               pakfire_path_relpath(root, path),
-               NULL,
+static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build,
+               struct pakfire_package* pkg, struct pakfire_scriptlet* scriptlet) {
+       int r;
+
+       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_package_add_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;
 }
 
@@ -539,6 +783,7 @@ static int pakfire_build_package(struct pakfire_build* build, struct pakfire_par
        }
 
        INFO(build->pakfire, "Building package '%s'...\n", name);
+       DEBUG(build->pakfire, "  buildroot = %s\n", buildroot);
 
        // Fetch build architecture
        const char* arch = pakfire_get_arch(build->pakfire);
@@ -554,24 +799,35 @@ static int pakfire_build_package(struct pakfire_build* build, struct pakfire_par
 
        // Set distribution
        const char* distribution = pakfire_parser_get(makefile, NULL, "DISTRO_NAME");
-       if (distribution)
-               pakfire_package_set_distribution(pkg, distribution);
+       if (distribution) {
+               r = pakfire_package_set_string(pkg, PAKFIRE_PKG_DISTRO, distribution);
+               if (r)
+                       goto ERROR;
+       }
 
        // Set build ID
-       pakfire_package_set_build_id_from_uuid(pkg, &build->id);
+       pakfire_package_set_uuid(pkg, PAKFIRE_PKG_BUILD_ID, build->id);
 
        // Set source package
        const char* source_name = pakfire_parser_get(makefile, NULL, "name");
-       if (source_name)
-               pakfire_package_set_source_name(pkg, source_name);
+       if (source_name) {
+               r = pakfire_package_set_string(pkg, PAKFIRE_PKG_SOURCE_NAME, source_name);
+               if (r)
+                       goto ERROR;
+       }
 
        // Set source EVR
        const char* source_evr = pakfire_parser_get(makefile, NULL, "evr");
-       if (source_evr)
-               pakfire_package_set_source_evr(pkg, source_evr);
+       if (source_evr) {
+               r = pakfire_package_set_string(pkg, PAKFIRE_PKG_SOURCE_EVR, source_evr);
+               if (r)
+                       goto ERROR;
+       }
 
        // Set source arch
-       pakfire_package_set_source_arch(pkg, "src");
+       r = pakfire_package_set_string(pkg, PAKFIRE_PKG_SOURCE_ARCH, "src");
+       if (r)
+               goto ERROR;
 
        // Create a packager
        r = pakfire_packager_create(&packager, build->pakfire, pkg);
@@ -590,20 +846,19 @@ static int pakfire_build_package(struct pakfire_build* build, struct pakfire_par
        if (r)
                goto ERROR;
 
+       const char* path = pakfire_repo_get_path(build->repo);
+
        // Write the finished package
-       r = pakfire_packager_finish_to_directory(packager, build->target, NULL);
+       r = pakfire_packager_finish_to_directory(packager, path, NULL);
        if (r) {
                ERROR(build->pakfire, "pakfire_packager_finish() failed: %m\n");
                goto ERROR;
        }
 
-#ifdef ENABLE_DEBUG
-       char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
-       if (dump) {
-               DEBUG(build->pakfire, "%s\n", dump);
-               free(dump);
-       }
-#endif
+       // Cleanup all packaged files
+       r = pakfire_packager_cleanup(packager);
+       if (r)
+               goto ERROR;
 
        // Success
        r = 0;
@@ -619,6 +874,18 @@ ERROR:
        return r;
 }
 
+static int pakfire_build_package_dump(struct pakfire* pakfire,
+               struct pakfire_package* pkg, void* p) {
+       char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
+       if (!dump)
+               return 1;
+
+       INFO(pakfire, "%s\n", dump);
+       free(dump);
+
+       return 0;
+}
+
 static int pakfire_build_packages(struct pakfire_build* build,
                struct pakfire_parser* makefile) {
        DEBUG(build->pakfire, "Creating packages...");
@@ -648,6 +915,21 @@ static int pakfire_build_packages(struct pakfire_build* build,
                        goto ERROR;
        }
 
+       // Rescan the build repository to import all packages again
+       r = pakfire_repo_scan(build->repo, 0);
+       if (r)
+               goto ERROR;
+
+       // Fetch all packages
+       r = pakfire_repo_create_packagelist(build->repo, &build->packages);
+       if (r)
+               goto ERROR;
+
+       // Dump them all
+       r = pakfire_packagelist_walk(build->packages, pakfire_build_package_dump, NULL);
+       if (r)
+               goto ERROR;
+
        // Success
        r = 0;
 
@@ -688,26 +970,146 @@ static int pakfire_build_stage(struct pakfire_build* build,
                goto ERROR;
        }
 
-       // Run the script
-       r = pakfire_jail_exec_script(build->jail, script, strlen(script), NULL, NULL);
-       if (r) {
-               ERROR(build->pakfire, "Build stage '%s' failed with status %d\n", stage, r);
-       }
+       // Run the script
+       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);
+       }
+
+ERROR:
+       if (envp) {
+               for (char** e = envp; *e; e++)
+                       free(*e);
+               free(envp);
+       }
+       if (script)
+               free(script);
+
+       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 (envp) {
-               for (char** e = envp; *e; e++)
-                       free(*e);
-               free(envp);
-       }
-       if (script)
-               free(script);
+       if (filelist)
+               pakfire_filelist_unref(filelist);
 
        return r;
 }
 
 static const char* post_build_scripts[] = {
-       "remove-static-libs",
        "check-symlinks",
        "check-unsafe-files",
        "check-libraries",
@@ -733,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;
        }
@@ -742,8 +1144,13 @@ static int pakfire_build_run_post_build_scripts(struct pakfire_build* build) {
 }
 
 static void pakfire_build_free(struct pakfire_build* build) {
-       if (build->repo)
+       if (build->packages)
+               pakfire_packagelist_unref(build->packages);
+
+       if (build->repo) {
+               pakfire_repo_clean(build->repo, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
                pakfire_repo_unref(build->repo);
+       }
 
        if (build->jail)
                pakfire_jail_unref(build->jail);
@@ -783,29 +1190,6 @@ static int pakfire_build_parse_id(struct pakfire_build* build, const char* id) {
        return 0;
 }
 
-/*
-       Sets the default target
-*/
-static int pakfire_build_set_default_target(struct pakfire_build* build) {
-       // Look for the "local" repository
-       if (build->repo) {
-               const char* target = pakfire_repo_get_path(build->repo);
-               if (target)
-                       pakfire_string_set(build->target, target);
-       }
-
-       // Default to the current working directory
-       if (!*build->target) {
-               char* cwd = getcwd(build->target, sizeof(build->target));
-               if (!cwd) {
-                       ERROR(build->pakfire, "getcwd() failed: %m\n");
-                       return 1;
-               }
-       }
-
-       return 0;
-}
-
 /*
        Sets up a new cgroup for this build
 */
@@ -938,26 +1322,37 @@ static int pakfire_build_setup_ccache(struct pakfire_build* build) {
        return 0;
 }
 
-static int pakfire_build_setup_repos(struct pakfire_build* build) {
+static int pakfire_build_setup_repo(struct pakfire_build* build) {
+       char path[PATH_MAX] = PAKFIRE_TMP_DIR "/pakfire-build-repo.XXXXXX";
+       char url[PATH_MAX];
        int r;
 
        // Create a new repository
-       r = pakfire_repo_create(&build->repo, build->pakfire, PAKFIRE_REPO_LOCAL);
+       r = pakfire_repo_create(&build->repo, build->pakfire, PAKFIRE_REPO_RESULT);
        if (r) {
-               ERROR(build->pakfire, "Could not create repository %s: %m\n", PAKFIRE_REPO_LOCAL);
-               return 1;
+               ERROR(build->pakfire, "Could not create repository %s: %m", PAKFIRE_REPO_RESULT);
+               return r;
        }
 
        // Set description
-       pakfire_repo_set_description(build->repo, _("Locally Built Packages"));
+       pakfire_repo_set_description(build->repo, _("Build Repository"));
+
+       // Create a temporary directory
+       const char* p = pakfire_mkdtemp(path);
+       if (!p) {
+               ERROR(build->pakfire, "Could not create a the build repository: %m\n");
+               return 1;
+       }
 
-       // Set path
-       pakfire_repo_set_baseurl(build->repo, PAKFIRE_REPO_LOCAL_PATH);
+       // Format the URL
+       r = pakfire_string_format(url, "file://%s", path);
+       if (r)
+               return r;
 
-       // Set priority
-       pakfire_repo_set_priority(build->repo, PAKFIRE_REPO_LOCAL_PRIORITY);
+       // Set the URL
+       pakfire_repo_set_baseurl(build->repo, url);
 
-       return 0;
+       return r;
 }
 
 PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
@@ -983,13 +1378,8 @@ PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
        if (r)
                goto ERROR;
 
-       // Setup repos
-       r = pakfire_build_setup_repos(b);
-       if (r)
-               goto ERROR;
-
-       // Set target
-       r = pakfire_build_set_default_target(b);
+       // Setup repo
+       r = pakfire_build_setup_repo(b);
        if (r)
                goto ERROR;
 
@@ -1037,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
@@ -1078,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;
 }
 
 /*
@@ -1114,15 +1475,6 @@ static int pakfire_build_init(struct pakfire_build* build) {
                return 0;
        }
 
-       // Refresh the local repository
-       if (build->repo) {
-               r = pakfire_repo_refresh(build->repo, 0);
-               if (r) {
-                       ERROR(build->pakfire, "Could not refresh the local repository: %m\n");
-                       return 1;
-               }
-       }
-
        // Compose path for snapshot
        r = pakfire_cache_path(build->pakfire, path, "%s", "snapshot.tar.zst");
        if (r) {
@@ -1167,8 +1519,8 @@ static int pakfire_build_read_makefile(struct pakfire_build* build,
 
        struct pakfire_parser_error* error = NULL;
 
-       const char* nevra = pakfire_package_get_nevra(package);
-       const char* name  = pakfire_package_get_name(package);
+       const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA);
+       const char* name  = pakfire_package_get_string(package, PAKFIRE_PKG_NAME);
 
        // Compose path to makefile
        r = pakfire_path(build->pakfire, path, "/usr/src/packages/%s/%s.nm", nevra, name);
@@ -1215,15 +1567,22 @@ static int pakfire_build_perform(struct pakfire_build* build,
                goto ERROR;
 
        // Test the build
-       r = pakfire_build_stage(build, makefile, "test");
-       if (r)
-               goto ERROR;
+       if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_TESTS)) {
+               r = pakfire_build_stage(build, makefile, "test");
+               if (r)
+                       goto ERROR;
+       }
 
        // Install everything
        r = pakfire_build_stage(build, makefile, "install");
        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)
@@ -1234,39 +1593,311 @@ 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;
 }
 
-PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) {
+static int __pakfire_build_unpackaged_file(struct pakfire* pakfire,
+               struct pakfire_file* file, void* p) {
+       char* s = pakfire_file_dump(file);
+       if (s) {
+               ERROR(pakfire, "%s\n", s);
+               free(s);
+       }
+
+       return 0;
+}
+
+static int pakfire_build_check_unpackaged_files(struct pakfire_build* build) {
+       struct pakfire_filelist* filelist = NULL;
+       int r;
+
+       // Create a new filelist
+       r = pakfire_filelist_create(&filelist, build->pakfire);
+       if (r)
+               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 (length) {
+               ERROR(build->pakfire, "Unpackaged files found:\n");
+
+               r = pakfire_filelist_walk(filelist, __pakfire_build_unpackaged_file, NULL);
+               if (r)
+                       goto ERROR;
+
+               // Report an error
+               r = 1;
+       }
+
+ERROR:
+       if (filelist)
+               pakfire_filelist_unref(filelist);
+
+       return r;
+}
+
+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_add_package(request, PAKFIRE_REQ_INSTALL, pkg,
+               PAKFIRE_REQUEST_ESSENTIAL);
+}
+
+static int pakfire_build_install_test(struct pakfire_build* build) {
+       struct pakfire_request* request = NULL;
+       struct pakfire_problem* problem = NULL;
+       struct pakfire_solution* solution = NULL;
+       const char* s = NULL;
+       int r;
+
+       // Create a new request
+       r = pakfire_request_create(&request, build->pakfire, 0);
+       if (r)
+               goto ERROR;
+
+       // Add all packages
+       r = pakfire_packagelist_walk(build->packages, pakfire_build_install_package, request);
+
+       // Solve the request
+       r = pakfire_request_solve(request);
+       switch (r) {
+               // All okay
+               case 0:
+                       break;
+
+               // Dependency Error
+               case 2:
+                       ERROR(build->pakfire, "Install test failed:\n");
+
+                       // Walk through all problems
+                       for (;;) {
+                               r = pakfire_request_next_problem(request, &problem);
+                               if (r)
+                                       goto ERROR;
+
+                               // There are no more problems
+                               if (!problem)
+                                       break;
+
+                               // Format the problem into something human-readable
+                               s = pakfire_problem_to_string(problem);
+                               if (!s)
+                                       continue;
+
+                               ERROR(build->pakfire, "  * %s\n", s);
+
+                               // Walk through all solutions
+                               for (;;) {
+                                       r = pakfire_problem_next_solution(problem, &solution);
+                                       if (r)
+                                               goto ERROR;
+
+                                       // There are no more solutions
+                                       if (!solution)
+                                               break;
+
+                                       // Format the solution into something human-readable
+                                       s = pakfire_solution_to_string(solution);
+                                       if (!s)
+                                               continue;
+
+                                       ERROR(build->pakfire, "    * %s\n", s);
+                               }
+                       }
+
+                       break;
+
+               // Any other errors
+               default:
+                       goto ERROR;
+       }
+
+ERROR:
+       if (r)
+               ERROR(build->pakfire, "Install test failed: %m\n");
+       if (request)
+               pakfire_request_unref(request);
+       if (problem)
+               pakfire_problem_unref(problem);
+       if (solution)
+               pakfire_solution_unref(solution);
+
+       return r;
+}
+
+static int pakfire_build_post_check(struct pakfire_build* build) {
+       int r;
+
+       // Check for unpackaged files
+       r = pakfire_build_check_unpackaged_files(build);
+       if (r)
+               return r;
+
+       // Perform install test
+       r = pakfire_build_install_test(build);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static int pakfire_build_copy_package(struct pakfire* pakfire,
+               struct pakfire_package* pkg, void* p) {
        struct pakfire_archive* archive = NULL;
-       struct pakfire_package* package = NULL;
-       struct pakfire_parser* makefile = NULL;
-       char* buildroot = NULL;
+       char path[PATH_MAX];
        int r;
 
-       // Set buildroot
-       r = pakfire_path(build->pakfire, build->buildroot, "%s", "/var/tmp/.pakfire-buildroot.XXXXXX");
+       const char* target = (const char*)p;
+
+       if (!target) {
+               errno = EINVAL;
+               return 1;
+       }
+
+       // Fetch the package filename
+       const char* filename = pakfire_package_get_string(pkg, PAKFIRE_PKG_FILENAME);
+       if (!filename) {
+               r = 1;
+               goto ERROR;
+       }
+
+       // Format the destination path
+       r = pakfire_string_format(path, "%s/%s", target, filename);
        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);
+       // Open the archive
+       archive = pakfire_package_get_archive(pkg);
+       if (!archive) {
+               r = 1;
                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");
+       // Copy it to its destination
+       r = pakfire_archive_copy(archive, path);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       if (archive)
+               pakfire_archive_unref(archive);
+
+       return r;
+}
+
+static int pakfire_build_copy_packages(struct pakfire_build* build) {
+       struct pakfire_repo* local = NULL;
+       int r = 0;
+
+       DEBUG(build->pakfire, "Copying built packages\n");
+
+       // Fetch local repository
+       local = pakfire_get_repo(build->pakfire, PAKFIRE_REPO_LOCAL);
+
+       // Copy all packages to the target path
+       if (*build->target) {
+               r = pakfire_packagelist_walk(build->packages,
+                       pakfire_build_copy_package, build->target);
+               if (r)
+                       goto ERROR;
+       }
+
+       // If we have a local repository, we copy all packages to it
+       if (local) {
+               const char* path = pakfire_repo_get_path(local);
+               if (path) {
+                       r = pakfire_packagelist_walk(build->packages,
+                               pakfire_build_copy_package, (void*)path);
+                       if (r)
+                               goto ERROR;
+               }
+       }
+
+ERROR:
+       if (local)
+               pakfire_repo_unref(local);
+
+       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;
        }
 
-       const char* nevra = pakfire_package_get_nevra(package);
+       // 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_package* package = NULL;
+       struct pakfire_parser* makefile = NULL;
+       char* buildroot = NULL;
+       int r;
+
+       // Set buildroot
+       r = pakfire_path(build->pakfire, build->buildroot, "%s",
+               PAKFIRE_TMP_DIR "/pakfire-buildroot.XXXXXX");
+       if (r)
+               goto ERROR;
+
+       // 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);
 
        INFO(build->pakfire, "Building %s...\n", nevra);
 
@@ -1275,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;
        }
 
@@ -1311,11 +1937,19 @@ PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* p
                goto ERROR;
        }
 
+       // Perform post build checks
+       r = pakfire_build_post_check(build);
+       if (r)
+               goto ERROR;
+
+       // Copy packages to their destination
+       r = pakfire_build_copy_packages(build);
+       if (r)
+               goto ERROR;
+
 ERROR:
        if (makefile)
                pakfire_parser_unref(makefile);
-       if (archive)
-               pakfire_archive_unref(archive);
        if (package)
                pakfire_package_unref(package);
 
@@ -1389,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;