Mercurial > treepkg
changeset 344:f06f707d9fda
merged branches/scratchbox into trunk
#### IMPORTANT FOR ALL TREEPKGS #####
pbuilderrc config variable name changed to builderconfig
author | Bjoern Ricks <bricks@intevation.de> |
---|---|
date | Fri, 23 Apr 2010 07:43:29 +0000 |
parents | c0808837fc64 |
children | 27eccce96949 |
files | bin/initbuilder.py bin/initpbuilder.py bin/treepkgbuilder.py demo.cfg recipes/kde/maemo/__init__.py recipes/kde/maemo/generic.py test/test_builder.py test/test_listpackages.py test/test_notifications.py test/test_packager.py test/test_readconfig.py treepkg/builder.py treepkg/packager.py treepkg/readconfig.py treepkg/sbuilder.py treepkg/util.py |
diffstat | 15 files changed, 478 insertions(+), 128 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/initbuilder.py Fri Apr 23 07:43:29 2010 +0000 @@ -0,0 +1,55 @@ +#! /usr/bin/python +# 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. + +"""Script to initialize the builder environment for the tree packager + +The script assumes that the config file for the tree packager already +contains the builder settings. Also, this script assumes that there is +only one builder setting for all packagers. +""" + +import sys + +import treepkgcmd +from treepkg.options import create_parser +from treepkg.packager import create_package_track, PackagerGroup +from treepkg.readconfig import read_config + + +def parse_commandline(): + parser = create_parser() + parser.set_defaults(distribution="etch") + parser.add_option("--mirrorsite", + help=("The debian mirror site" + " (pbuilder MIRRORSITE setting). Required.")) + parser.add_option("--othermirror", + help=("Extra contents of the OTHERMIRROR setting." + " See the pbuilder documentation for the format.")) + parser.add_option("--distribution", + help=("The debian distribution for the pbuilder chroot." + " Default is etch.")) + return parser.parse_args() + + +def main(): + options, args = parse_commandline() + + if options.mirrorsite is None: + print >>sys.stderr, "Missing required option --mirrorsite" + sys.exit(1) + + treepkg_opts, packager_opts = read_config(options.config_file) + group = PackagerGroup([create_package_track(**opts) + for opts in packager_opts], + **treepkg_opts) + track = group.get_package_tracks()[0] + track.builder.init_builder(distribution=options.distribution, + mirrorsite=options.mirrorsite, + extramirrors=options.othermirror) + +main()
--- a/bin/initpbuilder.py Thu Apr 22 19:25:02 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -#! /usr/bin/python -# 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. - -"""Script to initialize the pbuilder environment for the tree packager - -The script assumes that the config file for the tree packager already -contains the pbuilder settings. Also, this script assumes that there is -only one pbuilder setting for all packagers. -""" - -import sys - -import treepkgcmd -from treepkg.options import create_parser -from treepkg.packager import create_package_track, PackagerGroup -from treepkg.readconfig import read_config - - -def parse_commandline(): - parser = create_parser() - parser.set_defaults(distribution="etch") - parser.add_option("--mirrorsite", - help=("The debian mirror site" - " (pbuilder MIRRORSITE setting). Required.")) - parser.add_option("--othermirror", - help=("Extra contents of the OTHERMIRROR setting." - " See the pbuilder documentation for the format.")) - parser.add_option("--distribution", - help=("The debian distribution for the pbuilder chroot." - " Default is etch.")) - return parser.parse_args() - - -def main(): - options, args = parse_commandline() - - if options.mirrorsite is None: - print >>sys.stderr, "Missing required option --mirrorsite" - sys.exit(1) - - treepkg_opts, packager_opts = read_config(options.config_file) - group = PackagerGroup([create_package_track(**opts) - for opts in packager_opts], - **treepkg_opts) - track = group.get_package_tracks()[0] - track.builder.init_pbuilder(distribution=options.distribution, - mirrorsite=options.mirrorsite, - extramirrors=options.othermirror) - -main()
--- a/bin/treepkgbuilder.py Thu Apr 22 19:25:02 2010 +0000 +++ b/bin/treepkgbuilder.py Fri Apr 23 07:43:29 2010 +0000 @@ -76,7 +76,7 @@ def run(self): builder = self.get_builder() - builder.init_pbuilder(distribution=self.opts.distribution, + builder.init_builder(distribution=self.opts.distribution, mirrorsite=self.opts.mirrorsite, extramirrors=self.opts.othermirror)
--- a/demo.cfg Thu Apr 22 19:25:02 2010 +0000 +++ b/demo.cfg Fri Apr 23 07:43:29 2010 +0000 @@ -26,12 +26,12 @@ # specific values root_cmd: sudo -# The pbuilder config file to use. It should be an absolute filename. +# The builder config file to use. It should be an absolute filename. # The script initpbuilder.py can create it and the rest of the pbuilder # files and directories. You can override this in the pkg_ sections for -# individual packagers if necessary. You will have to adapt pbuilder +# individual packagers if necessary. You will have to adapt e.g. pbuilder # yourself, then, though. -pbuilderrc: %(treepkg_dir)s/pbuilder/pbuilderrc +builderconfig: %(treepkg_dir)s/pbuilder/pbuilderrc # Email address and name to use as the packager in the debian packages.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/recipes/kde/maemo/generic.py Fri Apr 23 07:43:29 2010 +0000 @@ -0,0 +1,131 @@ +# Copyright (C) 2007, 2008, 2009, 2010 by Intevation GmbH +# Authors: +# Bernhard Herzog <bh@intevation.de> +# Bjoern Ricks <bjoern.ricks@intevation.de> +# +# This program is free software under the GPL (>=v2) +# Read the file COPYING coming with the software for details. + +"""Base classes for all kde maemo packagers""" + +import re +import time +import logging + +import treepkg.packager +import treepkg.subversion as subversion +import treepkg.sbuilder + + +class SourcePackager(treepkg.packager.SourcePackager): + + def __init__(self, *args, **kw): + super(SourcePackager, self).__init__(*args, **kw) + self.enterprise_version = (self.parent.pkg_date + "." + + str(self.revision)) + self.maemo_version = "%s.%s" % (self.revision, + self.parent.pkg_date) + + def determine_package_version(self, directory): + enterprise_version = self.enterprise_version + maemo_version = self.maemo_version + revision = self.revision + rules_revision = self.parent.rules_revision + pkg_revision = self.parent.pkg_revision + pkg_date = self.parent.pkg_date + + return self.track.version_template % locals() + + def prepare_sources_for_tarball(self, pkgbasedir, pkgbaseversion): + self.update_version_numbers(pkgbasedir) + + def update_version_numbers(self, pkgbasedir): + """Updates the version numbers in the code in pkgbasedir. + The default implementation does nothing. Derived classes should + override this method if necessary. + """ + + def get_change_msg(self): + return self.changemsg_template % dict(revision=self.revision, + pkg_date=self.parent.pkg_date, + rules_revision=self.parent.rules_revision) + + +class RevisionPackager(treepkg.packager.RevisionPackager): + + source_packager_cls = SourcePackager + + def __init__(self, *args, **kw): + self.pkg_date = kw.pop("pkg_date", + time.strftime("%Y%m%d", time.localtime())) + super(RevisionPackager, self).__init__(*args, **kw) + + +class PackageTrack(treepkg.packager.PackageTrack): + + revision_packager_cls = RevisionPackager + builder_cls = treepkg.sbuilder.SbdmockBuilder + + extra_config_desc = [("tags_url", str, ""), + ("tags_pattern", str, ""), + ("tags_subdir", str, "")] + + def __init__(self, *args, **kw): + tags_url = kw.pop("tags_url") + tags_pattern = kw.pop("tags_pattern") + tags_subdir = kw.pop("tags_subdir") + super(PackageTrack, self).__init__(*args, **kw) + self.tag_detector = subversion.TagDetector(tags_url, tags_pattern, + tags_subdir) + + def packager_for_new_revision(self): + logging.info("Checking tags") + self.tag_url = None + tag_url, tag_revision = self.tag_detector.newest_tag_revision() + logging.info("Found: %s: %s", tag_url, tag_revision) + if tag_url is not None: + revision = (tag_revision, + self.rules_working_copy.last_changed_revision()) + logging.info("New revision is %s", revision) + if revision not in self.get_revision_numbers(): + logging.info("Revision %s has not been packaged yet", + revision) + self.tag_url = tag_url + self.tag_revision = tag_revision + tag_date, tag_change_count = self.tag_pkg_parameters(tag_url) + pkg_revision = (self.pkg_revision_template + % dict(pkg_revision=tag_change_count)) + return self.revision_packager_cls(self, tag=tag_url, + pkg_revision=pkg_revision, + pkg_date=tag_date, + *revision) + else: + logging.info("Revision %s has already been packaged.", + revision) + + return super(PackageTrack, self).packager_for_new_revision() + + def export_sources(self, to_dir): + if self.tag_url is not None: + logging.info("Exporting sources for tarball from %r to %r", + self.tag_url, to_dir) + self.working_copy.export_tag(self.tag_url, to_dir, + revision=self.tag_revision) + else: + super(PackageTrack, self).export_sources(to_dir) + + def tag_pkg_parameters(self, tag_url): + match = re.search(r"/enterprise[^.]*\.[^.]*\." + r"(?P<date>[0-9]{8})\.(?P<baserev>[0-9]+)/", + tag_url) + if match: + date = match.group("date") + baserev = match.group("baserev") + xml_log = subversion.log_xml(tag_url, baserev) + revisions = subversion.extract_tag_revisions(xml_log) + tag_change_count = len(revisions) + return (date, tag_change_count) + else: + raise RuntimeError("Cannot determine tag parameters from %r" + % tag_url) +
--- a/test/test_builder.py Thu Apr 22 19:25:02 2010 +0000 +++ b/test/test_builder.py Fri Apr 23 07:43:29 2010 +0000 @@ -44,14 +44,14 @@ class TestPBuilder(PBuilderTests): def test_init_pbuilder(self): - """Tests the PBuilder.init_pbuilder method.""" + """Tests the PBuilder.init_builder method.""" basedir = self.create_temp_dir("pbuilder") pbuilderrc = os.path.join(basedir, "pbuilderrc") builder = PBuilder(pbuilderrc, self.root_command) old_stdout = sys.stdout sys.stdout = captured_stdout = StringIO.StringIO() try: - builder.init_pbuilder(distribution="etch", + builder.init_builder(distribution="etch", mirrorsite="http://example.com/debian", extramirrors=None) finally: @@ -62,7 +62,7 @@ "aptcache", "extra-pkg"] if not os.path.isdir(os.path.join(basedir, dirname))] if missing: - self.fail("init_pbuilder did not create these directories: %s" + self.fail("init_builder did not create these directories: %s" % " ".join(missing)) # check the pbuilderrc. This test is a little too strict @@ -104,7 +104,7 @@ % dict(basedir_repr=repr(basedir)[1:-1])) def test_init_pbuilder_run_twice(self): - """Tests whether PBuilder.init_pbuilder prints an error when run twice. + """Tests whether PBuilder.init_builder prints an error when run twice. """ basedir = self.create_temp_dir("pbuilder") @@ -114,7 +114,7 @@ old_stdout = sys.stdout sys.stdout = captured_stdout = StringIO.StringIO() try: - builder.init_pbuilder(distribution="etch", + builder.init_builder(distribution="etch", mirrorsite="http://example.com/debian", extramirrors=None) finally: @@ -127,7 +127,7 @@ sys.stderr = captured_stderr = StringIO.StringIO() try: try: - builder.init_pbuilder(distribution="etch", + builder.init_builder(distribution="etch", mirrorsite="http://example.com/debian", extramirrors=None) except SystemExit, exc:
--- a/test/test_listpackages.py Thu Apr 22 19:25:02 2010 +0000 +++ b/test/test_listpackages.py Fri Apr 23 07:43:29 2010 +0000 @@ -64,7 +64,7 @@ [DEFAULT] tracks_dir: %(tracksdir)s root_cmd: true -pbuilderrc: +builderconfig: deb_email: packager@example.com deb_fullname: Sample Packager pkg_revision_template: treepkg%%%%(pkg_revision)s
--- a/test/test_notifications.py Thu Apr 22 19:25:02 2010 +0000 +++ b/test/test_notifications.py Fri Apr 23 07:43:29 2010 +0000 @@ -21,7 +21,7 @@ [DEFAULT] tracks_dir: %(tracksdir)s root_cmd: true -pbuilderrc: +builderconfig: deb_email: packager@example.com deb_fullname: Sample Packager pkg_revision_template: treepkg%%%%(pkg_revision)d
--- a/test/test_packager.py Thu Apr 22 19:25:02 2010 +0000 +++ b/test/test_packager.py Fri Apr 23 07:43:29 2010 +0000 @@ -111,7 +111,7 @@ rootcmd = os.path.join(os.path.dirname(__file__), os.pardir, "test", "mocksudopbuilder.py") track = PackageTrack(name="testpkg", base_dir=self.trackdir, - svn_url=self.svn_url, pbuilderrc="", + svn_url=self.svn_url, builderconfig="", root_cmd=[sys.executable, rootcmd], deb_email="treepkg@example.com", deb_fullname="treepkg tester", @@ -388,7 +388,7 @@ defaults = dict(base_dir="/home/builder/tracks/" + name, svn_url="svn://example.com", root_cmd=["false"], - pbuilderrc="/home/builder/pbuilderrc", + builderconfig="/home/builder/pbuilderrc", deb_email="treepkg@example.com", deb_fullname="treepkg", handle_dependencies=handle_dependencies) super(PackageTrackWithDependencies,
--- a/test/test_readconfig.py Thu Apr 22 19:25:02 2010 +0000 +++ b/test/test_readconfig.py Fri Apr 23 07:43:29 2010 +0000 @@ -25,7 +25,7 @@ tracks_dir: %(treepkg_dir)s/tracks root_cmd: sudo pbuilder_dir: %(treepkg_dir)s/pbuilder -pbuilderrc: %(pbuilder_dir)s/pbuilderrc +builderconfig: %(pbuilder_dir)s/pbuilderrc deb_email: treepkg@example.com deb_fullname: TreePKG pkg_revision_template: treepkg%%(pkg_revision)s @@ -95,7 +95,7 @@ pkg_revision_template="treepkg%(pkg_revision)s", handle_dependencies=False, packager_class="readconfig_test.extraargs", - pbuilderrc="/home/builder/mill/pbuilder/pbuilderrc", + builderconfig="/home/builder/mill/pbuilder/pbuilderrc", # pkg_basename is passed as an empty string by default. # The PackageTrack an empty pkg_basename it with the # value of name @@ -122,7 +122,7 @@ pkg_revision_template="treepkg%(pkg_revision)s", handle_dependencies=True, packager_class="readconfig_test.simple", - pbuilderrc="/home/builder/mill/pbuilder/pbuilderrc", + builderconfig="/home/builder/mill/pbuilder/pbuilderrc", pkg_basename="simple1", root_cmd=['sudo'], signing_key_id="abcd1234",
--- a/treepkg/builder.py Thu Apr 22 19:25:02 2010 +0000 +++ b/treepkg/builder.py Fri Apr 23 07:43:29 2010 +0000 @@ -17,8 +17,62 @@ 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 -class PBuilder(object): + 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""" @@ -41,12 +95,6 @@ PKGNAME_LOGFILE=yes ''' - 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") - 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 @@ -60,7 +108,7 @@ self.release_signing_keyid = release_signing_keyid self.base_dir = os.path.dirname(self.pbuilderrc) - def init_pbuilder(self, distribution, mirrorsite, extramirrors): + 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" @@ -104,27 +152,6 @@ run.call(cmdexpand("@root_cmd pbuilder create --configfile $pbuilderrc", root_cmd=self.root_cmd, pbuilderrc=self.pbuilderrc)) - 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 update(self, suppress_output=True, log_info=True): """Runs pbuilder update on this pbuilder instance""" if log_info: @@ -195,6 +222,8 @@ if logfile is not None and os.path.exists(logfile): run.call(cmdexpand("gzip -9 $logfile", logfile=logfile)) + if logfile is not None and os.path.exists(logfile): + run.call(cmdexpand("gzip -9 $logfile", logfile=logfile)) # remove the source package files put into the binary directory # by pbuilder if binary_dir is not None: @@ -202,27 +231,6 @@ if os.path.splitext(filename)[1] not in (".deb", ".changes"): os.remove(os.path.join(binary_dir, 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) def run_script(self, script, logfile, bindmounts=(), save_after_exec=False): """Execute a script in pbuilder's chroot environment
--- a/treepkg/packager.py Thu Apr 22 19:25:02 2010 +0000 +++ b/treepkg/packager.py Fri Apr 23 07:43:29 2010 +0000 @@ -171,7 +171,11 @@ def sign_package(self): """Signs the .dsc file created buy the instance""" - self.track.sign_file(util.listdir_abs(self.src_dir, "*.dsc")[0]) + src_files = util.listdir_abs(self.src_dir, "*.dsc") + if not src_files: + raise RuntimeError("Could not find .dsc file in source" + " directory %s" % self.src_dir) + self.track.sign_file(src_files[0]) def package(self): """Creates a source package from a subversion checkout. @@ -203,13 +207,15 @@ self.create_tarball(origtargz, self.work_dir, os.path.basename(pkgbasedir)) - changemsg = self.changemsg_template % dict(revision=self.revision) + changemsg = self.get_change_msg() self.copy_debian_directory(pkgbasedir, pkgbaseversion, changemsg) self.create_source_package(pkgbasedir, origtargz) self.move_source_package(pkgbasename) + def get_change_msg(self): + return self.changemsg_template % dict(revision=self.revision) class BinaryPackager(object): @@ -235,7 +241,10 @@ def sign_package(self): """Signs the .changes file created buy the instance""" - self.track.sign_file(util.listdir_abs(self.binary_dir, "*.changes")[0]) + dirs = util.listdir_abs(self.binary_dir, "*.changes") + if not dirs: + raise RuntimeError("Cannot find changes File in %r" % self.binary_dir) + self.track.sign_file(dirs[0]) class RevisionPackager(object): @@ -342,11 +351,13 @@ revision_packager_cls = RevisionPackager + builder_cls = PBuilder + svn_external_subdirs = [] extra_config_desc = [] - def __init__(self, name, base_dir, root_cmd, pbuilderrc, deb_email, + def __init__(self, name, base_dir, root_cmd, builderconfig, deb_email, deb_fullname, svn_url="", packager_class="treepkg.packager", version_template="%(revision)s", pkg_revision_template="treepkg%(pkg_revision)s", @@ -360,7 +371,7 @@ self.pkg_basename = pkg_basename self.changelog_msg_template = changelog_msg_template self.base_dir = base_dir - self.builder = PBuilder(pbuilderrc, root_cmd, + self.builder = self.builder_cls(builderconfig, root_cmd, release_signing_keyid=signing_key_id) self.deb_email = deb_email self.deb_fullname = deb_fullname
--- a/treepkg/readconfig.py Thu Apr 22 19:25:02 2010 +0000 +++ b/treepkg/readconfig.py Fri Apr 23 07:43:29 2010 +0000 @@ -69,7 +69,7 @@ ("svn_subset", convert_subversion_subset, ""), ("svn_externals", shlex.split, ""), ("rules_svn_url", str, ""), "packager_class", - ("root_cmd", shlex.split, "sudo"), "pbuilderrc", + ("root_cmd", shlex.split, "sudo"), "builderconfig", "deb_email", "deb_fullname", ("deb_build_options", str, ""), ("version_template", str, "%(revision)s"), "pkg_revision_template", ("pkg_basename", str, ""),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/sbuilder.py Fri Apr 23 07:43:29 2010 +0000 @@ -0,0 +1,195 @@ +# Copyright (C) 2010 by Intevation GmbH +# Authors: +# Bjoern Ricks <bjoern.ricks@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 +import builder +from cmdexpand import cmdexpand + + +class SbdmockBuilder(builder.Builder): + + """Represents a way to run and manage a specific sbdmock instance + to build binary for Maemo within scratchbox environment""" + + 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") + + def __init__(self, builderconfig, root_cmd, release_signing_keyid=None): + """Initialize the Builder instance with the configuration file. + The root_cmd parameter should be a list with a command that can + be used to get root permissions. 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. + """ + if not os.path.exists(builderconfig): + raise RuntimeError("Config file %s for sbdmock does not exist.", + builderconfig) + + if not os.path.isabs(builderconfig): + raise RuntimeError("Config file %s must be an absolute filename.", + builderconfig) + + self.builderconfig = builderconfig + self.root_cmd = root_cmd + self.release_signing_keyid = release_signing_keyid + self.base_dir = os.path.dirname(self.builderconfig) + self.mounted_dirs = [] + + def init_builder(self, distribution, mirrorsite, extramirrors): + """Initializes the builder instance""" + + basedir = os.path.dirname(self.builderconfig) + replacements = dict(basedir=basedir, + distribution=distribution, + mirrorsite=mirrorsite) + + # create the builder directories. basedir is created implicitly by + # creating its subdirectories. + for attr in ["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 + + # 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() + + def update(self, suppress_output=True, log_info=True): + """Runs nothing""" + if log_info: + logging.info("Update of apt cache is done on every start. skipping ...") + + 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 builder chroot + environment + extra_packages -- Extra packages to install + extra_env -- mapping with extra environment variables to set + when runing the builder process. If builder + is started via sudo, make sure that sudo does + not remove these variables when it starts + builder + """ + args = [] + if logfile is not None: + logdir = os.path.dirname(logfile) + args.extend(["--buildlog=%s" % logfile]) + args.extend(["--logdir=%s" % logdir]) + if binary_dir is not None: + args.extend(["--resultdir=%s" % binary_dir]) + util.ensure_directory(binary_dir) + if bindmounts: + self.mount(bindmounts) +# for pkg in extra_packages: +# args.extend(["--extrapackages", pkg]) + + logging.info("Mointing extra-pkg apt repository") + + self.mount([self.extra_pkg_dir]) + + logging.info("Starting build process with sbdmock ...") + + + cmd = cmdexpand("/usr/bin/sbdmock --cleanbuilddir" + " --config=$builderconfig" +# " --dbo=" # aren't build options setable in treepkg.cfg? + " -u -b $dsc @args", + builderconfig=self.builderconfig, + dsc=dsc_file, args=args) + logging.debug("sbdmock cmd: %s" % cmd) + + try: + run.call(cmd, suppress_output=True, extra_env=extra_env) + + # remove the source package files put into the binary directory + # by pbuilder (BR: not sure if this is necessary for sbdmock) + 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)) + finally: + # compress logfile + if logfile is not None and os.path.exists(logfile): + run.call(cmdexpand("gzip -9 $logfile", logfile=logfile)) + # remove all mounted directories + self.umount_all() + + def run_script(self, script, logfile, bindmounts=(), save_after_exec=False): + """Execute a script in builder'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 builder 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 builder instance. + """ + logging.error("It isn't possible to run scripts withon sbdmock.") + + def login(self, bindmounts=(), save_after_login=False): + """Start an interactive shell in the builder 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) + + def mount(self, bindmounts): + for mount in bindmounts: + mount_dir = "/scratchbox/users/%s/%s" % (util.getuser(), mount) + util.ensure_directory(mount_dir) + logging.info("Mounting %s to %s" % (mount, mount_dir)) + run.call(cmdexpand("@rootcmd mount --bind $mount $mountdir", rootcmd=self.root_cmd, mount=mount, mountdir=mount_dir)) + + #add mountpoint to a variable to for unmounting later + self.mounted_dirs.append(mount_dir) + + def umount(self, mounts): + for mount in mounts: + logging.info("Unmounting %s" % mount) + run.call(cmdexpand("@rootcmd umount $dir", rootcmd=self.root_cmd, dir=mount)) + self.mounted_dirs.remove(mount) + + def umount_all(self): + mounts = self.mounted_dirs[:] + self.umount(mounts)
--- a/treepkg/util.py Thu Apr 22 19:25:02 2010 +0000 +++ b/treepkg/util.py Fri Apr 23 07:43:29 2010 +0000 @@ -12,6 +12,7 @@ import tempfile import shutil import fnmatch +import pwd import run @@ -151,3 +152,7 @@ def get(self): return os.path.join(getattr(self, dir_attr), filename) return property(get) + +def getuser(): + """Returns the login name of the current user owning the proccess""" + return pwd.getpwuid(os.getuid())[0]