Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
533ef04740 | ||
|
|
8a3935551b | ||
|
|
1b33c4cac5 | ||
|
|
f734bffc1e | ||
|
|
b40852ec90 | ||
|
|
a232b09fee | ||
|
|
e3b2de0489 | ||
|
|
c3565d5025 | ||
|
|
31ac2ba03f | ||
|
|
083ece8bbc | ||
|
|
bdbbf18e43 | ||
|
|
45a7836cc6 | ||
|
|
d5fa5475d4 | ||
|
|
3f6a943b6d | ||
|
|
c7764d239b | ||
|
|
e072e82252 | ||
|
|
cf7f2e18ce | ||
|
|
a2a2a7b382 | ||
|
|
ba22589f6f | ||
|
|
e9bf990fd2 | ||
|
|
db09cfc4a9 | ||
|
|
2c0d6e3db1 | ||
|
|
89c8735719 | ||
|
|
7a2e951f48 | ||
|
|
ff942ad766 | ||
|
|
881cd66fae | ||
|
|
278661df77 | ||
|
|
520daa4c23 | ||
|
|
2679cb7f46 | ||
|
|
56f9a37165 | ||
|
|
5c91340ad0 |
@@ -1,6 +1,6 @@
|
||||
This project is licensed under the 'BSD 2-clause license'.
|
||||
|
||||
Copyright (c) 2016, Tingbot. All rights reserved.
|
||||
Copyright (c) 2017, Joe Rickerby. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -44,7 +44,7 @@ Usage
|
||||
- os: osx
|
||||
|
||||
script:
|
||||
- pip install cibuildwheel==0.1.3
|
||||
- pip install cibuildwheel==0.2.1
|
||||
- cibuildwheel --output-dir wheelhouse
|
||||
```
|
||||
|
||||
@@ -54,7 +54,7 @@ Usage
|
||||
|
||||
```
|
||||
build_script:
|
||||
- pip install cibuildwheel==0.1.3
|
||||
- pip install cibuildwheel==0.2.1
|
||||
- cibuildwheel --output-dir wheelhouse
|
||||
artifacts:
|
||||
- path: "wheelhouse\\*.whl"
|
||||
@@ -132,24 +132,38 @@ Space-separated list of dependencies required for running the tests.
|
||||
Example: `pytest`
|
||||
Example: `nose==1.3.7 moto==0.4.31`
|
||||
|
||||
| Environment variable: `CIBW_SKIP` | 🔶 coming soon 🔶
|
||||
| --- | ---
|
||||
| Environment variable: `CIBW_BEFORE_BUILD`
|
||||
| ---
|
||||
|
||||
Optional.
|
||||
|
||||
Space-separated list of builds to skip. Each build has an identifier like `cp27-linux_x86_64` or `cp34-macosx_10_6_intel` - you can list ones to skip here and `cibuildwheel` won't try to build them.
|
||||
Shell command to run before building the wheel. This option allows you to run a command in **each** Python environment before the `pip wheel` command. This is useful if you need to set up some dependency so it's available during the build.
|
||||
|
||||
The active Python binary can be accessed using `{python}`, and pip with `{pip}`. These are useful when you need to write `python3` or `pip3` on a Python 3.x build.
|
||||
|
||||
Example: `{pip} install pybind11`
|
||||
|
||||
Platform-specific variants also available:
|
||||
`CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX`
|
||||
|
||||
| Environment variable: `CIBW_SKIP`
|
||||
| ---
|
||||
|
||||
Optional.
|
||||
|
||||
Space-separated list of builds to skip. Each build has an identifier like `cp27-manylinux1_x86_64` or `cp34-macosx_10_6_intel` - you can list ones to skip here and `cibuildwheel` won't try to build them.
|
||||
|
||||
The format is `python_tag-platform_tag`. The tags are as defined in [PEP 0425](https://www.python.org/dev/peps/pep-0425/#details).
|
||||
|
||||
Python tags look like `cp27` `cp34` `cp35` `cp36`
|
||||
|
||||
Platform tags look like `macosx_10_6_intel` `linux_x86_64` `linux_i386` `win32` `win_amd64`
|
||||
Platform tags look like `macosx_10_6_intel` `manylinux1_x86_64` `manylinux1_i386` `win32` `win_amd64`
|
||||
|
||||
You can also use shell-style globbing syntax (as per `fnmatch`)
|
||||
|
||||
Example: `cp27-macosx_10_6_intel ` (don't build on Python 2 on Mac)
|
||||
Example: `cp27-win*` (don't build on Python 2.7 on Windows)
|
||||
Example: `cp34-*` (don't build on Python 3.4)
|
||||
Example: `cp34-* cp35-*` (don't build on Python 3.4 or Python 3.5)
|
||||
|
||||
--
|
||||
|
||||
@@ -197,6 +211,12 @@ Obviously, manual steps are for chumps, so we can automate this a little by usin
|
||||
|
||||
> Quick note from me - using S3 as a storage didn't work due to a [bug](https://issues.apache.org/jira/browse/LIBCLOUD-792) in libcloud. Feel free to use my fork of that package that fixes the bug `pip install https://github.com/joerick/libcloud/archive/v1.5.0-s3fix.zip`
|
||||
|
||||
### Automatic method
|
||||
|
||||
If you don't need much control over the release of a package, you can set up cibuildwheel to deliver the wheels straight to PyPI. This doesn't require any cloud storage to work - you just need to bump the version and tag it.
|
||||
|
||||
Check out [this example repo](https://github.com/joerick/cibuildwheel-autopypi-example) for instructions on how to set this up.
|
||||
|
||||
It didn't work!
|
||||
---------------
|
||||
|
||||
@@ -251,6 +271,7 @@ Credits
|
||||
- ⭐️ @matthew-brett for [matthew-brett/multibuild](http://github.com/matthew-brett/multibuild) and [matthew-brett/delocate](http://github.com/matthew-brett/delocate)
|
||||
- @PyPA for the manylinux Docker images [pypa/manylinux](https://github.com/pypa/manylinux)
|
||||
- @ogrisel for [wheelhouse-uploader](https://github.com/ogrisel/wheelhouse-uploader) and `run_with_env.cmd`
|
||||
- @zfrenchee for [help debugging many issues](https://github.com/joerick/cibuildwheel/issues/2)
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
__version__ = '0.2.1'
|
||||
|
||||
+83
-37
@@ -1,7 +1,27 @@
|
||||
from __future__ import print_function
|
||||
import argparse, os, subprocess, sys
|
||||
import argparse, os, subprocess, sys, textwrap
|
||||
|
||||
import cibuildwheel
|
||||
import cibuildwheel.linux, cibuildwheel.windows, cibuildwheel.macos
|
||||
from cibuildwheel.util import BuildSkipper
|
||||
|
||||
def get_option_from_environment(option_name, platform=None):
|
||||
'''
|
||||
Returns an option from the environment, optionally scoped by the platform.
|
||||
|
||||
Example:
|
||||
get_option_from_environment('CIBW_COLOR', platform='macos')
|
||||
|
||||
This will return the value of CIBW_COLOR_MACOS if it exists, otherwise the value of
|
||||
CIBW_COLOR.
|
||||
'''
|
||||
if platform:
|
||||
option = os.environ.get('%s_%s' % (option_name, platform.upper()))
|
||||
if option is not None:
|
||||
return option
|
||||
|
||||
return os.environ.get(option_name)
|
||||
|
||||
from cibuildwheel import linux, windows, macos
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
@@ -29,38 +49,6 @@ def main():
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
output_dir = args.output_dir
|
||||
test_command = os.environ.get('CIBW_TEST_COMMAND', None)
|
||||
test_requires = os.environ.get('CIBW_TEST_REQUIRES', '').split()
|
||||
project_dir = args.project_dir
|
||||
|
||||
try:
|
||||
project_setup_py = os.path.join(project_dir, 'setup.py')
|
||||
package_name = subprocess.check_output([sys.executable, project_setup_py, '--name'])
|
||||
except subprocess.CalledProcessError as err:
|
||||
if not os.path.exists(project_setup_py):
|
||||
print('cibuildwheel: Could not find setup.py at root of project', file=sys.stderr)
|
||||
exit(2)
|
||||
else:
|
||||
print('cibuildwheel: Failed to get name of the package. Command was %s' % err.cmd,
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
package_name = package_name.strip()
|
||||
|
||||
if package_name == '' or package_name == 'UNKNOWN':
|
||||
print('cibuildwheel: Invalid package name "%s". Check your setup.py' % package_name,
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
build_args = dict(
|
||||
project_dir=project_dir,
|
||||
package_name=package_name,
|
||||
output_dir=output_dir,
|
||||
test_command=test_command,
|
||||
test_requires=test_requires
|
||||
)
|
||||
|
||||
if args.platform != 'auto':
|
||||
platform = args.platform
|
||||
else:
|
||||
@@ -78,14 +66,72 @@ def main():
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
output_dir = args.output_dir
|
||||
test_command = os.environ.get('CIBW_TEST_COMMAND', None)
|
||||
test_requires = os.environ.get('CIBW_TEST_REQUIRES', '').split()
|
||||
project_dir = args.project_dir
|
||||
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
||||
skip_config = os.environ.get('CIBW_SKIP', '')
|
||||
|
||||
skip = BuildSkipper(skip_config)
|
||||
|
||||
try:
|
||||
project_setup_py = os.path.join(project_dir, 'setup.py')
|
||||
name_output = subprocess.check_output([sys.executable, project_setup_py, '--name'])
|
||||
# the last line of output is the name
|
||||
package_name = name_output.strip().splitlines()[-1]
|
||||
except subprocess.CalledProcessError as err:
|
||||
if not os.path.exists(project_setup_py):
|
||||
print('cibuildwheel: Could not find setup.py at root of project', file=sys.stderr)
|
||||
exit(2)
|
||||
else:
|
||||
print('cibuildwheel: Failed to get name of the package. Command was %s' % err.cmd,
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
if package_name == '' or package_name == 'UNKNOWN':
|
||||
print('cibuildwheel: Invalid package name "%s". Check your setup.py' % package_name,
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
build_options = dict(
|
||||
project_dir=project_dir,
|
||||
package_name=package_name,
|
||||
output_dir=output_dir,
|
||||
test_command=test_command,
|
||||
test_requires=test_requires,
|
||||
before_build=before_build,
|
||||
skip=skip,
|
||||
)
|
||||
|
||||
print_preamble(platform, build_options)
|
||||
|
||||
if platform == 'linux':
|
||||
linux.build(**build_args)
|
||||
cibuildwheel.linux.build(**build_options)
|
||||
elif platform == 'windows':
|
||||
windows.build(**build_args)
|
||||
cibuildwheel.windows.build(**build_options)
|
||||
elif platform == 'macos':
|
||||
macos.build(**build_args)
|
||||
cibuildwheel.macos.build(**build_options)
|
||||
else:
|
||||
raise Exception('Unsupported platform')
|
||||
|
||||
def print_preamble(platform, build_options):
|
||||
print(textwrap.dedent('''
|
||||
_ _ _ _ _ _ _
|
||||
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
|
||||
| _| | . | | | | | . | | | | | -_| -_| |
|
||||
|___|_|___|___|_|_|___|_____|_|_|___|___|_|
|
||||
'''))
|
||||
|
||||
print('cibuildwheel version %s\n' % cibuildwheel.__version__)
|
||||
|
||||
|
||||
print('Build options:')
|
||||
print(' platform: %r' % platform)
|
||||
for option, value in build_options.items():
|
||||
print(' %s: %r' % (option, value))
|
||||
|
||||
print('\nHere we go!\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
+63
-10
@@ -1,5 +1,7 @@
|
||||
from __future__ import print_function
|
||||
import os, subprocess
|
||||
from collections import namedtuple
|
||||
from .util import prepare_command
|
||||
|
||||
try:
|
||||
from shlex import quote as shlex_quote
|
||||
@@ -7,25 +9,68 @@ except ImportError:
|
||||
from pipes import quote as shlex_quote
|
||||
|
||||
|
||||
def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
for docker_image in ['quay.io/pypa/manylinux1_x86_64', 'quay.io/pypa/manylinux1_i686']:
|
||||
def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, skip):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(identifier='cp26-manylinux1_x86_64', path='/opt/python/cp26-cp26m'),
|
||||
PythonConfiguration(identifier='cp26-manylinux1_x86_64', path='/opt/python/cp26-cp26mu'),
|
||||
PythonConfiguration(identifier='cp27-manylinux1_x86_64', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(identifier='cp27-manylinux1_x86_64', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(identifier='cp33-manylinux1_x86_64', path='/opt/python/cp33-cp33m'),
|
||||
PythonConfiguration(identifier='cp34-manylinux1_x86_64', path='/opt/python/cp34-cp34m'),
|
||||
PythonConfiguration(identifier='cp35-manylinux1_x86_64', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux1_x86_64', path='/opt/python/cp36-cp36m'),
|
||||
PythonConfiguration(identifier='cp26-manylinux1_i686', path='/opt/python/cp26-cp26m'),
|
||||
PythonConfiguration(identifier='cp26-manylinux1_i686', path='/opt/python/cp26-cp26mu'),
|
||||
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27m'),
|
||||
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27mu'),
|
||||
PythonConfiguration(identifier='cp33-manylinux1_i686', path='/opt/python/cp33-cp33m'),
|
||||
PythonConfiguration(identifier='cp34-manylinux1_i686', path='/opt/python/cp34-cp34m'),
|
||||
PythonConfiguration(identifier='cp35-manylinux1_i686', path='/opt/python/cp35-cp35m'),
|
||||
PythonConfiguration(identifier='cp36-manylinux1_i686', path='/opt/python/cp36-cp36m'),
|
||||
]
|
||||
|
||||
# skip builds as required
|
||||
python_configurations = [c for c in python_configurations if not skip(c.identifier)]
|
||||
|
||||
platforms = [
|
||||
('manylinux1_x86_64', 'quay.io/pypa/manylinux1_x86_64'),
|
||||
('manylinux1_i686', 'quay.io/pypa/manylinux1_i686'),
|
||||
]
|
||||
|
||||
for platform_tag, docker_image in platforms:
|
||||
platform_configs = [c for c in python_configurations if c.identifier.endswith(platform_tag)]
|
||||
|
||||
bash_script = '''
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
cd /project
|
||||
|
||||
for PYBIN in /opt/python/*/bin; do
|
||||
"$PYBIN/pip" wheel . -w /tmp/linux_wheels
|
||||
for PYBIN in {pybin_paths}; do
|
||||
if [ ! -z {before_build} ]; then
|
||||
PATH=$PYBIN:$PATH sh -c {before_build}
|
||||
fi
|
||||
|
||||
# install the package first to take care of dependencies
|
||||
"$PYBIN/pip" install .
|
||||
|
||||
"$PYBIN/pip" wheel --no-deps . -w /tmp/linux_wheels
|
||||
done
|
||||
|
||||
for whl in /tmp/linux_wheels/*.whl; do
|
||||
auditwheel repair "$whl" -w /output
|
||||
if [[ "$whl" == *none-any.whl ]]; then
|
||||
# pure python wheel - just copy to the output
|
||||
cp "$whl" /output
|
||||
else
|
||||
auditwheel repair "$whl" -w /output
|
||||
fi
|
||||
done
|
||||
|
||||
# Install packages and test
|
||||
for PYBIN in /opt/python/*/bin/; do
|
||||
for PYBIN in {pybin_paths}; do
|
||||
# Install the wheel we just built
|
||||
"$PYBIN/pip" install {package_name} --no-index -f /output
|
||||
"$PYBIN/pip" install {package_name} \
|
||||
--upgrade --force-reinstall --no-deps --no-index -f /output
|
||||
|
||||
# Install any requirements to run the tests
|
||||
if [ ! -z "{test_requires}" ]; then
|
||||
@@ -34,13 +79,21 @@ def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
|
||||
# Run the tests from a different directory
|
||||
if [ ! -z {test_command} ]; then
|
||||
(cd "$HOME" && export PATH=$PYBIN:$PATH && sh -c {test_command})
|
||||
pushd $HOME
|
||||
PATH=$PYBIN:$PATH sh -c {test_command}
|
||||
popd
|
||||
fi
|
||||
done
|
||||
'''.format(
|
||||
package_name=package_name,
|
||||
pybin_paths=' '.join(c.path+'/bin' for c in platform_configs),
|
||||
test_requires=' '.join(test_requires),
|
||||
test_command=shlex_quote(test_command.format(project='/project') if test_command else ''),
|
||||
test_command=shlex_quote(
|
||||
test_command.format(project='/project') if test_command else ''
|
||||
),
|
||||
before_build=shlex_quote(
|
||||
prepare_command(before_build, python='python', pip='pip') if before_build else ''
|
||||
),
|
||||
)
|
||||
|
||||
docker_process = subprocess.Popen([
|
||||
@@ -52,7 +105,7 @@ def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
'-v', '%s:/output' % os.path.abspath(output_dir),
|
||||
docker_image,
|
||||
'/bin/bash'],
|
||||
stdin=subprocess.PIPE)
|
||||
stdin=subprocess.PIPE, universal_newlines=True)
|
||||
|
||||
docker_process.communicate(bash_script)
|
||||
|
||||
|
||||
+34
-15
@@ -1,5 +1,5 @@
|
||||
from __future__ import print_function
|
||||
import os, subprocess, shlex
|
||||
import os, subprocess, shlex, sys
|
||||
from collections import namedtuple
|
||||
from glob import glob
|
||||
try:
|
||||
@@ -7,14 +7,16 @@ try:
|
||||
except ImportError:
|
||||
from pipes import quote as shlex_quote
|
||||
|
||||
from .util import prepare_command
|
||||
|
||||
def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'url'])
|
||||
|
||||
def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, skip):
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(version='2.7', url='https://www.python.org/ftp/python/2.7.13/python-2.7.13-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.4', url='https://www.python.org/ftp/python/3.4.4/python-3.4.4-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.5', url='https://www.python.org/ftp/python/3.5.3/python-3.5.3-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.6', url='https://www.python.org/ftp/python/3.6.0/python-3.6.0-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='2.7', identifier='cp27-macosx_10_6_intel', url='https://www.python.org/ftp/python/2.7.13/python-2.7.13-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.4', identifier='cp34-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.4.4/python-3.4.4-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.5', identifier='cp35-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.5.3/python-3.5.3-macosx10.6.pkg'),
|
||||
PythonConfiguration(version='3.6', identifier='cp36-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.6.0/python-3.6.0-macosx10.6.pkg'),
|
||||
]
|
||||
|
||||
def shell(args, env=None, cwd=None):
|
||||
@@ -23,6 +25,10 @@ def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
return subprocess.check_call(args, env=env, cwd=cwd)
|
||||
|
||||
for config in python_configurations:
|
||||
if skip(config.identifier):
|
||||
print('cibuildwheel: Skipping build %s' % config.identifier, file=sys.stderr)
|
||||
continue
|
||||
|
||||
# download the pkg
|
||||
shell(['curl', '-L', '-o', '/tmp/Python.pkg', config.url])
|
||||
# install
|
||||
@@ -43,22 +49,35 @@ def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
|
||||
# install pip & wheel
|
||||
shell([python, '-m', 'ensurepip', '--upgrade'], env=env)
|
||||
shell([pip, '--version'])
|
||||
shell([pip, '--version'], env=env)
|
||||
shell([pip, 'install', 'wheel'], env=env)
|
||||
shell([pip, 'install', 'delocate'], env=env)
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
before_build_prepared = prepare_command(before_build, python=python, pip=pip)
|
||||
shell(shlex.split(before_build_prepared), env=env)
|
||||
|
||||
# install the package first to take care of dependencies
|
||||
shell([pip, 'install', project_dir], env=env)
|
||||
|
||||
# build the wheel to temp dir
|
||||
temp_wheel_dir = '/tmp/tmpwheel%s' % config.version
|
||||
shell([pip, 'wheel', project_dir, '-w', temp_wheel_dir], env=env)
|
||||
shell([pip, 'wheel', project_dir, '-w', temp_wheel_dir, '--no-deps'], env=env)
|
||||
temp_wheel = glob(temp_wheel_dir+'/*.whl')[0]
|
||||
|
||||
# list the dependencies
|
||||
shell(['delocate-listdeps', temp_wheel], env=env)
|
||||
# rebuild the wheel with shared libraries included and place in output dir
|
||||
shell(['delocate-wheel', '-w', output_dir, temp_wheel], env=env)
|
||||
if temp_wheel.endswith('none-any.whl'):
|
||||
# pure python wheel - just copy to output_dir
|
||||
shell(['cp', temp_wheel, output_dir], env=env)
|
||||
else:
|
||||
# list the dependencies
|
||||
shell(['delocate-listdeps', temp_wheel], env=env)
|
||||
# rebuild the wheel with shared libraries included and place in output dir
|
||||
shell(['delocate-wheel', '-w', output_dir, temp_wheel], env=env)
|
||||
|
||||
# install the wheel
|
||||
shell([pip, 'install', package_name, '--no-index', '--find-links', output_dir], env=env)
|
||||
# now install the package from the generated wheel
|
||||
shell([pip, 'install', package_name, '--upgrade', '--force-reinstall',
|
||||
'--no-deps', '--no-index', '--find-links', output_dir], env=env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
from fnmatch import fnmatch
|
||||
|
||||
|
||||
def prepare_command(command, python, pip):
|
||||
'''
|
||||
Preprocesses a command by expanding variables like {python} or {pip}.
|
||||
|
||||
For example, used for the before_build option, where the user would
|
||||
like to run a command like `python setup.py test`. If the command should run on
|
||||
Python 3, the user could write `{python} setup.py test`. This command would expand
|
||||
it out to python2 or python3 as appropriate.
|
||||
'''
|
||||
return command.format(python=python, pip=pip)
|
||||
|
||||
|
||||
class BuildSkipper(object):
|
||||
def __init__(self, skip_config):
|
||||
self.patterns = skip_config.split()
|
||||
|
||||
def __call__(self, build_id):
|
||||
return any(fnmatch(build_id, pattern) for pattern in self.patterns)
|
||||
|
||||
def __repr__(self):
|
||||
return 'BuildSkipper(%r)' % ' '.join(self.patterns)
|
||||
+36
-16
@@ -1,14 +1,20 @@
|
||||
from __future__ import print_function
|
||||
import os, tempfile, subprocess, urllib2
|
||||
import os, tempfile, subprocess, sys
|
||||
try:
|
||||
from urllib2 import urlopen
|
||||
except ImportError:
|
||||
from urllib.request import urlopen
|
||||
from collections import namedtuple
|
||||
|
||||
from .util import prepare_command
|
||||
|
||||
def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
|
||||
def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, skip):
|
||||
# run_with_env is a cmd file that sets the right environment variables to
|
||||
run_with_env = os.path.join(tempfile.gettempdir(), 'appveyor_run_with_env.cmd')
|
||||
if not os.path.exists(run_with_env):
|
||||
with open(run_with_env, 'wb') as f:
|
||||
request = urllib2.urlopen('https://github.com/ogrisel/python-appveyor-demo/raw/09a1c8672e5015a74d8f69d07add6ee803c176ec/appveyor/run_with_env.cmd')
|
||||
request = urlopen('https://github.com/ogrisel/python-appveyor-demo/raw/09a1c8672e5015a74d8f69d07add6ee803c176ec/appveyor/run_with_env.cmd')
|
||||
f.write(request.read())
|
||||
|
||||
def shell(args, env=None, cwd=None):
|
||||
@@ -17,21 +23,25 @@ def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
args = ['cmd', '/E:ON', '/V:ON', '/C', run_with_env] + args
|
||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
|
||||
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'path'])
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'path'])
|
||||
python_configurations = [
|
||||
PythonConfiguration(version='2.7.x', arch="32", path='C:\Python27'),
|
||||
PythonConfiguration(version='2.7.x', arch="64", path='C:\Python27-x64'),
|
||||
PythonConfiguration(version='3.3.x', arch="32", path='C:\Python33'),
|
||||
PythonConfiguration(version='3.3.x', arch="64", path='C:\Python33-x64'),
|
||||
PythonConfiguration(version='3.4.x', arch="32", path='C:\Python34'),
|
||||
PythonConfiguration(version='3.4.x', arch="64", path='C:\Python34-x64'),
|
||||
PythonConfiguration(version='3.5.x', arch="32", path='C:\Python35'),
|
||||
PythonConfiguration(version='3.5.x', arch="64", path='C:\Python35-x64'),
|
||||
PythonConfiguration(version='3.6.x', arch="32", path='C:\Python36'),
|
||||
PythonConfiguration(version='3.6.x', arch="64", path='C:\Python36-x64'),
|
||||
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.3.x', arch="32", identifier='cp33-win32', path='C:\Python33'),
|
||||
PythonConfiguration(version='3.3.x', arch="64", identifier='cp33-win_amd64', path='C:\Python33-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'),
|
||||
]
|
||||
|
||||
for config in python_configurations:
|
||||
if skip(config.identifier):
|
||||
print('cibuildwheel: Skipping build %s' % config.identifier, file=sys.stderr)
|
||||
continue
|
||||
|
||||
env = os.environ.copy()
|
||||
# set up environment variables for run_with_env
|
||||
env['PYTHON_VERSION'] = config.version
|
||||
@@ -51,11 +61,21 @@ def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
env=env)
|
||||
shell(['pip', 'install', 'wheel'], env=env)
|
||||
|
||||
# run the before_build command
|
||||
if before_build:
|
||||
before_build_prepared = prepare_command(before_build, python='python', pip='pip')
|
||||
shell([before_build_prepared], env=env)
|
||||
|
||||
# install the package first to take care of dependencies
|
||||
shell(['pip', 'install', project_dir], env=env)
|
||||
|
||||
# build the wheel
|
||||
shell(['pip', 'wheel', project_dir, '-w', output_dir], env=env)
|
||||
shell(['pip', 'wheel', project_dir, '-w', output_dir, '--no-deps'], env=env)
|
||||
|
||||
# install the wheel
|
||||
shell(['pip', 'install', package_name, '--no-index', '-f', output_dir], env=env)
|
||||
shell(['pip', 'install', package_name, '--upgrade',
|
||||
'--force-reinstall', '--no-deps', '--no-index', '-f',
|
||||
output_dir], env=env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
[bumpversion]
|
||||
current_version = 0.1.3
|
||||
current_version = 0.2.1
|
||||
commit = True
|
||||
tag = True
|
||||
message = Bump version
|
||||
|
||||
[bumpversion:file:setup.py]
|
||||
|
||||
[bumpversion:file:cibuildwheel/__init__.py]
|
||||
|
||||
[bumpversion:file:README.md]
|
||||
search = cibuildwheel=={current_version}
|
||||
replace = cibuildwheel=={new_version}
|
||||
|
||||
@@ -8,7 +8,7 @@ except ImportError:
|
||||
|
||||
setup(
|
||||
name='cibuildwheel',
|
||||
version='0.1.3',
|
||||
version='0.2.1',
|
||||
description="Build Python wheels on CI with minimal configuration.",
|
||||
long_description='For readme please see http://github.com/joerick/cibuildwheel',
|
||||
author="Joe Rickerby",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"CIBW_BEFORE_BUILD": "{python} -c \"import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)\"",
|
||||
"CIBW_BEFORE_BUILD_WINDOWS": "{python} -c \"import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)\""
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
from setuptools import setup, Extension
|
||||
import sys
|
||||
|
||||
if sys.argv[-1] != '--name':
|
||||
# assert that the Python version as written to version.txt in the CIBW_BEFORE_BUILD step
|
||||
# is the same one as is currently running.
|
||||
version_file = 'c:\\pythonversion.txt' if sys.platform == 'win32' else '/tmp/pythonversion.txt'
|
||||
with open(version_file) as f:
|
||||
stored_version = f.read()
|
||||
print('stored_version', stored_version)
|
||||
print('sys.version', sys.version)
|
||||
assert stored_version == sys.version
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <Python.h>
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
sts = system(command);
|
||||
return PyLong_FromLong(sts);
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
static struct PyModuleDef moduledef = { \
|
||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
||||
m = PyModule_Create(&moduledef);
|
||||
#define MOD_RETURN(m) return m;
|
||||
#else
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
m = Py_InitModule3(name, methods, doc);
|
||||
#define MOD_RETURN(m) return;
|
||||
#endif
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
||||
"Execute a shell command."},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
MOD_INIT(spam)
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
MOD_DEF(m,
|
||||
"spam",
|
||||
"Example module",
|
||||
module_methods,
|
||||
-1)
|
||||
|
||||
MOD_RETURN(m)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
3.6.0 (default, Feb 7 2017, 23:55:32)
|
||||
[GCC 4.8.2 20140120 (Red Hat 4.8.2-15)]
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"CIBW_SKIP": "cp26-* cp33-*"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
from setuptools import setup, Extension
|
||||
import sys
|
||||
|
||||
if sys.argv[-1] != '--name':
|
||||
# explode if run on Python 2.6 or Python 3.3 (these should be skipped)
|
||||
if sys.version_info[0:2] == (2, 6):
|
||||
raise Exception('Python 2.6 should be skipped')
|
||||
if sys.version_info[0:2] == (3, 3):
|
||||
raise Exception('Python 3.3 should be skipped')
|
||||
|
||||
setup(
|
||||
name="spam",
|
||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||
version="0.1.0",
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <Python.h>
|
||||
|
||||
static PyObject *
|
||||
spam_system(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *command;
|
||||
int sts;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &command))
|
||||
return NULL;
|
||||
sts = system(command);
|
||||
return PyLong_FromLong(sts);
|
||||
}
|
||||
|
||||
/* Module initialization */
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
static struct PyModuleDef moduledef = { \
|
||||
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
|
||||
m = PyModule_Create(&moduledef);
|
||||
#define MOD_RETURN(m) return m;
|
||||
#else
|
||||
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
|
||||
#define MOD_DEF(m, name, doc, methods, module_state_size) \
|
||||
m = Py_InitModule3(name, methods, doc);
|
||||
#define MOD_RETURN(m) return;
|
||||
#endif
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"system", (PyCFunction)spam_system, METH_VARARGS,
|
||||
"Execute a shell command."},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
MOD_INIT(spam)
|
||||
{
|
||||
PyObject* m;
|
||||
|
||||
MOD_DEF(m,
|
||||
"spam",
|
||||
"Example module",
|
||||
module_methods,
|
||||
-1)
|
||||
|
||||
MOD_RETURN(m)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
3.6.0 (default, Feb 7 2017, 23:55:32)
|
||||
[GCC 4.8.2 20140120 (Red Hat 4.8.2-15)]
|
||||
Reference in New Issue
Block a user