| diff --git a/scripts/pythondistdeps.py b/scripts/pythondistdeps.py |
| new file mode 100755 |
| index 0000000..8a2f43d |
| |
| |
| @@ -0,0 +1,223 @@ |
| +#!/usr/bin/python |
| +# -*- coding: utf-8 -*- |
| +# |
| +# Copyright 2010 Per Øyvind Karlsen <proyvind@moondrake.org> |
| +# Copyright 2015 Neal Gompa <ngompa13@gmail.com> |
| +# |
| +# This program is free software. It may be redistributed and/or modified under |
| +# the terms of the LGPL version 2.1 (or later). |
| +# |
| +# RPM python dependency generator, using .egg-info/.egg-link/.dist-info data |
| +# |
| + |
| +from __future__ import print_function |
| +from getopt import getopt |
| +from os.path import basename, dirname, isdir, sep |
| +from sys import argv, stdin, version |
| +from distutils.sysconfig import get_python_lib |
| + |
| + |
| +opts, args = getopt( |
| + argv[1:], 'hPRrCEMLl:', |
| + ['help', 'provides', 'requires', 'recommends', 'conflicts', 'extras', 'majorver-provides', 'legacy-provides' , 'legacy']) |
| + |
| +Provides = False |
| +Requires = False |
| +Recommends = False |
| +Conflicts = False |
| +Extras = False |
| +Provides_PyMajorVer_Variant = False |
| +legacy_Provides = False |
| +legacy = 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('-r, --recommends\tPrint Recommends') |
| + print('-C, --conflicts\tPrint Conflicts') |
| + print('-E, --extras\tPrint Extras ') |
| + print('-M, --majorver-provides\tPrint extra Provides with Python major version only') |
| + print('-L, --legacy-provides\tPrint extra legacy pythonegg Provides') |
| + print('-l, --legacy\tPrint legacy pythonegg Provides/Requires instead') |
| + exit(1) |
| + elif o in ('-P', '--provides'): |
| + Provides = True |
| + elif o in ('-R', '--requires'): |
| + Requires = True |
| + elif o in ('-r', '--recommends'): |
| + Recommends = True |
| + elif o in ('-C', '--conflicts'): |
| + Conflicts = True |
| + elif o in ('-E', '--extras'): |
| + Extras = True |
| + elif o in ('-M', '--majorver-provides'): |
| + Provides_PyMajorVer_Variant = True |
| + elif o in ('-L', '--legacy-provides'): |
| + legacy_Provides = True |
| + elif o in ('-l', '--legacy'): |
| + legacy = 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 name not 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 spec not in py_deps[name]: |
| + py_deps[name].append(spec) |
| + |
| + # XXX: hack to workaround RPM internal dependency generator not passing directories |
| + lower_dir = dirname(lower) |
| + if lower_dir.endswith('.egg') or \ |
| + lower_dir.endswith('.egg-info') or \ |
| + lower_dir.endswith('.egg-link') or \ |
| + lower_dir.endswith('.dist-info'): |
| + lower = lower_dir |
| + f = dirname(f) |
| + # Determine provide, requires, conflicts & recommends based on egg/dist metadata |
| + if lower.endswith('.egg') or \ |
| + lower.endswith('.egg-info') or \ |
| + lower.endswith('.egg-link') or \ |
| + lower.endswith('.dist-info'): |
| + # This import is very slow, so only do it if needed |
| + from pkg_resources import Distribution, FileMetadata, PathMetadata |
| + 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_PyMajorVer_Variant or legacy_Provides or legacy) and Provides: |
| + # Get the Python major version |
| + pyver_major = dist.py_version.split('.')[0] |
| + if Provides: |
| + # If egg/dist metadata says package name is python, we provide python(abi) |
| + if dist.key == 'python': |
| + name = 'python(abi)' |
| + if name not in py_deps: |
| + py_deps[name] = [] |
| + py_deps[name].append(('==', dist.py_version)) |
| + if not legacy: |
| + name = 'python{}dist({})'.format(dist.py_version, dist.key) |
| + if name not in py_deps: |
| + py_deps[name] = [] |
| + if Provides_PyMajorVer_Variant: |
| + pymajor_name = 'python{}dist({})'.format(pyver_major, dist.key) |
| + if pymajor_name not in py_deps: |
| + py_deps[pymajor_name] = [] |
| + if legacy or legacy_Provides: |
| + legacy_name = 'pythonegg({})({})'.format(pyver_major, dist.key) |
| + if legacy_name not in py_deps: |
| + py_deps[legacy_name] = [] |
| + if dist.version: |
| + spec = ('==', dist.version) |
| + if spec not in py_deps[name]: |
| + if not legacy: |
| + py_deps[name].append(spec) |
| + if Provides_PyMajorVer_Variant: |
| + py_deps[pymajor_name].append(spec) |
| + if legacy or legacy_Provides: |
| + py_deps[legacy_name].append(spec) |
| + if Requires or (Recommends and dist.extras): |
| + name = 'python(abi)' |
| + # If egg/dist 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 name not in py_deps: |
| + py_deps[name] = [] |
| + spec = ('==', dist.py_version) |
| + if spec not in py_deps[name]: |
| + py_deps[name].append(spec) |
| + deps = dist.requires() |
| + if Recommends: |
| + 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/recommends based on egg/dist metadata |
| + for dep in deps: |
| + if legacy: |
| + name = 'pythonegg({})({})'.format(pyver_major, dep.key) |
| + else: |
| + name = 'python{}dist({})'.format(dist.py_version, dep.key) |
| + for spec in dep.specs: |
| + if spec[0] != '!=': |
| + if name not in py_deps: |
| + py_deps[name] = [] |
| + if spec not 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/dist metadata |
| + # TODO: implement in rpm later, or...? |
| + if Extras: |
| + deps = dist.requires() |
| + extras = dist.extras |
| + print(extras) |
| + for extra in extras: |
| + print('%%package\textras-{}'.format(extra)) |
| + print('Summary:\t{} extra for {} python package'.format(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{} {} {}'.format(dep.key, '==', spec[1])) |
| + else: |
| + print('Requires:\t{} {} {}'.format(dep.key, spec[0], spec[1])) |
| + print('%%description\t{}'.format(extra)) |
| + print('{} extra for {} python package'.format(extra, dist.key)) |
| + print('%%files\t\textras-{}\n'.format(extra)) |
| + if Conflicts: |
| + # Should we really add conflicts for extras? |
| + # Creating a meta package per extra with recommends 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 name not in py_deps: |
| + py_deps[name] = [] |
| + spec = ('==', spec[1]) |
| + if spec not in py_deps[name]: |
| + py_deps[name].append(spec) |
| +names = list(py_deps.keys()) |
| +names.sort() |
| +for name in names: |
| + if py_deps[name]: |
| + # Print out versioned provides, requires, recommends, conflicts |
| + for spec in py_deps[name]: |
| + print('{} {} {}'.format(name, spec[0], spec[1])) |
| + else: |
| + # Print out unversioned provides, requires, recommends, conflicts |
| + print(name) |