bh@134: # Copyright (C) 2007, 2008 by Intevation GmbH bh@134: # Authors: bh@134: # Bernhard Herzog bh@134: # bh@134: # This program is free software under the GPL (>=v2) bh@134: # Read the file COPYING coming with the software for details. bh@134: bh@134: """Base classes for all gnupg packagers""" bh@134: bh@134: import os bh@134: import re bh@134: import inspect bh@134: import new bh@134: bh@134: import treepkg.util bh@134: import treepkg.packager bh@134: import treepkg.run as run bh@134: from treepkg.cmdexpand import cmdexpand bh@134: bh@134: bh@134: class BaseSourcePackager(treepkg.packager.SourcePackager): bh@134: bh@134: def orig_source_version(self, directory): bh@134: """Determines the version from the configure.ac file in directory""" bh@134: filename = os.path.join(directory, "configure.ac") bh@134: for line in open(filename): bh@134: bh@134: # Matches lines like bh@134: # m4_define(my_version, [1.1.7]) bh@134: # used by most of the gnupg packages bh@197: match = re.match(r"m4_define\(\[?my_version\]?, \[([^]]+)\]\)", bh@134: line) bh@134: if match: bh@134: return match.group(1) bh@134: bh@134: # Matches lines like. bh@134: # AC_INIT(pinentry, 0.7.6-cvs, [gnupg-devel@gnupg.org]) bh@134: # pinentry is the GnuPG package that actually needs this bh@134: match = re.match(r"AC_INIT\([a-zA-Z_]+, ([0-9.]+)", line) bh@134: if match: bh@134: return match.group(1) bh@134: bh@134: raise RuntimeError("Could not determine the version number from %s" bh@134: % filename) bh@134: bh@134: def determine_package_version(self, directory): aheinecke@327: return "%s+svn%s" % (self.orig_source_version(directory), self.revision) bh@134: bh@134: def do_package(self): bh@134: pkgbaseversion, pkgbasedir = self.export_sources() bh@134: bh@134: run.call(cmdexpand("/bin/sh autogen.sh"), cwd=pkgbasedir, bh@134: suppress_output=True) bh@134: orig_version = self.orig_source_version(pkgbasedir) bh@134: bh@134: # patch the version number in the newly generated configure bh@134: # file. autogen.sh normally determines it from svn, but here it bh@134: # ran on a copy that did not include the .svn subdirectories and bh@134: # thus could not find the svn revision. bh@134: treepkg.util.replace_in_file(os.path.join(pkgbasedir, "configure"), bh@134: re.escape(orig_version) + "-svn0", aheinecke@327: orig_version + "-svn%s" % self.revision) bh@134: bh@134: pkgbasename = self.pkg_basename + "_" + pkgbaseversion bh@134: origtargz = os.path.join(self.work_dir, bh@134: pkgbasename + ".orig.tar.gz") bh@134: self.create_tarball(origtargz, self.work_dir, bh@134: os.path.basename(pkgbasedir)) bh@134: bricks@554: changemsg = ("Update to rev. %s" % (self.revision,)) bh@134: self.copy_debian_directory(pkgbasedir, pkgbaseversion, bh@134: changemsg) bh@134: bh@134: self.create_source_package(pkgbasedir, origtargz) bh@134: self.move_source_package(pkgbasename) bh@134: bh@134: bh@134: class SmartSourcePackager(BaseSourcePackager): bh@134: bh@134: """SourcePackager that uses pbuilder to create the source tarball. bh@134: bh@134: We try to create source tarballs that are as close to the tarballs bh@206: created by the upstream maintainers as possible. For the gnupg bh@206: software this means we need to run 'make dist' in a configured SVN bh@206: working copy with some additional software installed like autoconf bh@206: and texinfo. We want to avoid running code from a working copy bh@206: outside of the pbuilder environment and having to install recipe bh@206: specific additional software packages in the treepkg host system. bh@206: Therefore we create the source tarball using 'pbuilder execute' with bh@206: a script. bh@134: """ bh@134: bh@134: createtarball_script = """\ bh@134: #! /bin/bash bh@134: set -e bh@134: bh@134: apt-get --assume-yes --force-yes install %(builddeps)s bh@134: bh@134: # copy the source tree to a directory that's under pbuilder control so bh@134: # that it gets removed along with the build environment. Otherwise we bh@134: # end up with a directory containing files that cannot be removed by bh@134: # treepkg bh@134: workdir=/tmp/work bh@134: cp -a %(basedir)s $workdir bh@134: cd $workdir bh@134: bh@134: # tweak autoconf settings so that make dist produces a tar.gz, not a bh@134: # tar.bz2. Removing all options whose names contain 'dist' should bh@134: # achieve that. bh@134: cp Makefile.am Makefile.am.orig bh@134: sed -e '/AUTOMAKE_OPTIONS/ s/[a-zA-Z0-9-]*dist[a-zA-Z0-9-]*//g' \ bh@134: Makefile.am.orig > Makefile.am bh@134: bh@134: ./autogen.sh bh@134: ./configure --enable-maintainer-mode bh@134: bh@134: # revert autoconf changes, so that the original Makefile.am ends up in bh@134: # the tarball bh@134: mv Makefile.am.orig Makefile.am bh@134: bh@134: %(make_dist_command)s bh@134: bh@134: mv *.tar.gz %(origtargz)s bh@134: """ bh@134: bh@134: make_dist_command = "make dist" bh@134: bh@134: def __init__(self, *args): bh@134: super(SmartSourcePackager, self).__init__(*args) bh@134: self.pkgbasename = None bh@134: self.pkgbaseversion = None bh@134: self.origtargz = None bricks@554: origbuilder = self.track.builder bricks@554: self.builder = PBuilder(origbuilder.builderconfig, bricks@554: origbuilder.root_cmd, bricks@554: origbuilder.release_signing_keyid) bh@134: bh@134: def copy_workingcopy(self, dest): bh@134: treepkg.util.copytree(self.track.checkout_dir, dest) bh@134: bh@134: def create_original_tarball(self): bh@134: copied_working_copy = os.path.join(self.work_dir, "copied_working_copy") bh@134: self.copy_workingcopy(copied_working_copy) bh@134: bh@134: self.pkgbaseversion = \ bh@134: self.determine_package_version(copied_working_copy) bh@134: self.pkgbasename = self.pkg_basename + "_" + self.pkgbaseversion bh@134: self.origtargz = os.path.join(self.work_dir, bh@134: self.pkgbasename + ".orig.tar.gz") bh@134: bh@134: script = (self.createtarball_script bh@134: % dict(builddeps=" ".join(self.track.dependencies_required() bh@134: | self.tarball_dependencies), bh@134: basedir=copied_working_copy, bh@134: origtargz=self.origtargz, bh@134: make_dist_command=self.make_dist_command)) bh@134: script_name = os.path.join(self.work_dir, "createtarball") bh@134: treepkg.util.writefile(script_name, script, 0755) bh@134: bh@134: treepkg.util.ensure_directory(self.src_dir) bh@137: treepkg.util.ensure_directory(self.log_dir) bricks@554: self.builder.run_script([script_name], bh@137: logfile=os.path.join(self.log_dir, bh@137: "tarball_log.txt"), bh@134: bindmounts=[self.work_dir, self.src_dir]) bh@134: bh@134: def create_orig_dir(self): bh@134: """Unpacks the tarball created by create_original_tarball into work_dir bh@134: """ bh@134: unpack_dir = os.path.join(self.work_dir, "unpack") bh@134: treepkg.util.ensure_directory(unpack_dir) bh@134: run.call(cmdexpand("tar xzf $origtargz -C $unpack_dir", bh@134: unpack_dir=unpack_dir, origtargz=self.origtargz)) bh@134: unpacked_files = treepkg.util.listdir_abs(unpack_dir) bh@134: if len(unpacked_files) != 1: bh@134: raise RuntimeError("%s should have extracted to a single directory", bh@134: origtargz) bh@134: unpacked_dir = unpacked_files[0] bh@134: bh@134: orig_dir = os.path.join(self.work_dir, os.path.basename(unpacked_dir)) bh@134: os.rename(unpacked_dir, orig_dir) bh@134: return orig_dir bh@134: bh@134: def do_package(self): bh@134: self.create_original_tarball() bh@134: orig_dir = self.create_orig_dir() bh@134: bricks@554: changemsg = ("Update to rev. %s" % (self.revision,)) bh@134: self.copy_debian_directory(orig_dir, self.pkgbaseversion, changemsg) bh@134: bh@134: self.create_source_package(orig_dir, self.origtargz) bh@134: self.move_source_package(self.pkgbasename) bh@134: bh@134: bh@134: def define_gnupg_packager(pkg_basename, bh@134: tarball_dependencies=("autoconf", "automake", bh@134: "texinfo", "subversion"), bh@134: make_dist_command=None): bh@134: """Create a SourcePackager for a GnuPG package in the caller's globals(). bh@134: This is a helper function for the modules in the recipe.gnupg package. bh@134: """ bh@134: base_class = BaseSourcePackager bh@134: class_attributes = dict(pkg_basename=pkg_basename) bh@134: if tarball_dependencies is not None: bh@134: base_class = SmartSourcePackager bh@134: class_attributes["tarball_dependencies"] = set(tarball_dependencies) bh@134: if make_dist_command is not None: bh@134: base_class = SmartSourcePackager bh@134: class_attributes["make_dist_command"] = make_dist_command bh@134: bh@134: caller_globals = inspect.currentframe().f_back.f_globals bh@134: caller_globals["SourcePackager"] = new.classobj("SourcePackager", bh@134: (base_class,), bh@134: class_attributes)