teichmann@542: #!/usr/bin/env python teichmann@542: # -*- coding: UTF-8 -*- teichmann@542: # teichmann@542: # Copyright (C) 2011 by Intevation GmbH teichmann@542: # Authors: teichmann@542: # Sascha L. Teichmann teichmann@542: # teichmann@542: # This program is free software under the GPL (>=v2) teichmann@542: # Read the file COPYING coming with the software for details. teichmann@542: teichmann@542: import os teichmann@542: import re teichmann@542: import sys teichmann@542: import subprocess teichmann@542: import logging teichmann@542: import traceback teichmann@542: teichmann@542: from optparse import OptionParser teichmann@542: from shutil import copyfile teichmann@542: teichmann@542: log = logging.getLogger(__name__) teichmann@542: log.setLevel(logging.WARNING) teichmann@542: log.addHandler(logging.StreamHandler(sys.stderr)) teichmann@542: teichmann@542: SAEGEWERKER = "saegewerker" teichmann@542: teichmann@542: FIELD = re.compile("([a-zA-Z]+):\s*(.+)") teichmann@542: teichmann@542: teichmann@542: class DebCmp(object): teichmann@542: """Helper class to make deb files comparable teichmann@542: by there versions. teichmann@542: """ teichmann@542: teichmann@542: def __init__(self, version, path): teichmann@542: self.version = version teichmann@542: self.path = path teichmann@542: teichmann@542: def __cmp__(self, other): teichmann@542: if self.version == other.version: teichmann@542: return 0 teichmann@542: if (subprocess.call([ teichmann@542: "dpkg", "--compare-versions", teichmann@542: self.version, "gt", other.version]) == 0): teichmann@542: return +1 teichmann@542: if (subprocess.call([ teichmann@542: "dpkg", "--compare-versions", teichmann@542: self.version, "lt", other.version]) == 0): teichmann@542: return -1 teichmann@542: return 0 teichmann@542: teichmann@542: def __str__(self): teichmann@542: return "version: %s / path: %s" % ( teichmann@542: self.version, teichmann@542: self.path) teichmann@542: teichmann@542: def deb_info(deb, fields=["Package", "Version"]): teichmann@542: """Extract some meta info from a deb file.""" teichmann@542: po = subprocess.Popen( teichmann@542: ["dpkg-deb", "-f", deb] + fields, teichmann@542: stdout=subprocess.PIPE) teichmann@542: out = po.communicate()[0] teichmann@542: return dict([m.groups() teichmann@542: for m in map(FIELD.match, out.splitlines()) if m]) teichmann@542: teichmann@542: teichmann@542: def copy_pkgs(src, dst, options): teichmann@542: teichmann@542: archs = {} teichmann@542: teichmann@542: for arch in os.listdir(src): teichmann@542: if arch == 'source': continue teichmann@542: arch_dir = os.path.join(src, arch) teichmann@542: if not os.path.isdir(arch_dir): continue teichmann@542: log.debug("found arch: '%s'" % arch) teichmann@542: teichmann@542: tracks = {} teichmann@542: teichmann@542: for track in os.listdir(arch_dir): teichmann@542: track_dir = os.path.join(arch_dir, track) teichmann@542: if not os.path.isdir(track_dir): continue teichmann@542: teichmann@542: packages = {} teichmann@542: teichmann@542: log.debug("track dir: '%s'" % track_dir) teichmann@542: for f in os.listdir(track_dir): teichmann@542: if not f.endswith(".deb"): continue teichmann@542: deb_path = os.path.join(track_dir, f) teichmann@542: if not os.path.isfile(deb_path): continue teichmann@542: teichmann@542: info = deb_info(deb_path) teichmann@542: deb_cmp = DebCmp(info['Version'], deb_path) teichmann@542: teichmann@542: packages.setdefault(info['Package'], []).append(deb_cmp) teichmann@542: teichmann@542: tracks[track] =[max(debs) for debs in packages.itervalues()] teichmann@542: teichmann@542: archs[arch] = tracks teichmann@542: teichmann@542: copy = options.no_hardlinks and copyfile or os.link teichmann@542: action = options.no_hardlinks and "copy" or "link" teichmann@542: teichmann@542: for arch, tracks in archs.iteritems(): teichmann@542: log.debug("writing arch '%s'" % arch) teichmann@542: for track, debs in tracks.iteritems(): teichmann@542: log.debug(" writing track '%s'" % track) teichmann@542: dst_dir = os.path.join(dst, arch, track) teichmann@542: if not os.path.exists(dst_dir): teichmann@542: try: teichmann@542: os.makedirs(dst_dir) teichmann@542: except: teichmann@542: log.warn(traceback.format_exc()) teichmann@542: continue teichmann@542: teichmann@542: for deb in debs: teichmann@542: src_path = deb.path teichmann@542: dst_path = os.path.join(dst_dir, os.path.basename(src_path)) teichmann@542: log.info(" %s '%s' -> '%s'" % (action, src_path, dst_path)) teichmann@542: if os.path.isfile(dst_path): teichmann@542: try: os.remove(dst_path) teichmann@542: except: log.warn(traceback.format_exc()); continue teichmann@542: try: copy(src_path, dst_path) teichmann@542: except: log.warn(traceback.format_exc()) teichmann@542: teichmann@542: teichmann@542: def main(): teichmann@542: usage = "usage: %prog [options] src-dir dst-dir" teichmann@542: parser = OptionParser(usage=usage) teichmann@542: parser.add_option( teichmann@542: "-v", "--verbose", action="store_true", teichmann@542: dest="verbose", teichmann@542: help="verbose output") teichmann@542: parser.add_option( teichmann@542: "-d", "--dry-run", action="store_true", teichmann@542: dest="dry_run", default=False, teichmann@542: help="don't copy the deb files") teichmann@542: parser.add_option( teichmann@542: "-n", "--no-saegewerker", action="store_true", teichmann@542: dest="no_saegewerker", default=False, teichmann@542: help="Don't force run as '%s'" % SAEGEWERKER) teichmann@542: parser.add_option( teichmann@542: "-l", "--no-hardlinks", action="store_false", teichmann@542: dest="no_hardlinks", default=False, teichmann@542: help="copy files instead of hard linking") teichmann@542: teichmann@542: options, args = parser.parse_args() teichmann@542: teichmann@542: if len(args) < 2: teichmann@542: log.error("need at least two arguments") teichmann@542: sys.exit(1) teichmann@542: teichmann@542: src, dst = args[0], args[1] teichmann@542: teichmann@542: for d in (src, dst): teichmann@542: if not os.path.isdir(d): teichmann@542: log.error("'%s' is not a directory." % d) teichmann@542: sys.exit(1) teichmann@542: teichmann@542: if options.verbose: log.setLevel(logging.INFO) teichmann@542: teichmann@542: if not options.no_saegewerker and os.environ['USER'] != SAEGEWERKER: teichmann@542: log.error("Need to run as '%s'" % SAEGEWERKER) teichmann@542: sys.exit(1) teichmann@542: teichmann@542: copy_pkgs(src, dst, options) teichmann@542: teichmann@542: if __name__ == '__main__': teichmann@542: main()