from . import _pakfire
from . import base
-from . import cgroup
+from . import cgroups
from . import config
from . import downloaders
from . import logger
"""
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)
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,
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()
# 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")
arch=self.builder.arch,
)
- self.setup()
-
@property
def environ(self):
env = MINIMAL_ENVIRONMENT.copy()
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:
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():
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
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)