# vim: set fileencoding=utf-8 : # # (C) 2017 Guido Günther # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, please see # import os import re from gbp.pkg.archive import Archive from gbp.format import format_str class PkgPolicy(object): """ Common helpers for packaging policy. """ version_mangle_re = (r'%\(version' r'%(?P[^%])' r'%(?P([^%]|\\%))+' r'\)s') packagename_re = None packagename_msg = None upstreamversion_re = None upstreamversion_msg = None @classmethod def is_valid_packagename(cls, name): """ Is this a valid package name? >>> PkgPolicy.is_valid_packagename('doesnotmatter') Traceback (most recent call last): ... NotImplementedError: Class needs to provide packagename_re """ if cls.packagename_re is None: raise NotImplementedError("Class needs to provide packagename_re") return True if cls.packagename_re.match(name) else False @classmethod def is_valid_upstreamversion(cls, version): """ Is this a valid upstream version number? >>> PkgPolicy.is_valid_upstreamversion('doesnotmatter') Traceback (most recent call last): ... NotImplementedError: Class needs to provide upstreamversion_re """ if cls.upstreamversion_re is None: raise NotImplementedError("Class needs to provide upstreamversion_re") return True if cls.upstreamversion_re.match(version) else False @staticmethod def guess_upstream_src_version(filename, extra_regex=r''): """ Guess the package name and version from the filename of an upstream archive. @param filename: filename (archive or directory) from which to guess @type filename: C{string} @param extra_regex: additional regex to apply, needs a 'package' and a 'version' group @return: (package name, version) or ('', '') @rtype: tuple >>> PkgPolicy.guess_upstream_src_version('foo-bar_0.2.orig.tar.gz') ('foo-bar', '0.2') >>> PkgPolicy.guess_upstream_src_version('foo-Bar_0.2.orig.tar.gz') ('', '') >>> PkgPolicy.guess_upstream_src_version('git-bar-0.2.tar.gz') ('git-bar', '0.2') >>> PkgPolicy.guess_upstream_src_version('git-bar-0.2-rc1.tar.gz') ('git-bar', '0.2-rc1') >>> PkgPolicy.guess_upstream_src_version('git-bar-0.2:~-rc1.tar.gz') ('git-bar', '0.2:~-rc1') >>> PkgPolicy.guess_upstream_src_version('git-Bar-0A2d:rc1.tar.bz2') ('git-Bar', '0A2d:rc1') >>> PkgPolicy.guess_upstream_src_version('git-1.tar.bz2') ('git', '1') >>> PkgPolicy.guess_upstream_src_version('kvm_87+dfsg.orig.tar.gz') ('kvm', '87+dfsg') >>> PkgPolicy.guess_upstream_src_version('foo-Bar-a.b.tar.gz') ('', '') >>> PkgPolicy.guess_upstream_src_version('foo-bar_0.2.orig.tar.xz') ('foo-bar', '0.2') >>> PkgPolicy.guess_upstream_src_version('foo-bar_0.2.orig.tar.lzma') ('foo-bar', '0.2') >>> PkgPolicy.guess_upstream_src_version('foo-bar-0.2.zip') ('foo-bar', '0.2') >>> PkgPolicy.guess_upstream_src_version('foo-bar-0.2.tlz') ('foo-bar', '0.2') >>> PkgPolicy.guess_upstream_src_version('foo-bar_0.2.tar.gz') ('foo-bar', '0.2') """ version_chars = r'[a-zA-Z\d\.\~\-\:\+]' basename = Archive.parse_filename(os.path.basename(filename))[0] version_filters = map( lambda x: x % version_chars, ( # Debian upstream tarball: package_'.orig.tar.gz' r'^(?P[a-z\d\.\+\-]+)_(?P%s+)\.orig', # Debian native: 'package_.tar.gz' r'^(?P[a-z\d\.\+\-]+)_(?P%s+)', # Upstream 'package-.tar.gz' # or directory 'package-': r'^(?P[a-zA-Z\d\.\+\-]+)(-)(?P[0-9]%s*)')) if extra_regex: version_filters = extra_regex + version_filters for filter in version_filters: m = re.match(filter, basename) if m: return (m.group('package'), m.group('version')) return ('', '') @staticmethod def has_origs(orig_files, dir): "Check orig tarball and additional tarballs exists in dir" for o in orig_files: if not os.path.exists(os.path.join(dir, o)): return False return True @classmethod def has_orig(cls, orig_file, dir): return cls.has_origs([orig_file], dir) @staticmethod def symlink_origs(orig_files, orig_dir, output_dir, force=False): """ symlink orig tarball from orig_dir to output_dir @return: [] if all links were created, list of failed links otherwise """ orig_dir = os.path.abspath(orig_dir) output_dir = os.path.abspath(output_dir) err = [] if orig_dir == output_dir: return [] for f in orig_files: src = os.path.join(orig_dir, f) dst = os.path.join(output_dir, f) if not os.access(src, os.F_OK): err.append(f) continue try: if os.path.lexists(dst) and force: os.unlink(dst) os.symlink(src, dst) except OSError: err.append(f) return err @classmethod def symlink_orig(cls, orig_file, orig_dir, output_dir, force=False): return cls.symlink_origs([orig_file], orig_dir, output_dir, force=force) @classmethod def version_subst(cls, format, version, sanitizer=lambda arg: arg): """Generate a string from a given format and a version. The extracted version can be passed through the sanitizer function argument before being formatted into a string. %(version)s provides a clean version. %(hversion)s provides the same thing, but with '.' replaced with '-'. hversion is useful for upstreams with tagging policies that prohibit . characters. %(version%A%B)s provides %(version)s with string 'A' replaced by 'B'. This way, simple version mangling is possible via substitution. Inside the substition string, '%' needs to be escaped. See the examples below. >>> PkgPolicy.version_subst("debian/%(version)s", "0:0~0") 'debian/0:0~0' >>> PkgPolicy.version_subst("libfoo-%(hversion)s", "1.8.1") 'libfoo-1-8-1' >>> PkgPolicy.version_subst("v%(version%.%_)s", "1.2.3") 'v1_2_3' >>> PkgPolicy.version_subst(r'%(version%-%\\%)s', "0-1.2.3") '0%1.2.3' """ r = re.search(cls.version_mangle_re, format) if r: format = re.sub(cls.version_mangle_re, "%(version)s", format) version = version.replace(r.group('M'), r.group('R').replace(r'\%', '%')) return format_str(format, dict(version=sanitizer(version), hversion=sanitizer(version).replace('.', '-')))