]> git.ipfire.org Git - pakfire.git/blobdiff - src/pakfire/builder.py
libpakfire: execute: Automatically set personality from arch
[pakfire.git] / src / pakfire / builder.py
index 3bd72e9f2d8f52dc020793ec5d43b10f07b75a53..ad77d57697e4f0485dc1b917297b7a949fad6809 100644 (file)
@@ -32,7 +32,7 @@ import uuid
 
 from . import _pakfire
 from . import base
-from . import cgroup
+from . import cgroups
 from . import config
 from . import downloaders
 from . import logger
@@ -64,7 +64,7 @@ BUILD_LOG_HEADER = """
 """
 
 class Builder(object):
-       def __init__(self, package=None, arch=None, build_id=None, logfile=None, **kwargs):
+       def __init__(self, arch=None, build_id=None, logfile=None, **kwargs):
                self.config = config.Config("general.conf", "builder.conf")
 
                distro_name = self.config.get("builder", "distro", None)
@@ -105,8 +105,8 @@ class Builder(object):
                if not _pakfire.arch_supported_by_host(self.arch):
                        raise BuildError(_("Cannot build for %s on this host") % self.arch)
 
-               # Initialize a cgroup (if supported)
-               self.cgroup = self.make_cgroup()
+               # Initialize cgroups
+               self.cgroup = self._make_cgroup()
 
                # Unshare namepsace.
                # If this fails because the kernel has no support for CLONE_NEWIPC or CLONE_NEWUTS,
@@ -148,26 +148,11 @@ class Builder(object):
                self.log.debug("Leaving %s" % self.path)
 
                # Kill all remaining processes in the build environment
-               if self.cgroup:
-                       # Move the builder process out of the cgroup.
-                       self.cgroup.migrate_task(self.cgroup.parent, os.getpid())
+               self.cgroup.killall()
 
-                       # Kill all still running processes in the cgroup.
-                       self.cgroup.kill_and_wait()
-
-                       # Remove cgroup and all parent cgroups if they are empty.
-                       self.cgroup.destroy()
-
-                       parent = self.cgroup.parent
-                       while parent:
-                               if not parent.is_empty(recursive=True):
-                                       break
-
-                               parent.destroy()
-                               parent = parent.parent
-
-               else:
-                       util.orphans_kill(self.path)
+               # Destroy the cgroup
+               self.cgroup.destroy()
+               self.cgroup = None
 
                # Umount the build environment
                self._umountall()
@@ -197,25 +182,21 @@ class Builder(object):
                        # If no logile was given, we use the root logger.
                        self.log = logging.getLogger("pakfire")
 
-       def make_cgroup(self):
+       def _make_cgroup(self):
                """
-                       Initialize cgroup (if the system supports it).
+                       Initialises a cgroup so that we can enforce resource limits
+                       and can identify processes belonging to this build environment.
                """
-               if not cgroup.supported():
-                       return
+               # Find our current group
+               parent = cgroups.get_own_group()
 
-               # Search for the cgroup this process is currently running in.
-               parent_cgroup = cgroup.find_by_pid(os.getpid())
-               if not parent_cgroup:
-                       return
-
-               # Create our own cgroup inside the parent cgroup.
-               c = parent_cgroup.create_child_cgroup("pakfire/builder/%s" % self.build_id)
+               # Create a sub-group
+               cgroup = parent.create_subgroup("pakfire-%s" % self.build_id)
 
-               # Attach the pakfire-builder process to the group.
-               c.attach()
+               # Make this process join the new group
+               cgroup.attach_self()
 
-               return c
+               return cgroup
 
        def lock(self):
                filename = os.path.join(self.path, ".lock")
@@ -450,8 +431,6 @@ class BuilderContext(object):
                        arch=self.builder.arch,
                )
 
-               self.setup()
-
        @property
        def environ(self):
                env = MINIMAL_ENVIRONMENT.copy()
@@ -486,24 +465,10 @@ class BuilderContext(object):
 
                return env
 
-       def setup(self, install=None):
-               self.log.info(_("Install packages needed for build..."))
-
-               packages = [
-                       "@Build",
-               ]
-
-               # If we have ccache enabled, we need to extract it
-               # to the build chroot
-               if self.builder.settings.get("enable_ccache"):
-                       packages.append("ccache")
-
-               # Install additional packages
-               if install:
-                       packages += install
-
-               # Logging
-               self.log.debug(_("Installing build requirements: %s") % ", ".join(packages))
+       def _install(self, packages):
+               self.log.debug(_("Installing packages in build environment:"))
+               for package in packages:
+                       self.log.debug("        %s" % package)
 
                # Initialise Pakfire
                with self.pakfire as p:
@@ -522,12 +487,25 @@ class BuilderContext(object):
                        transaction.run()
 
        def build(self, package, private_network=True, shell=True):
+               # Install build environment
+               packages = [
+                       "@Build",
+               ]
+
+               # If we have ccache enabled, we need to install it, too
+               if self.builder.settings.get("enable_ccache"):
+                       packages.append("ccache")
+
+               # Open the package archive
                archive = _pakfire.Archive(self.pakfire, package)
 
                requires = archive.get("dependencies.requires")
+               packages += requires.splitlines()
 
                # Setup the environment including any build dependencies
-               self.setup(install=requires.splitlines())
+               self._install(packages)
+
+               # XXX perform build
 
        def shell(self, install=[]):
                if not util.cli_is_interactive():
@@ -535,16 +513,10 @@ class BuilderContext(object):
                        return
 
                # Install our standard shell packages
-               install += SHELL_PACKAGES
-
-               self.setup(install=install)
+               self._install(SHELL_PACKAGES + install)
 
                command = "/usr/sbin/chroot %s %s %s" % (self.chrootPath(), SHELL_SCRIPT)
 
-               # Add personality if we require one
-               if self.pakfire.distro.personality:
-                       command = "%s %s" % (self.pakfire.distro.personality, command)
-
                for key, val in list(self.environ.items()):
                        command = "%s=\"%s\" " % (key, val) + command
 
@@ -555,201 +527,3 @@ class BuilderContext(object):
 
                shell = os.system(command)
                return os.WEXITSTATUS(shell)
-
-
-class BuilderInternal(object):
-       def __init__(self, pakfire, filename, resultdir, **kwargs):
-               self.pakfire = pakfire
-
-               self.filename = filename
-
-               self.resultdir = resultdir
-
-               # Open package file.
-               self.pkg = packages.Makefile(self.pakfire, self.filename)
-
-               self._environ = {
-                       "LANG"             : "C",
-               }
-
-       @property
-       def buildroot(self):
-               return self.pkg.buildroot
-
-       @property
-       def distro(self):
-               return self.pakfire.distro
-
-       @property
-       def environ(self):
-               environ = os.environ
-
-               # Get all definitions from the package.
-               environ.update(self.pkg.exports)
-
-               # Overwrite some definitions by default values.
-               environ.update(self._environ)
-
-               return environ
-
-       def execute(self, command, logger=None, **kwargs):
-               if logger is None:
-                       logger = logging.getLogger("pakfire")
-
-               # Make every shell to a login shell because we set a lot of
-               # environment things there.
-               command = ["bash", "--login", "-c", command]
-
-               args = {
-                       "cwd"         : "/%s" % LOCAL_TMP_PATH,
-                       "env"         : self.environ,
-                       "logger"      : logger,
-                       "personality" : self.distro.personality,
-                       "shell"       : False,
-               }
-               args.update(kwargs)
-
-               try:
-                       shellenv = shell.ShellExecuteEnvironment(command, **args)
-                       shellenv.execute()
-
-               except ShellEnvironmentError:
-                       logger.error("Command exited with an error: %s" % command)
-                       raise
-
-               return shellenv
-
-       def run_script(self, script, *args):
-               if not script.startswith("/"):
-                       script = os.path.join(SCRIPT_DIR, script)
-
-               assert os.path.exists(script), "Script we should run does not exist: %s" % script
-
-               cmd = [script,]
-               for arg in args:
-                       cmd.append(arg)
-               cmd = " ".join(cmd)
-
-               # Returns the output of the command, but the output won't get
-               # logged.
-               exe = self.execute(cmd, record_output=True, log_output=False)
-
-               # Return the output of the command.
-               if exe.exitcode == 0:
-                       return exe.output
-
-       def create_buildscript(self, stage):
-               # Get buildscript from the package.
-               script = self.pkg.get_buildscript(stage)
-
-               # Write script to an empty file.
-               f = tempfile.NamedTemporaryFile(mode="w", delete=False)
-               f.write("#!/bin/sh\n\n")
-               f.write("set -e\n")
-               f.write("set -x\n")
-               f.write("\n%s\n" % script)
-               f.write("exit 0\n")
-               f.close()
-
-               # Make the script executable.
-               os.chmod(f.name, 700)
-
-               return f.name
-
-       def build(self, stages=None):
-               # Create buildroot and remove all content if it was existant.
-               util.rm(self.buildroot)
-               os.makedirs(self.buildroot)
-
-               # Process stages in order.
-               for stage in ("prepare", "build", "test", "install"):
-                       # Skip unwanted stages.
-                       if stages and not stage in stages:
-                               continue
-
-                       # Run stage.
-                       self.build_stage(stage)
-
-               # Stop if install stage has not been processed.
-               if stages and not "install" in stages:
-                       return
-
-               # Run post-build stuff.
-               self.post_compress_man_pages()
-               self.post_remove_static_libs()
-               self.post_extract_debuginfo()
-
-               # Package the result.
-               # Make all these little package from the build environment.
-               log.info(_("Creating packages:"))
-               pkgs = []
-               for pkg in reversed(self.pkg.packages):
-                       packager = packages.packager.BinaryPackager(self.pakfire, pkg,
-                               self, self.buildroot)
-                       pkg = packager.run(self.resultdir)
-                       pkgs.append(pkg)
-               log.info("")
-
-       def build_stage(self, stage):
-               # Get the buildscript for this stage.
-               buildscript = self.create_buildscript(stage)
-
-               # Execute the buildscript of this stage.
-               log.info(_("Running stage %s:") % stage)
-
-               try:
-                       self.execute(buildscript)
-
-               finally:
-                       # Remove the buildscript.
-                       if os.path.exists(buildscript):
-                               os.unlink(buildscript)
-
-       def post_remove_static_libs(self):
-               keep_libs = self.pkg.lexer.build.get_var("keep_libraries")
-               keep_libs = keep_libs.split()
-
-               try:
-                       self.execute("%s/remove-static-libs %s %s" % \
-                               (SCRIPT_DIR, self.buildroot, " ".join(keep_libs)))
-               except ShellEnvironmentError as e:
-                       log.warning(_("Could not remove static libraries: %s") % e)
-
-       def post_compress_man_pages(self):
-               try:
-                       self.execute("%s/compress-man-pages %s" % (SCRIPT_DIR, self.buildroot))
-               except ShellEnvironmentError as e:
-                       log.warning(_("Compressing man pages did not complete successfully."))
-
-       def post_extract_debuginfo(self):
-               args = []
-
-               # Check if we need to run with strict build-id.
-               strict_id = self.pkg.lexer.build.get_var("debuginfo_strict_build_id", "true")
-               if strict_id in ("true", "yes", "1"):
-                       args.append("--strict-build-id")
-
-               args.append("--buildroot=%s" % self.pkg.buildroot)
-               args.append("--sourcedir=%s" % self.pkg.sourcedir)
-
-               # Get additional options to pass to script.
-               options = self.pkg.lexer.build.get_var("debuginfo_options", "")
-               args += options.split()
-
-               try:
-                       self.execute("%s/extract-debuginfo %s %s" % (SCRIPT_DIR, " ".join(args), self.pkg.buildroot))
-               except ShellEnvironmentError as e:
-                       log.error(_("Extracting debuginfo did not complete with success. Aborting build."))
-                       raise
-
-       def find_prerequires(self, scriptlet_file):
-               assert os.path.exists(scriptlet_file), "Scriptlet file does not exist: %s" % scriptlet_file
-
-               res = self.run_script("find-prerequires", scriptlet_file)
-               prerequires = set(res.splitlines())
-
-               return prerequires
-
-       def cleanup(self):
-               if os.path.exists(self.buildroot):
-                       util.rm(self.buildroot)