Toshaan Bharvani
2 weeks ago
commit
10595ff09c
9 changed files with 2038 additions and 0 deletions
@ -0,0 +1,515 @@
@@ -0,0 +1,515 @@
|
||||
"""Module/script to byte-compile all .py files to .pyc files. |
||||
|
||||
When called as a script with arguments, this compiles the directories |
||||
given as arguments recursively; the -l option prevents it from |
||||
recursing into directories. |
||||
|
||||
Without arguments, if compiles all modules on sys.path, without |
||||
recursing into subdirectories. (Even though it should do so for |
||||
packages -- for now, you'll have to deal with packages separately.) |
||||
|
||||
See module py_compile for details of the actual byte-compilation. |
||||
|
||||
License: |
||||
Compileall2 is an enhanced copy of Python's compileall module |
||||
and it follows Python licensing. For more info see: https://www.python.org/psf/license/ |
||||
""" |
||||
import os |
||||
import sys |
||||
import importlib.util |
||||
import py_compile |
||||
import struct |
||||
import filecmp |
||||
|
||||
from functools import partial |
||||
from pathlib import Path |
||||
|
||||
# Python 3.7 and higher |
||||
PY37 = sys.version_info[0:2] >= (3, 7) |
||||
# Python 3.6 and higher |
||||
PY36 = sys.version_info[0:2] >= (3, 6) |
||||
# Python 3.5 and higher |
||||
PY35 = sys.version_info[0:2] >= (3, 5) |
||||
|
||||
# Python 3.7 and above has a different structure and length |
||||
# of pyc files header. Also, multiple ways how to invalidate pyc file was |
||||
# introduced in Python 3.7. These cases are covered by variables here or by PY37 |
||||
# variable itself. |
||||
if PY37: |
||||
pyc_struct_format = '<4sll' |
||||
pyc_header_lenght = 12 |
||||
pyc_header_format = (pyc_struct_format, importlib.util.MAGIC_NUMBER, 0) |
||||
else: |
||||
pyc_struct_format = '<4sl' |
||||
pyc_header_lenght = 8 |
||||
pyc_header_format = (pyc_struct_format, importlib.util.MAGIC_NUMBER) |
||||
|
||||
__all__ = ["compile_dir","compile_file","compile_path"] |
||||
|
||||
def optimization_kwarg(opt): |
||||
"""Returns opt as a dictionary {optimization: opt} for use as **kwarg |
||||
for Python >= 3.5 and empty dictionary for Python 3.4""" |
||||
if PY35: |
||||
return dict(optimization=opt) |
||||
else: |
||||
# `debug_override` is a way how to enable optimized byte-compiled files |
||||
# (.pyo) in Python <= 3.4 |
||||
if opt: |
||||
return dict(debug_override=False) |
||||
else: |
||||
return dict() |
||||
|
||||
def _walk_dir(dir, maxlevels, quiet=0): |
||||
if PY36 and quiet < 2 and isinstance(dir, os.PathLike): |
||||
dir = os.fspath(dir) |
||||
else: |
||||
dir = str(dir) |
||||
if not quiet: |
||||
print('Listing {!r}...'.format(dir)) |
||||
try: |
||||
names = os.listdir(dir) |
||||
except OSError: |
||||
if quiet < 2: |
||||
print("Can't list {!r}".format(dir)) |
||||
names = [] |
||||
names.sort() |
||||
for name in names: |
||||
if name == '__pycache__': |
||||
continue |
||||
fullname = os.path.join(dir, name) |
||||
if not os.path.isdir(fullname): |
||||
yield fullname |
||||
elif (maxlevels > 0 and name != os.curdir and name != os.pardir and |
||||
os.path.isdir(fullname) and not os.path.islink(fullname)): |
||||
yield from _walk_dir(fullname, maxlevels=maxlevels - 1, |
||||
quiet=quiet) |
||||
|
||||
def compile_dir(dir, maxlevels=None, ddir=None, force=False, |
||||
rx=None, quiet=0, legacy=False, optimize=-1, workers=1, |
||||
invalidation_mode=None, stripdir=None, |
||||
prependdir=None, limit_sl_dest=None, hardlink_dupes=False): |
||||
"""Byte-compile all modules in the given directory tree. |
||||
|
||||
Arguments (only dir is required): |
||||
|
||||
dir: the directory to byte-compile |
||||
maxlevels: maximum recursion level (default `sys.getrecursionlimit()`) |
||||
ddir: the directory that will be prepended to the path to the |
||||
file as it is compiled into each byte-code file. |
||||
force: if True, force compilation, even if timestamps are up-to-date |
||||
quiet: full output with False or 0, errors only with 1, |
||||
no output with 2 |
||||
legacy: if True, produce legacy pyc paths instead of PEP 3147 paths |
||||
optimize: int or list of optimization levels or -1 for level of |
||||
the interpreter. Multiple levels leads to multiple compiled |
||||
files each with one optimization level. |
||||
workers: maximum number of parallel workers |
||||
invalidation_mode: how the up-to-dateness of the pyc will be checked |
||||
stripdir: part of path to left-strip from source file path |
||||
prependdir: path to prepend to beggining of original file path, applied |
||||
after stripdir |
||||
limit_sl_dest: ignore symlinks if they are pointing outside of |
||||
the defined path |
||||
hardlink_dupes: hardlink duplicated pyc files |
||||
""" |
||||
ProcessPoolExecutor = None |
||||
if ddir is not None and (stripdir is not None or prependdir is not None): |
||||
raise ValueError(("Destination dir (ddir) cannot be used " |
||||
"in combination with stripdir or prependdir")) |
||||
if ddir is not None: |
||||
stripdir = dir |
||||
prependdir = ddir |
||||
ddir = None |
||||
if workers is not None: |
||||
if workers < 0: |
||||
raise ValueError('workers must be greater or equal to 0') |
||||
elif workers != 1: |
||||
try: |
||||
# Only import when needed, as low resource platforms may |
||||
# fail to import it |
||||
from concurrent.futures import ProcessPoolExecutor |
||||
except ImportError: |
||||
workers = 1 |
||||
if maxlevels is None: |
||||
maxlevels = sys.getrecursionlimit() |
||||
files = _walk_dir(dir, quiet=quiet, maxlevels=maxlevels) |
||||
success = True |
||||
if workers is not None and workers != 1 and ProcessPoolExecutor is not None: |
||||
workers = workers or None |
||||
with ProcessPoolExecutor(max_workers=workers) as executor: |
||||
results = executor.map(partial(compile_file, |
||||
ddir=ddir, force=force, |
||||
rx=rx, quiet=quiet, |
||||
legacy=legacy, |
||||
optimize=optimize, |
||||
invalidation_mode=invalidation_mode, |
||||
stripdir=stripdir, |
||||
prependdir=prependdir, |
||||
limit_sl_dest=limit_sl_dest), |
||||
files) |
||||
success = min(results, default=True) |
||||
else: |
||||
for file in files: |
||||
if not compile_file(file, ddir, force, rx, quiet, |
||||
legacy, optimize, invalidation_mode, |
||||
stripdir=stripdir, prependdir=prependdir, |
||||
limit_sl_dest=limit_sl_dest, |
||||
hardlink_dupes=hardlink_dupes): |
||||
success = False |
||||
return success |
||||
|
||||
def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, |
||||
legacy=False, optimize=-1, |
||||
invalidation_mode=None, stripdir=None, prependdir=None, |
||||
limit_sl_dest=None, hardlink_dupes=False): |
||||
"""Byte-compile one file. |
||||
|
||||
Arguments (only fullname is required): |
||||
|
||||
fullname: the file to byte-compile |
||||
ddir: if given, the directory name compiled in to the |
||||
byte-code file. |
||||
force: if True, force compilation, even if timestamps are up-to-date |
||||
quiet: full output with False or 0, errors only with 1, |
||||
no output with 2 |
||||
legacy: if True, produce legacy pyc paths instead of PEP 3147 paths |
||||
optimize: int or list of optimization levels or -1 for level of |
||||
the interpreter. Multiple levels leads to multiple compiled |
||||
files each with one optimization level. |
||||
invalidation_mode: how the up-to-dateness of the pyc will be checked |
||||
stripdir: part of path to left-strip from source file path |
||||
prependdir: path to prepend to beggining of original file path, applied |
||||
after stripdir |
||||
limit_sl_dest: ignore symlinks if they are pointing outside of |
||||
the defined path. |
||||
hardlink_dupes: hardlink duplicated pyc files |
||||
""" |
||||
|
||||
if ddir is not None and (stripdir is not None or prependdir is not None): |
||||
raise ValueError(("Destination dir (ddir) cannot be used " |
||||
"in combination with stripdir or prependdir")) |
||||
|
||||
success = True |
||||
if PY36 and quiet < 2 and isinstance(fullname, os.PathLike): |
||||
fullname = os.fspath(fullname) |
||||
else: |
||||
fullname = str(fullname) |
||||
name = os.path.basename(fullname) |
||||
|
||||
dfile = None |
||||
|
||||
if ddir is not None: |
||||
if not PY36: |
||||
ddir = str(ddir) |
||||
dfile = os.path.join(ddir, name) |
||||
|
||||
if stripdir is not None: |
||||
fullname_parts = fullname.split(os.path.sep) |
||||
stripdir_parts = stripdir.split(os.path.sep) |
||||
ddir_parts = list(fullname_parts) |
||||
|
||||
for spart, opart in zip(stripdir_parts, fullname_parts): |
||||
if spart == opart: |
||||
ddir_parts.remove(spart) |
||||
|
||||
dfile = os.path.join(*ddir_parts) |
||||
|
||||
if prependdir is not None: |
||||
if dfile is None: |
||||
dfile = os.path.join(prependdir, fullname) |
||||
else: |
||||
dfile = os.path.join(prependdir, dfile) |
||||
|
||||
if isinstance(optimize, int): |
||||
optimize = [optimize] |
||||
|
||||
if hardlink_dupes: |
||||
raise ValueError(("Hardlinking of duplicated bytecode makes sense " |
||||
"only for more than one optimization level.")) |
||||
|
||||
if rx is not None: |
||||
mo = rx.search(fullname) |
||||
if mo: |
||||
return success |
||||
|
||||
if limit_sl_dest is not None and os.path.islink(fullname): |
||||
if Path(limit_sl_dest).resolve() not in Path(fullname).resolve().parents: |
||||
return success |
||||
|
||||
opt_cfiles = {} |
||||
|
||||
if os.path.isfile(fullname): |
||||
for opt_level in optimize: |
||||
if legacy: |
||||
opt_cfiles[opt_level] = fullname + 'c' |
||||
else: |
||||
if opt_level >= 0: |
||||
opt = opt_level if opt_level >= 1 else '' |
||||
opt_kwarg = optimization_kwarg(opt) |
||||
cfile = (importlib.util.cache_from_source( |
||||
fullname, **opt_kwarg)) |
||||
opt_cfiles[opt_level] = cfile |
||||
else: |
||||
cfile = importlib.util.cache_from_source(fullname) |
||||
opt_cfiles[opt_level] = cfile |
||||
|
||||
head, tail = name[:-3], name[-3:] |
||||
if tail == '.py': |
||||
if not force: |
||||
try: |
||||
mtime = int(os.stat(fullname).st_mtime) |
||||
expect = struct.pack(*(pyc_header_format + (mtime,))) |
||||
for cfile in opt_cfiles.values(): |
||||
with open(cfile, 'rb') as chandle: |
||||
actual = chandle.read(pyc_header_lenght) |
||||
if expect != actual: |
||||
break |
||||
else: |
||||
return success |
||||
except OSError: |
||||
pass |
||||
if not quiet: |
||||
print('Compiling {!r}...'.format(fullname)) |
||||
try: |
||||
for index, opt_level in enumerate(sorted(optimize)): |
||||
cfile = opt_cfiles[opt_level] |
||||
if PY37: |
||||
ok = py_compile.compile(fullname, cfile, dfile, True, |
||||
optimize=opt_level, |
||||
invalidation_mode=invalidation_mode) |
||||
else: |
||||
ok = py_compile.compile(fullname, cfile, dfile, True, |
||||
optimize=opt_level) |
||||
|
||||
if index > 0 and hardlink_dupes: |
||||
previous_cfile = opt_cfiles[optimize[index - 1]] |
||||
if previous_cfile == cfile and optimize[0] not in (1, 2): |
||||
# Python 3.4 has only one .pyo file for -O and -OO so |
||||
# we hardlink it only if there is a .pyc file |
||||
# with the same content |
||||
previous_cfile = opt_cfiles[optimize[0]] |
||||
if previous_cfile != cfile and filecmp.cmp(cfile, previous_cfile, shallow=False): |
||||
os.unlink(cfile) |
||||
os.link(previous_cfile, cfile) |
||||
|
||||
except py_compile.PyCompileError as err: |
||||
success = False |
||||
if quiet >= 2: |
||||
return success |
||||
elif quiet: |
||||
print('*** Error compiling {!r}...'.format(fullname)) |
||||
else: |
||||
print('*** ', end='') |
||||
# escape non-printable characters in msg |
||||
msg = err.msg.encode(sys.stdout.encoding, |
||||
errors='backslashreplace') |
||||
msg = msg.decode(sys.stdout.encoding) |
||||
print(msg) |
||||
except (SyntaxError, UnicodeError, OSError) as e: |
||||
success = False |
||||
if quiet >= 2: |
||||
return success |
||||
elif quiet: |
||||
print('*** Error compiling {!r}...'.format(fullname)) |
||||
else: |
||||
print('*** ', end='') |
||||
print(e.__class__.__name__ + ':', e) |
||||
else: |
||||
if ok == 0: |
||||
success = False |
||||
return success |
||||
|
||||
def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=0, |
||||
legacy=False, optimize=-1, |
||||
invalidation_mode=None): |
||||
"""Byte-compile all module on sys.path. |
||||
|
||||
Arguments (all optional): |
||||
|
||||
skip_curdir: if true, skip current directory (default True) |
||||
maxlevels: max recursion level (default 0) |
||||
force: as for compile_dir() (default False) |
||||
quiet: as for compile_dir() (default 0) |
||||
legacy: as for compile_dir() (default False) |
||||
optimize: as for compile_dir() (default -1) |
||||
invalidation_mode: as for compiler_dir() |
||||
""" |
||||
success = True |
||||
for dir in sys.path: |
||||
if (not dir or dir == os.curdir) and skip_curdir: |
||||
if quiet < 2: |
||||
print('Skipping current directory') |
||||
else: |
||||
success = success and compile_dir( |
||||
dir, |
||||
maxlevels, |
||||
None, |
||||
force, |
||||
quiet=quiet, |
||||
legacy=legacy, |
||||
optimize=optimize, |
||||
invalidation_mode=invalidation_mode, |
||||
) |
||||
return success |
||||
|
||||
|
||||
def main(): |
||||
"""Script main program.""" |
||||
import argparse |
||||
|
||||
parser = argparse.ArgumentParser( |
||||
description='Utilities to support installing Python libraries.') |
||||
parser.add_argument('-l', action='store_const', const=0, |
||||
default=None, dest='maxlevels', |
||||
help="don't recurse into subdirectories") |
||||
parser.add_argument('-r', type=int, dest='recursion', |
||||
help=('control the maximum recursion level. ' |
||||
'if `-l` and `-r` options are specified, ' |
||||
'then `-r` takes precedence.')) |
||||
parser.add_argument('-f', action='store_true', dest='force', |
||||
help='force rebuild even if timestamps are up to date') |
||||
parser.add_argument('-q', action='count', dest='quiet', default=0, |
||||
help='output only error messages; -qq will suppress ' |
||||
'the error messages as well.') |
||||
parser.add_argument('-b', action='store_true', dest='legacy', |
||||
help='use legacy (pre-PEP3147) compiled file locations') |
||||
parser.add_argument('-d', metavar='DESTDIR', dest='ddir', default=None, |
||||
help=('directory to prepend to file paths for use in ' |
||||
'compile-time tracebacks and in runtime ' |
||||
'tracebacks in cases where the source file is ' |
||||
'unavailable')) |
||||
parser.add_argument('-s', metavar='STRIPDIR', dest='stripdir', |
||||
default=None, |
||||
help=('part of path to left-strip from path ' |
||||
'to source file - for example buildroot. ' |
||||
'`-d` and `-s` options cannot be ' |
||||
'specified together.')) |
||||
parser.add_argument('-p', metavar='PREPENDDIR', dest='prependdir', |
||||
default=None, |
||||
help=('path to add as prefix to path ' |
||||
'to source file - for example / to make ' |
||||
'it absolute when some part is removed ' |
||||
'by `-s` option. ' |
||||
'`-d` and `-p` options cannot be ' |
||||
'specified together.')) |
||||
parser.add_argument('-x', metavar='REGEXP', dest='rx', default=None, |
||||
help=('skip files matching the regular expression; ' |
||||
'the regexp is searched for in the full path ' |
||||
'of each file considered for compilation')) |
||||
parser.add_argument('-i', metavar='FILE', dest='flist', |
||||
help=('add all the files and directories listed in ' |
||||
'FILE to the list considered for compilation; ' |
||||
'if "-", names are read from stdin')) |
||||
parser.add_argument('compile_dest', metavar='FILE|DIR', nargs='*', |
||||
help=('zero or more file and directory names ' |
||||
'to compile; if no arguments given, defaults ' |
||||
'to the equivalent of -l sys.path')) |
||||
parser.add_argument('-j', '--workers', default=1, |
||||
type=int, help='Run compileall concurrently') |
||||
parser.add_argument('-o', action='append', type=int, dest='opt_levels', |
||||
help=('Optimization levels to run compilation with. ' |
||||
'Default is -1 which uses optimization level of ' |
||||
'Python interpreter itself (specified by -O).')) |
||||
parser.add_argument('-e', metavar='DIR', dest='limit_sl_dest', |
||||
help='Ignore symlinks pointing outsite of the DIR') |
||||
parser.add_argument('--hardlink-dupes', action='store_true', |
||||
dest='hardlink_dupes', |
||||
help='Hardlink duplicated pyc files') |
||||
|
||||
if PY37: |
||||
invalidation_modes = [mode.name.lower().replace('_', '-') |
||||
for mode in py_compile.PycInvalidationMode] |
||||
parser.add_argument('--invalidation-mode', |
||||
choices=sorted(invalidation_modes), |
||||
help=('set .pyc invalidation mode; defaults to ' |
||||
'"checked-hash" if the SOURCE_DATE_EPOCH ' |
||||
'environment variable is set, and ' |
||||
'"timestamp" otherwise.')) |
||||
|
||||
args = parser.parse_args() |
||||
compile_dests = args.compile_dest |
||||
|
||||
if args.rx: |
||||
import re |
||||
args.rx = re.compile(args.rx) |
||||
|
||||
if args.limit_sl_dest == "": |
||||
args.limit_sl_dest = None |
||||
|
||||
if args.recursion is not None: |
||||
maxlevels = args.recursion |
||||
else: |
||||
maxlevels = args.maxlevels |
||||
|
||||
if args.opt_levels is None: |
||||
args.opt_levels = [-1] |
||||
|
||||
if len(args.opt_levels) == 1 and args.hardlink_dupes: |
||||
parser.error(("Hardlinking of duplicated bytecode makes sense " |
||||
"only for more than one optimization level.")) |
||||
|
||||
if args.ddir is not None and ( |
||||
args.stripdir is not None or args.prependdir is not None |
||||
): |
||||
parser.error("-d cannot be used in combination with -s or -p") |
||||
|
||||
# if flist is provided then load it |
||||
if args.flist: |
||||
try: |
||||
with (sys.stdin if args.flist=='-' else open(args.flist)) as f: |
||||
for line in f: |
||||
compile_dests.append(line.strip()) |
||||
except OSError: |
||||
if args.quiet < 2: |
||||
print("Error reading file list {}".format(args.flist)) |
||||
return False |
||||
|
||||
if args.workers is not None: |
||||
args.workers = args.workers or None |
||||
|
||||
if PY37 and args.invalidation_mode: |
||||
ivl_mode = args.invalidation_mode.replace('-', '_').upper() |
||||
invalidation_mode = py_compile.PycInvalidationMode[ivl_mode] |
||||
else: |
||||
invalidation_mode = None |
||||
|
||||
success = True |
||||
try: |
||||
if compile_dests: |
||||
for dest in compile_dests: |
||||
if os.path.isfile(dest): |
||||
if not compile_file(dest, args.ddir, args.force, args.rx, |
||||
args.quiet, args.legacy, |
||||
invalidation_mode=invalidation_mode, |
||||
stripdir=args.stripdir, |
||||
prependdir=args.prependdir, |
||||
optimize=args.opt_levels, |
||||
limit_sl_dest=args.limit_sl_dest, |
||||
hardlink_dupes=args.hardlink_dupes): |
||||
success = False |
||||
else: |
||||
if not compile_dir(dest, maxlevels, args.ddir, |
||||
args.force, args.rx, args.quiet, |
||||
args.legacy, workers=args.workers, |
||||
invalidation_mode=invalidation_mode, |
||||
stripdir=args.stripdir, |
||||
prependdir=args.prependdir, |
||||
optimize=args.opt_levels, |
||||
limit_sl_dest=args.limit_sl_dest, |
||||
hardlink_dupes=args.hardlink_dupes): |
||||
success = False |
||||
return success |
||||
else: |
||||
return compile_path(legacy=args.legacy, force=args.force, |
||||
quiet=args.quiet, |
||||
invalidation_mode=invalidation_mode) |
||||
except KeyboardInterrupt: |
||||
if args.quiet < 2: |
||||
print("\n[interrupted]") |
||||
return False |
||||
return True |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
exit_status = int(not main()) |
||||
sys.exit(exit_status) |
@ -0,0 +1,171 @@
@@ -0,0 +1,171 @@
|
||||
'''Script to perform import of each module given to %%py_check_import |
||||
''' |
||||
import argparse |
||||
import importlib |
||||
import fnmatch |
||||
import os |
||||
import re |
||||
import site |
||||
import sys |
||||
|
||||
from contextlib import contextmanager |
||||
from pathlib import Path |
||||
|
||||
|
||||
def read_modules_files(file_paths): |
||||
'''Read module names from the files (modules must be newline separated). |
||||
|
||||
Return the module names list or, if no files were provided, an empty list. |
||||
''' |
||||
|
||||
if not file_paths: |
||||
return [] |
||||
|
||||
modules = [] |
||||
for file in file_paths: |
||||
file_contents = file.read_text() |
||||
modules.extend(file_contents.split()) |
||||
return modules |
||||
|
||||
|
||||
def read_modules_from_cli(argv): |
||||
'''Read module names from command-line arguments (space or comma separated). |
||||
|
||||
Return the module names list. |
||||
''' |
||||
|
||||
if not argv: |
||||
return [] |
||||
|
||||
# %%py3_check_import allows to separate module list with comma or whitespace, |
||||
# we need to unify the output to a list of particular elements |
||||
modules_as_str = ' '.join(argv) |
||||
modules = re.split(r'[\s,]+', modules_as_str) |
||||
# Because of shell expansion in some less typical cases it may happen |
||||
# that a trailing space will occur at the end of the list. |
||||
# Remove the empty items from the list before passing it further |
||||
modules = [m for m in modules if m] |
||||
return modules |
||||
|
||||
|
||||
def filter_top_level_modules_only(modules): |
||||
'''Filter out entries with nested modules (containing dot) ie. 'foo.bar'. |
||||
|
||||
Return the list of top-level modules. |
||||
''' |
||||
|
||||
return [module for module in modules if '.' not in module] |
||||
|
||||
|
||||
def any_match(text, globs): |
||||
'''Return True if any of given globs fnmatchcase's the given text.''' |
||||
|
||||
return any(fnmatch.fnmatchcase(text, g) for g in globs) |
||||
|
||||
|
||||
def exclude_unwanted_module_globs(globs, modules): |
||||
'''Filter out entries which match the either of the globs given as argv. |
||||
|
||||
Return the list of filtered modules. |
||||
''' |
||||
|
||||
return [m for m in modules if not any_match(m, globs)] |
||||
|
||||
|
||||
def read_modules_from_all_args(args): |
||||
'''Return a joined list of modules from all given command-line arguments. |
||||
''' |
||||
|
||||
modules = read_modules_files(args.filename) |
||||
modules.extend(read_modules_from_cli(args.modules)) |
||||
if args.exclude: |
||||
modules = exclude_unwanted_module_globs(args.exclude, modules) |
||||
|
||||
if args.top_level: |
||||
modules = filter_top_level_modules_only(modules) |
||||
|
||||
# Error when someone accidentally managed to filter out everything |
||||
if len(modules) == 0: |
||||
raise ValueError('No modules to check were left') |
||||
|
||||
return modules |
||||
|
||||
|
||||
def import_modules(modules): |
||||
'''Procedure to perform import check for each module name from the given list of modules. |
||||
''' |
||||
|
||||
for module in modules: |
||||
print('Check import:', module, file=sys.stderr) |
||||
importlib.import_module(module) |
||||
|
||||
|
||||
def argparser(): |
||||
parser = argparse.ArgumentParser( |
||||
description='Generate list of all importable modules for import check.' |
||||
) |
||||
parser.add_argument( |
||||
'modules', nargs='*', |
||||
help=('Add modules to check the import (space or comma separated).'), |
||||
) |
||||
parser.add_argument( |
||||
'-f', '--filename', action='append', type=Path, |
||||
help='Add importable module names list from file.', |
||||
) |
||||
parser.add_argument( |
||||
'-t', '--top-level', action='store_true', |
||||
help='Check only top-level modules.', |
||||
) |
||||
parser.add_argument( |
||||
'-e', '--exclude', action='append', |
||||
help='Provide modules globs to be excluded from the check.', |
||||
) |
||||
return parser |
||||
|
||||
|
||||
@contextmanager |
||||
def remove_unwanteds_from_sys_path(): |
||||
'''Remove cwd and this script's parent from sys.path for the import test. |
||||
Bring the original contents back after import is done (or failed) |
||||
''' |
||||
|
||||
cwd_absolute = Path.cwd().absolute() |
||||
this_file_parent = Path(__file__).parent.absolute() |
||||
old_sys_path = list(sys.path) |
||||
for path in old_sys_path: |
||||
if Path(path).absolute() in (cwd_absolute, this_file_parent): |
||||
sys.path.remove(path) |
||||
try: |
||||
yield |
||||
finally: |
||||
sys.path = old_sys_path |
||||
|
||||
|
||||
def addsitedirs_from_environ(): |
||||
'''Load directories from the _PYTHONSITE environment variable (separated by :) |
||||
and load the ones already present in sys.path via site.addsitedir() |
||||
to handle .pth files in them. |
||||
|
||||
This is needed to properly import old-style namespace packages with nspkg.pth files. |
||||
See https://bugzilla.redhat.com/2018551 for a more detailed rationale.''' |
||||
for path in os.getenv('_PYTHONSITE', '').split(':'): |
||||
if path in sys.path: |
||||
site.addsitedir(path) |
||||
|
||||
|
||||
def main(argv=None): |
||||
|
||||
cli_args = argparser().parse_args(argv) |
||||
|
||||
if not cli_args.modules and not cli_args.filename: |
||||
raise ValueError('No modules to check were provided') |
||||
|
||||
modules = read_modules_from_all_args(cli_args) |
||||
|
||||
with remove_unwanteds_from_sys_path(): |
||||
addsitedirs_from_environ() |
||||
import_modules(modules) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
main() |
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
# Note that the path could itself be a python file, or a directory |
||||
|
||||
# Note that the py_byte_compile macro should work for all Python versions |
||||
# Which unfortunately makes the definition more complicated than it should be |
||||
|
||||
# Usage: |
||||
# %%py_byte_compile <interpereter> <path> |
||||
# Example: |
||||
# %%py_byte_compile %%{__python3} %%{buildroot}%%{_datadir}/spam/plugins/ |
||||
|
||||
# This will terminate build on SyntaxErrors, if you want to avoid that, |
||||
# use it in a subshell like this: |
||||
# (%%{py_byte_compile <interpereter> <path>}) || : |
||||
|
||||
# Setting PYTHONHASHSEED=0 disables Python hash seed randomization |
||||
# This should help with byte-compilation reproducibility: https://bugzilla.redhat.com/show_bug.cgi?id=1686078 |
||||
|
||||
%py_byte_compile()\ |
||||
py2_byte_compile () {\ |
||||
python_binary="env PYTHONHASHSEED=0 %1"\ |
||||
bytecode_compilation_path="%2"\ |
||||
failure=0\ |
||||
find $bytecode_compilation_path -type f -a -name "*.py" -print0 | xargs -0 $python_binary -s -c 'import py_compile, sys; [py_compile.compile(f, dfile=f.partition("'"$RPM_BUILD_ROOT"'")[2], doraise=True) for f in sys.argv[1:]]' || failure=1\ |
||||
find $bytecode_compilation_path -type f -a -name "*.py" -print0 | xargs -0 $python_binary -s -O -c 'import py_compile, sys; [py_compile.compile(f, dfile=f.partition("'"$RPM_BUILD_ROOT"'")[2], doraise=True) for f in sys.argv[1:]]' || failure=1\ |
||||
test $failure -eq 0\ |
||||
}\ |
||||
\ |
||||
py3_byte_compile () {\ |
||||
python_binary="env PYTHONHASHSEED=0 %1"\ |
||||
bytecode_compilation_path="%2"\ |
||||
PYTHONPATH="%{_rpmconfigdir}/redhat" $python_binary -s -B -m compileall2 -o 0 -o 1 -s $RPM_BUILD_ROOT -p / $bytecode_compilation_path \ |
||||
}\ |
||||
\ |
||||
py39_byte_compile () {\ |
||||
python_binary="env PYTHONHASHSEED=0 %1"\ |
||||
bytecode_compilation_path="%2"\ |
||||
$python_binary -s -B -m compileall -o 0 -o 1 -s $RPM_BUILD_ROOT -p / $bytecode_compilation_path \ |
||||
}\ |
||||
\ |
||||
# Path to intepreter should not contain any arguments \ |
||||
[[ "%1" =~ " -" ]] && echo "ERROR py_byte_compile: Path to interpreter should not contain any arguments" >&2 && exit 1 \ |
||||
# Get version without a dot (36 instead of 3.6), bash doesn't compare floats well \ |
||||
python_version=$(%1 -c "import sys; sys.stdout.write('{0.major}{0.minor}'.format(sys.version_info))") \ |
||||
# compileall2 is an enhanced fork of stdlib compileall module for Python >= 3.4 \ |
||||
# and it was merged back to stdlib in Python >= 3.9 \ |
||||
if [ "$python_version" -ge 39 ]; then \ |
||||
py39_byte_compile "%1" "%2"; \ |
||||
elif [ "$python_version" -ge 34 ]; then \ |
||||
py3_byte_compile "%1" "%2"; \ |
||||
else \ |
||||
py2_byte_compile "%1" "%2"; \ |
||||
fi |
@ -0,0 +1,146 @@
@@ -0,0 +1,146 @@
|
||||
# unversioned macros: used with user defined __python, no longer part of rpm >= 4.15 |
||||
# __python is defined to error by default in the srpm macros |
||||
# nb: $RPM_BUILD_ROOT is not set when the macros are expanded (at spec parse time) |
||||
# so we set it manually (to empty string), making our Python prefer the correct install scheme location |
||||
# platbase/base is explicitly set to %%{_prefix} to support custom values, such as /app for flatpaks |
||||
%python_sitelib %(RPM_BUILD_ROOT= %{__python} -Esc "import sysconfig; print(sysconfig.get_path('purelib', vars={'platbase': '%{_prefix}', 'base': '%{_prefix}'}))") |
||||
%python_sitearch %(RPM_BUILD_ROOT= %{__python} -Esc "import sysconfig; print(sysconfig.get_path('platlib', vars={'platbase': '%{_prefix}', 'base': '%{_prefix}'}))") |
||||
%python_version %(RPM_BUILD_ROOT= %{__python} -Esc "import sys; sys.stdout.write('{0.major}.{0.minor}'.format(sys.version_info))") |
||||
%python_version_nodots %(RPM_BUILD_ROOT= %{__python} -Esc "import sys; sys.stdout.write('{0.major}{0.minor}'.format(sys.version_info))") |
||||
%python_platform %(RPM_BUILD_ROOT= %{__python} -Esc "import sysconfig; print(sysconfig.get_platform())") |
||||
%python_platform_triplet %(RPM_BUILD_ROOT= %{__python} -Esc "import sysconfig; print(sysconfig.get_config_var('MULTIARCH'))") |
||||
%python_ext_suffix %(RPM_BUILD_ROOT= %{__python} -Esc "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))") |
||||
%python3_cache_tag %(RPM_BUILD_ROOT= %{__python3} -Ic "import sys; print(sys.implementation.cache_tag)") |
||||
|
||||
%py_setup setup.py |
||||
%_py_shebang_s s |
||||
%_pyshebang_P %(RPM_BUILD_ROOT= %{__python} -Esc "import sys; print('P' if hasattr(sys.flags, 'safe_path') else '')") |
||||
%py_shbang_opts -%{?_py_shebang_s}%{?_py_shebang_P} |
||||
%py_shbang_opts_nodash %(opts=%{py_shbang_opts}; echo ${opts#-}) |
||||
%py_shebang_flags %(opts=%{py_shbang_opts}; echo ${opts#-}) |
||||
%py_shebang_fix %{expand:\\\ |
||||
if [ -z "%{?py_shebang_flags}" ]; then |
||||
shebang_flags="-k" |
||||
else |
||||
shebang_flags="-ka%{py_shebang_flags}" |
||||
fi |
||||
%{__python} -B %{_rpmconfigdir}/redhat/pathfix.py -pni %{__python} $shebang_flags} |
||||
|
||||
# Use the slashes after expand so that the command starts on the same line as |
||||
# the macro |
||||
%py_build() %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
%{__python} %{py_setup} %{?py_setup_args} build --executable="%{__python} %{py_shbang_opts}" %{?*} |
||||
} |
||||
|
||||
%py_build_egg() %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
%{__python} %{py_setup} %{?py_setup_args} bdist_egg %{?*} |
||||
} |
||||
|
||||
%py_build_wheel() %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
%{__python} %{py_setup} %{?py_setup_args} bdist_wheel %{?*} |
||||
} |
||||
|
||||
%py_install() %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
%{__python} %{py_setup} %{?py_setup_args} install -O1 --skip-build --root %{buildroot} %{?*} |
||||
rm -rfv %{buildroot}%{_bindir}/__pycache__ |
||||
} |
||||
|
||||
%py_install_egg() %{expand:\\\ |
||||
mkdir -p %{buildroot}%{python_sitelib} |
||||
%{__python} -m easy_install -m --prefix %{buildroot}%{_prefix} -Z dist/*-py%{python_version}.egg %{?*} |
||||
rm -rfv %{buildroot}%{_bindir}/__pycache__ |
||||
} |
||||
|
||||
%py_install_wheel() %{expand:\\\ |
||||
%{__python} -m pip install -I dist/%{1} --root %{buildroot} --prefix %{_prefix} --no-deps --no-index --no-warn-script-location |
||||
rm -rfv %{buildroot}%{_bindir}/__pycache__ |
||||
for distinfo in %{buildroot}%{python_sitelib}/*.dist-info %{buildroot}%{python_sitearch}/*.dist-info; do |
||||
if [ -f ${distinfo}/direct_url.json ]; then |
||||
rm -fv ${distinfo}/direct_url.json |
||||
sed -i '/direct_url.json/d' ${distinfo}/RECORD |
||||
fi |
||||
done |
||||
} |
||||
|
||||
# With $PATH and $PYTHONPATH set to the %%buildroot, |
||||
# try to import the Python module(s) given as command-line args or read from file (-f). |
||||
# Respect the custom values of %%py_shebang_flags or set nothing if it's undefined. |
||||
# Filter and check import on only top-level modules using -t flag. |
||||
# Exclude unwanted modules by passing their globs to -e option. |
||||
# Useful as a smoke test in %%check when running tests is not feasible. |
||||
# Use spaces or commas as separators if providing list directly. |
||||
# Use newlines as separators if providing list in a file. |
||||
%py_check_import(e:tf:) %{expand:\\\ |
||||
PATH="%{buildroot}%{_bindir}:$PATH"\\\ |
||||
PYTHONPATH="${PYTHONPATH:-%{buildroot}%{python_sitearch}:%{buildroot}%{python_sitelib}}"\\\ |
||||
_PYTHONSITE="%{buildroot}%{python_sitearch}:%{buildroot}%{python_sitelib}"\\\ |
||||
PYTHONDONTWRITEBYTECODE=1\\\ |
||||
%{lua: |
||||
local command = "%{__python} " |
||||
if rpm.expand("%{?py_shebang_flags}") ~= "" then |
||||
command = command .. "-%{py_shebang_flags}" |
||||
end |
||||
command = command .. " %{_rpmconfigdir}/redhat/import_all_modules.py " |
||||
-- handle multiline arguments correctly, see https://bugzilla.redhat.com/2018809 |
||||
local args=rpm.expand('%{?**}'):gsub("[%s\\\\]*%s+", " ") |
||||
print(command .. args) |
||||
} |
||||
} |
||||
|
||||
%python_provide() %{lua: |
||||
local python = require "fedora.srpm.python" |
||||
function string.starts(String,Start) |
||||
return string.sub(String,1,string.len(Start))==Start |
||||
end |
||||
local package = rpm.expand("%{?1}") |
||||
local vr = rpm.expand("%{?epoch:%{epoch}:}%{version}-%{release}") |
||||
local provides = python.python_altprovides(package, vr) |
||||
local default_python3_pkgversion = rpm.expand("%{__default_python3_pkgversion}") |
||||
if (string.starts(package, "python3-")) then |
||||
for i, provide in ipairs(provides) do |
||||
print("\\nProvides: " .. provide) |
||||
end |
||||
--Obsoleting the previous default python package (if it doesn't have isa) |
||||
if (string.sub(package, "-1") ~= ")") then |
||||
print("\\nObsoletes: python-") |
||||
print(string.sub(package,9,string.len(package))) |
||||
print(" < " .. vr) |
||||
end |
||||
elseif (string.starts(package, "python" .. default_python3_pkgversion .. "-")) then |
||||
for i, provide in ipairs(provides) do |
||||
print("\\nProvides: " .. provide) |
||||
end |
||||
--Obsoleting the previous default python package (if it doesn't have isa) |
||||
if (string.sub(package, "-1") ~= ")") then |
||||
print("\\nObsoletes: python-") |
||||
print(string.sub(package,8+string.len(default_python3_pkgversion),string.len(package))) |
||||
print(" < " .. vr) |
||||
end |
||||
elseif (string.starts(package, "python")) then |
||||
--No unversioned provides as other python3 cases are not the default |
||||
elseif (string.starts(package, "pypy")) then |
||||
--No unversioned provides as pypy is not default either |
||||
else |
||||
print("%python_provide: ERROR: ") |
||||
print(package) |
||||
print(" not recognized.") |
||||
end |
||||
} |
||||
|
||||
# Environment variables for testing used standalone, e.g.: |
||||
# %%{py_test_envvars} %%{python} -m unittest |
||||
%py_test_envvars %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
PATH="%{buildroot}%{_bindir}:$PATH"\\\ |
||||
PYTHONPATH="${PYTHONPATH:-%{buildroot}%{python_sitearch}:%{buildroot}%{python_sitelib}}"\\\ |
||||
PYTHONDONTWRITEBYTECODE=1\\\ |
||||
%{?__pytest_addopts:PYTEST_ADDOPTS="${PYTEST_ADDOPTS:-} %{__pytest_addopts}"}\\\ |
||||
PYTEST_XDIST_AUTO_NUM_WORKERS=%{_smp_build_ncpus}} |
||||
|
||||
%python_disable_dependency_generator() \ |
||||
%undefine __pythondist_requires \ |
||||
%{nil} |
@ -0,0 +1,253 @@
@@ -0,0 +1,253 @@
|
||||
# There are multiple Python 3 versions packaged, but only one can be the "main" version |
||||
# That means that it owns the "python3" namespace: |
||||
# - python3 package name |
||||
# - /usr/bin/python3 command |
||||
# - python3-foo packages are meant for this version |
||||
# Other versions of Python 3 always contain the version in the namespace: |
||||
# - python3.XX package name |
||||
# - /usr/bin/python3.XX command |
||||
# - python3.XX-foo packages (if allowed) |
||||
# |
||||
# Python spec files use the version defined here to determine defaults for the |
||||
# %%py_provides and %%python_provide macros, as well as for the "pythonname" generator that |
||||
# provides python3-foo for python3.XX-foo and vice versa for the default "main" version. |
||||
# E.g. in Fedora 33, python3.9-foo will provide python3-foo, |
||||
# python3-foo will provide python3.9-foo. |
||||
# |
||||
# There are two macros: |
||||
# |
||||
# This always contains the major.minor version (with dots), default for %%python3_version. |
||||
%__default_python3_version 3.9 |
||||
# |
||||
# The pkgname version that determines the alternative provide name (e.g. python3.9-foo), |
||||
# set to the same as above, but historically hasn't included the dot. |
||||
# This is left intentionally a separate macro, in case the naming convention ever changes. |
||||
%__default_python3_pkgversion %__default_python3_version |
||||
|
||||
# python3_pkgversion specifies the version of Python 3 in the distro. |
||||
# For Fedora, this is usually just "3". |
||||
# It can be a specific version distro-wide (e.g. "36" in EPEL7). |
||||
# Alternatively, it can be overridden in spec (e.g. to "3.8") when building for alternate Python stacks. |
||||
%python3_pkgversion 3 |
||||
|
||||
# Define the Python interpreter paths in the SRPM macros so that |
||||
# - they can be used in Build/Requires |
||||
# - they can be used in non-Python packages where requiring pythonX-devel would |
||||
# be an overkill |
||||
|
||||
# use the underscored macros to redefine the behavior of %%python3_version etc. |
||||
%__python2 /usr/bin/python2 |
||||
%__python3 /usr/bin/python%{python3_pkgversion} |
||||
|
||||
# use the non-underscored macros to refer to Python in spec, etc. |
||||
%python2 %__python2 |
||||
%python3 %__python3 |
||||
|
||||
# See https://fedoraproject.org/wiki/Changes/PythonMacroError |
||||
%__python %{error:attempt to use unversioned python, define %%__python to %{__python2} or %{__python3} explicitly} |
||||
|
||||
# Users can use %%python only if they redefined %%__python (e.g. to %%__python3) |
||||
%python %__python |
||||
|
||||
# Define where Python wheels will be stored and the prefix of -wheel packages |
||||
# - In Fedora we want wheel subpackages named e.g. `python-pip-wheel` that |
||||
# install packages into `/usr/share/python-wheels`. Both names are not |
||||
# versioned, because they're used by all Python 3 stacks. |
||||
# - In RHEL we want wheel packages named e.g. `python3-pip-wheel` and |
||||
# `python3.11-pip-wheel` that install packages into similarly versioned |
||||
# locations. We want each Python stack in RHEL to have their own wheels, |
||||
# because the main python3 wheels (which we can't upgrade) will likely be |
||||
# quite old by the time we're adding new alternate Python stacks. |
||||
# - In ELN we want to follow Fedora, because builds for ELN and Fedora rawhide |
||||
# need to be interoperable. |
||||
%python_wheel_pkg_prefix python%{?rhel:%{!?eln:%{python3_pkgversion}}} |
||||
%python_wheel_dir %{_datadir}/%{python_wheel_pkg_prefix}-wheels |
||||
|
||||
# === Macros for Build/Requires tags using Python dist tags === |
||||
# - https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages |
||||
# - These macros need to be in macros.python-srpm, because BuildRequires tags |
||||
# get rendered as runtime requires into the metadata of SRPMs. |
||||
|
||||
# Converts Python dist name to a canonical format |
||||
%py_dist_name() %{lua:\ |
||||
name = rpm.expand("%{?1:%{1}}");\ |
||||
canonical = string.gsub(string.lower(name), "[^%w%[%]]+", "-");\ |
||||
print(canonical);\ |
||||
} |
||||
|
||||
# Creates Python 2 dist tag(s) after converting names to canonical format |
||||
# Needs to first put all arguments into a list, because invoking a different |
||||
# macro (%%py_dist_name) overwrites them |
||||
%py2_dist() %{lua:\ |
||||
args = {}\ |
||||
arg = 1\ |
||||
while (true) do\ |
||||
name = rpm.expand("%{?" .. arg .. ":%{" .. arg .. "}}");\ |
||||
if (name == nil or name == '') then\ |
||||
break\ |
||||
end\ |
||||
args[arg] = name\ |
||||
arg = arg + 1\ |
||||
end\ |
||||
for arg, name in ipairs(args) do\ |
||||
canonical = rpm.expand("%py_dist_name " .. name);\ |
||||
print("python2dist(" .. canonical .. ") ");\ |
||||
end\ |
||||
} |
||||
|
||||
# Creates Python 3 dist tag(s) after converting names to canonical format |
||||
# Needs to first put all arguments into a list, because invoking a different |
||||
# macro (%%py_dist_name) overwrites them |
||||
%py3_dist() %{lua:\ |
||||
python3_pkgversion = rpm.expand("%python3_pkgversion");\ |
||||
args = {}\ |
||||
arg = 1\ |
||||
while (true) do\ |
||||
name = rpm.expand("%{?" .. arg .. ":%{" .. arg .. "}}");\ |
||||
if (name == nil or name == '') then\ |
||||
break\ |
||||
end\ |
||||
args[arg] = name\ |
||||
arg = arg + 1\ |
||||
end\ |
||||
for arg, name in ipairs(args) do\ |
||||
canonical = rpm.expand("%py_dist_name " .. name);\ |
||||
print("python" .. python3_pkgversion .. "dist(" .. canonical .. ") ");\ |
||||
end\ |
||||
} |
||||
|
||||
# Macro to replace overly complicated references to PyPI source files. |
||||
# Expands to the pythonhosted URL for a package |
||||
# Accepts zero to three arguments: |
||||
# 1: The PyPI project name, defaulting to %%srcname if it is defined, then |
||||
# %%pypi_name if it is defined, then just %%name. |
||||
# 2: The PYPI version, defaulting to %%version with tildes stripped. |
||||
# 3: The file extension, defaulting to "tar.gz". (A period will be added |
||||
# automatically.) |
||||
# Requires %%__pypi_url and %%__pypi_default_extension to be defined. |
||||
%__pypi_url https://files.pythonhosted.org/packages/source/ |
||||
%__pypi_default_extension tar.gz |
||||
|
||||
%pypi_source() %{lua: |
||||
local src = rpm.expand('%1') |
||||
local ver = rpm.expand('%2') |
||||
local ext = rpm.expand('%3') |
||||
local url = rpm.expand('%__pypi_url') |
||||
\ |
||||
-- If no first argument, try %srcname, then %pypi_name, then %name |
||||
-- Note that rpm leaves macros unchanged if they are not defined. |
||||
if src == '%1' then |
||||
src = rpm.expand('%srcname') |
||||
end |
||||
if src == '%srcname' then |
||||
src = rpm.expand('%pypi_name') |
||||
end |
||||
if src == '%pypi_name' then |
||||
src = rpm.expand('%name') |
||||
end |
||||
\ |
||||
-- If no second argument, use %version |
||||
if ver == '%2' then |
||||
ver = rpm.expand('%version'):gsub('~', '') |
||||
end |
||||
\ |
||||
-- If no third argument, use the preset default extension |
||||
if ext == '%3' then |
||||
ext = rpm.expand('%__pypi_default_extension') |
||||
end |
||||
\ |
||||
local first = string.sub(src, 1, 1) |
||||
\ |
||||
print(url .. first .. '/' .. src .. '/' .. src .. '-' .. ver .. '.' .. ext) |
||||
} |
||||
|
||||
%py_provides() %{lua: |
||||
local python = require 'fedora.srpm.python' |
||||
local rhel = rpm.expand('%{?rhel}') |
||||
local name = rpm.expand('%1') |
||||
if name == '%1' then |
||||
rpm.expand('%{error:%%py_provides requires at least 1 argument, the name to provide}') |
||||
end |
||||
local evr = rpm.expand('%2') |
||||
if evr == '%2' then |
||||
evr = rpm.expand('%{?epoch:%{epoch}:}%{version}-%{release}') |
||||
end |
||||
print('Provides: ' .. name .. ' = ' .. evr .. '\\n') |
||||
local provides = python.python_altprovides(name, evr) |
||||
for i, provide in ipairs(provides) do |
||||
print('Provides: ' .. provide .. '\\n') |
||||
end |
||||
-- We only generate these Obsoletes on CentOS/RHEL to provide clean upgrade |
||||
-- path, e.g. python3-foo obsoletes python39-foo from previous RHEL. |
||||
-- In Fedora this is not needed as we don't ship ecosystem packages |
||||
-- for alternative Python interpreters. |
||||
if rhel ~= '' then |
||||
-- Create Obsoletes only if the name does not end in a parenthesis, |
||||
-- as Obsoletes can't include parentheses. |
||||
-- This most commonly happens when the name contains an isa. |
||||
if (string.sub(name, "-1") ~= ")") then |
||||
local obsoletes = python.python_altobsoletes(name, evr) |
||||
for i, obsolete in ipairs(obsoletes) do |
||||
print('Obsoletes: ' .. obsolete .. '\\n') |
||||
end |
||||
end |
||||
end |
||||
} |
||||
|
||||
%python_extras_subpkg(n:i:f:F) %{expand:%{lua: |
||||
local option_n = '-n (name of the base package)' |
||||
local option_i = '-i (buildroot path to metadata)' |
||||
local option_f = '-f (builddir path to a filelist)' |
||||
local option_F = '-F (skip %%files section)' |
||||
local value_n = rpm.expand('%{-n*}') |
||||
local value_i = rpm.expand('%{-i*}') |
||||
local value_f = rpm.expand('%{-f*}') |
||||
local value_F = rpm.expand('%{-F}') |
||||
local args = rpm.expand('%{*}') |
||||
if value_n == '' then |
||||
rpm.expand('%{error:%%%0: missing option ' .. option_n .. '}') |
||||
end |
||||
if value_i == '' and value_f == '' and value_F == '' then |
||||
rpm.expand('%{error:%%%0: missing option ' .. option_i .. ' or ' .. option_f .. ' or ' .. option_F .. '}') |
||||
end |
||||
if value_i ~= '' and value_f ~= '' then |
||||
rpm.expand('%{error:%%%0: simultaneous ' .. option_i .. ' and ' .. option_f .. ' options are not possible}') |
||||
end |
||||
if value_i ~= '' and value_F ~= '' then |
||||
rpm.expand('%{error:%%%0: simultaneous ' .. option_i .. ' and ' .. option_F .. ' options are not possible}') |
||||
end |
||||
if value_f ~= '' and value_F ~= '' then |
||||
rpm.expand('%{error:%%%0: simultaneous ' .. option_f .. ' and ' .. option_F .. ' options are not possible}') |
||||
end |
||||
if args == '' then |
||||
rpm.expand('%{error:%%%0 requires at least one argument with "extras" name}') |
||||
end |
||||
local requires = 'Requires: ' .. value_n .. ' = %{?epoch:%{epoch}:}%{version}-%{release}' |
||||
for extras in args:gmatch('[^%s,]+') do |
||||
local rpmname = value_n .. '+' .. extras |
||||
local pkgdef = '%package -n ' .. rpmname |
||||
local summary = 'Summary: Metapackage for ' .. value_n .. ': ' .. extras .. ' extras' |
||||
local description = '%description -n ' .. rpmname .. '\\\n' |
||||
local current_line = 'This is a metapackage bringing in' |
||||
for _, word in ipairs({extras, 'extras', 'requires', 'for', value_n .. '.'}) do |
||||
local line = current_line .. ' ' .. word |
||||
if line:len() > 79 then |
||||
description = description .. current_line .. '\\\n' |
||||
current_line = word |
||||
else |
||||
current_line = line |
||||
end |
||||
end |
||||
description = description .. current_line .. '\\\n' .. |
||||
'It makes sure the dependencies are installed.\\\n' |
||||
local files = '' |
||||
if value_i ~= '' then |
||||
files = '%files -n ' .. rpmname .. '\\\n' .. '%ghost ' .. value_i |
||||
elseif value_f ~= '' then |
||||
files = '%files -n ' .. rpmname .. ' -f ' .. value_f |
||||
end |
||||
for i, line in ipairs({pkgdef, summary, requires, description, files, ''}) do |
||||
print(line .. '\\\n') |
||||
end |
||||
end |
||||
}} |
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
# nb: $RPM_BUILD_ROOT is not set when the macros are expanded (at spec parse time) |
||||
# so we set it manually (to empty string), making our Python prefer the correct install scheme location |
||||
# platbase/base is explicitly set to %%{_prefix} to support custom values, such as /app for flatpaks |
||||
%python3_sitelib %(RPM_BUILD_ROOT= %{__python3} -Ic "import sysconfig; print(sysconfig.get_path('purelib', vars={'platbase': '%{_prefix}', 'base': '%{_prefix}'}))") |
||||
%python3_sitearch %(RPM_BUILD_ROOT= %{__python3} -Ic "import sysconfig; print(sysconfig.get_path('platlib', vars={'platbase': '%{_prefix}', 'base': '%{_prefix}'}))") |
||||
%python3_version %(RPM_BUILD_ROOT= %{__python3} -Ic "import sys; sys.stdout.write('{0.major}.{0.minor}'.format(sys.version_info))") |
||||
%python3_version_nodots %(RPM_BUILD_ROOT= %{__python3} -Ic "import sys; sys.stdout.write('{0.major}{0.minor}'.format(sys.version_info))") |
||||
%python3_platform %(RPM_BUILD_ROOT= %{__python3} -Ic "import sysconfig; print(sysconfig.get_platform())") |
||||
%python3_platform_triplet %(RPM_BUILD_ROOT= %{__python3} -Ic "import sysconfig; print(sysconfig.get_config_var('MULTIARCH'))") |
||||
%python3_ext_suffix %(RPM_BUILD_ROOT= %{__python3} -Ic "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))") |
||||
%python3_cache_tag %(RPM_BUILD_ROOT= %{__python3} -Ic "import sys; print(sys.implementation.cache_tag)") |
||||
%py3dir %{_builddir}/python3-%{name}-%{version}-%{release} |
||||
|
||||
%_py3_shebang_s s |
||||
%_py3_shebang_P %(RPM_BUILD_ROOT= %{__python3} -Ic "import sys; print('P' if hasattr(sys.flags, 'safe_path') else '')") |
||||
%py3_shbang_opts -%{?_py3_shebang_s}%{?_py3_shebang_P} |
||||
%py3_shbang_opts_nodash %(opts=%{py3_shbang_opts}; echo ${opts#-}) |
||||
%py3_shebang_flags %(opts=%{py3_shbang_opts}; echo ${opts#-}) |
||||
%py3_shebang_fix %{expand:\\\ |
||||
if [ -z "%{?py3_shebang_flags}" ]; then |
||||
shebang_flags="-k" |
||||
else |
||||
shebang_flags="-ka%{py3_shebang_flags}" |
||||
fi |
||||
%{__python3} -B %{_rpmconfigdir}/redhat/pathfix.py -pni %{__python3} $shebang_flags} |
||||
|
||||
# Use the slashes after expand so that the command starts on the same line as |
||||
# the macro |
||||
%py3_build() %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
%{__python3} %{py_setup} %{?py_setup_args} build --executable="%{__python3} %{py3_shbang_opts}" %{?*} |
||||
} |
||||
|
||||
%py3_build_egg() %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
%{__python3} %{py_setup} %{?py_setup_args} bdist_egg %{?*} |
||||
} |
||||
|
||||
%py3_build_wheel() %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
%{__python3} %{py_setup} %{?py_setup_args} bdist_wheel %{?*} |
||||
} |
||||
|
||||
%py3_install() %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
%{__python3} %{py_setup} %{?py_setup_args} install -O1 --skip-build --root %{buildroot} %{?*} |
||||
rm -rfv %{buildroot}%{_bindir}/__pycache__ |
||||
} |
||||
|
||||
%py3_install_egg() %{expand:\\\ |
||||
mkdir -p %{buildroot}%{python3_sitelib} |
||||
%{__python3} -m easy_install -m --prefix %{buildroot}%{_prefix} -Z dist/*-py%{python3_version}.egg %{?*} |
||||
rm -rfv %{buildroot}%{_bindir}/__pycache__ |
||||
} |
||||
|
||||
%py3_install_wheel() %{expand:\\\ |
||||
%{__python3} -m pip install -I dist/%{1} --root %{buildroot} --prefix %{_prefix} --no-deps --no-index --no-warn-script-location |
||||
rm -rfv %{buildroot}%{_bindir}/__pycache__ |
||||
for distinfo in %{buildroot}%{python3_sitelib}/*.dist-info %{buildroot}%{python3_sitearch}/*.dist-info; do |
||||
if [ -f ${distinfo}/direct_url.json ]; then |
||||
rm -fv ${distinfo}/direct_url.json |
||||
sed -i '/direct_url.json/d' ${distinfo}/RECORD |
||||
fi |
||||
done |
||||
} |
||||
|
||||
# With $PATH and $PYTHONPATH set to the %%buildroot, |
||||
# try to import the Python 3 module(s) given as command-line args or read from file (-f). |
||||
# Respect the custom values of %%py3_shebang_flags or set nothing if it's undefined. |
||||
# Filter and check import on only top-level modules using -t flag. |
||||
# Exclude unwanted modules by passing their globs to -e option. |
||||
# Useful as a smoke test in %%check when running tests is not feasible. |
||||
# Use spaces or commas as separators if providing list directly. |
||||
# Use newlines as separators if providing list in a file. |
||||
%py3_check_import(e:tf:) %{expand:\\\ |
||||
PATH="%{buildroot}%{_bindir}:$PATH"\\\ |
||||
PYTHONPATH="${PYTHONPATH:-%{buildroot}%{python3_sitearch}:%{buildroot}%{python3_sitelib}}"\\\ |
||||
_PYTHONSITE="%{buildroot}%{python3_sitearch}:%{buildroot}%{python3_sitelib}"\\\ |
||||
PYTHONDONTWRITEBYTECODE=1\\\ |
||||
%{lua: |
||||
local command = "%{__python3} " |
||||
if rpm.expand("%{?py3_shebang_flags}") ~= "" then |
||||
command = command .. "-%{py3_shebang_flags}" |
||||
end |
||||
command = command .. " %{_rpmconfigdir}/redhat/import_all_modules.py " |
||||
-- handle multiline arguments correctly, see https://bugzilla.redhat.com/2018809 |
||||
local args=rpm.expand('%{?**}'):gsub("[%s\\\\]*%s+", " ") |
||||
print(command .. args) |
||||
} |
||||
} |
||||
|
||||
# This only supports Python 3.5+ and will never work with Python 2. |
||||
# Hence, it has no Python version in the name. |
||||
%pycached() %{lua: |
||||
path = rpm.expand("%{?*}") |
||||
if (string.sub(path, "-3") ~= ".py") then |
||||
rpm.expand("%{error:%%pycached can only be used with paths explicitly ending with .py}") |
||||
else |
||||
print(path) |
||||
pyminor = path:match("/python3.(%d+)/") or "*" |
||||
dirname = path:match("(.*/)") |
||||
modulename = path:match(".*/([^/]+).py") |
||||
-- %%python3_cache_tag is not used here because this macro supports not-installed CPythons |
||||
print("\\n" .. dirname .. "__pycache__/" .. modulename .. ".cpython-3" .. pyminor .. "{,.opt-?}.pyc") |
||||
end |
||||
} |
||||
|
||||
# Environment variables used by %%pytest, %%tox or standalone, e.g.: |
||||
# %%{py3_test_envvars} %%{python3} -m unittest |
||||
%py3_test_envvars %{expand:\\\ |
||||
CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\ |
||||
PATH="%{buildroot}%{_bindir}:$PATH"\\\ |
||||
PYTHONPATH="${PYTHONPATH:-%{buildroot}%{python3_sitearch}:%{buildroot}%{python3_sitelib}}"\\\ |
||||
PYTHONDONTWRITEBYTECODE=1\\\ |
||||
%{?__pytest_addopts:PYTEST_ADDOPTS="${PYTEST_ADDOPTS:-} %{__pytest_addopts}"}\\\ |
||||
PYTEST_XDIST_AUTO_NUM_WORKERS=%{_smp_build_ncpus}} |
||||
|
||||
# This is intended for Python 3 only, hence also no Python version in the name. |
||||
%__pytest /usr/bin/pytest%(test %{python3_pkgversion} == 3 || echo -%{python3_version}) |
||||
%pytest %py3_test_envvars %__pytest |
@ -0,0 +1,199 @@
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3 |
||||
|
||||
import sys |
||||
import os |
||||
from stat import * |
||||
import getopt |
||||
|
||||
err = sys.stderr.write |
||||
dbg = err |
||||
rep = sys.stdout.write |
||||
|
||||
new_interpreter = None |
||||
preserve_timestamps = False |
||||
create_backup = True |
||||
keep_flags = False |
||||
add_flags = b'' |
||||
|
||||
|
||||
def main(): |
||||
global new_interpreter |
||||
global preserve_timestamps |
||||
global create_backup |
||||
global keep_flags |
||||
global add_flags |
||||
|
||||
usage = ('usage: %s -i /interpreter -p -n -k -a file-or-directory ...\n' % |
||||
sys.argv[0]) |
||||
try: |
||||
opts, args = getopt.getopt(sys.argv[1:], 'i:a:kpn') |
||||
except getopt.error as msg: |
||||
err(str(msg) + '\n') |
||||
err(usage) |
||||
sys.exit(2) |
||||
for o, a in opts: |
||||
if o == '-i': |
||||
new_interpreter = a.encode() |
||||
if o == '-p': |
||||
preserve_timestamps = True |
||||
if o == '-n': |
||||
create_backup = False |
||||
if o == '-k': |
||||
keep_flags = True |
||||
if o == '-a': |
||||
add_flags = a.encode() |
||||
if b' ' in add_flags: |
||||
err("-a option doesn't support whitespaces") |
||||
sys.exit(2) |
||||
if not new_interpreter or not new_interpreter.startswith(b'/') or \ |
||||
not args: |
||||
err('-i option or file-or-directory missing\n') |
||||
err(usage) |
||||
sys.exit(2) |
||||
bad = 0 |
||||
for arg in args: |
||||
if os.path.isdir(arg): |
||||
if recursedown(arg): bad = 1 |
||||
elif os.path.islink(arg): |
||||
err(arg + ': will not process symbolic links\n') |
||||
bad = 1 |
||||
else: |
||||
if fix(arg): bad = 1 |
||||
sys.exit(bad) |
||||
|
||||
|
||||
def ispython(name): |
||||
return name.endswith('.py') |
||||
|
||||
|
||||
def recursedown(dirname): |
||||
dbg('recursedown(%r)\n' % (dirname,)) |
||||
bad = 0 |
||||
try: |
||||
names = os.listdir(dirname) |
||||
except OSError as msg: |
||||
err('%s: cannot list directory: %r\n' % (dirname, msg)) |
||||
return 1 |
||||
names.sort() |
||||
subdirs = [] |
||||
for name in names: |
||||
if name in (os.curdir, os.pardir): continue |
||||
fullname = os.path.join(dirname, name) |
||||
if os.path.islink(fullname): pass |
||||
elif os.path.isdir(fullname): |
||||
subdirs.append(fullname) |
||||
elif ispython(name): |
||||
if fix(fullname): bad = 1 |
||||
for fullname in subdirs: |
||||
if recursedown(fullname): bad = 1 |
||||
return bad |
||||
|
||||
|
||||
def fix(filename): |
||||
## dbg('fix(%r)\n' % (filename,)) |
||||
try: |
||||
f = open(filename, 'rb') |
||||
except IOError as msg: |
||||
err('%s: cannot open: %r\n' % (filename, msg)) |
||||
return 1 |
||||
with f: |
||||
line = f.readline() |
||||
fixed = fixline(line) |
||||
if line == fixed: |
||||
rep(filename+': no change\n') |
||||
return |
||||
head, tail = os.path.split(filename) |
||||
tempname = os.path.join(head, '@' + tail) |
||||
try: |
||||
g = open(tempname, 'wb') |
||||
except IOError as msg: |
||||
err('%s: cannot create: %r\n' % (tempname, msg)) |
||||
return 1 |
||||
with g: |
||||
rep(filename + ': updating\n') |
||||
g.write(fixed) |
||||
BUFSIZE = 8*1024 |
||||
while 1: |
||||
buf = f.read(BUFSIZE) |
||||
if not buf: break |
||||
g.write(buf) |
||||
|
||||
# Finishing touch -- move files |
||||
|
||||
mtime = None |
||||
atime = None |
||||
# First copy the file's mode to the temp file |
||||
try: |
||||
statbuf = os.stat(filename) |
||||
mtime = statbuf.st_mtime |
||||
atime = statbuf.st_atime |
||||
os.chmod(tempname, statbuf[ST_MODE] & 0o7777) |
||||
except OSError as msg: |
||||
err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) |
||||
# Then make a backup of the original file as filename~ |
||||
if create_backup: |
||||
try: |
||||
os.rename(filename, filename + '~') |
||||
except OSError as msg: |
||||
err('%s: warning: backup failed (%r)\n' % (filename, msg)) |
||||
else: |
||||
try: |
||||
os.remove(filename) |
||||
except OSError as msg: |
||||
err('%s: warning: removing failed (%r)\n' % (filename, msg)) |
||||
# Now move the temp file to the original file |
||||
try: |
||||
os.rename(tempname, filename) |
||||
except OSError as msg: |
||||
err('%s: rename failed (%r)\n' % (filename, msg)) |
||||
return 1 |
||||
if preserve_timestamps: |
||||
if atime and mtime: |
||||
try: |
||||
os.utime(filename, (atime, mtime)) |
||||
except OSError as msg: |
||||
err('%s: reset of timestamp failed (%r)\n' % (filename, msg)) |
||||
return 1 |
||||
# Return success |
||||
return 0 |
||||
|
||||
|
||||
def parse_shebang(shebangline): |
||||
shebangline = shebangline.rstrip(b'\n') |
||||
start = shebangline.find(b' -') |
||||
if start == -1: |
||||
return b'' |
||||
return shebangline[start:] |
||||
|
||||
|
||||
def populate_flags(shebangline): |
||||
old_flags = b'' |
||||
if keep_flags: |
||||
old_flags = parse_shebang(shebangline) |
||||
if old_flags: |
||||
old_flags = old_flags[2:] |
||||
if not (old_flags or add_flags): |
||||
return b'' |
||||
# On Linux, the entire string following the interpreter name |
||||
# is passed as a single argument to the interpreter. |
||||
# e.g. "#! /usr/bin/python3 -W Error -s" runs "/usr/bin/python3 "-W Error -s" |
||||
# so shebang should have single '-' where flags are given and |
||||
# flag might need argument for that reasons adding new flags is |
||||
# between '-' and original flags |
||||
# e.g. #! /usr/bin/python3 -sW Error |
||||
return b' -' + add_flags + old_flags |
||||
|
||||
|
||||
def fixline(line): |
||||
if not line.startswith(b'#!'): |
||||
return line |
||||
|
||||
if b"python" not in line: |
||||
return line |
||||
|
||||
flags = populate_flags(line) |
||||
return b'#! ' + new_interpreter + flags + b'\n' |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
main() |
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
-- Convenience Lua functions that can be used within Python srpm/rpm macros |
||||
|
||||
-- Determine alternate names provided from the given name. |
||||
-- Used in pythonname provides generator, python_provide and py_provides. |
||||
-- If only_3_to_3_X is false/nil/unused there are 2 rules: |
||||
-- python3-foo -> python-foo, python3.X-foo |
||||
-- python3.X-foo -> python-foo, python3-foo |
||||
-- If only_3_to_3_X is true there is only 1 rule: |
||||
-- python3-foo -> python3X-foo |
||||
-- There is no python-foo -> rule, python-foo packages are version agnostic. |
||||
-- Returns a table/array with strings. Empty when no rule matched. |
||||
local function python_altnames(name, only_3_to_3_X) |
||||
local xy |
||||
if only_3_to_3_X then |
||||
-- Here we hardcode the xy prefix we want to obsolete to "39", because: |
||||
-- 1. Python 3.9 will remain the main Python version in RHEL 9 |
||||
-- 2. python39 in RHEL 8 is still using the dotless naming (as opposed to |
||||
-- python3.9) |
||||
xy = "39" |
||||
else |
||||
xy = rpm.expand('%{__default_python3_pkgversion}') |
||||
end |
||||
local altnames = {} |
||||
local replaced |
||||
-- NB: dash needs to be escaped! |
||||
if name:match('^python3%-') then |
||||
local prefixes = only_3_to_3_X and {} or {'python-'} |
||||
for i, prefix in ipairs({'python' .. xy .. '-', table.unpack(prefixes)}) do |
||||
replaced = name:gsub('^python3%-', prefix) |
||||
table.insert(altnames, replaced) |
||||
end |
||||
elseif name:match('^python' .. xy .. '%-') and not only_3_to_3_X then |
||||
for i, prefix in ipairs({'python-', 'python3-'}) do |
||||
replaced = name:gsub('^python' .. xy .. '%-', prefix) |
||||
table.insert(altnames, replaced) |
||||
end |
||||
end |
||||
return altnames |
||||
end |
||||
|
||||
|
||||
local function __python_alttags(name, evr, tag_type) |
||||
-- for the "provides" tag_type we want also unversioned provides |
||||
local only_3_to_3_X = tag_type ~= "provides" |
||||
local operator = tag_type == "provides" and ' = ' or ' < ' |
||||
|
||||
-- global cache that tells what package NEVRs were already processed for the |
||||
-- given tag type |
||||
if __python_alttags_beenthere == nil then |
||||
__python_alttags_beenthere = {} |
||||
end |
||||
if __python_alttags_beenthere[tag_type] == nil then |
||||
__python_alttags_beenthere[tag_type] = {} |
||||
end |
||||
__python_alttags_beenthere[tag_type][name .. ' ' .. evr] = true |
||||
local alttags = {} |
||||
for i, altname in ipairs(python_altnames(name, only_3_to_3_X)) do |
||||
table.insert(alttags, altname .. operator .. evr) |
||||
end |
||||
return alttags |
||||
end |
||||
|
||||
-- For any given name and epoch-version-release, return provides except self. |
||||
-- Uses python_altnames under the hood |
||||
-- Returns a table/array with strings. |
||||
local function python_altprovides(name, evr) |
||||
return __python_alttags(name, evr, "provides") |
||||
end |
||||
|
||||
-- For any given name and epoch-version-release, return versioned obsoletes except self. |
||||
-- Uses python_altnames under the hood |
||||
-- Returns a table/array with strings. |
||||
local function python_altobsoletes(name, evr) |
||||
return __python_alttags(name, evr, "obsoletes") |
||||
end |
||||
|
||||
|
||||
local function __python_alttags_once(name, evr, tag_type) |
||||
-- global cache that tells what provides were already processed |
||||
if __python_alttags_beenthere == nil |
||||
or __python_alttags_beenthere[tag_type] == nil |
||||
or __python_alttags_beenthere[tag_type][name .. ' ' .. evr] == nil then |
||||
return __python_alttags(name, evr, tag_type) |
||||
else |
||||
return nil |
||||
end |
||||
end |
||||
|
||||
-- Like python_altprovides but only return something once. |
||||
-- For each argument can only be used once, returns nil otherwise. |
||||
-- Previous usage of python_altprovides counts as well. |
||||
local function python_altprovides_once(name, evr) |
||||
return __python_alttags_once(name, evr, "provides") |
||||
end |
||||
|
||||
-- Like python_altobsoletes but only return something once. |
||||
-- For each argument can only be used once, returns nil otherwise. |
||||
-- Previous usage of python_altobsoletes counts as well. |
||||
local function python_altobsoletes_once(name, evr) |
||||
return __python_alttags_once(name, evr, "obsoletes") |
||||
end |
||||
|
||||
|
||||
return { |
||||
python_altnames = python_altnames, |
||||
python_altprovides = python_altprovides, |
||||
python_altobsoletes = python_altobsoletes, |
||||
python_altprovides_once = python_altprovides_once, |
||||
python_altobsoletes_once = python_altobsoletes_once, |
||||
} |
@ -0,0 +1,472 @@
@@ -0,0 +1,472 @@
|
||||
Name: python-rpm-macros |
||||
Version: 3.9 |
||||
Release: 53%{?dist} |
||||
Summary: The common Python RPM macros |
||||
URL: https://src.fedoraproject.org/rpms/python-rpm-macros/ |
||||
|
||||
# macros and lua: MIT |
||||
# import_all_modules.py: MIT |
||||
# compileall2.py: PSFv2 |
||||
# pathfix.py: PSFv2 |
||||
License: MIT and Python |
||||
|
||||
# Macros: |
||||
Source101: macros.python |
||||
Source102: macros.python-srpm |
||||
Source104: macros.python3 |
||||
Source105: macros.pybytecompile |
||||
|
||||
# Lua files |
||||
Source201: python.lua |
||||
|
||||
# Python code |
||||
%global compileall2_version 0.7.1 |
||||
Source301: https://github.com/fedora-python/compileall2/raw/v%{compileall2_version}/compileall2.py |
||||
Source302: import_all_modules.py |
||||
%global pathfix_version 1.0.0 |
||||
Source303: https://github.com/fedora-python/pathfix/raw/v%{pathfix_version}/pathfix.py |
||||
|
||||
BuildArch: noarch |
||||
|
||||
# For %%__default_python3_pkgversion used in %%python_provide |
||||
# For python.lua |
||||
# For compileall2.py |
||||
Requires: python-srpm-macros = %{version}-%{release} |
||||
|
||||
# The packages are called python(3)-(s)rpm-macros |
||||
# We never want python3-rpm-macros to provide python-rpm-macros |
||||
# We opt out from all Python name-based automatic provides and obsoletes |
||||
%undefine __pythonname_provides |
||||
%undefine __pythonname_obsoletes |
||||
|
||||
%description |
||||
This package contains the unversioned Python RPM macros, that most |
||||
implementations should rely on. |
||||
|
||||
You should not need to install this package manually as the various |
||||
python?-devel packages require it. So install a python-devel package instead. |
||||
|
||||
|
||||
%package -n python-srpm-macros |
||||
Summary: RPM macros for building Python source packages |
||||
|
||||
# For directory structure and flags macros |
||||
Requires: redhat-rpm-config |
||||
|
||||
# We bundle our own software here :/ |
||||
Provides: bundled(python3dist(compileall2)) = %{compileall2_version} |
||||
|
||||
%description -n python-srpm-macros |
||||
RPM macros for building Python source packages. |
||||
|
||||
|
||||
%package -n python3-rpm-macros |
||||
Summary: RPM macros for building Python 3 packages |
||||
|
||||
# For %%__python3 and %%python3 |
||||
Requires: python-srpm-macros = %{version}-%{release} |
||||
|
||||
# For %%py_setup and import_all_modules.py |
||||
Requires: python-rpm-macros = %{version}-%{release} |
||||
|
||||
# We obsolete the old python39-rpm-macros for a smoother upgrade from RHEL8. |
||||
# Since python39-rpm-macros are built from the python39 component in RHEL 8, |
||||
# they're fully versioned (currently `0:3.9.7`), with the patch version likely |
||||
# to increase in the future. RPM sorts this number as higher than `3.9`, which |
||||
# is the version we have in RHEL 9. Therefore we're obsoleting with an Epoch 1 |
||||
# so that all versions from RHEL 8 are obsoleted (but we keep the possibility |
||||
# of increasing the epoch in RHEL 8 to stop this). |
||||
Obsoletes: python39-rpm-macros < 1:%{version}-%{release} |
||||
|
||||
%description -n python3-rpm-macros |
||||
RPM macros for building Python 3 packages. |
||||
|
||||
|
||||
%prep |
||||
%autosetup -c -T |
||||
cp -a %{sources} . |
||||
|
||||
# We want to have shebang in the script upstream but not here so |
||||
# the package with macros does not depend on Python. |
||||
sed -i '1s=^#!/usr/bin/env python3==' pathfix.py |
||||
|
||||
|
||||
%install |
||||
mkdir -p %{buildroot}%{rpmmacrodir} |
||||
install -m 644 macros.* %{buildroot}%{rpmmacrodir}/ |
||||
|
||||
mkdir -p %{buildroot}%{_rpmluadir}/fedora/srpm |
||||
install -p -m 644 -t %{buildroot}%{_rpmluadir}/fedora/srpm python.lua |
||||
|
||||
mkdir -p %{buildroot}%{_rpmconfigdir}/redhat |
||||
install -m 644 compileall2.py %{buildroot}%{_rpmconfigdir}/redhat/ |
||||
install -m 644 import_all_modules.py %{buildroot}%{_rpmconfigdir}/redhat/ |
||||
|
||||
install -m 644 pathfix.py %{buildroot}%{_rpmconfigdir}/redhat/ |
||||
|
||||
|
||||
%check |
||||
# no macros in comments |
||||
! grep -E '^#[^%%]*%%[^%%]' %{buildroot}%{rpmmacrodir}/macros.* |
||||
|
||||
|
||||
%files |
||||
%{rpmmacrodir}/macros.python |
||||
%{rpmmacrodir}/macros.pybytecompile |
||||
%{_rpmconfigdir}/redhat/import_all_modules.py |
||||
%{_rpmconfigdir}/redhat/pathfix.py |
||||
|
||||
%files -n python-srpm-macros |
||||
%{rpmmacrodir}/macros.python-srpm |
||||
%{_rpmconfigdir}/redhat/compileall2.py |
||||
%{_rpmluadir}/fedora/srpm/python.lua |
||||
|
||||
%files -n python3-rpm-macros |
||||
%{rpmmacrodir}/macros.python3 |
||||
|
||||
|
||||
%changelog |
||||
* Thu Nov 02 2023 Tomas Orsava <torsava@redhat.com> - 3.9-53 |
||||
- Update macros from Fedora and add pathfix.py into python-rpm-macros |
||||
- Resolves: RHEL-6107 |
||||
|
||||
* Tue Feb 08 2022 Tomas Orsava <torsava@redhat.com> - 3.9-52 |
||||
- %%py_provides: Do not generate Obsoletes for names containing parentheses |
||||
- Related: rhbz#1990421 |
||||
|
||||
* Tue Feb 08 2022 Tomas Orsava <torsava@redhat.com> - 3.9-51 |
||||
- Add Obsoletes tags with the python39- prefix for smoother upgrade from RHEL8 |
||||
- Related: rhbz#1990421 |
||||
|
||||
* Tue Feb 01 2022 Miro Hrončok <mhroncok@redhat.com> - 3.9-50 |
||||
- Explicitly opt-out from Python name-based provides and obsoletes generators |
||||
|
||||
* Wed Jan 19 2022 Tomas Orsava <torsava@redhat.com> - 3.9-49 |
||||
- Add lua helper functions to make it possible to automatically generate |
||||
Obsoletes tags |
||||
- Modify the %%py_provides macro to also generate Obsoletes tags on CentOS/RHEL |
||||
- Resolves: rhbz#1990421 |
||||
|
||||
* Tue Dec 21 2021 Karolina Surma <ksurma@redhat.com> - 3.9-48 |
||||
- Fix CI test configuration, so that pytest can import the package code |
||||
|
||||
* Wed Dec 08 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-47 |
||||
- Set %%__python3 value according to %%python3_pkgversion |
||||
I.e. when %%python3_pkgversion is 3.12, %%__python3 is /usr/bin/python3.12 |
||||
|
||||
* Mon Nov 01 2021 Karolina Surma <ksurma@redhat.com> - 3.9-46 |
||||
- Fix multiline arguments processing for %%py_check_import |
||||
- Fix %%py_shebang_flags handling within %%py_check_import |
||||
- Process .pth files in buildroot's sitedirs in %%py_check_import |
||||
- Move import_all_modules.py from python-srpm-macros to python-rpm-macros |
||||
|
||||
* Mon Oct 25 2021 Karolina Surma <ksurma@redhat.com> - 3.9-45 |
||||
- Introduce -f (read from file) option to %%py{3}_check_import |
||||
- Introduce -t (filter top-level modules) option to %%py{3}_check_import |
||||
- Introduce -e (exclude module globs) option to %%py{3}_check_import |
||||
|
||||
* Thu Sep 09 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-44 |
||||
- Set $RPM_BUILD_ROOT in %%{python3_...} macros |
||||
to allow selecting alternate sysconfig install scheme based on that variable |
||||
|
||||
* Wed Aug 11 2021 Tomas Orsava <torsava@redhat.com> - 3.9-43 |
||||
- Define a new macros %%python_wheel_dir and %%python_wheel_pkg_prefix |
||||
- Related: rhbz#1982668 |
||||
|
||||
* Tue Aug 10 2021 Mohan Boddu <mboddu@redhat.com> - 3.9-42 |
||||
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags |
||||
Related: rhbz#1991688 |
||||
|
||||
* Wed Jul 07 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-41 |
||||
- Introduce %%py3_check_import |
||||
|
||||
* Mon Jun 28 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-40 |
||||
- %%pytest: Set $PYTEST_ADDOPTS when %%{__pytest_addopts} is defined |
||||
|
||||
* Tue Jun 15 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-39 |
||||
- Fix %%python_provide when fed python3.10-foo to obsolete python-foo instead of python--foo |
||||
|
||||
* Tue Apr 27 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-38 |
||||
- Escape %% symbols in macro files comments |
||||
- Fixes: rhbz#1953910 |
||||
|
||||
* Fri Apr 16 2021 Karolina Surma <ksurma@redhat.com> - 3.9-37 |
||||
- Use sysconfig.get_path() to get %%python3_sitelib and %%python3_sitearch |
||||
- Fixes: rhbz#1947468 |
||||
|
||||
* Fri Apr 16 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-36.1 |
||||
- Allow commas as argument separator for extras names in %%python_extras_subpkg |
||||
- Fixes: rhbz#1936486 |
||||
|
||||
* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 3.9-36 |
||||
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 |
||||
|
||||
* Sat Feb 20 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-35 |
||||
- Fix %%python_extras_subpkg with underscores in extras names |
||||
|
||||
* Mon Feb 08 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-34 |
||||
- Remove python2-rpm-macros |
||||
- https://fedoraproject.org/wiki/Changes/Disable_Python_2_Dist_RPM_Generators_and_Freeze_Python_2_Macros |
||||
|
||||
* Fri Feb 05 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-13 |
||||
- Automatically word-wrap the description of extras subpackages |
||||
- Fixes: rhbz#1922442 |
||||
|
||||
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.9-12 |
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild |
||||
|
||||
* Tue Dec 08 2020 Miro Hrončok <mhroncok@redhat.com> - 3.9-11 |
||||
- Support defining %%py3_shebang_flags to %%nil |
||||
|
||||
* Mon Sep 14 2020 Miro Hrončok <mhroncok@redhat.com> - 3.9-10 |
||||
- Add %%python3_platform_triplet and %%python3_ext_suffix |
||||
- https://fedoraproject.org/wiki/Changes/Python_Upstream_Architecture_Names |
||||
|
||||
* Fri Jul 24 2020 Lumír Balhar <lbalhar@redhat.com> - 3.9-9 |
||||
- Adapt %%py[3]_shebang_fix to use versioned pathfixX.Y.py |
||||
|
||||
* Fri Jul 24 2020 Lumír Balhar <lbalhar@redhat.com> - 3.9-8 |
||||
- Disable Python hash seed randomization in %%py_byte_compile |
||||
|
||||
* Tue Jul 21 2020 Lumír Balhar <lbalhar@redhat.com> - 3.9-7 |
||||
- Make %%py3_dist respect %%python3_pkgversion |
||||
|
||||
* Thu Jul 16 2020 Miro Hrončok <mhroncok@redhat.com> - 3.9-6 |
||||
- Make the unversioned %%__python macro error |
||||
- https://fedoraproject.org/wiki/Changes/PythonMacroError |
||||
- Make %%python macros more consistent with %%python3 macros |
||||
- Define %%python_platform (as a Python version agnostic option to %%python3_platform) |
||||
- Add --no-index --no-warn-script-location pip options to %%pyX_install_wheel |
||||
|
||||
* Wed Jul 08 2020 Miro Hrončok <mhroncok@redhat.com> - 3.9-5 |
||||
- Introduce %%python_extras_subpkg |
||||
- Adapt %%py_dist_name to keep square brackets |
||||
- https://fedoraproject.org/wiki/Changes/PythonExtras |
||||
|
||||
* Tue Jun 16 2020 Lumír Balhar <lbalhar@redhat.com> - 3.9-4 |
||||
- Use compileall from stdlib for Python >= 3.9 |
||||
|
||||
* Thu Jun 11 2020 Miro Hrončok <mhroncok@redhat.com> - 3.9-3 |
||||
- Allow to combine %%pycached with other macros (e.g. %%exclude or %%ghost) (#1838992) |
||||
|
||||
* Sat May 30 2020 Miro Hrončok <mhroncok@redhat.com> - 3.9-2 |
||||
- Require the exact same version-release of other subpackages of this package |
||||
|
||||
* Thu May 21 2020 Miro Hrončok <mhroncok@redhat.com> - 3.9-1 |
||||
- https://fedoraproject.org/wiki/Changes/Python3.9 |
||||
- Switch the %%py_dist_name macro to convert dots (".") into dashes as defined in PEP 503 (#1791530) |
||||
|
||||
* Mon May 11 2020 Miro Hrončok <mhroncok@redhat.com> - 3.8-8 |
||||
- Implement %%pytest |
||||
- Implement %%pyX_shebang_fix |
||||
- Strip tildes from %%version in %%pypi_source by default |
||||
|
||||
* Thu May 07 2020 Miro Hrončok <mhroncok@redhat.com> - 3.8-7 |
||||
- Change %%__default_python3_pkgversion from 38 to 3.8 |
||||
|
||||
* Tue May 05 2020 Miro Hrončok <mhroncok@redhat.com> - 3.8-6 |
||||
- Require recent enough SRPM macros from RPM macros, to prevent missing Lua files |
||||
|
||||
* Tue May 05 2020 Miro Hrončok <mhroncok@redhat.com> - 3.8-5 |
||||
- Implement %%py_provides |
||||
|
||||
* Mon May 04 2020 Tomas Hrnciar <thrnciar@redhat.com> - 3.8-4 |
||||
- Make %%py3_install_wheel macro remove direct_url.json file created by PEP 610. |
||||
- https://discuss.python.org/t/pep-610-usage-guidelines-for-linux-distributions/4012 |
||||
|
||||
* Mon Apr 27 2020 Miro Hrončok <mhroncok@redhat.com> - 3.8-3 |
||||
- Make pythonX-rpm-macros depend on python-rpm-macros (#1827811) |
||||
|
||||
* Tue Mar 31 2020 Lumír Balhar <lbalhar@redhat.com> - 3.8-2 |
||||
- Update of bundled compileall2 module to 0.7.1 (bugfix release) |
||||
|
||||
* Mon Mar 23 2020 Miro Hrončok <mhroncok@redhat.com> - 3.8-1 |
||||
- Hardcode the default Python 3 version in the SRPM macros (#1812087) |
||||
- Provide python38-foo for python3-foo and the other way around (future RHEL compatibility) |
||||
- %%python_provide: Allow any names starting with "python" or "pypy" |
||||
|
||||
* Mon Feb 10 2020 Miro Hrončok <mhroncok@redhat.com> - 3-54 |
||||
- Update of bundled compileall2 module to 0.7.0 |
||||
Adds the optional --hardlink-dupes flag for compileall2 for pyc deduplication |
||||
|
||||
* Thu Feb 06 2020 Miro Hrončok <mhroncok@redhat.com> - 3-53 |
||||
- Define %%py(2|3)?_shbang_opts_nodash to be used with pathfix.py -a |
||||
|
||||
* Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 3-52 |
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild |
||||
|
||||
* Sat Dec 28 2019 Miro Hrončok <mhroncok@redhat.com> - 3-51 |
||||
- Define %%python, but make it work only if %%__python is redefined |
||||
- Add the %%pycached macro |
||||
- Remove stray __pycache__ directory from /usr/bin when running %%py_install, |
||||
%%py_install_wheel and %%py_build_wheel macros |
||||
|
||||
* Tue Nov 26 2019 Lumír Balhar <lbalhar@redhat.com> - 3-50 |
||||
- Update of bundled compileall2 module |
||||
|
||||
* Fri Sep 27 2019 Miro Hrončok <mhroncok@redhat.com> - 3-49 |
||||
- Define %%python2 and %%python3 |
||||
|
||||
* Mon Aug 26 2019 Miro Hrončok <mhroncok@redhat.com> - 3-48 |
||||
- Drop --strip-file-prefix option from %%pyX_install_wheel macros, it is not needed |
||||
|
||||
* Fri Jul 26 2019 Fedora Release Engineering <releng@fedoraproject.org> - 3-47 |
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild |
||||
|
||||
* Fri Jul 12 2019 Miro Hrončok <mhroncok@redhat.com> - 3-46 |
||||
- %%python_provide: Switch python2 and python3 behavior |
||||
- https://fedoraproject.org/wiki/Changes/Python_means_Python3 |
||||
- Use compileall2 module for byte-compilation with Python >= 3.4 |
||||
- Do not allow passing arguments to Python during byte-compilation |
||||
- Use `-s` argument for Python during byte-compilation |
||||
|
||||
* Tue Jul 09 2019 Miro Hrončok <mhroncok@redhat.com> - 3-45 |
||||
- %%python_provide: Don't try to obsolete %%_isa provides |
||||
|
||||
* Mon Jun 17 2019 Miro Hrončok <mhroncok@redhat.com> - 3-44 |
||||
- Make %%__python /usr/bin/python once again until we are ready |
||||
|
||||
* Mon Jun 10 2019 Miro Hrončok <mhroncok@redhat.com> - 3-43 |
||||
- Define %%python_sitelib, %%python_sitearch, %%python_version, %%python_version_nodots, |
||||
in rpm 4.15 those are no longer defined, the meaning of python is derived from %%__python. |
||||
- Usage of %%__python or the above-mentioned macros will error unless user defined. |
||||
- The %%python_provide macro no longer gives the arched provide for arched packages (#1705656) |
||||
|
||||
* Sat Feb 02 2019 Fedora Release Engineering <releng@fedoraproject.org> - 3-42 |
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild |
||||
|
||||
* Thu Dec 20 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 3-41 |
||||
- Add %%python_disable_dependency_generator |
||||
|
||||
* Wed Dec 05 2018 Miro Hrončok <mhroncok@redhat.com> - 3-40 |
||||
- Workaround leaking buildroot PATH in %%py_byte_compile (#1647212) |
||||
|
||||
* Thu Nov 01 2018 Petr Viktorin <pviktori@redhat.com> - 3-39 |
||||
- Move "sleep 1" workaround from py3_build to py2_build (#1644923) |
||||
|
||||
* Thu Sep 20 2018 Tomas Orsava <torsava@redhat.com> - 3-38 |
||||
- Move the __python2/3 macros to the python-srpm-macros subpackage |
||||
- This facilitates using the %%{__python2/3} in Build/Requires |
||||
|
||||
* Wed Aug 15 2018 Miro Hrončok <mhroncok@redhat.com> - 3-37 |
||||
- Make %%py_byte_compile terminate build on SyntaxErrors (#1616219) |
||||
|
||||
* Wed Aug 15 2018 Miro Hrončok <mhroncok@redhat.com> - 3-36 |
||||
- Make %%py_build wokr if %%__python is defined to custom value |
||||
|
||||
* Sat Jul 28 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 3-35 |
||||
- Change way how enabling-depgen works internally |
||||
|
||||
* Sat Jul 14 2018 Tomas Orsava <torsava@redhat.com> - 3-34 |
||||
- macros.pybytecompile: Detect Python version through sys.version_info instead |
||||
of guessing from the executable name |
||||
|
||||
* Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 3-33 |
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild |
||||
|
||||
* Tue Jul 10 2018 Tomas Orsava <torsava@redhat.com> - 3-32 |
||||
- Fix %%py_byte_compile macro: when invoked with a Python 2 binary it also |
||||
mistakenly ran py3_byte_compile |
||||
|
||||
* Tue Jul 03 2018 Miro Hrončok <mhroncok@redhat.com> - 3-31 |
||||
- Add %%python3_platform useful for PYTHONPATH on arched builds |
||||
|
||||
* Mon Jun 18 2018 Jason L Tibbitts III <tibbs@math.uh.edu> - 3-30 |
||||
- Add %%pypi_source macro, as well as %%__pypi_url and |
||||
%%_pypi_default_extension. |
||||
|
||||
* Wed Apr 18 2018 Miro Hrončok <mhroncok@redhat.com> - 3-29 |
||||
- move macros.pybytecompile from python3-devel |
||||
|
||||
* Fri Apr 06 2018 Tomas Orsava <torsava@redhat.com> - 3-28 |
||||
- Fix the %%py_dist_name macro to not convert dots (".") into dashes, so that |
||||
submodules can be addressed as well |
||||
Resolves: rhbz#1564095 |
||||
|
||||
* Fri Mar 23 2018 Miro Hrončok <mhroncok@redhat.com> - 3-27 |
||||
- make LDFLAGS propagated whenever CFLAGS are |
||||
|
||||
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 3-26 |
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild |
||||
|
||||
* Fri Jan 19 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 3-25 |
||||
- Add %%python_enable_dependency_generator |
||||
|
||||
* Tue Nov 28 2017 Tomas Orsava <torsava@redhat.com> - 3-24 |
||||
- Remove platform-python macros (https://fedoraproject.org/wiki/Changes/Platform_Python_Stack) |
||||
|
||||
* Thu Oct 26 2017 Ville Skyttä <ville.skytta@iki.fi> - 3-23 |
||||
- Use -Es/-I to invoke macro scriptlets (#1506355) |
||||
|
||||
* Wed Aug 02 2017 Tomas Orsava <torsava@redhat.com> - 3-22 |
||||
- Add platform-python macros (https://fedoraproject.org/wiki/Changes/Platform_Python_Stack) |
||||
|
||||
* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3-21 |
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild |
||||
|
||||
* Fri Mar 03 2017 Michal Cyprian <mcyprian@redhat.com> - 3-20 |
||||
- Revert "Switch %%__python3 to /usr/libexec/system-python" |
||||
after the Fedora Change https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe |
||||
was postponed |
||||
|
||||
* Fri Feb 17 2017 Michal Cyprian <mcyprian@redhat.com> - 3-19 |
||||
- Switch %%__python3 to /usr/libexec/system-python |
||||
|
||||
* Sat Feb 11 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3-18 |
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild |
||||
|
||||
* Mon Jan 23 2017 Michal Cyprian <mcyprian@redhat.com> - 3-17 |
||||
- Add --no-deps option to py_install_wheel macros |
||||
|
||||
* Tue Jan 17 2017 Tomas Orsava <torsava@redhat.com> - 3-16 |
||||
- Added macros for Build/Requires tags using Python dist tags: |
||||
https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages |
||||
|
||||
* Thu Nov 24 2016 Orion Poplawski <orion@cora.nwra.com> 3-15 |
||||
- Make expanded macros start on the same line as the macro |
||||
|
||||
* Wed Nov 16 2016 Orion Poplawski <orion@cora.nwra.com> 3-14 |
||||
- Fix %%py3_install_wheel (bug #1395953) |
||||
|
||||
* Wed Nov 16 2016 Orion Poplawski <orion@cora.nwra.com> 3-13 |
||||
- Add missing sleeps to other build macros |
||||
- Fix build_egg macros |
||||
- Add %%py_build_wheel and %%py_install_wheel macros |
||||
|
||||
* Tue Nov 15 2016 Orion Poplawski <orion@cora.nwra.com> 3-12 |
||||
- Add %%py_build_egg and %%py_install_egg macros |
||||
- Allow multiple args to %%py_build/install macros |
||||
- Tidy up macro formatting |
||||
|
||||
* Wed Aug 24 2016 Orion Poplawski <orion@cora.nwra.com> 3-11 |
||||
- Use %%rpmmacrodir |
||||
|
||||
* Tue Jul 12 2016 Orion Poplawski <orion@cora.nwra.com> 3-10 |
||||
- Do not generate useless Obsoletes with %%{?_isa} |
||||
|
||||
* Fri May 13 2016 Orion Poplawski <orion@cora.nwra.com> 3-9 |
||||
- Make python-rpm-macros require python-srpm-macros (bug #1335860) |
||||
|
||||
* Thu May 12 2016 Jason L Tibbitts III <tibbs@math.uh.edu> - 3-8 |
||||
- Add single-second sleeps to work around setuptools bug. |
||||
|
||||
* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 3-7 |
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild |
||||
|
||||
* Thu Jan 14 2016 Orion Poplawski <orion@cora.nwra.com> 3-6 |
||||
- Fix typo in %%python_provide |
||||
|
||||
* Thu Jan 14 2016 Orion Poplawski <orion@cora.nwra.com> 3-5 |
||||
- Handle noarch python sub-packages (bug #1290900) |
||||
|
||||
* Wed Jan 13 2016 Orion Poplawski <orion@cora.nwra.com> 3-4 |
||||
- Fix python2/3-rpm-macros package names |
||||
|
||||
* Thu Jan 7 2016 Orion Poplawski <orion@cora.nwra.com> 3-3 |
||||
- Add empty %%prep and %%build |
||||
|
||||
* Mon Jan 4 2016 Orion Poplawski <orion@cora.nwra.com> 3-2 |
||||
- Combined package |
||||
|
||||
* Wed Dec 30 2015 Orion Poplawski <orion@cora.nwra.com> 3-1 |
||||
- Initial package |
Loading…
Reference in new issue