2021-10-28 13:14:37 -03:00
|
|
|
import os
|
|
|
|
import os.path
|
|
|
|
import shlex
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
2023-09-27 05:18:39 -03:00
|
|
|
import sysconfig
|
|
|
|
from test import support
|
2021-10-28 13:14:37 -03:00
|
|
|
|
|
|
|
|
2023-09-27 07:32:12 -03:00
|
|
|
def get_python_source_dir():
|
|
|
|
src_dir = sysconfig.get_config_var('abs_srcdir')
|
|
|
|
if not src_dir:
|
|
|
|
src_dir = sysconfig.get_config_var('srcdir')
|
|
|
|
return os.path.abspath(src_dir)
|
|
|
|
|
|
|
|
|
2021-10-28 13:14:37 -03:00
|
|
|
TESTS_DIR = os.path.dirname(__file__)
|
|
|
|
TOOL_ROOT = os.path.dirname(TESTS_DIR)
|
2023-09-27 07:32:12 -03:00
|
|
|
SRCDIR = get_python_source_dir()
|
2021-10-28 13:14:37 -03:00
|
|
|
|
|
|
|
MAKE = shutil.which('make')
|
|
|
|
FREEZE = os.path.join(TOOL_ROOT, 'freeze.py')
|
|
|
|
OUTDIR = os.path.join(TESTS_DIR, 'outdir')
|
|
|
|
|
|
|
|
|
|
|
|
class UnsupportedError(Exception):
|
|
|
|
"""The operation isn't supported."""
|
|
|
|
|
|
|
|
|
2023-10-05 22:08:34 -03:00
|
|
|
def _run_quiet(cmd, *, cwd=None):
|
|
|
|
if cwd:
|
|
|
|
print('+', 'cd', cwd, flush=True)
|
|
|
|
print('+', shlex.join(cmd), flush=True)
|
2021-11-10 22:01:53 -04:00
|
|
|
try:
|
|
|
|
return subprocess.run(
|
|
|
|
cmd,
|
|
|
|
cwd=cwd,
|
|
|
|
capture_output=True,
|
|
|
|
text=True,
|
|
|
|
check=True,
|
|
|
|
)
|
|
|
|
except subprocess.CalledProcessError as err:
|
|
|
|
# Don't be quiet if things fail
|
|
|
|
print(f"{err.__class__.__name__}: {err}")
|
|
|
|
print("--- STDOUT ---")
|
|
|
|
print(err.stdout)
|
|
|
|
print("--- STDERR ---")
|
|
|
|
print(err.stderr)
|
|
|
|
print("---- END ----")
|
|
|
|
raise
|
2021-10-28 13:14:37 -03:00
|
|
|
|
|
|
|
|
2023-10-05 22:08:34 -03:00
|
|
|
def _run_stdout(cmd):
|
|
|
|
proc = _run_quiet(cmd)
|
2021-10-28 13:14:37 -03:00
|
|
|
return proc.stdout.strip()
|
|
|
|
|
|
|
|
|
|
|
|
def find_opt(args, name):
|
|
|
|
opt = f'--{name}'
|
|
|
|
optstart = f'{opt}='
|
|
|
|
for i, arg in enumerate(args):
|
|
|
|
if arg == opt or arg.startswith(optstart):
|
|
|
|
return i
|
|
|
|
return -1
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_opt(args, name, value):
|
|
|
|
opt = f'--{name}'
|
|
|
|
pos = find_opt(args, name)
|
|
|
|
if value is None:
|
|
|
|
if pos < 0:
|
|
|
|
args.append(opt)
|
|
|
|
else:
|
|
|
|
args[pos] = opt
|
|
|
|
elif pos < 0:
|
|
|
|
args.extend([opt, value])
|
|
|
|
else:
|
|
|
|
arg = args[pos]
|
|
|
|
if arg == opt:
|
|
|
|
if pos == len(args) - 1:
|
|
|
|
raise NotImplementedError((args, opt))
|
|
|
|
args[pos + 1] = value
|
|
|
|
else:
|
|
|
|
args[pos] = f'{opt}={value}'
|
|
|
|
|
|
|
|
|
2021-11-23 09:43:40 -04:00
|
|
|
def copy_source_tree(newroot, oldroot):
|
2023-09-27 05:18:39 -03:00
|
|
|
print(f'copying the source tree from {oldroot} to {newroot}...')
|
2021-10-28 13:14:37 -03:00
|
|
|
if os.path.exists(newroot):
|
|
|
|
if newroot == SRCDIR:
|
|
|
|
raise Exception('this probably isn\'t what you wanted')
|
2021-11-23 09:43:40 -04:00
|
|
|
shutil.rmtree(newroot)
|
2023-02-12 00:54:28 -04:00
|
|
|
|
2023-09-27 05:18:39 -03:00
|
|
|
shutil.copytree(oldroot, newroot, ignore=support.copy_python_src_ignore)
|
2021-11-23 09:43:40 -04:00
|
|
|
if os.path.exists(os.path.join(newroot, 'Makefile')):
|
2023-10-05 22:08:34 -03:00
|
|
|
# Out-of-tree builds require a clean srcdir. "make clean" keeps
|
|
|
|
# the "python" program, so use "make distclean" instead.
|
|
|
|
_run_quiet([MAKE, 'distclean'], cwd=newroot)
|
2021-10-28 13:14:37 -03:00
|
|
|
|
|
|
|
|
|
|
|
##################################
|
|
|
|
# freezing
|
|
|
|
|
|
|
|
def prepare(script=None, outdir=None):
|
2023-10-05 22:08:34 -03:00
|
|
|
print()
|
|
|
|
print("cwd:", os.getcwd())
|
|
|
|
|
2021-10-28 13:14:37 -03:00
|
|
|
if not outdir:
|
|
|
|
outdir = OUTDIR
|
|
|
|
os.makedirs(outdir, exist_ok=True)
|
|
|
|
|
|
|
|
# Write the script to disk.
|
|
|
|
if script:
|
|
|
|
scriptfile = os.path.join(outdir, 'app.py')
|
2021-11-23 09:43:40 -04:00
|
|
|
print(f'creating the script to be frozen at {scriptfile}')
|
2021-11-30 00:16:40 -04:00
|
|
|
with open(scriptfile, 'w', encoding='utf-8') as outfile:
|
2021-10-28 13:14:37 -03:00
|
|
|
outfile.write(script)
|
|
|
|
|
2021-11-23 09:43:40 -04:00
|
|
|
# Make a copy of the repo to avoid affecting the current build
|
|
|
|
# (e.g. changing PREFIX).
|
2021-10-28 13:14:37 -03:00
|
|
|
srcdir = os.path.join(outdir, 'cpython')
|
2021-11-23 09:43:40 -04:00
|
|
|
copy_source_tree(srcdir, SRCDIR)
|
2021-10-28 13:14:37 -03:00
|
|
|
|
|
|
|
# We use an out-of-tree build (instead of srcdir).
|
|
|
|
builddir = os.path.join(outdir, 'python-build')
|
|
|
|
os.makedirs(builddir, exist_ok=True)
|
|
|
|
|
|
|
|
# Run configure.
|
|
|
|
print(f'configuring python in {builddir}...')
|
2023-09-27 05:18:39 -03:00
|
|
|
config_args = shlex.split(sysconfig.get_config_var('CONFIG_ARGS') or '')
|
|
|
|
cmd = [os.path.join(srcdir, 'configure'), *config_args]
|
2021-10-28 13:14:37 -03:00
|
|
|
ensure_opt(cmd, 'cache-file', os.path.join(outdir, 'python-config.cache'))
|
|
|
|
prefix = os.path.join(outdir, 'python-installation')
|
|
|
|
ensure_opt(cmd, 'prefix', prefix)
|
2023-10-05 22:08:34 -03:00
|
|
|
_run_quiet(cmd, cwd=builddir)
|
2021-10-28 13:14:37 -03:00
|
|
|
|
|
|
|
if not MAKE:
|
|
|
|
raise UnsupportedError('make')
|
|
|
|
|
2023-09-30 22:14:57 -03:00
|
|
|
cores = os.process_cpu_count()
|
2023-02-12 02:07:52 -04:00
|
|
|
if cores and cores >= 3:
|
|
|
|
# this test is most often run as part of the whole suite with a lot
|
|
|
|
# of other tests running in parallel, from 1-2 vCPU systems up to
|
|
|
|
# people's NNN core beasts. Don't attempt to use it all.
|
2023-10-05 22:08:34 -03:00
|
|
|
jobs = cores * 2 // 3
|
|
|
|
parallel = f'-j{jobs}'
|
2023-02-12 02:07:52 -04:00
|
|
|
else:
|
|
|
|
parallel = '-j2'
|
|
|
|
|
2021-10-28 13:14:37 -03:00
|
|
|
# Build python.
|
2023-02-12 02:07:52 -04:00
|
|
|
print(f'building python {parallel=} in {builddir}...')
|
2023-10-05 22:08:34 -03:00
|
|
|
_run_quiet([MAKE, parallel], cwd=builddir)
|
2021-10-28 13:14:37 -03:00
|
|
|
|
|
|
|
# Install the build.
|
|
|
|
print(f'installing python into {prefix}...')
|
2023-10-05 22:08:34 -03:00
|
|
|
_run_quiet([MAKE, 'install'], cwd=builddir)
|
2021-10-28 13:14:37 -03:00
|
|
|
python = os.path.join(prefix, 'bin', 'python3')
|
|
|
|
|
|
|
|
return outdir, scriptfile, python
|
|
|
|
|
|
|
|
|
|
|
|
def freeze(python, scriptfile, outdir):
|
|
|
|
if not MAKE:
|
|
|
|
raise UnsupportedError('make')
|
|
|
|
|
|
|
|
print(f'freezing {scriptfile}...')
|
|
|
|
os.makedirs(outdir, exist_ok=True)
|
2022-05-05 22:41:24 -03:00
|
|
|
# Use -E to ignore PYTHONSAFEPATH
|
2023-10-05 22:08:34 -03:00
|
|
|
_run_quiet([python, '-E', FREEZE, '-o', outdir, scriptfile], cwd=outdir)
|
|
|
|
_run_quiet([MAKE], cwd=os.path.dirname(scriptfile))
|
2021-10-28 13:14:37 -03:00
|
|
|
|
|
|
|
name = os.path.basename(scriptfile).rpartition('.')[0]
|
|
|
|
executable = os.path.join(outdir, name)
|
|
|
|
return executable
|
|
|
|
|
|
|
|
|
|
|
|
def run(executable):
|
|
|
|
return _run_stdout([executable])
|