]> git.ipfire.org Git - pakfire.git/blobdiff - src/libpakfire/build.c
jail: Remove callbacks from command calls
[pakfire.git] / src / libpakfire / build.c
index 9887f3480180991eecde6211644b55586c73a95f..96cdc66d506c871ebfa840355b02e1adf7c872b7 100644 (file)
 
 #include <pakfire/build.h>
 #include <pakfire/cgroup.h>
+#include <pakfire/config.h>
+#include <pakfire/ctx.h>
 #include <pakfire/dependencies.h>
 #include <pakfire/dist.h>
-#include <pakfire/fhs.h>
 #include <pakfire/file.h>
 #include <pakfire/i18n.h>
 #include <pakfire/jail.h>
 #include <pakfire/package.h>
 #include <pakfire/packager.h>
 #include <pakfire/parser.h>
+#include <pakfire/path.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/transaction.h>
 #include <pakfire/util.h>
 
 #define CCACHE_DIR "/var/cache/ccache"
 // We allow only up to 2048 processes/threads for every build container
 #define PAKFIRE_BUILD_PID_LIMIT                                (size_t)2048
 
+// A list of packages that is installed by default
+static const char* PAKFIRE_BUILD_PACKAGES[] = {
+       "build-essential",
+       NULL,
+};
+
 struct pakfire_build {
+       struct pakfire_ctx* ctx;
        struct pakfire* pakfire;
        int nrefs;
 
@@ -73,7 +82,7 @@ struct pakfire_build {
        char target[PATH_MAX];
 
        // Times
-       time_t time_start;
+       struct timespec time_start;
 
        // cgroup
        struct pakfire_cgroup* cgroup;
@@ -90,8 +99,18 @@ struct pakfire_build {
        // Buildroot
        char buildroot[PATH_MAX];
 
+       // ccache path
+       char ccache_path[PATH_MAX];
+
        // States
        int init:1;
+
+       // Callbacks
+       struct pakfire_build_callbacks {
+               // Log callback
+               pakfire_build_log_callback log;
+               void* log_data;
+       } callbacks;
 };
 
 #define TEMPLATE \
@@ -104,19 +123,95 @@ struct pakfire_build {
        "\n" \
        "exit 0\n"
 
+/*
+       Convenience macro to call the logger callback
+*/
+static int pakfire_build_log(struct pakfire_build* build, int priority, int error,
+               const char* file, int line, const char* function, const char* format, ...) {
+       char* buffer = NULL;
+       va_list args;
+       int r;
+
+       // Don't log messages of a lower loglevel
+       if (pakfire_ctx_get_log_level(build->ctx) < priority)
+               return 0;
+
+       // Format message
+       va_start(args, format);
+       r = vasprintf(&buffer, format, args);
+       va_end(args);
+
+       // Fail if we could not format the message
+       if (r < 0)
+               return r;
+
+       // Send everything to the context logger
+       pakfire_ctx_log(build->ctx, priority, file, line, function, "%s", buffer);
+
+       // Call the build logger callback
+       if (build->callbacks.log)
+               r = build->callbacks.log(build, build->callbacks.log_data, priority, error,
+                       file, line, function, "%s", buffer);
+       else
+               r = 0;
+
+       // Cleanup
+       if (buffer)
+               free(buffer);
+
+       return r;
+}
+
+#define BUILD_LOG_ERRNO(build, priority, r, arg...) \
+       pakfire_build_log(build, priority, r, __FILE__, __LINE__, __FUNCTION__, ## arg)
+#define BUILD_LOG(build, priority, arg...) BUILD_LOG_ERRNO(build, priority, 0, ## arg)
+
+#define BUILD_INFO_ERRNO(build, r, arg...) BUILD_LOG_ERRNO(build, LOG_INFO, r, ## arg)
+#define BUILD_ERROR_ERRNO(build, r, arg...) BUILD_LOG_ERRNO(build, LOG_ERR, r, ## arg)
+#define BUILD_DEBUG_ERRNO(build, r, arg...) BUILD_LOG_ERRNO(build, LOG_DEBUG, r, ## arg)
+
+#define BUILD_INFO(build, arg...) BUILD_INFO_ERRNO(build, 0, ## arg)
+#define BUILD_ERROR(build, arg...) BUILD_ERROR_ERRNO(build, 0, ## arg)
+#define BUILD_DEBUG(build, arg...) BUILD_DEBUG_ERRNO(build, 0, ## arg)
+
 static int pakfire_build_has_flag(struct pakfire_build* build, int flag) {
        return build->flags & flag;
 }
 
-static time_t pakfire_build_duration(struct pakfire_build* build) {
+static double pakfire_build_duration(struct pakfire_build* build) {
+       struct timespec now;
+       int r;
+
        // What time is it now?
-       time_t now = time(NULL);
+       r = clock_gettime(CLOCK_MONOTONIC, &now);
+       if (r < 0)
+               return r;
+
+       return pakfire_timespec_delta(&now, &build->time_start);
+}
+
+static int pakfire_build_jail_log_callback(struct pakfire* pakfire,
+               void* data, int priority, const char* line, size_t length) {
+       struct pakfire_build* build = data;
+
+       // Get the runtime of the build
+       const double t = pakfire_build_duration(build);
+       if (t < 0)
+               return t;
+
+       const unsigned int h  = (unsigned int)t / 3600;
+       const unsigned int m  = (unsigned int)t % 3600 / 60;
+       const unsigned int s  = (unsigned int)t % 60;
+       const unsigned int ms = (unsigned int)(t * 1000.0) % 1000;
+
+       if (h)
+               return BUILD_LOG(build, priority, "[%02d:%02d:%02d.%04d] %.*s", h, m, s, ms, length, line);
 
-       // Return any errors
-       if (now < 0)
-               return now;
+       else if (m)
+               return BUILD_LOG(build, priority, "[   %02d:%02d.%04d] %.*s", m, s, ms, length, line);
 
-       return now - build->time_start;
+       else
+               return BUILD_LOG(build, priority, "[      %02d.%04d] %.*s", s, ms, length, line);
 }
 
 static int __pakfire_build_setup_repo(struct pakfire* pakfire,
@@ -137,12 +232,12 @@ static int __pakfire_build_setup_repo(struct pakfire* pakfire,
 
        const char* name = pakfire_repo_get_name(repo);
 
-       DEBUG(pakfire, "Exporting repository configuration for '%s'\n", name);
+       BUILD_DEBUG(build, "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);
+               BUILD_ERROR(build, "Could not make repository configuration path for %s: %m\n", name);
                goto ERROR;
        }
 
@@ -154,14 +249,14 @@ static int __pakfire_build_setup_repo(struct pakfire* pakfire,
        // Open the repository configuration
        f = fopen(path, "w");
        if (!f) {
-               ERROR(pakfire, "Could not open %s for writing: %m\n", path);
+               BUILD_ERROR(build, "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);
+               BUILD_ERROR(build, "Could not write repository configuration for %s: %m\n", name);
                goto ERROR;
        }
 
@@ -170,10 +265,12 @@ static int __pakfire_build_setup_repo(struct pakfire* pakfire,
                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;
+               if (pakfire_path_exists(_path)) {
+                       r = pakfire_jail_bind(build->jail, _path, _path, MS_RDONLY);
+                       if (r) {
+                               BUILD_ERROR(build, "Could not bind-mount the repository at %s: %m\n", _path);
+                               goto ERROR;
+                       }
                }
        }
 
@@ -214,25 +311,25 @@ static int pakfire_build_read_script(struct pakfire_build* build,
        int r;
 
        // Compose the source path
-       r = pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename);
+       r = pakfire_path_append(path, PAKFIRE_SCRIPTS_DIR, filename);
        if (r) {
                ERROR(build->pakfire, "Could not compose path for script '%s': %m\n", filename);
                goto ERROR;
        }
 
-       DEBUG(build->pakfire, "Reading script from %s...\n", path);
+       BUILD_DEBUG(build, "Reading script from %s...\n", path);
 
        // Open the file
        f = fopen(path, "r");
        if (!f) {
-               ERROR(build->pakfire, "Could not open script %s: %m\n", path);
+               BUILD_ERROR(build, "Could not open script %s: %m\n", path);
                goto ERROR;
        }
 
        // 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: %m\n");
+               BUILD_ERROR(build, "Could not read script: %m\n");
                goto ERROR;
        }
 
@@ -246,16 +343,13 @@ ERROR:
 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) {
+               const char* args[]) {
        int r;
 
        char* script = NULL;
        size_t length = 0;
 
-       DEBUG(build->pakfire, "Running build script '%s'...\n", filename);
+       BUILD_DEBUG(build, "Running build script '%s'...\n", filename);
 
        // Read the script
        r = pakfire_build_read_script(build, filename, &script, &length);
@@ -265,11 +359,9 @@ static int pakfire_build_run_script(
        }
 
        // Execute the script
-       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);
-       }
+       r = pakfire_jail_exec_script(build->jail, script, length, args);
+       if (r)
+               BUILD_ERROR(build, "Script '%s' failed with status %d\n", filename, r);
 
        if (script)
                free(script);
@@ -278,6 +370,7 @@ static int pakfire_build_run_script(
 }
 
 struct pakfire_find_deps_ctx {
+       struct pakfire_build* build;
        struct pakfire_package* pkg;
        int dep;
        struct pakfire_scriptlet* scriptlet;
@@ -314,21 +407,22 @@ ERROR:
        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;
+static int pakfire_build_send_filelist(struct pakfire_ctx* ctx,
+               struct pakfire_jail* jail, void* data, int fd) {
+       struct pakfire_find_deps_ctx* p = (struct pakfire_find_deps_ctx*)data;
        struct pakfire_file* file = NULL;
        int r = 0;
 
-       const size_t length = pakfire_filelist_length(ctx->filelist);
+       const size_t length = pakfire_filelist_length(p->filelist);
 
        // Check if we have reached the end of the filelist
-       if (ctx->i >= length)
+       if (p->i >= length)
                return EOF;
 
        // Fetch the next file
-       file = pakfire_filelist_get(ctx->filelist, ctx->i);
+       file = pakfire_filelist_get(p->filelist, p->i);
        if (!file) {
-               DEBUG(pakfire, "Could not fetch file %d: %m\n", ctx->i);
+               CTX_DEBUG(ctx, "Could not fetch file %u: %m\n", p->i);
                r = 1;
                goto ERROR;
        }
@@ -336,13 +430,13 @@ static int pakfire_build_send_filelist(struct pakfire* pakfire, void* data, int
        // 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");
+               CTX_ERROR(ctx, "Received a file with an empty path\n");
                r = 1;
                goto ERROR;
        }
 
        // Skip files that don't match what we are looking for
-       if (ctx->class && !pakfire_file_matches_class(file, ctx->class))
+       if (p->class && !pakfire_file_matches_class(file, p->class))
                goto SKIP;
 
        // Write path to stdin
@@ -352,7 +446,7 @@ static int pakfire_build_send_filelist(struct pakfire* pakfire, void* data, int
 
 SKIP:
        // Move on to the next file
-       ctx->i++;
+       p->i++;
 
        // Success
        r = 0;
@@ -364,9 +458,9 @@ ERROR:
        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;
+static int pakfire_build_process_deps(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
+               void* data, const char* buffer, const size_t length) {
+       const struct pakfire_find_deps_ctx* p = (struct pakfire_find_deps_ctx*)data;
        char dep[PATH_MAX];
        pcre2_match_data* match = NULL;
        int r = 0;
@@ -375,85 +469,75 @@ static int pakfire_build_process_deps(struct pakfire* pakfire,
        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;
+       // 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);
+       BUILD_DEBUG(p->build, "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;
+       // Filter out any dependencies that are provided by this package
+       if (p->dep == PAKFIRE_PKG_REQUIRES) {
+               // If this is a file, we check if it is on the filelist
+               if (pakfire_filelist_contains(p->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;
-                       }
+               // Otherwise check if this dependency is provided by this package
+               else if (pakfire_package_matches_dep(p->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;
-                               }
-                       }
+       // Check if this dependency should be filtered
+       if (p->filter) {
+               match = pcre2_match_data_create_from_pattern(p->filter, NULL);
+               if (!match) {
+                       CTX_ERROR(ctx, "Could not allocate PCRE match data: %m\n");
+                       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;
+               // Perform matching
+               r = pcre2_jit_match(p->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) {
+                               BUILD_ERROR(p->build, "Could not fetch PCRE error message: %m\n");
+                               r = 1;
+                               goto ERROR;
                        }
-                       break;
 
-               // Send everything else to the default logger
-               default:
-                       ERROR(pakfire, "%s\n", buffer);
-                       break;
+                       BUILD_ERROR(p->build, "Could not match the filter: %s\n", error);
+                       r = 1;
+                       goto ERROR;
+
+               // Match!
+               } else {
+                       BUILD_DEBUG(p->build, "Skipping dependency that has been filtered: %s\n", dep);
+                       r = 0;
+                       goto ERROR;
+               }
+       }
+
+       // Add dependency
+       r = pakfire_package_add_dep(p->pkg, p->dep, buffer);
+       if (r) {
+               BUILD_ERROR(p->build, "Could not process dependency '%s': %m\n", buffer);
+               return r;
        }
 
        goto ERROR;
 
 SKIP:
-       DEBUG(pakfire, "Skipping dependency that is provided by the package itself: %s\n", dep);
+       BUILD_DEBUG(p->build, "Skipping dependency that is provided by the package itself: %s\n", dep);
 
 ERROR:
        if (match)
@@ -473,6 +557,7 @@ static int pakfire_build_find_deps(struct pakfire_build* build,
                struct pakfire_filelist* filelist, int class, const pcre2_code* filter) {
        // Construct the context
        struct pakfire_find_deps_ctx ctx = {
+               .build    = build,
                .pkg      = pkg,
                .dep      = dep,
                .class    = class,
@@ -496,11 +581,14 @@ static int pakfire_build_find_deps(struct pakfire_build* build,
                NULL,
        };
 
+       // Set callbacks
+       pakfire_jail_set_stdin_callback(build->jail, pakfire_build_send_filelist, &ctx);
+       pakfire_jail_set_stdout_callback(build->jail, pakfire_build_process_deps, &ctx);
+
        // Run the script
-       r = pakfire_build_run_script(build, script, args,
-               pakfire_build_send_filelist, pakfire_build_process_deps, &ctx);
+       r = pakfire_build_run_script(build, script, args);
        if (r)
-               ERROR(build->pakfire, "%s returned with error %d\n", script, r);
+               BUILD_ERROR(build, "%s returned with error %d\n", script, r);
 
        return r;
 }
@@ -600,7 +688,8 @@ static int pakfire_build_package_mark_config_files(struct pakfire_build* build,
        if (!configfiles)
                return 0;
 
-       r = pakfire_filelist_walk(filelist, __pakfire_build_package_mark_config_files, configfiles);
+       r = pakfire_filelist_walk(filelist, __pakfire_build_package_mark_config_files,
+               configfiles, 0);
 
        // Cleanup
        if (configfiles) {
@@ -634,11 +723,11 @@ static int pakfire_build_package_add_files(struct pakfire_build* build,
 
        // Scan for files
        r = pakfire_filelist_scan(filelist, build->buildroot,
-               (const char**)includes, (const char**)excludes);
+               (const char**)includes, (const char**)excludes, PAKFIRE_FILELIST_EXTENDED_MATCHING);
        if (r)
                goto ERROR;
 
-       DEBUG(build->pakfire, "%zu file(s) found\n", pakfire_filelist_length(filelist));
+       BUILD_DEBUG(build, "%zu file(s) found\n", pakfire_filelist_length(filelist));
 
        // Nothing to do if the filelist is empty
        if (pakfire_filelist_is_empty(filelist))
@@ -650,7 +739,7 @@ static int pakfire_build_package_add_files(struct pakfire_build* build,
        // Find dependencies
        r = pakfire_build_find_dependencies(build, makefile, namespace, pkg, filelist);
        if (r) {
-               ERROR(build->pakfire, "Finding dependencies failed: %m\n");
+               BUILD_ERROR(build, "Finding dependencies failed: %m\n");
                goto ERROR;
        }
 
@@ -681,21 +770,22 @@ ERROR:
        return 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;
+static int pakfire_build_send_scriptlet(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
+               void* data, int fd) {
+       const struct pakfire_find_deps_ctx* __ctx = (struct pakfire_find_deps_ctx*)data;
        size_t length = 0;
 
        // Fetch the scriptlet
-       const char* p = pakfire_scriptlet_get_data(ctx->scriptlet, &length);
+       const char* p = pakfire_scriptlet_get_data(__ctx->scriptlet, &length);
        if (!p) {
-               ERROR(pakfire, "Could not fetch scriptlet: %m\n");
+               CTX_ERROR(ctx, "Could not fetch scriptlet: %m\n");
                return 1;
        }
 
        // Write it into the pipe
        ssize_t bytes_written = write(fd, p, length);
        if (bytes_written < 0) {
-               ERROR(pakfire, "Could not send scriptlet: %m\n");
+               CTX_ERROR(ctx, "Could not send scriptlet: %m\n");
                return 1;
        }
 
@@ -707,14 +797,18 @@ static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build,
        int r;
 
        struct pakfire_find_deps_ctx ctx = {
+               .build     = build,
                .pkg       = pkg,
                .dep       = PAKFIRE_PKG_PREREQUIRES,
                .scriptlet = scriptlet,
        };
 
+       // Set callbacks
+       pakfire_jail_set_stdin_callback(build->jail, pakfire_build_send_scriptlet, &ctx);
+       pakfire_jail_set_stdout_callback(build->jail, pakfire_build_process_deps, &ctx);
+
        // Find all pre-requires
-       r = pakfire_build_run_script(build, "find-prerequires", NULL,
-               pakfire_build_send_scriptlet, pakfire_build_process_deps, &ctx);
+       r = pakfire_build_run_script(build, "find-prerequires", NULL);
        if (r)
                goto ERROR;
 
@@ -735,21 +829,21 @@ static int pakfire_build_package_add_scriptlet(struct pakfire_build* build,
                goto ERROR;
 
        // Create a scriptlet
-       r = pakfire_scriptlet_create(&scriptlet, build->pakfire, type, shell, 0);
+       r = pakfire_scriptlet_create(&scriptlet, build->ctx, type, shell, 0);
        if (r)
                goto ERROR;
 
        // Add it to the package
        r = pakfire_packager_add_scriptlet(packager, scriptlet);
        if (r) {
-               ERROR(build->pakfire, "Could not add scriptlet %s\n", type);
+               BUILD_ERROR(build, "Could not add scriptlet %s\n", type);
                goto ERROR;
        }
 
        // Add scriptlet requirements
        r = pakfire_build_add_scriptlet_requires(build, pkg, scriptlet);
        if (r) {
-               ERROR(build->pakfire, "Could not add scriptlet requirements: %m\n");
+               BUILD_ERROR(build, "Could not add scriptlet requirements: %m\n");
                goto ERROR;
        }
 
@@ -808,8 +902,8 @@ static int pakfire_build_package(struct pakfire_build* build, struct pakfire_par
                goto ERROR;
        }
 
-       INFO(build->pakfire, "Building package '%s'...\n", name);
-       DEBUG(build->pakfire, "  buildroot = %s\n", buildroot);
+       BUILD_INFO(build, "Building package '%s'...\n", name);
+       BUILD_DEBUG(build, "  buildroot = %s\n", buildroot);
 
        // Fetch build architecture
        const char* arch = pakfire_get_arch(build->pakfire);
@@ -819,7 +913,7 @@ static int pakfire_build_package(struct pakfire_build* build, struct pakfire_par
        // Fetch package from makefile
        r = pakfire_parser_create_package(makefile, &pkg, NULL, namespace, arch);
        if (r) {
-               ERROR(build->pakfire, "Could not create package from makefile: %m\n");
+               BUILD_ERROR(build, "Could not create package from makefile: %m\n");
                goto ERROR;
        }
 
@@ -877,7 +971,7 @@ static int pakfire_build_package(struct pakfire_build* build, struct pakfire_par
        // Write the finished package
        r = pakfire_packager_finish_to_directory(packager, path, NULL);
        if (r) {
-               ERROR(build->pakfire, "pakfire_packager_finish() failed: %m\n");
+               BUILD_ERROR(build, "pakfire_packager_finish() failed: %m\n");
                goto ERROR;
        }
 
@@ -900,13 +994,15 @@ ERROR:
        return r;
 }
 
-static int pakfire_build_package_dump(struct pakfire* pakfire,
+static int pakfire_build_package_dump(struct pakfire_ctx* ctx,
                struct pakfire_package* pkg, void* p) {
+       struct pakfire_build* build = p;
+
        char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
        if (!dump)
                return 1;
 
-       INFO(pakfire, "%s\n", dump);
+       BUILD_INFO(build, "%s\n", dump);
        free(dump);
 
        return 0;
@@ -914,7 +1010,7 @@ static int pakfire_build_package_dump(struct pakfire* pakfire,
 
 static int pakfire_build_packages(struct pakfire_build* build,
                struct pakfire_parser* makefile) {
-       DEBUG(build->pakfire, "Creating packages...");
+       BUILD_INFO(build, "Creating packages...");
        int r = 1;
 
        const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
@@ -922,7 +1018,7 @@ static int pakfire_build_packages(struct pakfire_build* build,
        // Fetch a list all all packages
        char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*");
        if (!packages) {
-               ERROR(build->pakfire, "Could not find any packages: %m\n");
+               BUILD_ERROR(build, "Could not find any packages: %m\n");
                goto ERROR;
        }
 
@@ -932,7 +1028,7 @@ static int pakfire_build_packages(struct pakfire_build* build,
        for (char** package = packages; *package; package++)
                num_packages++;
 
-       DEBUG(build->pakfire, "Found %d package(s)\n", num_packages);
+       DEBUG(build->pakfire, "Found %u package(s)\n", num_packages);
 
        // Build packages in reverse order
        for (int i = num_packages - 1; i >= 0; i--) {
@@ -947,7 +1043,7 @@ static int pakfire_build_packages(struct pakfire_build* build,
                goto ERROR;
 
        // Create a new packagelist
-       r = pakfire_packagelist_create(&build->packages, build->pakfire);
+       r = pakfire_packagelist_create(&build->packages, build->ctx);
        if (r)
                goto ERROR;
 
@@ -957,7 +1053,7 @@ static int pakfire_build_packages(struct pakfire_build* build,
                goto ERROR;
 
        // Dump them all
-       r = pakfire_packagelist_walk(build->packages, pakfire_build_package_dump, NULL);
+       r = pakfire_packagelist_walk(build->packages, pakfire_build_package_dump, build);
        if (r)
                goto ERROR;
 
@@ -986,26 +1082,26 @@ static int pakfire_build_stage(struct pakfire_build* build,
        // Create the build script
        char* script = pakfire_parser_expand(makefile, "build", template);
        if (!script) {
-               ERROR(build->pakfire, "Could not generate the build script for stage '%s': %m\n",
-                       stage);
+               BUILD_ERROR(build, "Could not generate the build script for stage '%s': %m\n", stage);
                goto ERROR;
        }
 
-       INFO(build->pakfire, "Running build stage '%s'\n", stage);
+       BUILD_INFO(build, "Running build stage '%s'\n", stage);
 
        // Import environment
        // XXX is this a good idea?
        r = pakfire_jail_import_env(build->jail, (const char**)envp);
        if (r) {
-               ERROR(build->pakfire, "Could not import environment: %m\n");
+               BUILD_ERROR(build, "Could not import environment: %m\n");
                goto ERROR;
        }
 
+#warning We are thworing away the output here. Do we want this?
+
        // 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);
-       }
+       r = pakfire_jail_exec_script(build->jail, script, strlen(script), NULL);
+       if (r)
+               BUILD_ERROR(build, "Build stage '%s' failed with status %d\n", stage, r);
 
 ERROR:
        if (envp) {
@@ -1022,6 +1118,7 @@ ERROR:
 enum {
        PAKFIRE_BUILD_CLEANUP_FILES         = (1 << 0),
        PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY    = (1 << 1),
+       PAKFIRE_BUILD_SHOW_PROGRESS         = (1 << 2),
 };
 
 /*
@@ -1041,20 +1138,21 @@ static int pakfire_build_post_process_files(struct pakfire_build* build,
                goto ERROR;
 
        // Find all files that need to be removed
-       r = pakfire_filelist_walk(filelist, callback, removees);
+       r = pakfire_filelist_walk(filelist, callback, removees,
+               (flags & PAKFIRE_BUILD_SHOW_PROGRESS) ? PAKFIRE_FILELIST_SHOW_PROGRESS : 0);
        if (r)
                goto ERROR;
 
        if (!pakfire_filelist_is_empty(removees)) {
                if (description)
-                       INFO(build->pakfire, "%s\n", description);
+                       BUILD_INFO(build, "%s\n", description);
 
                // Show all files which will be removed
-               pakfire_filelist_dump(removees, PAKFIRE_FILE_DUMP_FULL|PAKFIRE_FILE_DUMP_HARDENING);
+               pakfire_filelist_dump(removees, PAKFIRE_FILE_DUMP_FULL|PAKFIRE_FILE_DUMP_ISSUES);
 
                // Remove all files on the removee list
                if (flags & PAKFIRE_BUILD_CLEANUP_FILES) {
-                       r = pakfire_filelist_cleanup(removees);
+                       r = pakfire_filelist_cleanup(removees, PAKFIRE_FILE_CLEANUP_TIDY);
                        if (r)
                                goto ERROR;
 
@@ -1110,35 +1208,6 @@ static int pakfire_build_post_remove_static_libraries(
                PAKFIRE_BUILD_CLEANUP_FILES);
 }
 
-static int __pakfire_build_post_check_stripped(
-               struct pakfire* pakfire, struct pakfire_file* file, void* data) {
-       struct pakfire_filelist* filelist = (struct pakfire_filelist*)data;
-       int r;
-
-       // Skip anything that isn't an ELF file
-       if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF))
-               return 0;
-
-       // Collect all stripped files
-       if (pakfire_file_is_stripped(file)) {
-               r = pakfire_filelist_add(filelist, file);
-               if (r) {
-                       ERROR(pakfire, "Could not add file to filelist: %m\n");
-                       return r;
-               }
-       }
-
-       return 0;
-}
-
-static int pakfire_build_post_check_stripped(
-               struct pakfire_build* build, struct pakfire_filelist* filelist) {
-       return pakfire_build_post_process_files(build, filelist,
-               "Files lacking debugging information:",
-               __pakfire_build_post_check_stripped,
-               PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
-}
-
 static int __pakfire_build_remove_libtool_archives(
                struct pakfire* pakfire, struct pakfire_file* file, void* data) {
        struct pakfire_filelist* removees = (struct pakfire_filelist*)data;
@@ -1191,53 +1260,19 @@ static int pakfire_build_post_check_broken_symlinks(
 }
 
 /*
-       Filesystem Layout Check
-*/
-static int __pakfire_build_post_check_filesystem(
-               struct pakfire* pakfire, struct pakfire_file* file, void* data) {
-       struct pakfire_filelist* illegal = (struct pakfire_filelist*)data;
-       int r;
-
-       // Perform FHS check
-       r = pakfire_fhs_check_file(pakfire, file);
-       if (r) {
-               r = pakfire_filelist_add(illegal, file);
-               if (r)
-                       return r;
-       }
-
-       return 0;
-}
-
-static int pakfire_build_post_check_filesystem(
-               struct pakfire_build* build, struct pakfire_filelist* filelist) {
-       return pakfire_build_post_process_files(
-               build,
-               filelist,
-               "Illegal files:",
-               __pakfire_build_post_check_filesystem,
-               PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
-}
-
-/*
-       Hardening
+       File Issues
 */
 
-static int __pakfire_build_post_check_hardening(
+static int __pakfire_build_post_check_files(
                struct pakfire* pakfire, struct pakfire_file* file, void* data) {
        struct pakfire_filelist* broken = (struct pakfire_filelist*)data;
        int issues = 0;
        int r;
 
-       // Skip anything that isn't an ELF file
-       if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF))
-               return 0;
-
-       // Check hardening
-       r = pakfire_file_check_hardening(file, &issues);
+       // Check file for issues
+       r = pakfire_file_check(file, &issues);
        if (r) {
-               ERROR(pakfire, "%s: Hardening Check failed: %m\n",
-                       pakfire_file_get_path(file));
+               //BUILD_ERROR(build, "%s: File Check failed: %m\n", pakfire_file_get_path(file));
                return r;
        }
 
@@ -1251,14 +1286,14 @@ static int __pakfire_build_post_check_hardening(
        return 0;
 }
 
-static int pakfire_build_post_check_hardening(
+static int pakfire_build_post_check_files(
                struct pakfire_build* build, struct pakfire_filelist* filelist) {
        return pakfire_build_post_process_files(
                build,
                filelist,
-               "Hardening Issues:",
-               __pakfire_build_post_check_hardening,
-               PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
+               "File Issues:",
+               __pakfire_build_post_check_files,
+               PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY|PAKFIRE_BUILD_SHOW_PROGRESS);
 }
 
 static int pakfire_build_run_post_build_checks(struct pakfire_build* build) {
@@ -1268,12 +1303,12 @@ static int pakfire_build_run_post_build_checks(struct pakfire_build* build) {
        // 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");
+               BUILD_ERROR(build, "Could not create filelist: %m\n");
                goto ERROR;
        }
 
        // Scan for all files in BUILDROOT
-       r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL);
+       r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL, 0);
        if (r)
                goto ERROR;
 
@@ -1284,11 +1319,6 @@ static int pakfire_build_run_post_build_checks(struct pakfire_build* build) {
                goto ERROR;
        }
 
-       // Check if binaries have been stripped
-       r = pakfire_build_post_check_stripped(build, filelist);
-       if (r)
-               goto ERROR;
-
        // Remove any static libraries
        r = pakfire_build_post_remove_static_libraries(build, filelist);
        if (r)
@@ -1304,13 +1334,8 @@ static int pakfire_build_run_post_build_checks(struct pakfire_build* build) {
        if (r)
                goto ERROR;
 
-       // Check filesystem layout
-       r = pakfire_build_post_check_filesystem(build, filelist);
-       if (r)
-               goto ERROR;
-
-       // Check hardening
-       r = pakfire_build_post_check_hardening(build, filelist);
+       // Check files
+       r = pakfire_build_post_check_files(build, filelist);
        if (r)
                goto ERROR;
 
@@ -1322,12 +1347,6 @@ ERROR:
 }
 
 static const char* post_build_scripts[] = {
-       "check-unsafe-files",
-       "check-rpaths",
-       "check-buildroot",
-       "check-include",
-       "check-hardening",
-       "check-interpreters",
        "compress-man-pages",
        "strip",
        NULL,
@@ -1344,7 +1363,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, NULL, NULL);
+               int r = pakfire_build_run_script(build, *script, args);
                if (r)
                        return r;
        }
@@ -1372,7 +1391,10 @@ static void pakfire_build_free(struct pakfire_build* build) {
                pakfire_cgroup_unref(build->cgroup);
        }
 
-       pakfire_unref(build->pakfire);
+       if (build->pakfire)
+               pakfire_unref(build->pakfire);
+       if (build->ctx)
+               pakfire_ctx_unref(build->ctx);
        free(build);
 }
 
@@ -1384,8 +1406,7 @@ static int pakfire_build_parse_id(struct pakfire_build* build, const char* id) {
                r = uuid_parse(id, build->id);
                if (r) {
                        ERROR(build->pakfire, "Could not parse build ID '%s'\n", id);
-                       errno = EINVAL;
-                       return r;
+                       return -EINVAL;
                }
 
        // Otherwise initialize the Build ID with something random
@@ -1415,7 +1436,7 @@ static int pakfire_build_setup_cgroup(struct pakfire_build* build) {
        }
 
        // Create a new cgroup
-       r = pakfire_cgroup_open(&build->cgroup, build->pakfire, path,
+       r = pakfire_cgroup_open(&build->cgroup, build->ctx, path,
                PAKFIRE_CGROUP_ENABLE_ACCOUNTING);
        if (r) {
                ERROR(build->pakfire, "Could not create cgroup for build %s: %m\n", build->_id);
@@ -1467,7 +1488,7 @@ static int pakfire_build_setup_jail(struct pakfire_build* build) {
        int r;
 
        // Create a new jail
-       r = pakfire_jail_create(&build->jail, build->pakfire, 0);
+       r = pakfire_jail_create(&build->jail, build->pakfire);
        if (r) {
                ERROR(build->pakfire, "Could not create jail for build %s: %m\n", build->_id);
                return r;
@@ -1480,6 +1501,13 @@ static int pakfire_build_setup_jail(struct pakfire_build* build) {
                return r;
        }
 
+       // Build everything with a slightly lower priority
+       r = pakfire_jail_nice(build->jail, 5);
+       if (r) {
+               ERROR(build->pakfire, "Could not set nice level: %m\n");
+               return r;
+       }
+
        // Done
        return 0;
 }
@@ -1487,8 +1515,30 @@ static int pakfire_build_setup_jail(struct pakfire_build* build) {
 /*
        Sets up the ccache for this build
 */
+static int pakfire_build_mount_ccache(struct pakfire_build* build) {
+       int r;
+
+       // Do nothing if the ccache is disabled
+       if (pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_CCACHE))
+               return 0;
+
+       // Check that the path is set
+       if (!*build->ccache_path)
+               return -ENOTSUP;
+
+       // Make sure the path exists
+       r = pakfire_mkdir(build->ccache_path, 0755);
+       if (r) {
+               ERROR(build->pakfire, "Could not create %s: %m\n", build->ccache_path);
+               return r;
+       }
+
+       // Bind-mount the directory
+       return pakfire_jail_bind(build->jail, build->ccache_path, CCACHE_DIR,
+               MS_NOSUID|MS_NOEXEC|MS_NODEV);
+}
+
 static int pakfire_build_setup_ccache(struct pakfire_build* build) {
-       char path[PATH_MAX];
        int r;
 
        // Check if we want a ccache
@@ -1505,29 +1555,25 @@ static int pakfire_build_setup_ccache(struct pakfire_build* build) {
                return 0;
        }
 
-       // Compose path
-       r = pakfire_cache_path(build->pakfire, path, "%s", "ccache");
+       // Set CCACHE_DIR
+       r = pakfire_jail_set_env(build->jail, "CCACHE_DIR", CCACHE_DIR);
        if (r) {
-               ERROR(build->pakfire, "Could not compose ccache path: %m\n");
-               return 1;
-       }
-
-       DEBUG(build->pakfire, "Mounting ccache from %s\n", path);
-
-       // Ensure path exists
-       r = pakfire_mkdir(path, 0755);
-       if (r && errno != EEXIST) {
-               ERROR(build->pakfire, "Could not create ccache directory %s: %m\n", path);
+               ERROR(build->pakfire, "Could not set ccache directory: %m\n");
                return r;
        }
 
-       // Bind-mount the directory
-       r = pakfire_jail_bind(build->jail, path, CCACHE_DIR, MS_NOSUID|MS_NOEXEC|MS_NODEV);
+       // Set CCACHE_TEMPDIR
+       r = pakfire_jail_set_env(build->jail, "CCACHE_TEMPDIR", "/tmp");
        if (r) {
-               ERROR(build->pakfire, "Could not mount ccache: %m\n");
+               ERROR(build->pakfire, "Could not set ccache tempdir: %m\n");
                return r;
        }
 
+       // Set a default path
+       r = pakfire_cache_path(build->pakfire, build->ccache_path, "%s", "ccache");
+       if (r)
+               return r;
+
        return 0;
 }
 
@@ -1565,16 +1611,14 @@ static int pakfire_build_setup_repo(struct pakfire_build* build) {
 }
 
 static int pakfire_build_set_time_start(struct pakfire_build* build) {
-       const time_t now = time(NULL);
+       int r;
 
-       if (now < 0) {
+       // Fetch current time
+       r = clock_gettime(CLOCK_MONOTONIC, &build->time_start);
+       if (r < 0)
                ERROR(build->pakfire, "Could not fetch start time: %m\n");
-               return 1;
-       }
 
-       build->time_start = now;
-
-       return 0;
+       return r;
 }
 
 PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
@@ -1586,6 +1630,9 @@ PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
        if (!b)
                return 1;
 
+       // Reference the context
+       b->ctx = pakfire_ctx(pakfire);
+
        // Reference pakfire
        b->pakfire = pakfire_ref(pakfire);
 
@@ -1647,46 +1694,78 @@ PAKFIRE_EXPORT struct pakfire_build* pakfire_build_unref(struct pakfire_build* b
        return NULL;
 }
 
+PAKFIRE_EXPORT void pakfire_build_set_log_callback(struct pakfire_build* build,
+               pakfire_build_log_callback callback, void* data) {
+       build->callbacks.log = callback;
+       build->callbacks.log_data = data;
+}
+
+PAKFIRE_EXPORT int pakfire_build_set_ccache_path(
+               struct pakfire_build* build, const char* path) {
+       // Check if this can be called
+       if (pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_CCACHE))
+               return -EPERM;
+
+       // Check input value
+       if (!path || !*path)
+               return -EINVAL;
+
+       // Store the path
+       return pakfire_string_set(build->ccache_path, path);
+}
+
 PAKFIRE_EXPORT int pakfire_build_set_target(
                struct pakfire_build* build, const char* target) {
        return pakfire_string_set(build->target, target);
 }
 
-static int pakfire_build_install_packages(struct pakfire_build* build,
-               int* snapshot_needs_update) {
+static int pakfire_build_install_packages(
+               struct pakfire_build* build, int* snapshot_needs_update) {
+       struct pakfire_transaction* transaction = NULL;
+       char* problems = NULL;
        int r;
 
-       const char* packages[] = {
-               "build-essential",
-               NULL,
-       };
-
-       int changed = 0;
+       // Create a new transaction
+       r = pakfire_transaction_create(&transaction, build->pakfire, 0);
+       if (r)
+               goto ERROR;
 
-       // Install everything
-       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");
-               return r;
+       // Install all build dependencies
+       for (const char** p = PAKFIRE_BUILD_PACKAGES; *p; p++) {
+               r = pakfire_transaction_request(transaction,
+                       PAKFIRE_JOB_INSTALL, *p, PAKFIRE_JOB_ESSENTIAL);
+               if (r)
+                       goto ERROR;
        }
 
-       // Mark snapshot as changed if new packages were installed
-       if (changed)
-               *snapshot_needs_update = 1;
+       // Also update everything that has already been installed
+       r = pakfire_transaction_request(transaction, PAKFIRE_JOB_SYNC, NULL, 0);
+       if (r)
+               goto ERROR;
 
-       // Update everything
-       r = pakfire_sync(build->pakfire, 0, 0, &changed, NULL, NULL);
+       // Solve the transaction
+       r = pakfire_transaction_solve(transaction, 0, &problems);
        if (r) {
-               ERROR(build->pakfire, "Could not update packages: %m\n");
-               return r;
+               BUILD_ERROR(build, "Could not install build dependencies:\n%s\n", problems);
+               goto ERROR;
        }
 
-       // Has anything changed?
-       if (changed)
+       // If there are changes, we have to update the snapshot
+       if (pakfire_transaction_count(transaction))
                *snapshot_needs_update = 1;
 
-       return 0;
+       // Run the transaction
+       r = pakfire_transaction_run(transaction);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       if (transaction)
+               pakfire_transaction_unref(transaction);
+       if (problems)
+               free(problems);
+
+       return r;
 }
 
 /*
@@ -1701,11 +1780,13 @@ static int pakfire_build_init(struct pakfire_build* build) {
                return 0;
        }
 
+       const int use_snapshot = !pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT);
+
        // Tells us whether we need to (re-)create the snapshot
        int snapshot_needs_update = 0;
 
        // Extract snapshot
-       if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT)) {
+       if (use_snapshot) {
                r = pakfire_snapshot_restore(build->pakfire);
                if (r)
                        return r;
@@ -1717,7 +1798,7 @@ static int pakfire_build_init(struct pakfire_build* build) {
                return r;
 
        // Update the snapshot if there were changes
-       if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT) && snapshot_needs_update) {
+       if (use_snapshot && snapshot_needs_update) {
                // Store the snapshot
                r = pakfire_snapshot_store(build->pakfire);
                if (r)
@@ -1749,10 +1830,10 @@ static int pakfire_build_read_makefile(struct pakfire_build* build,
        r = pakfire_read_makefile(parser, build->pakfire, path, &error);
        if (r) {
                if (error) {
-                       ERROR(build->pakfire, "Could not parse makefile %s: %s\n", path,
+                       BUILD_ERROR(build, "Could not parse makefile %s: %s\n", path,
                                pakfire_parser_error_get_message(error));
                } else {
-                       ERROR(build->pakfire, "Could not parse makefile %s: %m\n", path);
+                       BUILD_ERROR(build, "Could not parse makefile %s: %m\n", path);
                }
 
                goto ERROR;
@@ -1799,7 +1880,7 @@ static int pakfire_build_perform(struct pakfire_build* build,
        // Run post build checks
        r = pakfire_build_run_post_build_checks(build);
        if (r) {
-               ERROR(build->pakfire, "Post build checks failed\n");
+               BUILD_ERROR(build, "Post build checks failed\n");
                goto ERROR;
        }
 
@@ -1821,9 +1902,11 @@ ERROR:
 
 static int __pakfire_build_unpackaged_file(struct pakfire* pakfire,
                struct pakfire_file* file, void* p) {
+       struct pakfire_build* build = p;
+
        char* s = pakfire_file_dump(file, PAKFIRE_FILE_DUMP_FULL);
        if (s) {
-               ERROR(pakfire, "%s\n", s);
+               BUILD_ERROR(build, "%s\n", s);
                free(s);
        }
 
@@ -1840,14 +1923,14 @@ static int pakfire_build_check_unpackaged_files(struct pakfire_build* build) {
                goto ERROR;
 
        // Scan for all files in BUILDROOT
-       r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL);
+       r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL, 0);
        if (r)
                goto ERROR;
 
        if (!pakfire_filelist_is_empty(filelist)) {
-               ERROR(build->pakfire, "Unpackaged files found:\n");
+               BUILD_ERROR(build, "Unpackaged files found:\n");
 
-               r = pakfire_filelist_walk(filelist, __pakfire_build_unpackaged_file, NULL);
+               r = pakfire_filelist_walk(filelist, __pakfire_build_unpackaged_file, build, 0);
                if (r)
                        goto ERROR;
 
@@ -1862,31 +1945,29 @@ ERROR:
        return r;
 }
 
-static int pakfire_build_install_package(struct pakfire* pakfire,
+static int pakfire_build_install_package(struct pakfire_ctx* ctx,
                struct pakfire_package* pkg, void* p) {
-       struct pakfire_request* request = (struct pakfire_request*)p;
+       struct pakfire_transaction* transaction = (struct pakfire_transaction*)p;
 
-       return pakfire_request_add_package(request, PAKFIRE_REQ_INSTALL, pkg,
-               PAKFIRE_REQUEST_ESSENTIAL);
+       return pakfire_transaction_request_package(transaction,
+               PAKFIRE_JOB_INSTALL, pkg, PAKFIRE_JOB_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;
+       struct pakfire_transaction* transaction = NULL;
+       char* problems = NULL;
        int r;
 
-       // Create a new request
-       r = pakfire_request_create(&request, build->pakfire, 0);
+       // Create a new transaction
+       r = pakfire_transaction_create(&transaction, build->pakfire, 0);
        if (r)
                goto ERROR;
 
        // Add all packages
-       r = pakfire_packagelist_walk(build->packages, pakfire_build_install_package, request);
+       r = pakfire_packagelist_walk(build->packages, pakfire_build_install_package, transaction);
 
        // Solve the request
-       r = pakfire_request_solve(request);
+       r = pakfire_transaction_solve(transaction, PAKFIRE_SOLVE_SHOW_SOLUTIONS, &problems);
        switch (r) {
                // All okay
                case 0:
@@ -1894,60 +1975,18 @@ static int pakfire_build_install_test(struct pakfire_build* build) {
 
                // 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);
-                               }
-                       }
-
+                       BUILD_ERROR(build, "Install test failed:\n%s\n", problems);
                        break;
 
                // Any other errors
                default:
+                       BUILD_ERROR(build, "Install test failed: %m\n");
                        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);
+       if (problems)
+               free(problems);
 
        return r;
 }
@@ -1968,7 +2007,7 @@ static int pakfire_build_post_check(struct pakfire_build* build) {
        return 0;
 }
 
-static int pakfire_build_copy_package(struct pakfire* pakfire,
+static int pakfire_build_copy_package(struct pakfire_ctx* ctx,
                struct pakfire_package* pkg, void* p) {
        struct pakfire_archive* archive = NULL;
        char path[PATH_MAX];
@@ -1976,10 +2015,8 @@ static int pakfire_build_copy_package(struct pakfire* pakfire,
 
        const char* target = (const char*)p;
 
-       if (!target) {
-               errno = EINVAL;
-               return 1;
-       }
+       if (!target)
+               return -EINVAL;
 
        // Fetch the package filename
        const char* filename = pakfire_package_get_string(pkg, PAKFIRE_PKG_FILENAME);
@@ -2049,51 +2086,58 @@ ERROR:
 
 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;
+       char* problems = NULL;
        int r;
 
-       // Create a new request
-       r = pakfire_request_create(&request, build->pakfire, 0);
+       // Create a new transaction
+       r = pakfire_transaction_create(&transaction, 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;
+       // Request to install all essential packages
+       for (const char** p = PAKFIRE_BUILD_PACKAGES; *p; p++) {
+               r = pakfire_transaction_request(transaction,
+                       PAKFIRE_JOB_INSTALL, *p, PAKFIRE_JOB_ESSENTIAL);
+               if (r)
+                       goto ERROR;
+       }
 
-       // Solve the request
-       r = pakfire_request_solve(request);
+       // Add the source package
+       r = pakfire_transaction_request_package(transaction,
+               PAKFIRE_JOB_INSTALL, package, PAKFIRE_JOB_ESSENTIAL);
        if (r)
                goto ERROR;
 
-       // Fetch the transaction
-       r = pakfire_request_get_transaction(request, &transaction);
-       if (r)
+       // Solve the transaction
+       r = pakfire_transaction_solve(transaction, 0, &problems);
+       if (r) {
+               if (problems)
+                       BUILD_ERROR(build, "Could not install the source package:\n%s\n", problems);
+
                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");
+               BUILD_ERROR(build, "The source package did not get installed\n");
                r = 1;
                goto ERROR;
        }
 
        // Run the transaction
-       r = pakfire_transaction_run(transaction, 0);
+       r = pakfire_transaction_run(transaction);
        if (r)
                goto ERROR;
 
 ERROR:
        if (transaction)
                pakfire_transaction_unref(transaction);
-       if (request)
-               pakfire_request_unref(request);
+       if (problems)
+               free(problems);
 
        return r;
 }
@@ -2102,9 +2146,13 @@ PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* p
        struct pakfire_package* package = NULL;
        struct pakfire_parser* makefile = NULL;
        char* buildroot = NULL;
+       char* problems = NULL;
        char duration[TIME_STRING_MAX];
        int r;
 
+       // Fetch architecture
+       const char* arch = pakfire_get_arch(build->pakfire);
+
        // Set buildroot
        r = pakfire_path(build->pakfire, build->buildroot, "%s",
                PAKFIRE_TMP_DIR "/pakfire-buildroot.XXXXXX");
@@ -2117,8 +2165,23 @@ PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* p
                goto ERROR;
 
        const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA);
+       const char* uuid = pakfire_package_get_string(package, PAKFIRE_PKG_UUID);
+
+       BUILD_INFO(build, "Building %s (%s)...\n", nevra, uuid);
 
-       INFO(build->pakfire, "Building %s...\n", nevra);
+       // Check if this package can be build in this environment
+       if (!pakfire_package_supports_build_arch(package, arch)) {
+               ERROR(build->pakfire, "%s does not support being built on %s\n", nevra, arch);
+               r = -ENOTSUP;
+               goto ERROR;
+       }
+
+       // Perform an install check to see whether we can build this at all
+       r = pakfire_package_installcheck(package, &problems, 0);
+       if (r) {
+               BUILD_ERROR(build, "Cannot build %s:\n%s\n", nevra, problems);
+               goto ERROR;
+       }
 
        // Initialize the build environment
        r = pakfire_build_init(build);
@@ -2128,14 +2191,21 @@ PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* p
        // Install the source package
        r = pakfire_build_install_source_package(build, package);
        if (r) {
-               ERROR(build->pakfire, "Could not install the source package: %m\n");
+               BUILD_ERROR(build, "Could not install the source package: %m\n");
+               goto ERROR;
+       }
+
+       // Mount the ccache
+       r = pakfire_build_mount_ccache(build);
+       if (r) {
+               BUILD_ERROR(build, "Could not mount the ccache: %m\n");
                goto ERROR;
        }
 
        // Create BUILDROOT
        buildroot = pakfire_mkdtemp(build->buildroot);
        if (!buildroot) {
-               ERROR(build->pakfire, "Could not create BUILDROOT: %m\n");
+               BUILD_ERROR(build, "Could not create BUILDROOT: %m\n");
                goto ERROR;
        }
 
@@ -2152,7 +2222,7 @@ PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* p
        // Create the packages
        r = pakfire_build_packages(build, makefile);
        if (r) {
-               ERROR(build->pakfire, "Could not create packages: %m\n");
+               BUILD_ERROR(build, "Could not create packages: %m\n");
                goto ERROR;
        }
 
@@ -2171,13 +2241,15 @@ PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* p
        if (r)
                goto ERROR;
 
-       INFO(build->pakfire, "Build successfully completed in %s\n", duration);
+       BUILD_INFO(build, "Build successfully completed in %s\n", duration);
 
 ERROR:
        if (makefile)
                pakfire_parser_unref(makefile);
        if (package)
                pakfire_package_unref(package);
+       if (problems)
+               free(problems);
 
        // Cleanup buildroot
        if (buildroot)
@@ -2186,38 +2258,206 @@ ERROR:
        return r;
 }
 
-/*
-       Compatibility function to keep the legacy API.
-*/
-PAKFIRE_EXPORT int pakfire_build(struct pakfire* pakfire, const char* path,
-               const char* target, const char* id, int flags) {
-       struct pakfire_build* build = NULL;
+static int pakfire_build_mkimage_install_deps(struct pakfire_build* build,
+               const char* type) {
+       struct pakfire_transaction* transaction = NULL;
+       char requires[NAME_MAX];
+       char* problems = NULL;
        int r;
 
-       // Check if path is set
-       if (!path) {
-               errno = EINVAL;
-               return 1;
+       // Format requires
+       r = pakfire_string_format(requires, "mkimage(%s)", type);
+       if (r)
+               goto ERROR;
+
+       // Create a new transaction
+       r = pakfire_transaction_create(&transaction, build->pakfire, 0);
+       if (r) {
+               ERROR(build->pakfire, "Could not create transaction: %m\n");
+               goto ERROR;
        }
 
-       // Create a new build environment
-       r = pakfire_build_create(&build, pakfire, id, flags);
+       // Add requires to the request
+       r = pakfire_transaction_request(transaction,
+                       PAKFIRE_JOB_INSTALL, requires, PAKFIRE_JOB_ESSENTIAL);
+       if (r) {
+               ERROR(build->pakfire, "Could not add '%s' to the transaction: %m\n", requires);
+               goto ERROR;
+       }
+
+       // Solve the request
+       r = pakfire_transaction_solve(transaction, 0, &problems);
+       if (r) {
+               ERROR(build->pakfire, "Could not solve the request:\n%s\n", problems);
+               goto ERROR;
+       }
+
+       // Run the transaction
+       r = pakfire_transaction_run(transaction);
        if (r)
                goto ERROR;
 
-       // Set target
-       if (target) {
-               r = pakfire_build_set_target(build, target);
-               if (r)
-                       goto ERROR;
+ERROR:
+       if (transaction)
+               pakfire_transaction_unref(transaction);
+       if (problems)
+               free(problems);
+
+       return r;
+}
+
+static int pakfire_build_collect_packages(struct pakfire_build* build, const char* path) {
+       struct pakfire_transaction* transaction = NULL;
+       char* problems = NULL;
+       char* p = NULL;
+       int r;
+
+       // Create a new transaction
+       r = pakfire_transaction_create(&transaction, build->pakfire, 0);
+       if (r) {
+               ERROR(build->pakfire, "Could not create transaction: %m\n");
+               goto ERROR;
        }
 
-       // Run build
-       r = pakfire_build_exec(build, path);
+       // XXX
+       // Install the base system
+       r = pakfire_transaction_request(transaction,
+                       PAKFIRE_JOB_INSTALL, "ipfire-release", PAKFIRE_JOB_ESSENTIAL);
+       if (r) {
+               ERROR(build->pakfire, "Could not install 'ipfire-release': %m\n");
+               goto ERROR;
+       }
+
+       // Solve the transaction
+       r = pakfire_transaction_solve(transaction, 0, &problems);
+       if (r) {
+               ERROR(build->pakfire, "Could not solve request:\n%s\n", problems);
+               goto ERROR;
+       }
+
+       // Empty transaction?
+       if (!pakfire_transaction_count(transaction)) {
+               ERROR(build->pakfire, "The transaction is unexpectedly empty\n");
+               r = 1;
+               goto ERROR;
+       }
+
+       // Dump the transaction
+       p = pakfire_transaction_dump(transaction, 80);
+       if (!p) {
+               ERROR(build->pakfire, "Could not dump the transaction: %m\n");
+               r = 1;
+               goto ERROR;
+       }
+
+       // Log the dump
+       INFO(build->pakfire, "%s\n", p);
+
+       // Download all packages
+       r = pakfire_transaction_download(transaction);
+       if (r) {
+               ERROR(build->pakfire, "Could not download the transaction: %m\n");
+               goto ERROR;
+       }
+
+       // Create a repository with all packages in this transaction
+       r = pakfire_transaction_compose_repo(transaction, NULL, path);
+       if (r) {
+               ERROR(build->pakfire, "Could not create repository: %m\n");
+               goto ERROR;
+       }
+
+       // XXX Should we perform installcheck here?
 
 ERROR:
-       if (build)
-               pakfire_build_unref(build);
+       if (transaction)
+               pakfire_transaction_unref(transaction);
+       if (problems)
+               free(problems);
+       if (p)
+               free(p);
+
+       return r;
+}
+
+PAKFIRE_EXPORT int pakfire_build_mkimage(struct pakfire_build* build,
+               const char* type, FILE* f) {
+       FILE* t = NULL;
+       char packagesdir[PATH_MAX];
+       char path[PATH_MAX];
+       char* p = NULL;
+       int r;
+
+       // Check inputs
+       if (!type || !f)
+               return -EINVAL;
+
+       // Create a path inside the build environment
+       r = pakfire_path(build->pakfire, path, "%s",
+               PAKFIRE_TMP_DIR "/pakfire-image.XXXXXX");
+       if (r)
+               goto ERROR;
+
+       // Allocate a temporary file
+       t = pakfire_mktemp(path, 0600);
+       if (!t) {
+               ERROR(build->pakfire, "Could not allocate a temporary file: %m\n");
+               r = 1;
+               goto ERROR;
+       }
+
+       // Create a path for all packages
+       r = pakfire_path(build->pakfire, packagesdir, "%s",
+               PAKFIRE_TMP_DIR "/pakfire-packages.XXXXXX");
+       if (r)
+               goto ERROR;
+
+       p = pakfire_mkdtemp(packagesdir);
+       if (!p)
+               goto ERROR;
+
+       // Collect all packages
+       r = pakfire_build_collect_packages(build, packagesdir);
+       if (r)
+               goto ERROR;
+
+       // Initialize the build environment
+       r = pakfire_build_init(build);
+       if (r)
+               goto ERROR;
+
+       // Install all dependencies
+       r = pakfire_build_mkimage_install_deps(build, type);
+       if (r)
+               goto ERROR;
+
+       const char* args[] = {
+               type,
+               pakfire_relpath(build->pakfire, path),
+               pakfire_relpath(build->pakfire, packagesdir),
+               NULL,
+       };
+
+       // Run the mkimage script
+       r = pakfire_build_run_script(build, "mkimage", args);
+       if (r)
+               goto ERROR;
+
+       // Copy data to its destination
+       r = pakfire_copy(build->pakfire, t, f);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       if (t)
+               fclose(t);
+
+       // Unlink the temporary file
+       if (*path)
+               unlink(path);
+
+       // Remove all packages
+       pakfire_rmtree(packagesdir, 0);
 
        return r;
 }
@@ -2245,6 +2485,44 @@ ERROR:
        return r;
 }
 
+static int pakfire_build_install(struct pakfire_build* build, const char** packages) {
+       struct pakfire_transaction* transaction = NULL;
+       char* problems = NULL;
+       int r;
+
+       // Create a new transaction
+       r = pakfire_transaction_create(&transaction, build->pakfire, 0);
+       if (r)
+               goto ERROR;
+
+       // Install all packages
+       for (const char** p = packages; *p; p++) {
+               r = pakfire_transaction_request(transaction, PAKFIRE_JOB_INSTALL, *p, 0);
+               if (r)
+                       goto ERROR;
+       }
+
+       // Solve the transaction
+       r = pakfire_transaction_solve(transaction, 0, &problems);
+       if (r) {
+               ERROR(build->pakfire, "Could not install packages:\n%s\n", problems);
+               goto ERROR;
+       }
+
+       // Run the transaction
+       r = pakfire_transaction_run(transaction);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       if (transaction)
+               pakfire_transaction_unref(transaction);
+       if (problems)
+               free(problems);
+
+       return r;
+}
+
 /*
        This is a convenience function that sets up a build environment and
        then drops the user into an interactive shell.
@@ -2270,9 +2548,9 @@ PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages,
 
        // Install any additional packages
        if (packages) {
-               r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0, NULL, NULL, NULL);
+               r = pakfire_build_install(build, packages);
                if (r)
-                       return r;
+                       goto ERROR;
        }
 
        // Run shell