# HG changeset patch # User Bernhard Herzog # Date 1173439561 -3600 # Node ID fee641fec94e91f8e5c4b6bff680242e45f2ba43 # Parent 7e9db903ba1662f0b9bcc4ea257c7d6aac9e7b80 Separate the kolab specific parts. diff -r 7e9db903ba16 -r fee641fec94e enterprise/kdepim.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enterprise/kdepim.py Fri Mar 09 12:26:01 2007 +0100 @@ -0,0 +1,82 @@ +# Copyright (C) 2007 by Intevation GmbH +# Authors: +# Bernhard Herzog +# +# This program is free software under the GPL (>=v2) +# Read the file COPYING coming with the software for details. + +"""Packager that builds KDE-PIM debian packages from the enterprise branch""" + +import os +import time +import re + +import treepkg.util +import treepkg.packager + + +class SourcePackager(treepkg.packager.SourcePackager): + + pkg_basename = "kdepim" + + def __init__(self, *args, **kw): + super(SourcePackager, self).__init__(*args, **kw) + self.enterprise_version = (time.strftime("%Y%m%d", time.localtime()) \ + + "." + str(self.revision)) + + def kdepim_version(self, directory): + """Determine the kdepim version. + + The version is taken from the kdepim.lsm file. + """ + return treepkg.util.extract_lsm_version(os.path.join(directory, + "kdepim.lsm")) + + def determine_package_version(self, directory): + enterprise_version = self.enterprise_version + kdepimversion = self.kdepim_version(directory) + version_template = "%(kdepimversion)s.enterprise.0.%(enterprise_version)s" + return version_template % locals() + + def update_version_numbers(self, pkgbasedir): + """Overrides the inherited method to update version numbers in the code + """ + versionstring = "(enterprise %s)" % self.enterprise_version + for versionfile in ["kmail/kmversion.h", "kontact/src/main.cpp", + "korganizer/version.h"]: + filename = os.path.join(pkgbasedir, versionfile) + patched = re.sub("\(enterprise ([^)]*)\)", versionstring, + open(filename).read()) + f = open(filename, "w") + f.write(patched) + f.close() + + def do_package(self): + pkgbaseversion, pkgbasedir = self.export_sources() + self.update_version_numbers(pkgbasedir) + + pkgbasename = "kdepim_" + pkgbaseversion + origtargz = os.path.join(self.work_dir, + pkgbasename + ".orig.tar.gz") + self.create_tarball(origtargz, self.work_dir, + os.path.basename(pkgbasedir)) + + changemsg = ("Update to SVN enterprise branch rev. %d" + % (self.revision,)) + self.copy_debian_directory(pkgbasedir, pkgbaseversion, + changemsg) + + self.create_source_package(pkgbasedir, origtargz) + self.move_source_package(pkgbasename) + + +class RevisionPackager(treepkg.packager.RevisionPackager): + + source_packager_cls = SourcePackager + + +class AssemblyLine(treepkg.packager.AssemblyLine): + + revision_packager_cls = RevisionPackager + + svn_external_subdirs = ["admin"] diff -r 7e9db903ba16 -r fee641fec94e runtreepkg.py --- a/runtreepkg.py Thu Mar 08 20:23:28 2007 +0100 +++ b/runtreepkg.py Fri Mar 09 12:26:01 2007 +0100 @@ -12,7 +12,7 @@ import logging from optparse import OptionParser -from treepkg.packager import AssemblyLine, Packager +from treepkg.packager import create_packager, Packager from treepkg.readconfig import read_config def initialize_logging(): @@ -36,8 +36,8 @@ initialize_logging() - treepkg_opts, assembly_line_opts = read_config(options.config_file) - packager = Packager([AssemblyLine(**opts) for opts in assembly_line_opts], + treepkg_opts, packager_opts = read_config(options.config_file) + packager = Packager([create_packager(**opts) for opts in packager_opts], **treepkg_opts) packager.run() diff -r 7e9db903ba16 -r fee641fec94e treepkg/packager.py --- a/treepkg/packager.py Thu Mar 08 20:23:28 2007 +0100 +++ b/treepkg/packager.py Fri Mar 09 12:26:01 2007 +0100 @@ -27,56 +27,87 @@ class SourcePackager(object): + # Derived classes must supply the package basename + pkg_basename = None + def __init__(self, plant, status, work_dir, src_dir, revision): self.plant = plant self.status = status self.work_dir = work_dir self.src_dir = src_dir self.revision = revision - self.enterprise_version = (time.strftime("%Y%m%d", time.localtime()) \ - + "." + str(self.revision)) - - def kdepim_version(self, directory): - """Determine the kdepim version. - - The version is taken from the kdepim.lsm file in the plants - checkout dir. - """ - return util.extract_lsm_version(os.path.join(directory, "kdepim.lsm")) + assert(self.pkg_basename) def determine_package_version(self, directory): - enterprise_version = self.enterprise_version - kdepimversion = self.kdepim_version(directory) - version_template = "%(kdepimversion)s.enterprise.0.%(enterprise_version)s" - return version_template % locals() + """Returns the version number of the new package as a string + + The directory parameter is the name of the directory containing + the newly exported sources. The sources were exported with the + export_sources method. + + The default implementation simply returns the revision converted + to a string. + """ + return str(self.revision) def export_sources(self): + """Export the sources from the subversion working directory + + This method first exports the sources to a temporary directory + and then renames the directory. The new name is of the form + + - + + Where pkg_basename is the value of self.pkg_basename and version + is the return value of the determine_package_version() method. + """ temp_dir = os.path.join(self.work_dir, "temp") self.plant.export_sources(temp_dir) pkgbaseversion = self.determine_package_version(temp_dir) - pkgbasedir = os.path.join(self.work_dir, "kdepim-" + pkgbaseversion) + pkgbasedir = os.path.join(self.work_dir, + self.pkg_basename + "-" + pkgbaseversion) os.rename(temp_dir, pkgbasedir) return pkgbaseversion, pkgbasedir def update_version_numbers(self, pkgbasedir): - versionstring = "(enterprise %s)" % self.enterprise_version - for versionfile in ["kmail/kmversion.h", "kontact/src/main.cpp", - "korganizer/version.h"]: - filename = os.path.join(pkgbasedir, versionfile) - patched = re.sub("\(enterprise ([^)]*)\)", versionstring, - open(filename).read()) - f = open(filename, "w") - f.write(patched) - f.close() + """Updates the version numbers in the code in pkgbasedir. + + The default implementation does nothing. Derived classes should + override this method if necessary. + """ def create_tarball(self, tarballname, workdir, basedir): + """Creates a new tarball. + + Parameters: + + tarballname -- the filename of the new tarball + workdir -- The directory into which to change before running tar. + (actually this is done with GNUI tar's -C option) + basedir -- The basedirectory of the files that are packaged + into the tarfile. This should be a relative + filename directly in workdir. + """ logging.info("Creating tarball %r", tarballname) run.call(["tar", "czf", tarballname, "-C", workdir, basedir]) def copy_debian_directory(self, pkgbasedir, pkgbaseversion, changemsg): + """Copies the debian directory and updates the copy's changelog + + Parameter: + pkgbasedir -- The directory holding the unpacked source package + pkgbaseversion -- The version to update the changelog to + changemsg -- The message for the changelog + + When determining the actual version for the new package, this + function looks at the previous version in the changelog. If it + has a prefix separated from the version number by a colon this + prefix is prepended to the pkgbaseversion parameter. Debian + uses such prefixes for the kde packages. + """ debian_dir = os.path.join(pkgbasedir, "debian") changelog = os.path.join(debian_dir, "changelog") @@ -95,6 +126,7 @@ def create_source_package(self, pkgbasedir, origtargz): + """Creates a new source package from pkgbasedir and origtargz""" logging.info("Creating new source package") run.call(["dpkg-source", "-b", os.path.basename(pkgbasedir), os.path.basename(origtargz)], @@ -102,37 +134,41 @@ suppress_output=True, env=self.plant.debian_environment()) + def move_source_package(self, pkgbasename): + """Moves the new source package from the work_dir to the src_dir""" + logging.info("Moving source package to %r", self.src_dir) + util.ensure_directory(self.src_dir) + for filename in [filename for filename in os.listdir(self.work_dir) + if filename.startswith(pkgbasename)]: + os.rename(os.path.join(self.work_dir, filename), + os.path.join(self.src_dir, filename)) + def package(self): + """Creates a source package from a subversion checkout. + + After setting up the working directory, this method calls the + do_package method to do the actual packaging. Afterwards the + work directory is removed. + """ util.ensure_directory(self.work_dir) try: self.status.set("creating_source_package") - pkgbaseversion, pkgbasedir = self.export_sources() - self.update_version_numbers(pkgbasedir) - - pkgbasename = "kdepim_" + pkgbaseversion - origtargz = os.path.join(self.work_dir, - pkgbasename + ".orig.tar.gz") - self.create_tarball(origtargz, self.work_dir, - os.path.basename(pkgbasedir)) - - changemsg = ("Update to SVN enterprise branch rev. %d" - % (self.revision,)) - self.copy_debian_directory(pkgbasedir, pkgbaseversion, - changemsg) - - self.create_source_package(pkgbasedir, origtargz) - - logging.info("Moving source package to %r", self.src_dir) - util.ensure_directory(self.src_dir) - for filename in [filename for filename in os.listdir(self.work_dir) - if filename.startswith(pkgbasename)]: - os.rename(os.path.join(self.work_dir, filename), - os.path.join(self.src_dir, filename)) + self.do_package() self.status.set("source_package_created") finally: logging.info("Removing workdir %r", self.work_dir) shutil.rmtree(self.work_dir) + def do_package(self): + """Does the work of creating a source package + This method must be overriden by derived classes. + + The method should do the work in self.work_dir. When the + package is done, the source package files should be in + self.src_dir. + """ + raise NotImplementedError + class BinaryPackager(object): @@ -155,6 +191,9 @@ class RevisionPackager(object): + source_packager_cls = SourcePackager + binary_packager_cls = BinaryPackager + def __init__(self, plant, revision): self.plant = plant self.revision = revision @@ -173,19 +212,19 @@ def package(self): try: - src_packager = SourcePackager(self.plant, self.status, - self.work_dir, self.src_dir, - self.revision) + src_packager = self.source_packager_cls(self.plant, self.status, + self.work_dir, self.src_dir, + self.revision) src_packager.package() dsc_file = self.find_dsc_file() if dsc_file is None: raise RuntimeError("Cannot find dsc File in %r" % self.src_dir) - bin_packager = BinaryPackager(self.plant, self.status, - self.binary_dir, dsc_file, - os.path.join(self.base_dir, - "build.log")) + bin_packager = self.binary_packager_cls(self.plant, self.status, + self.binary_dir, dsc_file, + os.path.join(self.base_dir, + "build.log")) bin_packager.package() except: self.status.set("failed", traceback.format_exc()) @@ -198,8 +237,14 @@ class AssemblyLine(object): + revision_packager_cls = RevisionPackager + + svn_external_subdirs = [] + + extra_config_desc = [] + def __init__(self, name, base_dir, svn_url, root_cmd, deb_email, - deb_fullname): + deb_fullname, packager_class="treepkg.packager"): self.name = name self.base_dir = base_dir self.svn_url = svn_url @@ -219,10 +264,11 @@ self.pkg_dir_template % locals()) def last_changed_revision(self): - rev1 = subversion.last_changed_revision(self.checkout_dir) - rev2 = subversion.last_changed_revision(os.path.join(self.checkout_dir, - "admin")) - return max(rev1, rev2) + revisions = [] + for directory in [self.checkout_dir] + self.svn_external_subdirs: + directory = os.path.join(self.checkout_dir, directory) + revisions.append(subversion.last_changed_revision(directory)) + return max(revisions) def last_packaged_revision(self): """Returns the revision number of the highest packaged revision. @@ -265,10 +311,11 @@ # some versions of svn (notably version 1.4.2 shipped with etch) # do export externals such as the admin subdirectory. We may # have to do that in an extra step. - admindir = os.path.join(to_dir, "admin") - if not os.path.isdir(admindir): - subversion.export(os.path.join(self.checkout_dir, "admin"), - admindir) + for subdir in self.svn_external_subdirs: + absdir = os.path.join(to_dir, subdir) + if not os.path.isdir(absdir): + subversion.export(os.path.join(self.checkout_dir, subdir), + absdir) def copy_debian_directory(self, to_dir): logging.info("Copying debian directory to %r", to_dir) @@ -290,11 +337,15 @@ logging.info("Previously packaged revision was %d", previous_revision) if current_revision > previous_revision: logging.info("New revision is not packaged yet") - return RevisionPackager(self, current_revision) + return self.revision_packager_cls(self, current_revision) else: logging.info("New revision already packaged.") +def create_packager(packager_class, **kw): + module = util.import_dotted_name(packager_class) + return module.AssemblyLine(**kw) + class Packager(object): diff -r 7e9db903ba16 -r fee641fec94e treepkg/readconfig.py --- a/treepkg/readconfig.py Thu Mar 08 20:23:28 2007 +0100 +++ b/treepkg/readconfig.py Fri Mar 09 12:26:01 2007 +0100 @@ -11,10 +11,12 @@ import shlex from ConfigParser import SafeConfigParser, NoOptionError +import util + defaults = dict(root_cmd="sudo") packager_desc = [ - "name", "base_dir", "svn_url", + "name", "base_dir", "svn_url", "packager_class", ("root_cmd", shlex.split), "deb_email", "deb_fullname", ] @@ -59,7 +61,10 @@ packagers = [] for section in parser.sections(): if section.startswith("pkg_"): - packagers.append(read_config_section(parser, section, packager_desc, + packager_class = parser.get(section, "packager_class") + module = util.import_dotted_name(packager_class) + desc = packager_desc + module.AssemblyLine.extra_config_desc + packagers.append(read_config_section(parser, section, desc, dict(name=section[4:]))) # main config diff -r 7e9db903ba16 -r fee641fec94e treepkg/run.py --- a/treepkg/run.py Thu Mar 08 20:23:28 2007 +0100 +++ b/treepkg/run.py Fri Mar 09 12:26:01 2007 +0100 @@ -29,6 +29,7 @@ """ if suppress_output: kw["stdout"] = open(os.devnull, "w") + kw["stderr"] = open(os.devnull, "w") ret = subprocess.call(command, **kw) if ret != 0: raise SubprocessError(command, ret) diff -r 7e9db903ba16 -r fee641fec94e treepkg/util.py --- a/treepkg/util.py Thu Mar 08 20:23:28 2007 +0100 +++ b/treepkg/util.py Fri Mar 09 12:26:01 2007 +0100 @@ -9,10 +9,17 @@ import os import tempfile +import shutil import run +def import_dotted_name(dotted_name): + module = __import__(dotted_name) + for name in dotted_name.split(".")[1:]: + module = getattr(module, name) + return module + def extract_value_for_key(lines, key): """Parses a sequence of strings for a key and returns the associated value @@ -42,6 +49,40 @@ os.makedirs(directory) +def copytree(src, dst, symlinks=False): + """Recursively copy a directory tree using copy2(). + + This version is basically the same as the one in the shutil module + in the python standard library, however, it's OK if the destination + directory already exists. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. + """ + names = os.listdir(src) + ensure_directory(dst) + errors = [] + for name in names: + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if symlinks and os.path.islink(srcname): + linkto = os.readlink(srcname) + os.symlink(linkto, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks) + else: + shutil.copy2(srcname, dstname) + # XXX What about devices, sockets etc.? + except (IOError, os.error), why: + errors.append((srcname, dstname, why)) + if errors: + raise Error, errors + + + def writefile(filename, contents): """Write contents to filename in an atomic way.