#!/usr/bin/env python import os, os.path, re, sys from warnings import warn from debian_linux.patches import PatchSeries _default_home = "@home@" _default_revisions = "@revisions@" _default_source = "@source@" class MatchExtra(object): def __init__(self, arch, featureset): self.arch, self.featureset = arch, featureset self.matched_arch = self.matched_featureset = False def __call__(self, obj): data = obj.data match_arch = [] match_featureset = [] for i in data: if i.startswith("arch="): match_arch.append(i[5:]) elif i.startswith("featureset="): match_featureset.append(i[11:]) ret_arch = ret_featureset = False if self.arch is not None: if match_arch: if self.arch in match_arch: self.matched_arch = True ret_arch = True else: ret_arch = True if not match_featureset: ret_featureset = ret_arch if self.featureset is not None: if match_featureset: if self.featureset in match_featureset: self.matched_featureset = True ret_featureset = True else: ret_featureset = True return ret_arch and ret_featureset def __nonzero__(self): return self.arch is not None or self.featureset is not None def info(self): ret = [] if self.matched_arch: ret.append("arch=%s" % self.arch) if self.matched_featureset: ret.append("featureset=%s" % self.featureset) return ret class SeriesList(list): def __call__(self, cond = bool, reverse = False): for i in self: i(cond = cond, reverse = reverse) if reverse: print "--> %s fully unapplied." % i.name else: print "--> %s fully applied." % i.name @classmethod def read(cls, revisions, home): ret = cls() for i in revisions: try: fp = file(os.path.join(home, 'series', i)) ret.append(PatchSeries(i, home, fp)) except IOError: pass return ret @classmethod def read_extra(cls, revisions, home): return cls.read((i + '-extra' for i in revisions), home) class version(object): __slots__ = "upstream", "revision" def __init__(self, string = None): if string is not None: self.upstream, self.revision = self.parse(string) def __str__(self): return "%s-%s" % (self.upstream, self.revision) _re = r""" ^ ( \d+\.\d+\.\d+ (?: -.+? )? ) - ([^-]+) $ """ def parse(self, version): match = re.match(self._re, version, re.X) if match is None: raise ValueError return match.groups() class version_file(object): _file = 'version.Debian' extra = None in_progress = False def __init__(self, ver = None, overwrite = False): if overwrite: self._read(ver) elif os.path.exists(self._file): s = file(self._file).readline().strip() self._read(s) elif ver: warn('No %s file, assuming pristine Linux %s' % (self._file, ver.upstream)) self.version = version() self.version.upstream = ver.upstream self.version.revision = '0' else: raise RuntimeError, "Not possible to determine version" def __str__(self): if self.in_progress: return "unstable" if self.extra is not None: return ' '.join([str(self.version)] + self.extra.info()) return str(self.version) def _read(self, s): list = s.split() try: self.version = version(list[0]) except ValueError: raise RuntimeError, 'Can\'t read version in %s: "%s"' % (self._file, list[0]) arch = featureset = None for i in list[1:]: if i.startswith("arch="): arch = i[5:] elif i.startswith("featureset="): featureset = i[11:] else: raise RuntimeError("Can't parse extra information") self.extra = MatchExtra(arch, featureset) def _write(self): if os.path.lexists(self._file): os.unlink(self._file) file(self._file, 'w').write('%s\n' % self) def begin(self): self.in_progress = True self._write() def commit(self, version = None, extra = None): self.in_progress = False if version is not None: self.version = version if extra is not None: self.extra = extra self._write() def main(): options, args = parse_options() if len(args) > 1: print "Too much arguments" return home = options.home revisions = ['0'] + options.revisions.split() source = version(options.source) if len(args) == 1: target = version(args[0]) else: target = source if options.current is not None: vfile = version_file(options.current, True) else: vfile = version_file(source) current = vfile.version current_extra = vfile.extra target_extra = MatchExtra(options.arch, options.subarch) if current.revision not in revisions: raise RuntimeError, "Current revision is not in our list of revisions" if target.revision not in revisions: raise RuntimeError, "Target revision is not in our list of revisions" if current.revision == target.revision and current_extra == target_extra: print "Nothing to do" return current_index = revisions.index(current.revision) source_index = revisions.index(source.revision) target_index = revisions.index(target.revision) if current_extra: if current_index != source_index: raise RuntimeError, "Can't patch from %s with options %s" % (current, ' '.join(current_extra)) consider = revisions[1:current_index + 1] s = SeriesList.read_extra(consider, home) vfile.begin() s(cond = current_extra, reverse = True) vfile.commit(current) if current_index < target_index: consider = revisions[current_index + 1:target_index + 1] s = SeriesList.read(consider, home) vfile.begin() s() vfile.commit(target) elif current_index > target_index: consider = revisions[current_index + 1:target_index + 1] s = SeriesList.read(consider, home) vfile.begin() s(reverse = True) vfile.commit(target) if target_extra: consider = revisions[1:target_index + 1] s = SeriesList.read_extra(consider, home) vfile.begin() s(cond = target_extra) vfile.commit(target, target_extra) def parse_options(): from optparse import OptionParser parser = OptionParser( usage = "%prog [OPTION]... [TARGET]", ) parser.add_option( '-a', '--arch', dest = 'arch', help = "arch", ) parser.add_option( '-f', '--flavour', dest = 'flavour', help = "flavour", ) parser.add_option( '-s', '--subarch', dest = 'subarch', help = "subarch", ) parser.add_option( '-C', '--overwrite-current', dest = 'current', help = "overwrite current", ) parser.add_option( '-H', '--overwrite-home', default = _default_home, dest = 'home', help = "overwrite home [default: %default]", ) parser.add_option( '-R', '--overwrite-revisions', default = _default_revisions, dest = 'revisions', help = "overwrite revisions [default: %default]", ) parser.add_option( '-S', '--overwrite-source', default = _default_source, dest = 'source', help = "overwrite source [default: %default]", ) options, args = parser.parse_args() if options.arch is None and options.subarch is not None: raise RuntimeError('You specified a subarch without an arch, this is not really working') if options.subarch is None and options.flavour is not None: raise RuntimeError('You specified a flavour without a subarch, this is not really working') return options, args if __name__ == '__main__': def showwarning(message, category, filename, lineno): sys.stderr.write("Warning: %s\n" % message) import warnings warnings.showwarning = showwarning try: main() except RuntimeError, e: sys.stderr.write("Error: %s\n" % e) raise SystemExit, 1