Implement testing for universal2/arm64 wheels
This commit is contained in:
+60
-9
@@ -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,16 +330,67 @@ 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...')
|
|
||||||
|
machine_arch = platform.machine()
|
||||||
|
testing_archs: List[str] = []
|
||||||
|
|
||||||
|
if machine_arch == 'x86_64':
|
||||||
|
if config.identifier.endswith('_arm64'):
|
||||||
|
log.warning(wrap_text('''
|
||||||
|
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']
|
||||||
|
|
||||||
|
if options.test_command and len(testing_archs) > 0:
|
||||||
|
for testing_arch in testing_archs:
|
||||||
|
log.step('Testing wheel...' if testing_arch == machine_arch else f'Testing wheel on {testing_arch}...')
|
||||||
|
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
# there are no dependencies that were pulled in at build time.
|
# there are no dependencies that were pulled in at build time.
|
||||||
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
||||||
venv_dir = Path(tempfile.mkdtemp())
|
venv_dir = Path(tempfile.mkdtemp())
|
||||||
|
|
||||||
|
arch_prefix = []
|
||||||
|
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}")
|
||||||
|
|
||||||
|
# define a custom 'call' function that adds the arch prefix each time
|
||||||
|
def call_with_arch(args: Sequence[PathOrStr], **kwargs: Any) -> int:
|
||||||
|
if isinstance(args, str):
|
||||||
|
args = ' '.join(arch_prefix) + ' ' + args
|
||||||
|
else:
|
||||||
|
args = [*arch_prefix, *args]
|
||||||
|
return call(args, **kwargs)
|
||||||
|
|
||||||
# Use --no-download to ensure determinism by using seed libraries
|
# Use --no-download to ensure determinism by using seed libraries
|
||||||
# built into virtualenv
|
# built into virtualenv
|
||||||
call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
call_with_arch(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
||||||
|
|
||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
virtualenv_env['PATH'] = os.pathsep.join([
|
virtualenv_env['PATH'] = os.pathsep.join([
|
||||||
@@ -348,18 +399,18 @@ def build(options: BuildOptions) -> None:
|
|||||||
])
|
])
|
||||||
|
|
||||||
# check that we are using the Python from the virtual environment
|
# check that we are using the Python from the virtual environment
|
||||||
call(['which', 'python'], env=virtualenv_env)
|
call_with_arch(['which', 'python'], env=virtualenv_env)
|
||||||
|
|
||||||
if options.before_test:
|
if options.before_test:
|
||||||
before_test_prepared = prepare_command(options.before_test, project='.', package=options.package_dir)
|
before_test_prepared = prepare_command(options.before_test, project='.', package=options.package_dir)
|
||||||
call(before_test_prepared, env=virtualenv_env, shell=True)
|
call_with_arch(before_test_prepared, env=virtualenv_env, shell=True)
|
||||||
|
|
||||||
# install the wheel
|
# install the wheel
|
||||||
call(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
|
call_with_arch(['pip', 'install', str(repaired_wheel) + options.test_extras], env=virtualenv_env)
|
||||||
|
|
||||||
# test the wheel
|
# test the wheel
|
||||||
if options.test_requires:
|
if options.test_requires:
|
||||||
call(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
call_with_arch(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
||||||
|
|
||||||
# run the tests from $HOME, with an absolute path in the command
|
# run the tests from $HOME, with an absolute path in the command
|
||||||
# (this ensures that Python runs the tests against the installed wheel
|
# (this ensures that Python runs the tests against the installed wheel
|
||||||
@@ -369,7 +420,7 @@ def build(options: BuildOptions) -> None:
|
|||||||
project=Path('.').resolve(),
|
project=Path('.').resolve(),
|
||||||
package=options.package_dir.resolve()
|
package=options.package_dir.resolve()
|
||||||
)
|
)
|
||||||
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
|
call_with_arch(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
|
||||||
|
|
||||||
# clean up
|
# clean up
|
||||||
shutil.rmtree(venv_dir)
|
shutil.rmtree(venv_dir)
|
||||||
|
|||||||
Reference in New Issue
Block a user