#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2010 Per Øyvind Karlsen # # This program is free software. It may be redistributed and/or modified under # the terms of the LGPL version 2.1 (or later). # # RPM5 python (egg) dependency generator. # from getopt import getopt from os.path import basename, dirname, isdir, sep, splitext from sys import argv, stdin, version from pkg_resources import Distribution, FileMetadata, PathMetadata from distutils.sysconfig import get_python_lib opts, args = getopt(argv[1:], 'hPRSCOE', ['help', 'provides', 'requires', 'suggests', 'conflicts', 'obsoletes', 'extras']) Provides = False Requires = False Suggests = False Conflicts = False Obsoletes = False Extras = False for o, a in opts: if o in ('-h', '--help'): print '-h, --help\tPrint help' print '-P, --provides\tPrint Provides' print '-R, --requires\tPrint Requires' print '-S, --suggests\tPrint Suggests' print '-C, --conflicts\tPrint Conflicts' print '-O, --obsoletes\tPrint Obsoletes (unused)' print '-E, --extras\tPrint Extras ' exit(1) elif o in ('-P', '--provides'): Provides = True elif o in ('-R', '--requires'): Requires = True elif o in ('-S', '--suggests'): Suggests = True elif o in ('-C', '--conflicts'): Conflicts = True elif o in ('-O', '--obsoletes'): Obsoletes = True elif o in ('-E', '--extras'): Extras = True if Requires: py_abi = True else: py_abi = False py_deps = {} if args: files = args else: files = stdin.readlines() for f in files: f = f.strip() lower = f.lower() name = 'python(abi)' # add dependency based on path, versioned if within versioned python directory if py_abi and (lower.endswith('.py') or lower.endswith('.pyc') or lower.endswith('.pyo')): if not name in py_deps: py_deps[name] = [] purelib = get_python_lib(standard_lib=1, plat_specific=0).split(version[:3])[0] platlib = get_python_lib(standard_lib=1, plat_specific=1).split(version[:3])[0] for lib in (purelib, platlib): if lib in f: spec = ('==',f.split(lib)[1].split(sep)[0]) if not spec in py_deps[name]: py_deps[name].append(spec) # Determine provide, requires, conflicts & suggests based on egg metadata if lower.endswith('.egg') or \ lower.endswith('.egg-info') or \ lower.endswith('.egg-link'): dist_name = basename(f) if isdir(f): path_item = dirname(f) metadata = PathMetadata(path_item, f) else: path_item = f metadata = FileMetadata(f) dist = Distribution.from_location(path_item, dist_name, metadata) if Provides: # If egg metadata says package name is python, we provide python(abi) if dist.key == 'python': name = 'python(abi)' if not name in py_deps: py_deps[name] = [] py_deps[name].append(('==', dist.py_version)) name = 'pythonegg(%s)' % dist.key if not name in py_deps: py_deps[name] = [] if dist.version: spec = ('==', dist.version) if not spec in py_deps[name]: py_deps[name].append(spec) if Requires or (Suggests and dist.extras): name = 'python(abi)' # If egg metadata says package name is python, we don't add dependency on python(abi) if dist.key == 'python': py_abi = False if name in py_deps: py_deps.pop(name) elif py_abi and dist.py_version: if not name in py_deps: py_deps[name] = [] spec = ('==', dist.py_version) if not spec in py_deps[name]: py_deps[name].append(spec) deps = dist.requires() if Suggests: depsextras = dist.requires(extras=dist.extras) if not Requires: for dep in reversed(depsextras): if dep in deps: depsextras.remove(dep) deps = depsextras # add requires/suggests based on egg metadata for dep in deps: name = 'pythonegg(%s)' % dep.key for spec in dep.specs: if spec[0] != '!=': if not name in py_deps: py_deps[name] = [] if not spec in py_deps[name]: py_deps[name].append(spec) if not dep.specs: py_deps[name] = [] # Unused, for automatic sub-package generation based on 'extras' from egg metadata # TODO: implement in rpm later, or...? if Extras: deps = dist.requires() extras = dist.extras print extras for extra in extras: print '%%package\textras-%s' % extra print 'Summary:\t%s extra for %s python egg' % (extra, dist.key) print 'Group:\t\tDevelopment/Python' depsextras = dist.requires(extras=[extra]) for dep in reversed(depsextras): if dep in deps: depsextras.remove(dep) deps = depsextras for dep in deps: for spec in dep.specs: if spec[0] == '!=': print 'Conflicts:\t%s %s %s' % (dep.key, '==', spec[1]) else: print 'Requires:\t%s %s %s' % (dep.key, spec[0], spec[1]) print '%%description\t%s' % extra print '%s extra for %s python egg' % (extra, dist.key) print '%%files\t\textras-%s\n' % extra if Conflicts: # Should we really add conflicts for extras? # Creating a meta package per extra with suggests on, which has # the requires/conflicts in stead might be a better solution... for dep in dist.requires(extras=dist.extras): name = dep.key for spec in dep.specs: if spec[0] == '!=': if not name in py_deps: py_deps[name] = [] spec = ('==', spec[1]) if not spec in py_deps[name]: py_deps[name].append(spec) names = py_deps.keys() names.sort() for name in names: if py_deps[name]: # Print out versioned provides, requires, suggests, conflicts for spec in py_deps[name]: print '%s %s %s' % (name, spec[0], spec[1]) else: # Print out unversioned provides, requires, suggests, conflicts print name