diff options
author | Guido Günther <agx@sigxcpu.org> | 2009-08-22 14:08:29 +0200 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2009-08-23 17:28:41 +0200 |
commit | 715d42a8dbbe0f78076bd5ab486b9c933d4f35e7 (patch) | |
tree | 8b640090f13e316f39f1d73333d75e9fd46c7f97 /gbp/deb.py | |
parent | 29cacc0286b1bd0af964fea71a889f44525f838f (diff) |
drop superfluous _utils from module names
Diffstat (limited to 'gbp/deb.py')
-rw-r--r-- | gbp/deb.py | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/gbp/deb.py b/gbp/deb.py new file mode 100644 index 0000000..6a60f9b --- /dev/null +++ b/gbp/deb.py @@ -0,0 +1,270 @@ +# vim: set fileencoding=utf-8 : +# +# (C) 2006,2007 Guido Guenther <agx@sigxcpu.org> +"""provides some debian source package related helpers""" + +import commands +import email +import os +import re +import shutil +import subprocess +import sys +import glob +import command_wrappers as gbpc +from errors import GbpError + +# When trying to parse a version-number from a dsc or changes file, these are +# the valid characters. +debian_version_chars = 'a-zA-Z\d.~+-' + +class NoChangelogError(Exception): + """no changelog found""" + pass + +class ParseChangeLogError(Exception): + """problem parsing changelog""" + pass + +class DscFile(object): + """Keeps all needed data read from a dscfile""" + pkg_re = re.compile('Source:\s+(?P<pkg>.+)\s*') + version_re = re.compile("Version:\s((?P<epoch>\d+)\:)?(?P<version>[%s]+)\s*$" % debian_version_chars) + tar_re = re.compile('^\s\w+\s\d+\s+(?P<tar>[^_]+_[^_]+(\.orig)?\.tar\.(gz|bz2))') + diff_re = re.compile('^\s\w+\s\d+\s+(?P<diff>[^_]+_[^_]+\.diff.(gz|bz2))') + + def __init__(self, dscfile): + self.pkg = "" + self.tgz = "" + self.diff = "" + self.debian_version = "" + self.upstream_version = "" + self.native = False + self.dscfile = os.path.abspath(dscfile) + + f = file(self.dscfile) + fromdir = os.path.dirname(os.path.abspath(dscfile)) + for line in f: + m = self.version_re.match(line) + if m and not self.upstream_version: + if '-' in m.group('version'): + self.debian_version = m.group('version').split("-")[-1] + self.upstream_version = "-".join(m.group('version').split("-")[0:-1]) + self.native = False + else: + self.native = True # Debian native package + self.upstream_version = m.group('version') + if m.group('epoch'): + self.epoch = m.group('epoch') + else: + self.epoch = "" + continue + m = self.pkg_re.match(line) + if m: + self.pkg = m.group('pkg') + continue + m = self.tar_re.match(line) + if m: + self.tgz = os.path.join(fromdir, m.group('tar')) + continue + m = self.diff_re.match(line) + if m: + self.diff = os.path.join(fromdir, m.group('diff')) + continue + f.close() + + if not self.pkg: + raise GbpError, "Cannot parse package name from '%s'" % self.dscfile + elif not self.tgz: + raise GbpError, "Cannot parse archive name from '%s'" % self.dscfile + if not self.upstream_version: + raise GbpError, "Cannot parse version number from '%s'" % self.dscfile + if not self.native and not self.debian_version: + raise GbpError, "Cannot parse Debian version number from '%s'" % self.dscfile + + def _get_version(self): + version = [ "", self.epoch + ":" ][len(self.epoch) > 0] + if self.native: + version += self.upstream_version + else: + version += "%s-%s" % (self.upstream_version, self.debian_version) + return version + + version = property(_get_version) + + def __str__(self): + return "<%s object %s>" % (self.__class__.__name__, self.dscfile) + + +def parse_dsc(dscfile): + """parse dsc by creating a DscFile object""" + try: + dsc = DscFile(dscfile) + except IOError, err: + raise GbpError, "Error reading dsc file: %s" % err + + return dsc + + +def parse_changelog(changelog): + """ + parse changelog file changelog + + cp['Version']: full version string including epoch + cp['Upstream-Version']: upstream version, if not debian native + cp['Debian-Version']: debian release + cp['Epoch']: epoch, if any + cp['NoEpoch-Version']: full version string excluding epoch + """ + if not os.access(changelog, os.F_OK): + raise NoChangelogError, "Changelog %s not found" % (changelog, ) + status, output = commands.getstatusoutput('dpkg-parsechangelog -l%s' % (changelog, )) + if status: + raise ParseChangeLogError, output + cp = email.message_from_string(output) + try: + if ':' in cp['Version']: + cp['Epoch'], cp['NoEpoch-Version'] = cp['Version'].split(':', 1) + else: + cp['NoEpoch-Version'] = cp['Version'] + if '-' in cp['NoEpoch-Version']: + cp['Upstream-Version'], cp['Debian-Version'] = cp['NoEpoch-Version'].rsplit('-', 1) + else: + cp['Debian-Version'] = cp['NoEpoch-Version'] + except TypeError: + raise ParseChangeLogError, output.split('\n')[0] + return cp + + +def orig_file(cp): + "The name of the orig.tar.gz belonging to changelog cp" + return "%s_%s.orig.tar.gz" % (cp['Source'], cp['Upstream-Version']) + + +def is_native(cp): + "Is this a debian native package" + return [ True, False ]['-' in cp['Version']] + + +def has_epoch(cp): + """does the topmost version number contain an epoch""" + try: + if cp['Epoch']: + return True + except KeyError: + return False + +def has_orig(cp, dir): + "Check if orig.tar.gz exists in dir" + try: + os.stat( os.path.join(dir, orig_file(cp)) ) + except OSError: + return False + return True + +def symlink_orig(cp, orig_dir, output_dir, force=False): + """ + symlink orig.tar.gz from orig_dir to output_dir + @return: True if link was created or src == dst + False in case of error or src doesn't exist + """ + orig_dir = os.path.abspath(orig_dir) + output_dir = os.path.abspath(output_dir) + + if orig_dir == output_dir: + return True + + src = os.path.join(orig_dir, orig_file(cp)) + dst = os.path.join(output_dir, orig_file(cp)) + if not os.access(src, os.F_OK): + return False + try: + if os.access(dst, os.F_OK) and force: + os.unlink(dst) + os.symlink(src, dst) + except OSError: + return False + return True + +def unpack_orig(archive, tmpdir, filters): + """ + unpack a .orig.tar.gz to tmpdir, leave the cleanup to the caller in case of + an error + """ + try: + unpackArchive = gbpc.UnpackTarArchive(archive, tmpdir, filters) + unpackArchive() + except gbpc.CommandExecFailed: + print >>sys.stderr, "Unpacking of %s failed" % archive + raise GbpError + return unpackArchive.dir + +def repack_orig(archive, tmpdir, dest): + """ + recreate a new .orig.tar.gz from tmpdir (useful when using filter option) + """ + try: + repackArchive = gbpc.RepackTarArchive(archive, tmpdir, dest) + repackArchive() + except gbpc.CommandExecFailed: + print >>sys.stderr, "Failed to create %s" % archive + raise GbpError + return repackArchive.dir + +def tar_toplevel(dir): + """tar archives can contain a leading directory not""" + unpacked = glob.glob('%s/*' % dir) + unpacked.extend(glob.glob("%s/.*" % dir)) # include hidden files and folders + # Check that dir contains nothing but a single folder: + if len(unpacked) == 1 and os.path.isdir(unpacked[0]): + return unpacked[0] + else: + return dir + + +def get_arch(): + pipe = subprocess.Popen(["dpkg", "--print-architecture"], shell=False, stdout=subprocess.PIPE) + arch = pipe.stdout.readline().strip() + return arch + + +def guess_upstream_version(archive, version_regex=r''): + """ + guess the version from the filename of an upstgream archive + @archive: filename to guess to version for + @version_regex: additional version regex to apply, needs a 'version' group + + >>> guess_upstream_version('foo-bar_0.2.orig.tar.gz') + '0.2' + >>> guess_upstream_version('foo-Bar_0.2.orig.tar.gz') + >>> guess_upstream_version('git-bar-0.2.tar.gz') + '0.2' + >>> guess_upstream_version('git-bar-0.2-rc1.tar.gz') + '0.2-rc1' + >>> guess_upstream_version('git-bar-0.2:~-rc1.tar.gz') + '0.2:~-rc1' + >>> guess_upstream_version('git-Bar-0A2d:rc1.tar.bz2') + '0A2d:rc1' + >>> guess_upstream_version('git-1.tar.bz2') + '1' + >>> guess_upstream_version('kvm_87+dfsg.orig.tar.gz') + '87+dfsg' + >>> guess_upstream_version('foo-Bar_0.2.orig.tar.gz') + >>> guess_upstream_version('foo-Bar-a.b.tar.gz') + """ + version_chars = r'[a-zA-Z\d\.\~\-\:\+]' + extensions = r'\.tar\.(gz|bz2)' + + version_filters = map ( lambda x: x % (version_chars, extensions), + ( # Debian package_<version>.orig.tar.gz: + r'^[a-z\d\.\+\-]+_(?P<version>%s+)\.orig%s', + # Upstream package-<version>.tar.gz: + r'^[a-zA-Z\d\.\+\-]+-(?P<version>[0-9]%s*)%s')) + if version_regex: + version_filters = version_regex + version_filters + for filter in version_filters: + m = re.match(filter, os.path.basename(archive)) + if m: + return m.group('version') + +# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: |