]> git.ipfire.org Git - pakfire.git/blobdiff - src/libpakfire/build.c
jail: Remove callbacks from command calls
[pakfire.git] / src / libpakfire / build.c
index 8385b80005ef0b0bac5c334b9bdd5f8280dadbbf..96cdc66d506c871ebfa840355b02e1adf7c872b7 100644 (file)
@@ -30,6 +30,8 @@
 
 #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/file.h>
@@ -40,6 +42,7 @@
 #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>
@@ -65,6 +68,7 @@ static const char* PAKFIRE_BUILD_PACKAGES[] = {
 };
 
 struct pakfire_build {
+       struct pakfire_ctx* ctx;
        struct pakfire* pakfire;
        int nrefs;
 
@@ -100,6 +104,13 @@ struct pakfire_build {
 
        // States
        int init:1;
+
+       // Callbacks
+       struct pakfire_build_callbacks {
+               // Log callback
+               pakfire_build_log_callback log;
+               void* log_data;
+       } callbacks;
 };
 
 #define TEMPLATE \
@@ -112,6 +123,57 @@ 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;
 }
@@ -128,6 +190,30 @@ static double pakfire_build_duration(struct pakfire_build* build) {
        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);
+
+       else if (m)
+               return BUILD_LOG(build, priority, "[   %02d:%02d.%04d] %.*s", m, s, ms, length, line);
+
+       else
+               return BUILD_LOG(build, priority, "[      %02d.%04d] %.*s", s, ms, length, line);
+}
+
 static int __pakfire_build_setup_repo(struct pakfire* pakfire,
                struct pakfire_repo* repo, void* p) {
        char path[PATH_MAX];
@@ -146,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;
        }
 
@@ -163,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;
        }
 
@@ -182,7 +268,7 @@ static int __pakfire_build_setup_repo(struct pakfire* pakfire,
                if (pakfire_path_exists(_path)) {
                        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);
+                               BUILD_ERROR(build, "Could not bind-mount the repository at %s: %m\n", _path);
                                goto ERROR;
                        }
                }
@@ -225,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;
        }
 
@@ -257,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);
@@ -276,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);
@@ -289,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;
@@ -325,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;
        }
@@ -347,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
@@ -363,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;
@@ -375,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;
@@ -386,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)
@@ -484,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,
@@ -507,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;
 }
@@ -650,7 +727,7 @@ static int pakfire_build_package_add_files(struct pakfire_build* build,
        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))
@@ -662,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;
        }
 
@@ -693,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;
        }
 
@@ -719,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;
 
@@ -747,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;
        }
 
@@ -820,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);
@@ -831,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;
        }
 
@@ -889,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;
        }
 
@@ -912,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;
@@ -926,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);
@@ -934,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;
        }
 
@@ -944,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--) {
@@ -959,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;
 
@@ -969,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;
 
@@ -998,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) {
@@ -1061,7 +1145,7 @@ static int pakfire_build_post_process_files(struct pakfire_build* build,
 
        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_ISSUES);
@@ -1188,7 +1272,7 @@ static int __pakfire_build_post_check_files(
        // Check file for issues
        r = pakfire_file_check(file, &issues);
        if (r) {
-               ERROR(pakfire, "%s: File 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;
        }
 
@@ -1219,7 +1303,7 @@ 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;
        }
 
@@ -1279,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;
        }
@@ -1307,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);
 }
 
@@ -1349,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);
@@ -1394,45 +1481,6 @@ ERROR:
        return r;
 }
 
-static int pakfire_build_log_strftime(
-               char* buffer, const size_t length, const double 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 __pakfire_string_format(buffer, length, "%02d:%02d:%02d.%04d", h, m, s, ms);
-
-       else if (m)
-               return __pakfire_string_format(buffer, length, "   %02d:%02d.%04d", m, s, ms);
-
-       else
-               return __pakfire_string_format(buffer, length, "      %02d.%04d", s, ms);
-}
-
-static int pakfire_build_log_callback(struct pakfire* pakfire,
-               void* data, int priority, const char* line, size_t length) {
-       struct pakfire_build* build = data;
-       char buffer[128];
-       int r;
-
-       // Get the runtime of the build
-       const double t = pakfire_build_duration(build);
-       if (t < 0)
-               return t;
-
-       // Format the timestamp
-       r = pakfire_build_log_strftime(buffer, sizeof(buffer), t);
-       if (r < 0)
-               return r;
-
-       // Pass the message to the upstream logger
-       pakfire_log_condition(pakfire, priority, 0, "[%s] %s", buffer, line);
-
-       return 0;
-}
-
 /*
        Sets up a new jail for this build
 */
@@ -1446,9 +1494,6 @@ static int pakfire_build_setup_jail(struct pakfire_build* build) {
                return r;
        }
 
-       // Configure our custom logging callback
-       pakfire_jail_set_log_callback(build->jail, pakfire_build_log_callback, build);
-
        // Connect the jail to our cgroup
        r = pakfire_jail_set_cgroup(build->jail, build->cgroup);
        if (r) {
@@ -1585,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);
 
@@ -1646,6 +1694,12 @@ 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
@@ -1665,36 +1719,53 @@ PAKFIRE_EXPORT int pakfire_build_set_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;
 
-       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, PAKFIRE_BUILD_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;
 }
 
 /*
@@ -1759,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;
@@ -1809,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;
        }
 
@@ -1831,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);
        }
 
@@ -1855,9 +1928,9 @@ static int pakfire_build_check_unpackaged_files(struct pakfire_build* build) {
                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, 0);
+               r = pakfire_filelist_walk(filelist, __pakfire_build_unpackaged_file, build, 0);
                if (r)
                        goto ERROR;
 
@@ -1872,7 +1945,7 @@ 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_transaction* transaction = (struct pakfire_transaction*)p;
 
@@ -1902,12 +1975,12 @@ static int pakfire_build_install_test(struct pakfire_build* build) {
 
                // Dependency Error
                case 2:
-                       ERROR(build->pakfire, "Install test failed:\n%s\n", problems);
+                       BUILD_ERROR(build, "Install test failed:\n%s\n", problems);
                        break;
 
                // Any other errors
                default:
-                       ERROR(build->pakfire, "Install test failed: %m\n");
+                       BUILD_ERROR(build, "Install test failed: %m\n");
                        goto ERROR;
        }
 
@@ -1934,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];
@@ -2040,7 +2113,7 @@ static int pakfire_build_install_source_package(
        r = pakfire_transaction_solve(transaction, 0, &problems);
        if (r) {
                if (problems)
-                       ERROR(build->pakfire, "Could not install the source package:\n%s\n", problems);
+                       BUILD_ERROR(build, "Could not install the source package:\n%s\n", problems);
 
                goto ERROR;
        }
@@ -2050,7 +2123,7 @@ static int pakfire_build_install_source_package(
 
        // 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;
        }
@@ -2073,6 +2146,7 @@ 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;
 
@@ -2091,8 +2165,9 @@ 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);
 
-       INFO(build->pakfire, "Building %s...\n", nevra);
+       BUILD_INFO(build, "Building %s (%s)...\n", nevra, uuid);
 
        // Check if this package can be build in this environment
        if (!pakfire_package_supports_build_arch(package, arch)) {
@@ -2101,6 +2176,13 @@ PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* p
                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);
        if (r)
@@ -2109,21 +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) {
-               ERROR(build->pakfire, "Could not mount the ccache: %m\n");
+               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;
        }
 
@@ -2140,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;
        }
 
@@ -2159,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)
@@ -2355,7 +2439,7 @@ PAKFIRE_EXPORT int pakfire_build_mkimage(struct pakfire_build* build,
        };
 
        // Run the mkimage script
-       r = pakfire_build_run_script(build, "mkimage", args, NULL, NULL, NULL);
+       r = pakfire_build_run_script(build, "mkimage", args);
        if (r)
                goto ERROR;
 
@@ -2401,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.
@@ -2426,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