commit
66e569df82
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
# If using normal root, avoid changing anything.
|
||||||
|
if [ -z "$RPM_BUILD_ROOT" -o "$RPM_BUILD_ROOT" = "/" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Defined as %py_reproducible_pyc_path macro and passed here as
|
||||||
|
# the first command-line argument
|
||||||
|
path_to_fix=$1
|
||||||
|
|
||||||
|
# First, check that the parser is available:
|
||||||
|
if [ ! -x /usr/bin/marshalparser ]; then
|
||||||
|
echo "ERROR: If %py_reproducible_pyc_path is defined, you have to also BuildRequire: /usr/bin/marshalparser !"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set pipefail so if $path_to_fix does not exist, the build fails
|
||||||
|
set -o pipefail
|
||||||
|
find "$path_to_fix" -type f -name "*.pyc" | xargs /usr/bin/marshalparser --fix --overwrite
|
@ -0,0 +1,157 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
errors_terminate=$2
|
||||||
|
|
||||||
|
# Usage of %_python_bytecompile_extra is not allowed anymore
|
||||||
|
# See: https://fedoraproject.org/wiki/Changes/No_more_automagic_Python_bytecompilation_phase_3
|
||||||
|
# Therefore $1 ($default_python) is not needed and is invoked with "" by default.
|
||||||
|
# $default_python stays in the arguments for backward compatibility and $extra for the following check:
|
||||||
|
extra=$3
|
||||||
|
if [ 0$extra -eq 1 ]; then
|
||||||
|
echo -e "%_python_bytecompile_extra is discontinued, use %py_byte_compile instead.\nSee: https://fedoraproject.org/wiki/Changes/No_more_automagic_Python_bytecompilation_phase_3" >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
compileall_flags="$4"
|
||||||
|
|
||||||
|
# If using normal root, avoid changing anything.
|
||||||
|
if [ -z "$RPM_BUILD_ROOT" -o "$RPM_BUILD_ROOT" = "/" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# This function clamps the source mtime, see https://fedoraproject.org/wiki/Changes/ReproducibleBuildsClampMtimes
|
||||||
|
function python_clamp_source_mtime()
|
||||||
|
{
|
||||||
|
local _=$1
|
||||||
|
local python_binary=$2
|
||||||
|
local _=$3
|
||||||
|
local python_libdir="$4"
|
||||||
|
PYTHONPATH=/usr/lib/rpm/redhat/ $python_binary -B -m clamp_source_mtime -q "$python_libdir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# This function now implements Python byte-compilation in three different ways:
|
||||||
|
# Python >= 3.4 and < 3.9 uses a new module compileall2 - https://github.com/fedora-python/compileall2
|
||||||
|
# In Python >= 3.9, compileall2 was merged back to standard library (compileall) so we can use it directly again.
|
||||||
|
# Python < 3.4 (inc. Python 2) uses compileall module from stdlib with some hacks
|
||||||
|
function python_bytecompile()
|
||||||
|
{
|
||||||
|
local options=$1
|
||||||
|
local python_binary=$2
|
||||||
|
local exclude=$3
|
||||||
|
local python_libdir="$4"
|
||||||
|
local compileall_flags="$5"
|
||||||
|
|
||||||
|
python_version=$($python_binary -c "import sys; sys.stdout.write('{0.major}{0.minor}'.format(sys.version_info))")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Python 3.4 and higher
|
||||||
|
#
|
||||||
|
if [ "$python_version" -ge 34 ]; then
|
||||||
|
|
||||||
|
# We compile all opt levels in one go: only when $options is empty.
|
||||||
|
if [ -n "$options" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$python_version" -ge 39 ]; then
|
||||||
|
# For Pyhon 3.9+, use the standard library
|
||||||
|
compileall_module=compileall
|
||||||
|
else
|
||||||
|
# For older Pythons, use compileall2
|
||||||
|
compileall_module=compileall2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$python_version" -ge 37 ]; then
|
||||||
|
# Force the TIMESTAMP invalidation mode
|
||||||
|
invalidation_option=--invalidation-mode=timestamp
|
||||||
|
else
|
||||||
|
# For older Pythons, the option does not exist
|
||||||
|
# as the invalidation is always based on size+mtime
|
||||||
|
invalidation_option=
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ ! -z $exclude ] && exclude="-x '$exclude'"
|
||||||
|
|
||||||
|
# PYTHONPATH is needed for compileall2, but doesn't hurt for the stdlib
|
||||||
|
# -o 0 -o 1 are the optimization levels
|
||||||
|
# -q disables verbose output
|
||||||
|
# -f forces the process to overwrite existing compiled files
|
||||||
|
# -x excludes paths defined by regex
|
||||||
|
# -e excludes symbolic links pointing outside the build root
|
||||||
|
# -x and -e together implements the same functionality as the Filter class below
|
||||||
|
# -s strips $RPM_BUILD_ROOT from the path
|
||||||
|
# -p prepends the leading slash to the path to make it absolute
|
||||||
|
PYTHONPATH=/usr/lib/rpm/redhat/ $python_binary -B -m $compileall_module $compileall_flags -o 0 -o 1 -q -f $exclude -s "$RPM_BUILD_ROOT" -p / --hardlink-dupes $invalidation_option -e "$RPM_BUILD_ROOT" "$python_libdir"
|
||||||
|
|
||||||
|
else
|
||||||
|
#
|
||||||
|
# Python 3.3 and lower (incl. Python 2)
|
||||||
|
#
|
||||||
|
|
||||||
|
local real_libdir=${python_libdir/$RPM_BUILD_ROOT/}
|
||||||
|
|
||||||
|
cat << EOF | $python_binary $options
|
||||||
|
import compileall, sys, os, re
|
||||||
|
|
||||||
|
python_libdir = "$python_libdir"
|
||||||
|
depth = sys.getrecursionlimit()
|
||||||
|
real_libdir = "$real_libdir"
|
||||||
|
build_root = "$RPM_BUILD_ROOT"
|
||||||
|
exclude = r"$exclude"
|
||||||
|
|
||||||
|
class Filter:
|
||||||
|
def search(self, path):
|
||||||
|
ret = not os.path.realpath(path).startswith(build_root)
|
||||||
|
if exclude:
|
||||||
|
ret = ret or re.search(exclude, path)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
sys.exit(not compileall.compile_dir(python_libdir, depth, real_libdir, force=1, rx=Filter(), quiet=1))
|
||||||
|
EOF
|
||||||
|
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# .pyc/.pyo files embed a "magic" value, identifying the ABI version of Python
|
||||||
|
# bytecode that they are for.
|
||||||
|
#
|
||||||
|
# The files below RPM_BUILD_ROOT could be targeting multiple versions of
|
||||||
|
# python (e.g. a single build that emits several subpackages e.g. a
|
||||||
|
# python26-foo subpackage, a python31-foo subpackage etc)
|
||||||
|
#
|
||||||
|
# Support this by assuming that below each /usr/lib/python$VERSION/, all
|
||||||
|
# .pyc/.pyo files are to be compiled for /usr/bin/python$VERSION.
|
||||||
|
#
|
||||||
|
# For example, below /usr/lib/python2.6/, we're targeting /usr/bin/python2.6
|
||||||
|
# and below /usr/lib/python3.1/, we're targeting /usr/bin/python3.1
|
||||||
|
|
||||||
|
# Disable Python hash seed randomization
|
||||||
|
# This should help with byte-compilation reproducibility: https://bugzilla.redhat.com/show_bug.cgi?id=1686078
|
||||||
|
# Python 3.11+ no longer needs this: https://github.com/python/cpython/pull/27926 (but we support older Pythons as well)
|
||||||
|
export PYTHONHASHSEED=0
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
find "$RPM_BUILD_ROOT" -type d -print0|grep -z -E "/(usr|app)/lib(64)?/python[0-9]\.[0-9]+$" | while read -d "" python_libdir;
|
||||||
|
do
|
||||||
|
python_binary=$(basename "$python_libdir")
|
||||||
|
echo "Bytecompiling .py files below $python_libdir using $python_binary"
|
||||||
|
|
||||||
|
# Generate normal (.pyc) byte-compiled files.
|
||||||
|
python_clamp_source_mtime "" "$python_binary" "" "$python_libdir" ""
|
||||||
|
if [ $? -ne 0 -a 0$errors_terminate -ne 0 ]; then
|
||||||
|
# One or more of the files had inaccessible mtime
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
python_bytecompile "" "$python_binary" "" "$python_libdir" "$compileall_flags"
|
||||||
|
if [ $? -ne 0 -a 0$errors_terminate -ne 0 ]; then
|
||||||
|
# One or more of the files had a syntax error
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate optimized (.pyo) byte-compiled files.
|
||||||
|
# N.B. For Python 3.4+, this call does nothing
|
||||||
|
python_bytecompile "-O" "$python_binary" "" "$python_libdir" "$compileall_flags"
|
||||||
|
if [ $? -ne 0 -a 0$errors_terminate -ne 0 ]; then
|
||||||
|
# One or more of the files had a syntax error
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# If using normal root, avoid changing anything.
|
||||||
|
if [ -z "$RPM_BUILD_ROOT" ] || [ "$RPM_BUILD_ROOT" = "/" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
hardlink_if_same() {
|
||||||
|
if cmp -s "$1" "$2" ; then
|
||||||
|
ln -f "$1" "$2"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Hardlink identical *.pyc, *.pyo, and *.opt-[12].pyc.
|
||||||
|
# Originally from PLD's rpm-build-macros
|
||||||
|
find "$RPM_BUILD_ROOT" -type f -name "*.pyc" -not -name "*.opt-[12].pyc" | while read pyc ; do
|
||||||
|
hardlink_if_same "$pyc" "${pyc%c}o"
|
||||||
|
o1pyc="${pyc%pyc}opt-1.pyc"
|
||||||
|
hardlink_if_same "$pyc" "$o1pyc"
|
||||||
|
o2pyc="${pyc%pyc}opt-2.pyc"
|
||||||
|
hardlink_if_same "$pyc" "$o2pyc" || hardlink_if_same "$o1pyc" "$o2pyc"
|
||||||
|
done
|
||||||
|
exit 0
|
@ -0,0 +1,163 @@
|
|||||||
|
"""Module/script to clamp the mtimes of all .py files to $SOURCE_DATE_EPOCH
|
||||||
|
|
||||||
|
When called as a script with arguments, this compiles the directories
|
||||||
|
given as arguments recursively.
|
||||||
|
|
||||||
|
If upstream is interested, this can be later integrated to the compileall module
|
||||||
|
as an additional option (e.g. --clamp-source-mtime).
|
||||||
|
|
||||||
|
License:
|
||||||
|
This has been derived from the Python's compileall module
|
||||||
|
and it follows Python licensing. For more info see: https://www.python.org/psf/license/
|
||||||
|
"""
|
||||||
|
from __future__ import print_function
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Python 3.6 and higher
|
||||||
|
PY36 = sys.version_info[0:2] >= (3, 6)
|
||||||
|
|
||||||
|
__all__ = ["clamp_dir", "clamp_file"]
|
||||||
|
|
||||||
|
|
||||||
|
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)):
|
||||||
|
for result in _walk_dir(fullname, maxlevels=maxlevels - 1,
|
||||||
|
quiet=quiet):
|
||||||
|
yield result
|
||||||
|
|
||||||
|
|
||||||
|
def clamp_dir(dir, source_date_epoch, quiet=0):
|
||||||
|
"""Clamp the mtime of all modules in the given directory tree.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
dir: the directory to byte-compile
|
||||||
|
source_date_epoch: integer parsed from $SOURCE_DATE_EPOCH
|
||||||
|
quiet: full output with False or 0, errors only with 1,
|
||||||
|
no output with 2
|
||||||
|
"""
|
||||||
|
maxlevels = sys.getrecursionlimit()
|
||||||
|
files = _walk_dir(dir, quiet=quiet, maxlevels=maxlevels)
|
||||||
|
success = True
|
||||||
|
for file in files:
|
||||||
|
if not clamp_file(file, source_date_epoch, quiet=quiet):
|
||||||
|
success = False
|
||||||
|
return success
|
||||||
|
|
||||||
|
|
||||||
|
def clamp_file(fullname, source_date_epoch, quiet=0):
|
||||||
|
"""Clamp the mtime of one file.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
fullname: the file to byte-compile
|
||||||
|
source_date_epoch: integer parsed from $SOURCE_DATE_EPOCH
|
||||||
|
quiet: full output with False or 0, errors only with 1,
|
||||||
|
no output with 2
|
||||||
|
"""
|
||||||
|
if PY36 and quiet < 2 and isinstance(fullname, os.PathLike):
|
||||||
|
fullname = os.fspath(fullname)
|
||||||
|
else:
|
||||||
|
fullname = str(fullname)
|
||||||
|
name = os.path.basename(fullname)
|
||||||
|
|
||||||
|
if os.path.isfile(fullname) and not os.path.islink(fullname):
|
||||||
|
if name[-3:] == '.py':
|
||||||
|
try:
|
||||||
|
mtime = int(os.stat(fullname).st_mtime)
|
||||||
|
atime = int(os.stat(fullname).st_atime)
|
||||||
|
except OSError as e:
|
||||||
|
if quiet >= 2:
|
||||||
|
return False
|
||||||
|
elif quiet:
|
||||||
|
print('*** Error checking mtime of {!r}...'.format(fullname))
|
||||||
|
else:
|
||||||
|
print('*** ', end='')
|
||||||
|
print(e.__class__.__name__ + ':', e)
|
||||||
|
return False
|
||||||
|
if mtime > source_date_epoch:
|
||||||
|
if not quiet:
|
||||||
|
print('Clamping mtime of {!r}'.format(fullname))
|
||||||
|
try:
|
||||||
|
os.utime(fullname, (atime, source_date_epoch))
|
||||||
|
except OSError as e:
|
||||||
|
if quiet >= 2:
|
||||||
|
return False
|
||||||
|
elif quiet:
|
||||||
|
print('*** Error clamping mtime of {!r}...'.format(fullname))
|
||||||
|
else:
|
||||||
|
print('*** ', end='')
|
||||||
|
print(e.__class__.__name__ + ':', e)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Script main program."""
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
source_date_epoch = os.getenv('SOURCE_DATE_EPOCH')
|
||||||
|
if not source_date_epoch:
|
||||||
|
print("Not clamping source mtimes, $SOURCE_DATE_EPOCH not set")
|
||||||
|
return True # This is a success, no action needed
|
||||||
|
try:
|
||||||
|
source_date_epoch = int(source_date_epoch)
|
||||||
|
except ValueError:
|
||||||
|
print("$SOURCE_DATE_EPOCH must be an integer")
|
||||||
|
return False
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Clamp .py source mtime to $SOURCE_DATE_EPOCH.')
|
||||||
|
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('clamp_dest', metavar='FILE|DIR', nargs='+',
|
||||||
|
help=('zero or more file and directory paths '
|
||||||
|
'to clamp'))
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
clamp_dests = args.clamp_dest
|
||||||
|
|
||||||
|
success = True
|
||||||
|
try:
|
||||||
|
for dest in clamp_dests:
|
||||||
|
if os.path.isfile(dest):
|
||||||
|
if not clamp_file(dest, quiet=args.quiet,
|
||||||
|
source_date_epoch=source_date_epoch):
|
||||||
|
success = False
|
||||||
|
else:
|
||||||
|
if not clamp_dir(dest, quiet=args.quiet,
|
||||||
|
source_date_epoch=source_date_epoch):
|
||||||
|
success = False
|
||||||
|
return success
|
||||||
|
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,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 @@
|
|||||||
|
'''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,69 @@
|
|||||||
|
# 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
|
||||||
|
# Python 3.11+ no longer needs this: https://github.com/python/cpython/pull/27926 (but we support older Pythons as well)
|
||||||
|
|
||||||
|
%py_byte_compile()\
|
||||||
|
clamp_source_mtime () {\
|
||||||
|
python_binary="%{__env_unset_source_date_epoch_if_not_clamp_mtime} %1"\
|
||||||
|
bytecode_compilation_path="%2"\
|
||||||
|
PYTHONPATH="%{_rpmconfigdir}/redhat" $python_binary -s -B -m clamp_source_mtime $bytecode_compilation_path \
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
py2_byte_compile () {\
|
||||||
|
python_binary="%{__env_unset_source_date_epoch_if_not_clamp_mtime} 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\
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
py34_byte_compile () {\
|
||||||
|
python_binary="%{__env_unset_source_date_epoch_if_not_clamp_mtime} PYTHONHASHSEED=0 %1"\
|
||||||
|
bytecode_compilation_path="%2"\
|
||||||
|
PYTHONPATH="%{_rpmconfigdir}/redhat" $python_binary -s -B -m compileall2 %{?_smp_build_ncpus:-j%{_smp_build_ncpus}} -o 0 -o 1 -s $RPM_BUILD_ROOT -p / --hardlink-dupes $bytecode_compilation_path \
|
||||||
|
}\
|
||||||
|
py37_byte_compile () {\
|
||||||
|
python_binary="%{__env_unset_source_date_epoch_if_not_clamp_mtime} PYTHONHASHSEED=0 %1"\
|
||||||
|
bytecode_compilation_path="%2"\
|
||||||
|
PYTHONPATH="%{_rpmconfigdir}/redhat" $python_binary -s -B -m compileall2 %{?_smp_build_ncpus:-j%{_smp_build_ncpus}} -o 0 -o 1 -s $RPM_BUILD_ROOT -p / --hardlink-dupes --invalidation-mode=timestamp $bytecode_compilation_path \
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
py39_byte_compile () {\
|
||||||
|
python_binary="%{__env_unset_source_date_epoch_if_not_clamp_mtime} PYTHONHASHSEED=0 %1"\
|
||||||
|
bytecode_compilation_path="%2"\
|
||||||
|
$python_binary -s -B -m compileall %{?_smp_build_ncpus:-j%{_smp_build_ncpus}} -o 0 -o 1 -s $RPM_BUILD_ROOT -p / --hardlink-dupes --invalidation-mode=timestamp $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 \
|
||||||
|
# First, clamp source mtime https://fedoraproject.org/wiki/Changes/ReproducibleBuildsClampMtimes \
|
||||||
|
clamp_source_mtime "%1" "%2"; \
|
||||||
|
# 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 \
|
||||||
|
# Only Python 3.7+ supports and needs the --invalidation-mode option \
|
||||||
|
if [ "$python_version" -ge 39 ]; then \
|
||||||
|
py39_byte_compile "%1" "%2"; \
|
||||||
|
elif [ "$python_version" -ge 37 ]; then \
|
||||||
|
py37_byte_compile "%1" "%2"; \
|
||||||
|
elif [ "$python_version" -ge 34 ]; then \
|
||||||
|
py34_byte_compile "%1" "%2"; \
|
||||||
|
else \
|
||||||
|
py2_byte_compile "%1" "%2"; \
|
||||||
|
fi
|
@ -0,0 +1,176 @@
|
|||||||
|
# Memoize a macro to avoid calling the same expensive code multiple times in
|
||||||
|
# the specfile.
|
||||||
|
# There is no error handling,
|
||||||
|
# memoizing an undefined macro (or using such a key) has undefined behavior.
|
||||||
|
# Options:
|
||||||
|
# -n - The name of the macro to wrap
|
||||||
|
# -k - The name of the macro to use as a cache key
|
||||||
|
%_python_memoize(n:k:) %{lua:
|
||||||
|
local name = opt.n
|
||||||
|
-- NB: We use rpm.expand() here instead of the macros table to make sure errors
|
||||||
|
-- are propogated properly.
|
||||||
|
local cache_key = rpm.expand("%{" .. opt.k .. "}")
|
||||||
|
if not _python_macro_cache then
|
||||||
|
-- This is intentionally a global lua table
|
||||||
|
_python_macro_cache = {}
|
||||||
|
end
|
||||||
|
if not _python_macro_cache[cache_key] then
|
||||||
|
_python_macro_cache[cache_key] = {}
|
||||||
|
end
|
||||||
|
if not _python_macro_cache[cache_key][name] then
|
||||||
|
_python_macro_cache[cache_key][name] = rpm.expand("%{" .. name .. "}")
|
||||||
|
end
|
||||||
|
print(_python_macro_cache[cache_key][name])
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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_sitelib %{_python_memoize -n __python_sitelib -k __python}
|
||||||
|
|
||||||
|
%__python_sitearch %(RPM_BUILD_ROOT= %{__python} -Esc "import sysconfig; print(sysconfig.get_path('platlib', vars={'platbase': '%{_prefix}', 'base': '%{_prefix}'}))")
|
||||||
|
%python_sitearch %{_python_memoize -n __python_sitearch -k __python}
|
||||||
|
|
||||||
|
%__python_version %(RPM_BUILD_ROOT= %{__python} -Esc "import sys; sys.stdout.write('{0.major}.{0.minor}'.format(sys.version_info))")
|
||||||
|
%python_version %{_python_memoize -n __python_version -k __python}
|
||||||
|
|
||||||
|
%__python_version_nodots %(RPM_BUILD_ROOT= %{__python} -Esc "import sys; sys.stdout.write('{0.major}{0.minor}'.format(sys.version_info))")
|
||||||
|
%python_version_nodots %{_python_memoize -n __python_version_nodots -k __python}
|
||||||
|
|
||||||
|
%__python_platform %(RPM_BUILD_ROOT= %{__python} -Esc "import sysconfig; print(sysconfig.get_platform())")
|
||||||
|
%python_platform %{_python_memoize -n __python_platform -k __python}
|
||||||
|
|
||||||
|
%__python_platform_triplet %(RPM_BUILD_ROOT= %{__python} -Esc "import sysconfig; print(sysconfig.get_config_var('MULTIARCH'))")
|
||||||
|
%python_platform_triplet %{_python_memoize -n __python_platform_triplet -k __python}
|
||||||
|
|
||||||
|
%__python_ext_suffix %(RPM_BUILD_ROOT= %{__python} -Esc "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))")
|
||||||
|
%python_ext_suffix %{_python_memoize -n __python_ext_suffix -k __python}
|
||||||
|
|
||||||
|
%__python_cache_tag %(RPM_BUILD_ROOT= %{__python} -Esc "import sys; print(sys.implementation.cache_tag)")
|
||||||
|
%python_cache_tag %{_python_memoize -n __python_cache_tag -k __python}
|
||||||
|
|
||||||
|
%py_setup setup.py
|
||||||
|
%_py_shebang_s s
|
||||||
|
%__py_shebang_P %(RPM_BUILD_ROOT= %{__python} -Esc "import sys; print('P' if hasattr(sys.flags, 'safe_path') else '')")
|
||||||
|
%_py_shebang_P %{_python_memoize -n __py_shebang_P -k __python}
|
||||||
|
%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_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} --prefix %{_prefix} %{?*}
|
||||||
|
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="${PYTEST_XDIST_AUTO_NUM_WORKERS:-%{_smp_build_ncpus}}"}
|
||||||
|
|
||||||
|
%python_disable_dependency_generator() \
|
||||||
|
%undefine __pythondist_requires \
|
||||||
|
%{nil}
|
@ -0,0 +1,295 @@
|
|||||||
|
# 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.12
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
### BRP scripts (and related macros)
|
||||||
|
|
||||||
|
## Automatically compile python files
|
||||||
|
%py_auto_byte_compile 1
|
||||||
|
## Should python bytecompilation errors terminate a build?
|
||||||
|
%_python_bytecompile_errors_terminate_build 1
|
||||||
|
## Should python bytecompilation compile outside python specific directories?
|
||||||
|
## This always causes errors when enabled, see https://fedoraproject.org/wiki/Changes/No_more_automagic_Python_bytecompilation_phase_3
|
||||||
|
%_python_bytecompile_extra 0
|
||||||
|
## Helper macro to unset $SOURCE_DATE_EPOCH if %%clamp_mtime_to_source_date_epoch is not set
|
||||||
|
## https://fedoraproject.org/wiki/Changes/ReproducibleBuildsClampMtimes#Python_bytecode
|
||||||
|
%__env_unset_source_date_epoch_if_not_clamp_mtime %[0%{?clamp_mtime_to_source_date_epoch} == 0 ? "env -u SOURCE_DATE_EPOCH" : "env"]
|
||||||
|
|
||||||
|
## The individual BRP scripts
|
||||||
|
%__brp_python_bytecompile %{__env_unset_source_date_epoch_if_not_clamp_mtime} %{_rpmconfigdir}/redhat/brp-python-bytecompile "" "%{?_python_bytecompile_errors_terminate_build}" "%{?_python_bytecompile_extra}" "%{?_smp_build_ncpus:-j%{_smp_build_ncpus}}"
|
||||||
|
%__brp_fix_pyc_reproducibility %{_rpmconfigdir}/redhat/brp-fix-pyc-reproducibility
|
||||||
|
%__brp_python_hardlink %{_rpmconfigdir}/redhat/brp-python-hardlink
|
||||||
|
|
||||||
|
## This macro is included in redhat-rpm-config's %%__os_install_post
|
||||||
|
# Note that the order matters:
|
||||||
|
# 1. brp-python-bytecompile can create (or replace) pyc files
|
||||||
|
# 2. brp-fix-pyc-reproducibility can modify the pyc files from above
|
||||||
|
# 3. brp-python-hardlink de-duplicates identical pyc files
|
||||||
|
%__os_install_post_python \
|
||||||
|
%{?py_auto_byte_compile:%{?__brp_python_bytecompile}} \
|
||||||
|
%{?py_reproducible_pyc_path:%{?__brp_fix_pyc_reproducibility} "%{py_reproducible_pyc_path}"} \
|
||||||
|
%{?__brp_python_hardlink} \
|
||||||
|
%{nil}
|
||||||
|
|
||||||
|
|
||||||
|
# === 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 python3.9-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:FaA) %{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 option_a = '-a (insert BuildArch: noarch)'
|
||||||
|
local option_A = '-A (do not insert BuildArch: noarch (default))'
|
||||||
|
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 value_a = rpm.expand('%{-a}')
|
||||||
|
local value_A = rpm.expand('%{-A}')
|
||||||
|
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 value_a ~= '' and value_A ~= '' then
|
||||||
|
rpm.expand('%{error:%%%0: simultaneous ' .. option_a .. ' and ' .. option_A .. ' 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
|
||||||
|
local tags = summary .. '\\\n' .. requires
|
||||||
|
if value_a ~= '' then
|
||||||
|
tags = tags .. '\\\nBuildArch: noarch'
|
||||||
|
end
|
||||||
|
for i, line in ipairs({pkgdef, tags, description, files, ''}) do
|
||||||
|
print(line .. '\\\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}}
|
@ -0,0 +1,126 @@
|
|||||||
|
# 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} -Esc "import sysconfig; print(sysconfig.get_path('purelib', vars={'platbase': '%{_prefix}', 'base': '%{_prefix}'}))")
|
||||||
|
%python3_sitelib %{_python_memoize -n __python3_sitelib -k __python3}
|
||||||
|
|
||||||
|
%__python3_sitearch %(RPM_BUILD_ROOT= %{__python3} -Esc "import sysconfig; print(sysconfig.get_path('platlib', vars={'platbase': '%{_prefix}', 'base': '%{_prefix}'}))")
|
||||||
|
%python3_sitearch %{_python_memoize -n __python3_sitearch -k __python3}
|
||||||
|
|
||||||
|
%__python3_version %(RPM_BUILD_ROOT= %{__python3} -Esc "import sys; sys.stdout.write('{0.major}.{0.minor}'.format(sys.version_info))")
|
||||||
|
%python3_version %{_python_memoize -n __python3_version -k __python3}
|
||||||
|
|
||||||
|
%__python3_version_nodots %(RPM_BUILD_ROOT= %{__python3} -Esc "import sys; sys.stdout.write('{0.major}{0.minor}'.format(sys.version_info))")
|
||||||
|
%python3_version_nodots %{_python_memoize -n __python3_version_nodots -k __python3}
|
||||||
|
|
||||||
|
%__python3_platform %(RPM_BUILD_ROOT= %{__python3} -Esc "import sysconfig; print(sysconfig.get_platform())")
|
||||||
|
%python3_platform %{_python_memoize -n __python3_platform -k __python3}
|
||||||
|
|
||||||
|
%__python3_platform_triplet %(RPM_BUILD_ROOT= %{__python3} -Esc "import sysconfig; print(sysconfig.get_config_var('MULTIARCH'))")
|
||||||
|
%python3_platform_triplet %{_python_memoize -n __python3_platform_triplet -k __python3}
|
||||||
|
|
||||||
|
%__python3_ext_suffix %(RPM_BUILD_ROOT= %{__python3} -Esc "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))")
|
||||||
|
%python3_ext_suffix %{_python_memoize -n __python3_ext_suffix -k __python3}
|
||||||
|
|
||||||
|
%__python3_cache_tag %(RPM_BUILD_ROOT= %{__python3} -Esc "import sys; print(sys.implementation.cache_tag)")
|
||||||
|
%python3_cache_tag %{_python_memoize -n __python3_cache_tag -k __python3}
|
||||||
|
|
||||||
|
%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_shebang_P %{_python_memoize -n __py3_shebang_P -k __python3}
|
||||||
|
%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_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} --prefix %{_prefix} %{?*}
|
||||||
|
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="${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 @@
|
|||||||
|
#!/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,101 @@
|
|||||||
|
-- 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 -> python3.X-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 = rpm.expand('%{__default_python3_pkgversion}')
|
||||||
|
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,588 @@
|
|||||||
|
Name: python-rpm-macros
|
||||||
|
Summary: The common Python RPM macros
|
||||||
|
|
||||||
|
URL: https://src.fedoraproject.org/rpms/python-rpm-macros/
|
||||||
|
|
||||||
|
# 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
|
||||||
|
Source304: clamp_source_mtime.py
|
||||||
|
|
||||||
|
# BRP scripts
|
||||||
|
# This one is from redhat-rpm-config < 190
|
||||||
|
# A new upstream is forming in https://github.com/rpm-software-management/python-rpm-packaging/blob/main/scripts/brp-python-bytecompile
|
||||||
|
# But our version is riddled with Fedora-isms
|
||||||
|
# We might eventually move to upstream source + Fedora patches, but we are not there yet
|
||||||
|
Source401: brp-python-bytecompile
|
||||||
|
# This one is from https://github.com/rpm-software-management/python-rpm-packaging/blob/main/scripts/brp-python-hardlink
|
||||||
|
# But we don't use a link in case it changes in upstream, there are no "versions" there yet
|
||||||
|
# This was removed from RPM 4.17+ so we maintain it here instead
|
||||||
|
Source402: brp-python-hardlink
|
||||||
|
# This one is from redhat-rpm-config < 190
|
||||||
|
# It has no upstream yet
|
||||||
|
Source403: brp-fix-pyc-reproducibility
|
||||||
|
|
||||||
|
# macros and lua: MIT
|
||||||
|
# import_all_modules.py: MIT
|
||||||
|
# compileall2.py, clamp_source_mtime.py: PSF-2.0
|
||||||
|
# pathfix.py: PSF-2.0
|
||||||
|
# brp scripts: GPL-2.0-or-later
|
||||||
|
License: MIT AND PSF-2.0 AND GPL-2.0-or-later
|
||||||
|
|
||||||
|
# The package version MUST be always the same as %%{__default_python3_version}.
|
||||||
|
# To have only one source of truth, we load the macro and use it.
|
||||||
|
# The macro is defined in python-srpm-macros.
|
||||||
|
%{lua:
|
||||||
|
if posix.stat(rpm.expand('%{SOURCE102}')) then
|
||||||
|
rpm.load(rpm.expand('%{SOURCE102}'))
|
||||||
|
elseif posix.stat('macros.python-srpm') then
|
||||||
|
-- something is parsing the spec without _sourcedir macro properly set
|
||||||
|
rpm.load('macros.python-srpm')
|
||||||
|
end
|
||||||
|
}
|
||||||
|
Version: %{__default_python3_version}
|
||||||
|
Release: 8.1%{?dist}
|
||||||
|
|
||||||
|
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
|
||||||
|
# Versions before 190 contained some brp scripts moved into python-srpm-macros
|
||||||
|
Requires: redhat-rpm-config >= 190
|
||||||
|
|
||||||
|
# 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}
|
||||||
|
|
||||||
|
%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 clamp_source_mtime.py %{buildroot}%{_rpmconfigdir}/redhat/
|
||||||
|
install -m 644 import_all_modules.py %{buildroot}%{_rpmconfigdir}/redhat/
|
||||||
|
install -m 644 pathfix.py %{buildroot}%{_rpmconfigdir}/redhat/
|
||||||
|
install -m 755 brp-* %{buildroot}%{_rpmconfigdir}/redhat/
|
||||||
|
|
||||||
|
|
||||||
|
# We define our own BRPs here to use the ones from the %%{buildroot},
|
||||||
|
# that way, this package can be built when it includes them for the first time.
|
||||||
|
# It also ensures that:
|
||||||
|
# - our BRPs can execute
|
||||||
|
# - if our BRPs affect this package, we don't need to build it twice
|
||||||
|
%define add_buildroot() %{lua:print((macros[macros[1]]:gsub(macros._rpmconfigdir, macros.buildroot .. macros._rpmconfigdir)))}
|
||||||
|
%global __brp_python_bytecompile %{add_buildroot __brp_python_bytecompile}
|
||||||
|
%global __brp_python_hardlink %{add_buildroot __brp_python_hardlink}
|
||||||
|
%global __brp_fix_pyc_reproducibility %{add_buildroot __brp_fix_pyc_reproducibility}
|
||||||
|
|
||||||
|
|
||||||
|
%check
|
||||||
|
# no macros in comments
|
||||||
|
grep -E '^#[^%%]*%%[^%%]' %{buildroot}%{rpmmacrodir}/macros.* && exit 1 || true
|
||||||
|
|
||||||
|
|
||||||
|
%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
|
||||||
|
%{_rpmconfigdir}/redhat/clamp_source_mtime.py
|
||||||
|
%{_rpmconfigdir}/redhat/brp-python-bytecompile
|
||||||
|
%{_rpmconfigdir}/redhat/brp-python-hardlink
|
||||||
|
%{_rpmconfigdir}/redhat/brp-fix-pyc-reproducibility
|
||||||
|
%{_rpmluadir}/fedora/srpm/python.lua
|
||||||
|
|
||||||
|
%files -n python3-rpm-macros
|
||||||
|
%{rpmmacrodir}/macros.python3
|
||||||
|
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* Tue Nov 26 2024 MSVSphere Packaging Team <packager@msvsphere-os.ru> - 3.12-8.1
|
||||||
|
- Rebuilt for MSVSphere 10
|
||||||
|
|
||||||
|
* Tue Jun 25 2024 Cristian Le <fedora@lecris.me> - 3.12-8.1
|
||||||
|
- %%python_extras_subpkg: Add option -a to include BuildArch: noarch
|
||||||
|
|
||||||
|
* Mon Jun 24 2024 Troy Dawson <tdawson@redhat.com> - 3.12-8
|
||||||
|
- Bump release for June 2024 mass rebuild
|
||||||
|
|
||||||
|
* Thu Jan 25 2024 Miro Hrončok <mhroncok@redhat.com> - 3.12-7
|
||||||
|
- %%py3_test_envvars: Only set $PYTEST_XDIST_AUTO_NUM_WORKERS if not already set
|
||||||
|
|
||||||
|
* Mon Jan 22 2024 Fedora Release Engineering <releng@fedoraproject.org> - 3.12-6
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||||
|
|
||||||
|
* Mon Oct 09 2023 Maxwell G <maxwell@gtmx.me> - 3.12-5
|
||||||
|
- Fix python macro memoizing to account for changing %%__python3
|
||||||
|
|
||||||
|
* Tue Sep 05 2023 Maxwell G <maxwell@gtmx.me> - 3.12-4
|
||||||
|
- Remove %%py3_build_egg and %%py3_install_egg macros.
|
||||||
|
|
||||||
|
* Wed Aug 09 2023 Karolina Surma <ksurma@redhat.com> - 3.12-3
|
||||||
|
- Declare the license as an SPDX expression
|
||||||
|
|
||||||
|
* Fri Jul 21 2023 Fedora Release Engineering <releng@fedoraproject.org> - 3.12-2
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
|
||||||
|
|
||||||
|
* Tue Jun 13 2023 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.12-1
|
||||||
|
- Update main Python to Python 3.12
|
||||||
|
- https://fedoraproject.org/wiki/Changes/Python3.12
|
||||||
|
|
||||||
|
* Thu Mar 16 2023 Miro Hrončok <mhroncok@redhat.com> - 3.11-10
|
||||||
|
- Don't assume %%_smp_mflags only ever contains -jX, use -j%%_smp_build_ncpus directly
|
||||||
|
- Fixes: rhbz#2179149
|
||||||
|
|
||||||
|
* Fri Jan 20 2023 Miro Hrončok <mhroncok@redhat.com> - 3.11-9
|
||||||
|
- Memoize values of macros that execute python to get their value
|
||||||
|
- Fixes: rhbz#2155505
|
||||||
|
|
||||||
|
* Fri Jan 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 3.11-8
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
|
||||||
|
|
||||||
|
* Mon Dec 19 2022 Miro Hrončok <mhroncok@redhat.com> - 3.11-7
|
||||||
|
- Bytecompilation: Unset $SOURCE_DATE_EPOCH when %%clamp_mtime_to_source_date_epoch is not set
|
||||||
|
- Bytecompilation: Pass --invalidation-mode=timestamp to compileall (on Python 3.7+)
|
||||||
|
- Bytecompilation: Clamp source mtime: https://fedoraproject.org/wiki/Changes/ReproducibleBuildsClampMtimes
|
||||||
|
- Bytecompilation: Compile Python files in parallel, according to %%_smp_mflags
|
||||||
|
|
||||||
|
* Sun Nov 13 2022 Miro Hrončok <mhroncok@redhat.com> - 3.11-6
|
||||||
|
- Set PYTEST_XDIST_AUTO_NUM_WORKERS=%%{_smp_build_ncpus} from %%pytest
|
||||||
|
- pytest-xdist 3+ respects this value when -n auto is used
|
||||||
|
- Expose the environment variables used by %%pytest via %%{py3_test_envvars}
|
||||||
|
|
||||||
|
* Tue Oct 25 2022 Lumír Balhar <lbalhar@redhat.com> - 3.11-5
|
||||||
|
- Include pathfix.py in this package
|
||||||
|
|
||||||
|
* Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 3.10-4
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
|
||||||
|
|
||||||
|
* Tue Jul 19 2022 Miro Hrončok <mhroncok@redhat.com> - 3.11-3
|
||||||
|
- Add "P" to %%py3_shbang_opts, %%py3_shbang_opts_nodash, %%py3_shebang_flags
|
||||||
|
and to %%py_shbang_opts, %%py_shbang_opts_nodash, %%py_shebang_flags
|
||||||
|
- https://fedoraproject.org/wiki/Changes/PythonSafePath
|
||||||
|
|
||||||
|
* Mon Jun 20 2022 Miro Hrončok <mhroncok@redhat.com> - 3.11-2
|
||||||
|
- Define %%python3_cache_tag / %%python_cache_tag, e.g. cpython-311
|
||||||
|
|
||||||
|
* Mon Jun 13 2022 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.11-1
|
||||||
|
- Update main Python to Python 3.11
|
||||||
|
- https://fedoraproject.org/wiki/Changes/Python3.11
|
||||||
|
|
||||||
|
* Thu May 26 2022 Owen Taylor <otaylor@redhat.com> - 3.10-18
|
||||||
|
- Support installing to %%{_prefix} other than /usr
|
||||||
|
|
||||||
|
* Tue Feb 08 2022 Tomas Orsava <torsava@redhat.com> - 3.10-17
|
||||||
|
- %%py_provides: Do not generate Obsoletes for names containing parentheses
|
||||||
|
|
||||||
|
* Mon Jan 31 2022 Miro Hrončok <mhroncok@redhat.com> - 3.10-16
|
||||||
|
- Explicitly opt-out from Python name-based provides and obsoletes generators
|
||||||
|
|
||||||
|
* Tue Dec 21 2021 Tomas Orsava <torsava@redhat.com> - 3.10-15
|
||||||
|
- 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
|
||||||
|
|
||||||
|
* Wed Dec 08 2021 Miro Hrončok <mhroncok@redhat.com> - 3.10-14
|
||||||
|
- 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.10-13
|
||||||
|
- Fix multiline arguments processing for %%py_check_import
|
||||||
|
Resolves: rhbz#2018809
|
||||||
|
- Fix %%py_shebang_flags handling within %%py_check_import
|
||||||
|
Resolves: rhbz#2018615
|
||||||
|
- Process .pth files in buildroot's sitedirs in %%py_check_import
|
||||||
|
Resolves: rhbz#2018551
|
||||||
|
- Move import_all_modules.py from python-srpm-macros to python-rpm-macros
|
||||||
|
|
||||||
|
* Mon Oct 25 2021 Karolina Surma <ksurma@redhat.com> - 3.10-12
|
||||||
|
- 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
|
||||||
|
|
||||||
|
* Wed Oct 20 2021 Tomas Orsava <torsava@redhat.com> - 3.10-11
|
||||||
|
- Define a new macros %%python_wheel_dir and %%python_wheel_pkg_prefix
|
||||||
|
|
||||||
|
* Tue Oct 12 2021 Lumír Balhar <lbalhar@redhat.com> - 3.10-10
|
||||||
|
- Non-existing path in py_reproducible_pyc_path causes build to fail
|
||||||
|
Resolves: rhbz#2011056
|
||||||
|
|
||||||
|
* Thu Sep 09 2021 Miro Hrončok <mhroncok@redhat.com> - 3.10-9
|
||||||
|
- Set $RPM_BUILD_ROOT in %%{python3_...} macros
|
||||||
|
to allow selecting alternate sysconfig install scheme based on that variable
|
||||||
|
|
||||||
|
* Thu Sep 09 2021 Petr Viktorin <pviktori@redhat.com> - 3.10-8
|
||||||
|
- Use --hardlink-dupes in %%py_byte_compile and brp-python-bytecompile
|
||||||
|
(for Python 3)
|
||||||
|
- Resolves: rhbz#1977895
|
||||||
|
|
||||||
|
* Fri Jul 23 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.9-7
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
|
||||||
|
|
||||||
|
* Wed Jul 07 2021 Miro Hrončok <mhroncok@redhat.com> - 3.10-6
|
||||||
|
- Move Python related BuildRoot Policy scripts from redhat-rpm-config to python-srpm-macros
|
||||||
|
|
||||||
|
* Wed Jul 07 2021 Miro Hrončok <mhroncok@redhat.com> - 3.10-5
|
||||||
|
- Introduce %%py3_check_import
|
||||||
|
|
||||||
|
* Wed Jun 30 2021 Miro Hrončok <mhroncok@redhat.com> - 3.10-4
|
||||||
|
- Include brp-python-hardlink in python-srpm-macros since it is no longer in RPM 4.17+
|
||||||
|
|
||||||
|
* Mon Jun 28 2021 Miro Hrončok <mhroncok@redhat.com> - 3.10-3
|
||||||
|
- %%pytest: Set $PYTEST_ADDOPTS when %%{__pytest_addopts} is defined
|
||||||
|
- Related: rhzb#1935212
|
||||||
|
|
||||||
|
* Tue Jun 15 2021 Miro Hrončok <mhroncok@redhat.com> - 3.10-2
|
||||||
|
- Fix %%python_provide when fed python3.10-foo to obsolete python-foo instead of python--foo
|
||||||
|
|
||||||
|
* Tue Jun 01 2021 Miro Hrončok <mhroncok@redhat.com> - 3.10-1
|
||||||
|
- Update main Python to Python 3.10
|
||||||
|
- https://fedoraproject.org/wiki/Changes/Python3.10
|
||||||
|
|
||||||
|
* Tue Apr 27 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-38
|
||||||
|
- Escape %% symbols in macro files comments
|
||||||
|
- Fixes: rhbz#1953910
|
||||||
|
|
||||||
|
* Wed Apr 07 2021 Karolina Surma <ksurma@redhat.com> - 3.9-37
|
||||||
|
- Use sysconfig.get_path() to get %%python3_sitelib and %%python3_sitearch
|
||||||
|
- Fixes: rhbz#1946972
|
||||||
|
|
||||||
|
* Mon Mar 29 2021 Miro Hrončok <mhroncok@redhat.com> - 3.9-36
|
||||||
|
- Allow commas as argument separator for extras names in %%python_extras_subpkg
|
||||||
|
- Fixes: rhbz#1936486
|
||||||
|
|
||||||
|
* 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