diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index 7c03f815d..afc433eec 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -2,6 +2,7 @@ import os, tempfile, subprocess, shutil, sys from collections import namedtuple from glob import glob +from zipfile import ZipFile try: from shlex import quote as shlex_quote @@ -20,9 +21,14 @@ 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): @@ -31,25 +37,28 @@ def get_nuget_args(configuration): python_name = python_name + "x86" return [python_name, "-Version", configuration.version, "-OutputDirectory", "C:/cibw/python"] + 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.5', arch="32", identifier='cp37-win32'), - PythonConfiguration(version='3.7.5', arch="64", identifier='cp37-win_amd64'), - PythonConfiguration(version='3.8.0', arch="32", identifier='cp38-win32'), - PythonConfiguration(version='3.8.0', 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.5', arch="32", identifier='cp37-win32', url=None), + PythonConfiguration(version='3.7.5', arch="64", identifier='cp37-win_amd64', url=None), + PythonConfiguration(version='3.8.0', arch="32", identifier='cp38-win32', url=None), + PythonConfiguration(version='3.8.0', 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)] @@ -73,6 +82,10 @@ def download(url, dest): file.write(response.read()) finally: response.close() + 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: @@ -102,7 +115,17 @@ def shell(args, env=None, cwd=None): 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 c2c02eb5f..f11885488 100644 --- a/test/shared/utils.py +++ b/test/shared/utils.py @@ -92,8 +92,13 @@ def get_platform_tags(python_abi_tag): 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': def get_platform_tags(python_abi_tag): if python_abi_tag == 'cp38-cp38': @@ -113,7 +118,7 @@ def get_platform_tags(python_abi_tag): 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