diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index abb3a0a2..01e1a8a2 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -4,6 +4,7 @@ import subprocess import tempfile from collections import namedtuple from glob import glob +from zipfile import ZipFile from .util import ( download, @@ -16,9 +17,14 @@ IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache') IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows' -def get_python_path(config): - nuget_args = get_nuget_args(config) - return os.path.join(nuget_args[-1], nuget_args[0] + "." + config.version, "tools") +def get_python_path(config): + if config.identifier.startswith('cp'): + nuget_args = get_nuget_args(config) + return os.path.join(nuget_args[-1], nuget_args[0] + "." + config.version, "tools") + elif config.identifier.startswith('pp'): + # Inside the PyPy zip file is a directory with the same name + filename = config.url.rsplit('/', 1)[-1] + return os.path.join("C:\\PyPy", os.path.splitext(filename)[0]) def get_nuget_args(configuration): @@ -29,24 +35,26 @@ def get_nuget_args(configuration): def get_python_configurations(build_selector): - PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier']) + PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'url']) python_configurations = [ - PythonConfiguration(version='2.7.17', arch="32", identifier='cp27-win32'), - PythonConfiguration(version='2.7.17', arch="64", identifier='cp27-win_amd64'), - PythonConfiguration(version='3.5.4', arch="32", identifier='cp35-win32'), - PythonConfiguration(version='3.5.4', arch="64", identifier='cp35-win_amd64'), - PythonConfiguration(version='3.6.8', arch="32", identifier='cp36-win32'), - PythonConfiguration(version='3.6.8', arch="64", identifier='cp36-win_amd64'), - PythonConfiguration(version='3.7.6', arch="32", identifier='cp37-win32'), - PythonConfiguration(version='3.7.6', arch="64", identifier='cp37-win_amd64'), - PythonConfiguration(version='3.8.1', arch="32", identifier='cp38-win32'), - PythonConfiguration(version='3.8.1', arch="64", identifier='cp38-win_amd64'), + PythonConfiguration(version='2.7.17', arch="32", identifier='cp27-win32', url=None), + PythonConfiguration(version='2.7.17', arch="64", identifier='cp27-win_amd64', url=None), + PythonConfiguration(version='3.5.4', arch="32", identifier='cp35-win32', url=None), + PythonConfiguration(version='3.5.4', arch="64", identifier='cp35-win_amd64', url=None), + PythonConfiguration(version='3.6.8', arch="32", identifier='cp36-win32', url=None), + PythonConfiguration(version='3.6.8', arch="64", identifier='cp36-win_amd64', url=None), + PythonConfiguration(version='3.7.6', arch="32", identifier='cp37-win32', url=None), + PythonConfiguration(version='3.7.6', arch="64", identifier='cp37-win_amd64', url=None), + PythonConfiguration(version='3.8.1', arch="32", identifier='cp38-win32', url=None), + PythonConfiguration(version='3.8.1', arch="64", identifier='cp38-win_amd64', url=None), + PythonConfiguration(version='2.7-v7.2.0', arch="32", identifier='pp272-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.2.0-win32.zip'), + PythonConfiguration(version='3.6-v7.2.0', arch="32", identifier='pp372-win32', url='https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.2.0-win32.zip'), ] if IS_RUNNING_ON_TRAVIS: # cannot install VCForPython27.msi which is needed for compiling C software # try with (and similar): msiexec /i VCForPython27.msi ALLUSERS=1 ACCEPT=YES /passive - python_configurations = [c for c in python_configurations if not c.version.startswith('2.7.')] + python_configurations = [c for c in python_configurations if not c.version.startswith('2.7')] # skip builds as required python_configurations = [c for c in python_configurations if build_selector(c.identifier)] @@ -60,6 +68,10 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef args = ['cmd', '/E:ON', '/V:ON', '/C'] + args return subprocess.check_call(' '.join(args), env=env, cwd=cwd) + def extract_zip(zip_src, dest): + with ZipFile(zip_src) as zip: + zip.extractall(dest) + if IS_RUNNING_ON_AZURE or IS_RUNNING_ON_TRAVIS: shell = simple_shell else: @@ -89,7 +101,17 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef for config in python_configurations: # install Python config_python_path = get_python_path(config) - simple_shell([nuget, "install"] + get_nuget_args(config)) + if config.identifier.startswith('cp'): + simple_shell([nuget, "install"] + get_nuget_args(config)) + elif config.identifier.startswith('pp') and not os.path.exists(config_python_path): + pypy_zip = config.url.rsplit('/', 1)[-1] + download(config.url, pypy_zip) + # Extract to the parent of config_python_path because the zip file still contains a directory + extract_zip(pypy_zip, os.path.dirname(config_python_path)) + pypy_exe = 'pypy.exe' if config.version[0] == '2' else 'pypy3.exe' + simple_shell(['mklink', os.path.join(config_python_path, 'python.exe'), os.path.join(config_python_path, pypy_exe)]) + simple_shell(['mklink', '/d', os.path.join(config_python_path, 'Scripts'), os.path.join(config_python_path, 'bin')]) + simple_shell(['chcp', '437']) # Workaround for https://bitbucket.org/pypy/pypy/issues/3081/ assert os.path.exists(os.path.join(config_python_path, 'python.exe')) # set up PATH and environment variables for run_with_env diff --git a/test/shared/utils.py b/test/shared/utils.py index fcacceb9..28b5dcf7 100644 --- a/test/shared/utils.py +++ b/test/shared/utils.py @@ -98,9 +98,13 @@ def expected_wheels(package_name, package_version, manylinux_versions=['manylinu for manylinux_version in manylinux_versions for architecture in architectures] elif platform == 'windows': - + # Second step: adding PyPy support on Windows + python_abi_tags.extend(['pp272-pypy_41', 'pp372-pp372']) def get_platform_tags(python_abi_tag): - return ['win32', 'win_amd64'] + if python_abi_tag.startswith('pp'): + return ['win32'] + else: + return ['win32', 'win_amd64'] elif platform == 'macos': @@ -120,7 +124,7 @@ def expected_wheels(package_name, package_version, manylinux_versions=['manylinu if IS_WINDOWS_RUNNING_ON_TRAVIS: # Python 2.7 isn't supported on Travis. - templates = [t for t in templates if '-cp27-' not in t] + templates = [t for t in templates if '-cp27-' not in t and '-pp2' not in t] return templates