teknoraver / rpms / rpm

Forked from rpms/rpm 4 months ago
Clone
Blob Blame History Raw
diff --git a/scripts/pythondistdeps.py b/scripts/pythondistdeps.py
new file mode 100755
index 0000000..8a2f43d
--- /dev/null
+++ b/scripts/pythondistdeps.py
@@ -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)