Implement testing for universal2/arm64 wheels

This commit is contained in:
Joe Rickerby
2021-01-05 15:58:53 +00:00
parent df60457001
commit 577fd5e8e3
+87 -36
View File
@@ -7,7 +7,7 @@ import sys
import tempfile import tempfile
import textwrap import textwrap
from pathlib import Path from pathlib import Path
from typing import Dict, List, NamedTuple, Optional, Sequence, Tuple, cast from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, cast
from .environment import ParsedEnvironment from .environment import ParsedEnvironment
from .logger import log from .logger import log
@@ -330,49 +330,100 @@ def build(options: BuildOptions) -> None:
repaired_wheel.rename(renamed_wheel) repaired_wheel.rename(renamed_wheel)
repaired_wheel = renamed_wheel repaired_wheel = renamed_wheel
if options.test_command: log.step_end()
log.step('Testing wheel...')
# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
venv_dir = Path(tempfile.mkdtemp())
# Use --no-download to ensure determinism by using seed libraries machine_arch = platform.machine()
# built into virtualenv testing_archs: List[str] = []
call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
virtualenv_env = env.copy() if machine_arch == 'x86_64':
virtualenv_env['PATH'] = os.pathsep.join([ if config.identifier.endswith('_arm64'):
str(venv_dir / 'bin'), log.warning(wrap_text('''
virtualenv_env['PATH'], While arm64 wheels can be built on x86_64, they cannot be tested. The
]) ability to test the arm64 wheels will be added in a future release of
cibuildwheel, once Apple Silicon CI runners are widely available.
'''))
testing_archs = []
elif config.identifier.endswith('_universal2'):
log.warning(wrap_text('''
While universal2 wheels can be built on x86_64, the arm64 part of them
cannot currently be tested. The ability to test the arm64 part of a
universal2 wheel will be added in a future release of cibuildwheel, once
Apple Silicon CI runners are widely available.
'''))
testing_archs = ['x86_64']
else:
testing_archs = ['x86_64']
elif machine_arch == 'arm64':
if config.identifier.endswith('_x86_64'):
# testing using rosetta2 emulation
testing_archs = ['x86_64']
elif config.identifier.endswith('_universal2'):
# testing the x86_64 using rosetta2 emulation
testing_archs = ['arm64', 'x86_64']
else:
testing_archs = ['arm64']
# check that we are using the Python from the virtual environment if options.test_command and len(testing_archs) > 0:
call(['which', 'python'], env=virtualenv_env) for testing_arch in testing_archs:
log.step('Testing wheel...' if testing_arch == machine_arch else f'Testing wheel on {testing_arch}...')
if options.before_test: # set up a virtual environment to install and test from, to make sure
before_test_prepared = prepare_command(options.before_test, project='.', package=options.package_dir) # there are no dependencies that were pulled in at build time.
call(before_test_prepared, env=virtualenv_env, shell=True) call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
venv_dir = Path(tempfile.mkdtemp())
# install the wheel arch_prefix = []
call(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env) if testing_arch != machine_arch:
if machine_arch == 'arm64' and testing_arch == 'x86_64':
# rosetta2 will provide the emulation with just the arch prefix.
arch_prefix = ['arch', '-x86_64']
else:
raise RuntimeError("don't know how to emulate {testing_arch} on {machine_arch}")
# test the wheel # define a custom 'call' function that adds the arch prefix each time
if options.test_requires: def call_with_arch(args: Sequence[PathOrStr], **kwargs: Any) -> int:
call(['pip', 'install'] + options.test_requires, env=virtualenv_env) if isinstance(args, str):
args = ' '.join(arch_prefix) + ' ' + args
else:
args = [*arch_prefix, *args]
return call(args, **kwargs)
# run the tests from $HOME, with an absolute path in the command # Use --no-download to ensure determinism by using seed libraries
# (this ensures that Python runs the tests against the installed wheel # built into virtualenv
# and not the repo code) call_with_arch(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
test_command_prepared = prepare_command(
options.test_command,
project=Path('.').resolve(),
package=options.package_dir.resolve()
)
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
# clean up virtualenv_env = env.copy()
shutil.rmtree(venv_dir) virtualenv_env['PATH'] = os.pathsep.join([
str(venv_dir / 'bin'),
virtualenv_env['PATH'],
])
# check that we are using the Python from the virtual environment
call_with_arch(['which', 'python'], env=virtualenv_env)
if options.before_test:
before_test_prepared = prepare_command(options.before_test, project='.', package=options.package_dir)
call_with_arch(before_test_prepared, env=virtualenv_env, shell=True)
# install the wheel
call_with_arch(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
# test the wheel
if options.test_requires:
call_with_arch(['pip', 'install'] + options.test_requires, env=virtualenv_env)
# run the tests from $HOME, with an absolute path in the command
# (this ensures that Python runs the tests against the installed wheel
# and not the repo code)
test_command_prepared = prepare_command(
options.test_command,
project=Path('.').resolve(),
package=options.package_dir.resolve()
)
call_with_arch(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
# clean up
shutil.rmtree(venv_dir)
# we're all done here; move it to output (overwrite existing) # we're all done here; move it to output (overwrite existing)
shutil.move(str(repaired_wheel), options.output_dir) shutil.move(str(repaired_wheel), options.output_dir)