changeset 4:fee641fec94e

Separate the kolab specific parts.
author Bernhard Herzog <bh@intevation.de>
date Fri, 09 Mar 2007 12:26:01 +0100
parents 7e9db903ba16
children 5ea8e9b67395
files enterprise/__init__.py enterprise/kdepim.py runtreepkg.py treepkg/packager.py treepkg/readconfig.py treepkg/run.py treepkg/util.py
diffstat 6 files changed, 248 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- /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 <bh@intevation.de>
+#
+# 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"]
--- 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()
 
--- 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
+
+          <pkg_basename>-<version>
+
+        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):
 
--- 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
--- 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)
--- 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.
 
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)