diff --git a/.travis.yml b/.travis.yml index a38e2362..e96e532c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,15 @@ matrix: - brew update - brew outdated python || brew upgrade python + - os: windows + language: shell + before_install: + - choco install python3 --version 3.6.8 --no-progress -y + install: + - C:\\Python36\\python -m pip install -r requirements-dev.txt + script: + - C:\\Python36\\python ./bin/run_tests.py + install: $PYTHON -m pip install -r requirements-dev.txt script: $PYTHON ./bin/run_tests.py diff --git a/README.md b/README.md index c14b3ad3..7016ae4c 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,15 @@ What does it do? | | macOS 10.6+ | manylinux i686 | manylinux x86_64 | Windows 32bit | Windows 64bit | |---|---|---|---|---|---| -| Python 2.7 | ✅ | ✅ | ✅ | ✅ | ✅ | -| Python 3.4 | ✅ | ✅ | ✅ | ✅* | ✅* | +| Python 2.7 | ✅ | ✅ | ✅ | ✅² | ✅² | +| Python 3.4 | ✅ | ✅ | ✅ | ✅¹²| ✅¹²| | Python 3.5 | ✅ | ✅ | ✅ | ✅ | ✅ | | Python 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ | | Python 3.7 | ✅ | ✅ | ✅ | ✅ | ✅ | -> \* Not supported on Azure Pipelines +> ¹ Not supported on Azure Pipelines +> +> ² Not supported on Travis - Builds manylinux, macOS and Windows (32 and 64bit) wheels using Azure Pipelines, Travis CI, AppVeyor, and CircleCI - Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate) @@ -34,7 +36,7 @@ Usage | | Linux | macOS | Windows | |-----------------|-------|-------|---------| | Azure Pipelines | ✅ | ✅ | ✅ | -| Travis CI | ✅ | ✅ | | +| Travis CI | ✅ | ✅ | ✅ | | AppVeyor | | | ✅ | | CircleCI | ✅ | ✅ | | @@ -101,11 +103,12 @@ jobs: Travis CI + - To build Linux and Mac wheels on Travis CI, create a `.travis.yml` file in your repo. - ``` + ```yaml language: python matrix: @@ -123,6 +126,18 @@ jobs: - cibuildwheel --output-dir wheelhouse ``` + To build on Windows too, add this matrix entry: + ```yaml + - os: windows + language: shell + before_install: + - choco install python3 --version 3.6.8 --no-progress -y + env: + - PATH=/c/Python36:/c/Python36/Scripts:$PATH + ``` + + Note that building Windows Python 2.7 wheels on Travis is unsupported. + Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/), or see [Delivering to PyPI](#delivering-to-pypi) below. diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index 847b608a..67d90827 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -65,6 +65,8 @@ def main(): platform = 'linux' elif os.environ.get('TRAVIS_OS_NAME') == 'osx': platform = 'macos' + elif os.environ.get('TRAVIS_OS_NAME') == 'windows': + platform = 'windows' elif 'APPVEYOR' in os.environ: platform = 'windows' elif 'BITRISE_BUILD_NUMBER' in os.environ: diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index a25dab69..da929bcc 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -1,12 +1,18 @@ from __future__ import print_function -import os, tempfile, subprocess, shutil +import os, tempfile, subprocess, shutil, sys from collections import namedtuple from glob import glob +try: + from shlex import quote as shlex_quote +except ImportError: + from pipes import quote as shlex_quote + from .util import prepare_command, get_build_verbosity_extra_flags 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): if IS_RUNNING_ON_AZURE: @@ -22,6 +28,12 @@ def get_python_path(config): return glob(path_pattern)[0] except IndexError: raise Exception('Could not find a Python install at ' + path_pattern) + elif IS_RUNNING_ON_TRAVIS: + if config.version == "3.4.x": + return config.path + else: + nuget_args = get_nuget_args(config) + return os.path.join(nuget_args[-1], nuget_args[0] + "." + config.nuget_version, "tools") else: # Assume we're running on AppVeyor major, minor = config.version.split('.')[:2] @@ -32,32 +44,49 @@ def get_python_path(config): ) +def get_nuget_args(configuration): + if configuration.nuget_version is None: + return None + python_name = "python" if configuration.version[0] == '3' else "python2" + if configuration.arch == "32": + python_name = python_name + "x86" + return [python_name, "-Version", configuration.nuget_version, "-OutputDirectory", "C:/python"] + def get_python_configurations(build_selector): - PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'path']) + PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'path', "nuget_version"]) python_configurations = [ - PythonConfiguration(version='2.7.x', arch="32", identifier='cp27-win32', path='C:\Python27'), - PythonConfiguration(version='2.7.x', arch="64", identifier='cp27-win_amd64', path='C:\Python27-x64'), - PythonConfiguration(version='3.4.x', arch="32", identifier='cp34-win32', path='C:\Python34'), - PythonConfiguration(version='3.4.x', arch="64", identifier='cp34-win_amd64', path='C:\Python34-x64'), - PythonConfiguration(version='3.5.x', arch="32", identifier='cp35-win32', path='C:\Python35'), - PythonConfiguration(version='3.5.x', arch="64", identifier='cp35-win_amd64', path='C:\Python35-x64'), - PythonConfiguration(version='3.6.x', arch="32", identifier='cp36-win32', path='C:\Python36'), - PythonConfiguration(version='3.6.x', arch="64", identifier='cp36-win_amd64', path='C:\Python36-x64'), - PythonConfiguration(version='3.7.x', arch="32", identifier='cp37-win32', path='C:\Python37'), - PythonConfiguration(version='3.7.x', arch="64", identifier='cp37-win_amd64', path='C:\Python37-x64'), + PythonConfiguration(version='2.7.x', arch="32", identifier='cp27-win32', path='C:\\Python27', nuget_version="2.7.16"), + PythonConfiguration(version='2.7.x', arch="64", identifier='cp27-win_amd64', path='C:\\Python27-x64', nuget_version="2.7.16"), + PythonConfiguration(version='3.4.x', arch="32", identifier='cp34-win32', path='C:\\Python34', nuget_version=None), + PythonConfiguration(version='3.4.x', arch="64", identifier='cp34-win_amd64', path='C:\\Python34-x64', nuget_version=None), + PythonConfiguration(version='3.5.x', arch="32", identifier='cp35-win32', path='C:\\Python35', nuget_version="3.5.4"), + PythonConfiguration(version='3.5.x', arch="64", identifier='cp35-win_amd64', path='C:\\Python35-x64', nuget_version="3.5.4"), + PythonConfiguration(version='3.6.x', arch="32", identifier='cp36-win32', path='C:\\Python36', nuget_version="3.6.8"), + PythonConfiguration(version='3.6.x', arch="64", identifier='cp36-win_amd64', path='C:\\Python36-x64', nuget_version="3.6.8"), + PythonConfiguration(version='3.7.x', arch="32", identifier='cp37-win32', path='C:\\Python37', nuget_version="3.7.4"), + PythonConfiguration(version='3.7.x', arch="64", identifier='cp37-win_amd64', path='C:\\Python37-x64', nuget_version="3.7.4") ] if IS_RUNNING_ON_AZURE: # Python 3.4 isn't supported on Azure. # See https://github.com/Microsoft/azure-pipelines-tasks/issues/9674 python_configurations = [c for c in python_configurations if c.version != '3.4.x'] + + 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 + # no easy and stable way fo installing python 3.4 + python_configurations = [c for c in python_configurations if c.version != '2.7.x' and c.version != '3.4.x'] + + # skip builds as required + python_configurations = [c for c in python_configurations if build_selector(c.identifier)] + + return python_configurations - # skip builds as required - return [c for c in python_configurations if build_selector(c.identifier)] def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, environment): - if IS_RUNNING_ON_AZURE: + if IS_RUNNING_ON_AZURE or IS_RUNNING_ON_TRAVIS: def shell(args, env=None, cwd=None): print('+ ' + ' '.join(args)) args = ['cmd', '/E:ON', '/V:ON', '/C'] + args @@ -77,10 +106,21 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef temp_dir = tempfile.mkdtemp(prefix='cibuildwheel') built_wheel_dir = os.path.join(temp_dir, 'built_wheel') - python_configurations = get_python_configurations(build_selector) + if IS_RUNNING_ON_TRAVIS: + # instal nuget as best way for provide python + shell(["choco", "install", "nuget.commandline"]) + # get pip fo this installation which not have. + get_pip_url = 'https://bootstrap.pypa.io/get-pip.py' + get_pip_script = 'C:\\get-pip.py' + shell(['curl', '-L', '-o', get_pip_script, get_pip_url]) + python_configurations = get_python_configurations(build_selector) for config in python_configurations: config_python_path = get_python_path(config) + if IS_RUNNING_ON_TRAVIS and config.nuget_version is not None and not os.path.exists(config_python_path): + shell(["nuget", "install"] + get_nuget_args(config)) + if not os.path.exists(os.path.join(config_python_path, 'Scripts', 'pip.exe')): + shell([os.path.join(config_python_path, 'python.exe'), get_pip_script ]) # check python & pip exist for this configuration assert os.path.exists(os.path.join(config_python_path, 'python.exe')) @@ -107,8 +147,12 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)\"'], env=env) # prepare the Python environment - shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'], + if config.version == "3.4.x": + shell(['python', '-m', 'pip', 'install', 'pip==19.1.1'], env=env) + else: + shell(['python', '-m', 'pip', 'install', '--upgrade', 'pip'], + env=env) shell(['pip', 'install', '--upgrade', 'setuptools'], env=env) shell(['pip', 'install', 'wheel'], env=env) diff --git a/test/shared/utils.py b/test/shared/utils.py index 86cb198a..78f09050 100644 --- a/test/shared/utils.py +++ b/test/shared/utils.py @@ -6,7 +6,8 @@ This file is added to the PYTHONPATH in the test runner at bin/run_test.py. import subprocess, sys, os -IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache') +IS_WINDOWS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache') +IS_WINDOWS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows' def cibuildwheel_get_build_identifiers(project_path, env=None): @@ -97,9 +98,12 @@ def expected_wheels(package_name, package_version): else: raise Exception('unsupported platform') - if IS_RUNNING_ON_AZURE: + if IS_WINDOWS_RUNNING_ON_AZURE: # Python 3.4 isn't supported on Azure. templates = [t for t in templates if '-cp34-' not in t] + if IS_WINDOWS_RUNNING_ON_TRAVIS: + # Python 2.7 and 3.4 isn't supported on Travis. + templates = [t for t in templates if '-cp27-' not in t and '-cp34-' not in t] return [filename.format(package_name=package_name, package_version=package_version) for filename in templates]