Mercurial > treepkg > treepkg
view treepkg/builder.py @ 565:7de7869962ef
Correct syntax and display abspath of config file
author | Bjoern Ricks <bricks@intevation.de> |
---|---|
date | Fri, 02 Sep 2011 10:30:52 +0000 |
parents | 058856954e2d |
children |
line wrap: on
line source
# Copyright (C) 2007, 2008, 2009 by Intevation GmbH # Authors: # Bernhard Herzog <bh@intevation.de> # # This program is free software under the GPL (>=v2) # Read the file COPYING coming with the software for details. """Build binary packages from source packages""" import sys import os import shutil import logging import tempfile import util import run from cmdexpand import cmdexpand class Builder: basetgz_dir = util.filenameproperty("base") build_dir = util.filenameproperty("build") result_dir = util.filenameproperty("result") aptcache_dir = util.filenameproperty("aptcache") extra_pkg_dir = util.filenameproperty("extra-pkg") """ Parent class for all Builders """ def __init__(self): pass def update_extra_pkg_dir(self): run.call(cmdexpand("apt-ftparchive packages ."), stdout=open(os.path.join(self.extra_pkg_dir, "Packages"), "w"), cwd=self.extra_pkg_dir) release_filename = os.path.join(self.extra_pkg_dir, "Release") run.call(cmdexpand("apt-ftparchive release ."), stdout=open(release_filename, "w"), cwd=self.extra_pkg_dir) # handle signatures. remove any existing signature because it # will be invalid now. signature = release_filename + ".gpg" try: os.remove(signature) except OSError: pass if self.release_signing_keyid: run.call(cmdexpand("gpg --detach-sign --armor --local-user=$keyid" " -o $sig $release", keyid=self.release_signing_keyid, sig=release_filename + ".gpg", release=release_filename)) def add_binaries_to_extra_pkg(self, filenames, subdirectory="auto"): """Adds binary packages to the extra-pkg directory. The filenames parameter should be sequence of absolute filenames. The files named will be copied to a subdirectory of the extra-pkg directory which is assumed to reside in the same directory as the pbuilderrc. The subdirectory is specified with the subdirectory parameter and defaults to 'auto'. Afterwards, the method generates a Packages file in the directory and runs pbuilder update. All of this assumes that pbuilder was set up the way bin/initpbuilder.py does. """ target_dir = os.path.join(self.extra_pkg_dir, subdirectory) util.ensure_directory(target_dir) for filename in filenames: logging.info("Copying %s into %s", filename, target_dir) shutil.copy(filename, target_dir) logging.info("Running apt-ftparchive in %s", self.extra_pkg_dir) self.update_extra_pkg_dir() self.update(suppress_output=True, log_info=True) class PBuilder(Builder): """Represents a way to run and manage a specific pbuilder instance""" pbuilderrc_template = '''\ # This file was automatically generated by initpbuilder.py. # for the possible settings see "man pbuilderrc" BASETGZ=%(basetgz_dir)s/base.tgz BUILDPLACE=%(build_dir)s USEPROC=yes USEDEVPTS=yes BUILDRESULT=%(result_dir)s DISTRIBUTION=%(distribution)s APTCACHE=%(aptcache_dir)s APTCACHEHARDLINK=yes REMOVEPACKAGES=lilo MIRRORSITE="%(mirrorsite)s" OTHERMIRROR="%(othermirror)s" BINDMOUNTS="%(extra_pkg_dir)s" PKGNAME_LOGFILE=yes ''' def __init__(self, pbuilderrc, root_cmd, release_signing_keyid=None): """Initialize the PBuilder instance with the configuration file. The root_cmd parameter should be a list with a command that can be used to get root permissions to run pbuilder. It may be an empty list if no command is needed. It's a list so that commands with several shell-words can be used without having to worry about quoting. """ self.pbuilderrc = pbuilderrc self.root_cmd = root_cmd self.release_signing_keyid = release_signing_keyid self.base_dir = os.path.dirname(self.pbuilderrc) def init_builder(self, distribution, mirrorsite, extramirrors): """Initializes the pbuilder instance""" if not os.path.isabs(self.pbuilderrc): print >>sys.stderr, "pbuilderrc must be an absolute filename" sys.exit(1) if os.path.exists(self.pbuilderrc): print >>sys.stderr, ("pbuilderrc %r already exists." % self.pbuilderrc) sys.exit(1) basedir = os.path.dirname(self.pbuilderrc) replacements = dict(basedir=basedir, distribution=distribution, mirrorsite=mirrorsite) # create the pbuilder directories. basedir is created implicitly by # creating its subdirectories. for attr in ["basetgz_dir", "build_dir", "result_dir", "aptcache_dir", "extra_pkg_dir"]: directory = getattr(self, attr) replacements[attr] = directory print "creating directory:", repr(directory) util.ensure_directory(directory) # build OTHERMIRROR value. We always include the extra-pkg dir. othermirror = "deb file://%(extra_pkg_dir)s ./" % replacements if extramirrors: othermirror += " | " + extramirrors replacements["othermirror"] = othermirror # create the pbuilderrcfile print "creating pbuilderrc:", repr(self.pbuilderrc) util.writefile(self.pbuilderrc, self.pbuilderrc_template % replacements) # turn the extra-pkg directory into a proper deb archive print "turning the extra-pkg dir into a debian archive" self.update_extra_pkg_dir() # create the base.tgz chroot print "running pbuilder create" run.call(cmdexpand("@root_cmd pbuilder create --configfile $pbuilderrc", root_cmd=self.root_cmd, pbuilderrc=self.pbuilderrc)) def update(self, suppress_output=True, log_info=True): """Runs pbuilder update on this pbuilder instance""" if log_info: logging.info("Running pbuilder update for %s", self.pbuilderrc) run.call(cmdexpand("@rootcmd /usr/sbin/pbuilder update" " --configfile $pbuilderrc", rootcmd=self.root_cmd, pbuilderrc=self.pbuilderrc), suppress_output=suppress_output) def add_apt_key(self, keyid): """Runs apt-key add in the chroot""" # Creates a temporary file in extra_pkg_dir (because that's # bind-mounted by default) with a script that adds the desired # key. The exported key is included in the script file so that # only one file has to be created script = tempfile.NamedTemporaryFile(dir=self.extra_pkg_dir) try: script.write("#! /bin/sh\n") script.write("apt-key add $0\n") script.write("exit\n\n") script.flush() run.call(cmdexpand("gpg --export --armor $keyid", **locals()), stdout=script.fileno()) self.run_script([script.name], logfile=None, save_after_exec=True) finally: script.close() def build(self, dsc_file, binary_dir=None, logfile=None, bindmounts=(), extra_packages=(), extra_env=None): """Build a binary packager from a source package Parameters: dsc_file -- name of the debian .dsc file of the source package binary_dir -- name of the directory to receive the binary packages logfile -- name of the logfile of the build bindmounts -- Sequence of directory names that should be bind-mounted in the pbuilder chroot environment extra_packages -- Extra packages to install extra_env -- mapping with extra environment variables to set when runing the pbuilder process. If pbuilder is started via sudo, make sure that sudo does not remove these variables when it starts pbuilder """ args = [] if logfile is not None: args.extend(["--logfile", logfile]) if binary_dir is not None: args.extend(["--buildresult", binary_dir]) util.ensure_directory(binary_dir) for mount in bindmounts: args.extend(["--bindmounts", mount]) for pkg in extra_packages: args.extend(["--extrapackages", pkg]) run.call(cmdexpand("@rootcmd /usr/sbin/pbuilder build" " --configfile $pbuilderrc @args" " --debbuildopts -b $dsc", rootcmd=self.root_cmd, pbuilderrc=self.pbuilderrc, dsc=dsc_file, args=args), suppress_output=True, extra_env=extra_env) # remove the source package files put into the binary directory # by pbuilder if binary_dir is not None: for filename in os.listdir(binary_dir): if os.path.splitext(filename)[1] not in (".deb", ".changes"): os.remove(os.path.join(binary_dir, filename)) def run_script(self, script, logfile, bindmounts=(), save_after_exec=False): """Execute a script in pbuilder's chroot environment Parameters: script -- A list of strings with the command line to invoke the script logfile -- name of the logfile of the build bindmounts -- Sequence of directory names that should be bind-mounted in the pbuilder chroot environment (optional) save_after_exec -- Boolean indicating whether the chroot environment should be copied back so that modifications are available in subsequent uses of the pbuilder instance. """ logging.info("Running pbuilder execute on %s", script) args = [] if logfile: args.extend(["--logfile", logfile]) # create the logfile. This makes sure that it is owned by # the user the tree packager is running as and not root, as # would be the case when it is created indirectly by # pbuilder open(logfile, "w").close() for mount in bindmounts: args.extend(["--bindmounts", mount]) if save_after_exec: args.append("--save-after-exec") run.call(cmdexpand("@rootcmd /usr/sbin/pbuilder execute" " --configfile $pbuilderrc @args -- @script", rootcmd=self.root_cmd, pbuilderrc=self.pbuilderrc, args=args, script=script), suppress_output=False) def login(self, bindmounts=(), save_after_login=False): """Start an interactive shell in the pbuilder environment""" args = [] for mount in bindmounts: args.extend(["--bindmounts", mount]) if save_after_login: args.extend(["--save-after-login"]) run.call(cmdexpand("@rootcmd /usr/sbin/pbuilder login" " --configfile $pbuilderrc @args", rootcmd=self.root_cmd, pbuilderrc=self.pbuilderrc, args=args), suppress_output=False)