Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c0dd55c1a | ||
|
|
e43d06f928 | ||
|
|
f26da27db8 | ||
|
|
b2fc3f4220 | ||
|
|
609f90c88b | ||
|
|
6e0d6b7fbc | ||
|
|
5b7f105cb3 | ||
|
|
b5006fc4bc | ||
|
|
c28e85df9e | ||
|
|
726939506e | ||
|
|
3ad2443df1 | ||
|
|
e475859d89 | ||
|
|
dbb9bcb4d5 | ||
|
|
c585dbd6f3 | ||
|
|
fdda1ee2e1 | ||
|
|
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 |
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at joerick@mac.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -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,24 +44,24 @@ Usage
|
||||
- os: osx
|
||||
|
||||
script:
|
||||
- pip install cibuildwheel==0.1.3
|
||||
- pip install cibuildwheel==0.4.0
|
||||
- cibuildwheel --output-dir wheelhouse
|
||||
```
|
||||
|
||||
Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/).
|
||||
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.
|
||||
|
||||
- Create an `appveyor.yml` file in your repo.
|
||||
|
||||
```
|
||||
build_script:
|
||||
- pip install cibuildwheel==0.1.3
|
||||
- pip install cibuildwheel==0.4.0
|
||||
- cibuildwheel --output-dir wheelhouse
|
||||
artifacts:
|
||||
- path: "wheelhouse\\*.whl"
|
||||
name: Wheels
|
||||
```
|
||||
|
||||
Appveyor will store the built wheels for you - you can access them from the project console. Alternatively, you may want to store them in the same place as the Travis CI build. See [Appveyor deployment docs](https://www.appveyor.com/docs/deployment/) for more info.
|
||||
Appveyor will store the built wheels for you - you can access them from the project console. Alternatively, you may want to store them in the same place as the Travis CI build. See [Appveyor deployment docs](https://www.appveyor.com/docs/deployment/) for more info, or see [Delivering to PyPI](#delivering-to-pypi) below.
|
||||
|
||||
- Commit those files, enable building of your repo on Travis CI and Appveyor, and push.
|
||||
|
||||
@@ -132,24 +132,41 @@ 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.
|
||||
|
||||
If dependencies are required to build your wheel (for example if you include a header from a Python module), set this to `{pip} install .`, and the dependencies will be installed automatically by pip. However, this means your package will be built twice - if your package takes a long time to build, you might wish to manually list the dependencies here instead.
|
||||
|
||||
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 .`
|
||||
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 +214,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!
|
||||
---------------
|
||||
|
||||
@@ -220,6 +243,32 @@ Legal note
|
||||
|
||||
Since `cibuildwheel` runs the wheel through delocate or auditwheel, it will automatically bundle library dependencies. This is similar to static linking - it might have some licence implications. Check the license for any code you're pulling in to make sure that's allowed.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
### 0.3.0
|
||||
|
||||
- Removed Python 2.6 support on Linux (#12)
|
||||
|
||||
### 0.2.1
|
||||
|
||||
11 June 2017
|
||||
|
||||
- Changed the build process to install the package before building the wheel - this allows direct dependencies to be installed first (#9, thanks @tgarc!)
|
||||
- Added Python 3 support for the main process, for systems where Python 3 is the default (#8, thanks @tgarc).
|
||||
|
||||
### 0.2.0
|
||||
|
||||
13 April 2017
|
||||
|
||||
- Added `CIBW_SKIP` option, letting users explicitly skip a build
|
||||
- Added `CIBW_BEFORE_BUILD` option, letting users run a shell command before the build starts
|
||||
|
||||
### 0.1.3
|
||||
|
||||
31 March 2017
|
||||
|
||||
- First public release!
|
||||
|
||||
Contributing
|
||||
============
|
||||
@@ -243,6 +292,12 @@ Other notes:
|
||||
- The platforms are very similar, until they're not. I'd rather have straight-forward code than totally DRY code, so let's keep airy platfrom abstractions to a minimum.
|
||||
- I might want to break the options into a shared config file one day, so that config is more easily shared. That has motivated some of the design decisions.
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
|
||||
- Joe Rickerby [@joerick](https://github.com/joerick)
|
||||
- Tomas Garcia [@tgarc](https://github.com/tgarc)
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
@@ -251,6 +306,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.4.0'
|
||||
|
||||
+88
-38
@@ -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:
|
||||
@@ -71,21 +59,83 @@ def main():
|
||||
elif 'APPVEYOR' in os.environ:
|
||||
platform = 'windows'
|
||||
else:
|
||||
print('Unable to detect platform. cibuildwheel should run on your CI server, '
|
||||
print('cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server, '
|
||||
'Travis CI and Appveyor are supported. You can run on your development '
|
||||
'machine using the --platform argument. Check --help output for more '
|
||||
'information.',
|
||||
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'],
|
||||
universal_newlines=True)
|
||||
# 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 not os.path.exists(output_dir):
|
||||
os.mkdir(output_dir)
|
||||
|
||||
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()
|
||||
|
||||
+85
-16
@@ -1,5 +1,7 @@
|
||||
from __future__ import print_function
|
||||
import os, subprocess
|
||||
import os, subprocess, sys
|
||||
from collections import namedtuple
|
||||
from .util import prepare_command
|
||||
|
||||
try:
|
||||
from shlex import quote as shlex_quote
|
||||
@@ -7,25 +9,77 @@ 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):
|
||||
try:
|
||||
subprocess.check_call(['docker', '--version'])
|
||||
except:
|
||||
print('cibuildwheel: Docker not found. Docker is required to run Linux builds. '
|
||||
'If you\'re building on Travis CI, add `services: [docker]` to your .travis.yml.',
|
||||
file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path'])
|
||||
python_configurations = [
|
||||
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='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)]
|
||||
if not platform_configs:
|
||||
continue
|
||||
|
||||
bash_script = '''
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
cd /project
|
||||
|
||||
for PYBIN in /opt/python/*/bin; do
|
||||
"$PYBIN/pip" wheel . -w /tmp/linux_wheels
|
||||
done
|
||||
for PYBIN in {pybin_paths}; do
|
||||
# Setup
|
||||
rm -rf /tmp/built_wheel
|
||||
rm -rf /tmp/delocated_wheel
|
||||
mkdir /tmp/built_wheel
|
||||
mkdir /tmp/delocated_wheel
|
||||
|
||||
for whl in /tmp/linux_wheels/*.whl; do
|
||||
auditwheel repair "$whl" -w /output
|
||||
done
|
||||
if [ ! -z {before_build} ]; then
|
||||
PATH=$PYBIN:$PATH sh -c {before_build}
|
||||
fi
|
||||
|
||||
# Build that wheel
|
||||
"$PYBIN/pip" wheel . -w /tmp/built_wheel --no-deps
|
||||
built_wheel=(/tmp/built_wheel/*.whl)
|
||||
|
||||
# Delocate the wheel
|
||||
# NOTE: 'built_wheel' here is a bash array of glob matches; "$built_wheel" returns
|
||||
# the first element
|
||||
if [[ "$built_wheel" == *none-any.whl ]]; then
|
||||
# pure python wheel - just copy
|
||||
mv "$built_wheel" /tmp/delocated_wheel
|
||||
else
|
||||
auditwheel repair "$built_wheel" -w /tmp/delocated_wheel
|
||||
fi
|
||||
delocated_wheel=(/tmp/delocated_wheel/*.whl)
|
||||
|
||||
# Install packages and test
|
||||
for PYBIN in /opt/python/*/bin/; do
|
||||
# Install the wheel we just built
|
||||
"$PYBIN/pip" install {package_name} --no-index -f /output
|
||||
"$PYBIN/pip" install "$delocated_wheel"
|
||||
|
||||
# Install any requirements to run the tests
|
||||
if [ ! -z "{test_requires}" ]; then
|
||||
@@ -34,13 +88,24 @@ 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
|
||||
|
||||
# we're all done here; move it to output
|
||||
mv "$delocated_wheel" /output
|
||||
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,9 +117,13 @@ 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)
|
||||
try:
|
||||
docker_process.communicate(bash_script)
|
||||
except KeyboardInterrupt:
|
||||
docker_process.kill()
|
||||
docker_process.wait()
|
||||
|
||||
if docker_process.returncode != 0:
|
||||
exit(1)
|
||||
|
||||
+43
-17
@@ -1,5 +1,5 @@
|
||||
from __future__ import print_function
|
||||
import os, subprocess, shlex
|
||||
import os, subprocess, shlex, sys, shutil
|
||||
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,39 @@ 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)
|
||||
|
||||
# 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)
|
||||
temp_wheel = glob(temp_wheel_dir+'/*.whl')[0]
|
||||
# setup dirs
|
||||
if os.path.exists('/tmp/built_wheel'):
|
||||
shutil.rmtree('/tmp/built_wheel')
|
||||
os.makedirs('/tmp/built_wheel')
|
||||
if os.path.exists('/tmp/delocated_wheel'):
|
||||
shutil.rmtree('/tmp/delocated_wheel')
|
||||
os.makedirs('/tmp/delocated_wheel')
|
||||
|
||||
# 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)
|
||||
# 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)
|
||||
|
||||
# build the wheel
|
||||
shell([pip, 'wheel', project_dir, '-w', '/tmp/built_wheel', '--no-deps'], env=env)
|
||||
built_wheel = glob('/tmp/built_wheel/*.whl')[0]
|
||||
|
||||
if built_wheel.endswith('none-any.whl'):
|
||||
# pure python wheel - just move
|
||||
shutil.move(built_wheel, '/tmp/delocated_wheel')
|
||||
else:
|
||||
# list the dependencies
|
||||
shell(['delocate-listdeps', built_wheel], env=env)
|
||||
# rebuild the wheel with shared libraries included and place in output dir
|
||||
shell(['delocate-wheel', '-w', '/tmp/delocated_wheel', built_wheel], env=env)
|
||||
delocated_wheel = glob('/tmp/delocated_wheel/*.whl')[0]
|
||||
|
||||
# install the wheel
|
||||
shell([pip, 'install', package_name, '--no-index', '--find-links', output_dir], env=env)
|
||||
shell([pip, 'install', delocated_wheel], env=env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
@@ -70,3 +93,6 @@ def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
test_command_absolute = test_command.format(project=abs_project_dir)
|
||||
shell(shlex.split(test_command_absolute), cwd=os.environ['HOME'], env=env)
|
||||
|
||||
# we're all done here; move it to output
|
||||
shutil.move(delocated_wheel, output_dir)
|
||||
|
||||
@@ -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)
|
||||
+45
-17
@@ -1,37 +1,56 @@
|
||||
from __future__ import print_function
|
||||
import os, tempfile, subprocess, urllib2
|
||||
import os, tempfile, subprocess, sys, shutil
|
||||
try:
|
||||
from urllib2 import urlopen
|
||||
except ImportError:
|
||||
from urllib.request import urlopen
|
||||
from collections import namedtuple
|
||||
from glob import glob
|
||||
|
||||
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):
|
||||
# print the command executing for the logs
|
||||
print('+ ' + ' '.join(args))
|
||||
args = ['cmd', '/E:ON', '/V:ON', '/C', run_with_env] + args
|
||||
return subprocess.check_call(' '.join(args), env=env, cwd=cwd)
|
||||
return subprocess.check_output(' '.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'),
|
||||
]
|
||||
|
||||
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
|
||||
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
|
||||
|
||||
for config in python_configurations:
|
||||
if skip(config.identifier):
|
||||
print('cibuildwheel: Skipping build %s' % config.identifier, file=sys.stderr)
|
||||
continue
|
||||
|
||||
# setup dirs
|
||||
if os.path.exists(built_wheel_dir):
|
||||
shutil.rmtree(built_wheel_dir)
|
||||
os.makedirs(built_wheel_dir)
|
||||
|
||||
env = os.environ.copy()
|
||||
# set up environment variables for run_with_env
|
||||
env['PYTHON_VERSION'] = config.version
|
||||
@@ -51,11 +70,17 @@ 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)
|
||||
|
||||
# build the wheel
|
||||
shell(['pip', 'wheel', project_dir, '-w', output_dir], env=env)
|
||||
shell(['pip', 'wheel', project_dir, '-w', built_wheel_dir, '--no-deps'], env=env)
|
||||
built_wheel = glob(built_wheel_dir+'/*.whl')[0]
|
||||
|
||||
# install the wheel
|
||||
shell(['pip', 'install', package_name, '--no-index', '-f', output_dir], env=env)
|
||||
shell(['pip', 'install', built_wheel], env=env)
|
||||
|
||||
# test the wheel
|
||||
if test_requires:
|
||||
@@ -67,3 +92,6 @@ def build(project_dir, package_name, output_dir, test_command, test_requires):
|
||||
abs_project_dir = os.path.abspath(project_dir)
|
||||
test_command_absolute = test_command.format(project=abs_project_dir)
|
||||
shell([test_command_absolute], cwd='c:\\', env=env)
|
||||
|
||||
# we're all done here; move it to output
|
||||
shutil.move(built_wheel, output_dir)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
[bumpversion]
|
||||
current_version = 0.1.3
|
||||
current_version = 0.4.0
|
||||
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.4.0',
|
||||
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