Compare commits

...
105 Commits
Author SHA1 Message Date
Joe Rickerby 203ce7879d Bump version 2017-09-07 22:59:56 +01:00
Joe Rickerby d0a211dcf5 Update README to combine environment sections 2017-09-07 18:35:21 +01:00
Joe Rickerby 48fb148341 Merge branch 'set-cibw-env-variable' of https://github.com/mayeut/cibuildwheel into mayeut-set-cibw-env-variable 2017-09-07 18:31:21 +01:00
Joe Rickerby 43dcd58b0b Merge pull request #32 from joerick/before-build-shell
before_build executes in shell
2017-09-07 18:23:51 +01:00
Joe Rickerby d381b63b00 Update README 2017-09-07 18:21:01 +01:00
Joe Rickerby 4457a128f6 Merge pull request #21 from joerick/environment
CIBW_ENVIRONMENT
Fixes #16
2017-09-07 18:12:23 +01:00
Joe Rickerby aca294f5bb os.path.samefile is not available on windows. 2017-09-06 22:33:01 +01:00
Joe Rickerby b64a1c1465 Add shell-style execution on the Mac 2017-09-06 22:11:05 +01:00
Joe Rickerby ddb9c077a5 Use os.path.samefile to get around filename case insensitivity 2017-09-06 22:07:24 +01:00
Joe Rickerby bade2da061 Add test for before_build executing in a shell 2017-09-06 22:02:10 +01:00
Joe Rickerby 523dfc0643 Fix some linter warnings 2017-09-06 19:18:28 +01:00
Joe Rickerby 7c416329d4 Add CIBW_ENVIRONMENT to README and reorder options 2017-09-06 19:14:06 +01:00
Joe Rickerby 34e8ee0f25 Fix empty env string case 2017-09-06 18:35:42 +01:00
Joe Rickerby b18684aee5 Merge branch 'environment' of github.com:joerick/cibuildwheel into environment
* 'environment' of github.com:joerick/cibuildwheel:
  Making call from run_tests to run_test subprocess.check_call
  Fixing 'ValueError: Attempted relative import in non-package'
  Reducing code duplication in bin/run_test.py and bin/run_tests.py
2017-09-06 18:27:13 +01:00
Joe Rickerby be474fd7f7 Fix string splitting for the env string 2017-09-06 18:27:06 +01:00
Yannick Jadoul 93d2a593c4 Making call from run_tests to run_test subprocess.check_call 2017-09-06 16:14:39 +02:00
Yannick Jadoul 3188c978c9 Fixing 'ValueError: Attempted relative import in non-package' 2017-09-06 15:46:31 +02:00
Joe Rickerby 0facf0ae12 Update README.md
Add Yannick to collaborators
2017-09-06 14:22:44 +01:00
Yannick Jadoul a7c7a58407 Reducing code duplication in bin/run_test.py and bin/run_tests.py 2017-09-06 15:07:58 +02:00
Joe Rickerby 60ed370002 Semicolons need quoting 2017-09-03 10:01:59 +01:00
Joe Rickerby cc852f1cc1 Ignore vscode project settings 2017-09-03 10:00:18 +01:00
Joe Rickerby 7229ddeb67 Windows uses a different pathsep so have to use that in the test 2017-09-02 22:20:28 +01:00
Joe Rickerby f56ddda157 Make shell evals work and add tests for that 2017-09-02 22:09:32 +01:00
Joe Rickerby f64cb35d22 Merge remote-tracking branch 'origin/master' into environment 2017-09-01 13:42:45 +01:00
Joe Rickerby 4d26e4360f Merge pull request #31 from beniwohli/issue/nested-output-dir
use os.makedirs to allow nested output directory
2017-09-01 13:34:21 +01:00
Joe Rickerby fcb681d283 Use parse_environment in the main code 2017-09-01 13:33:29 +01:00
Joe Rickerby 9091533fcc Run the unit tests in CI 2017-09-01 13:32:34 +01:00
Joe Rickerby 5ae4da631f Add environment code satisfying unit test 2017-09-01 13:24:12 +01:00
Benjamin Wohlwend 0766cbd52f use os.makedirs to allow nested output directory 2017-08-31 09:49:57 +02:00
Joe Rickerby fd385a9bf8 Add some tests for the environment functionality 2017-08-30 19:05:06 +01:00
mayeut 0a7a1b8fb3 Set CIBUILDWHEEL environment variable to 1 during build 2017-08-29 18:18:40 +02:00
Joe Rickerby 9faa8f8728 Merge pull request #27 from aaugustin/patch-1
Add websockets to list of users.
2017-08-21 20:51:11 +01:00
Aymeric Augustin e9eba34869 Add websockets to list of users. 2017-08-20 14:16:07 +02:00
Joe Rickerby a5a23114a9 Update changelog 2017-08-14 21:39:42 +01:00
Joe Rickerby 77ab9a7627 Bump version 2017-08-14 21:37:37 +01:00
Joe Rickerby adf98443a1 Merge pull request #24 from YannickJadoul/windows-output
Unbuffered stdout for Windows and displaying the output of subprocess calls

Fix #23
2017-08-14 21:35:35 +01:00
Joe Rickerby abfbe42d6f Add -u flag so the test output is in the right order on Appveyor 2017-08-14 21:11:04 +01:00
Joe Rickerby b7a52b502d Move the stdout flush to the windows file, since it's only needed there 2017-08-14 21:10:48 +01:00
Joe Rickerby 573098f38f Mac/Windows stubs and test case for environment inheritance 2017-08-14 21:07:08 +01:00
Yannick Jadoul 84c2b30676 Flushing stdout in print_preamble 2017-08-14 15:12:46 +02:00
Yannick Jadoul f3e06f0a99 Unbuffered stdout for Windows and displaying the output of subprocess calls 2017-08-14 14:45:55 +02:00
Joe Rickerby 6aba5f81f1 Add a Linux implementation of environment 2017-07-24 22:39:37 +01:00
Joe Rickerby 38f89e955f Added test for environment and a script for local running 2017-07-24 22:39:16 +01:00
Joe Rickerby ed104d1315 Update readme 2017-07-23 22:39:15 +01:00
Joe Rickerby 4c0dd55c1a Bump version 2017-07-23 22:24:56 +01:00
Joe Rickerby e43d06f928 Update README.md
Capture the `pip install .` wisdom for quick-building projects.
2017-07-20 12:13:15 +01:00
tdos.apone f26da27db8 Merge pull request #18 from tgarc/master
revert PR9; add --no-deps to pip wheel

Revert PR9; it was never actually needed in the first place it just fixed an
issue by side effect.

1) add --no-deps to pip wheel step in order to avoid building wheels for
   dependencies. This also addresses the issues raised in PR #11.

2) Modify the pip install step across platforms to allow installation of
   dependencies from the index. (This is required since we will no longer be
   building wheels for dependencies).
2017-07-20 00:57:01 -05:00
Joe Rickerby b2fc3f4220 Remove more Python 2.6 support that I missed. See #12. 2017-07-18 23:12:07 +01:00
Joe Rickerby 609f90c88b Update README.md 2017-07-13 22:21:44 +01:00
Joe Rickerby 6e0d6b7fbc Update README.md 2017-07-13 22:20:33 +01:00
Joe Rickerby 5b7f105cb3 Merge pull request #14 from tgarc/master
minor fixes including python3 fix
2017-07-05 09:36:39 +01:00
tdos.apone b5006fc4bc minor fixes including python3 fix
+ In linux environment, if there was no matching python configurations for a
  particular architecture (i.e. if that architecture was skipped) the docker
  build script would just crash. The offending line:

    for whl in /tmp/linux_wheels/*.whl; do

  would result in whl being equal to the unexpanded glob expression. Rather
  than checking this in the bash script, python checks whether there are any
  actual wheels to build and skips the script if there are none.

+ added universal_newlines=True when making the call to grab the package name -
  this fix is required for python3 since other a bytes prefix (b'') will be
  added to the package name

+ In the linux/docker environment, added a try/catch statement around the
  docker script for KeyboardInterrupt that will kill the docker subprocess in
  cases where cibuildwheel is being run interactively.
2017-07-02 17:11:15 -05:00
Joe Rickerby c28e85df9e Create CODE_OF_CONDUCT.md 2017-06-27 20:14:39 +01:00
Joe Rickerby 726939506e Update Readme 2017-06-27 19:57:47 +01:00
Joe Rickerby 3ad2443df1 Add Docker check to linux run 2017-06-27 18:18:20 +01:00
Joe Rickerby e475859d89 Update changelog 2017-06-27 18:18:07 +01:00
Joe Rickerby dbb9bcb4d5 Bump version 2017-06-27 17:48:34 +01:00
Joe Rickerby c585dbd6f3 Remove Python 2.6 support on Linux
See #12 for discussion. Fix #12.
2017-06-27 16:44:41 +01:00
Joe Rickerby fdda1ee2e1 Add changelog to README 2017-06-11 16:32:13 +01:00
Joe Rickerby 533ef04740 Bump version 2017-06-11 16:20:44 +01:00
Joe Rickerby 8a3935551b Merge pull request #9 from tgarc/master
add a 'pip install' step before building wheels
2017-06-11 16:20:19 +01:00
tdos.apone 1b33c4cac5 add a 'pip install' step before building wheels 2017-06-03 12:37:13 -05:00
Joe Rickerby f734bffc1e Merge pull request #8 from tgarc/master
fix python 3 support
2017-05-27 18:37:40 +01:00
tdos.apone b40852ec90 fix python 3 support
+ urllib2 doesn't exist in python 3 - import from urllib.request instead

+ for linux builds open docker process with universal_newlines; this
  opens a pipe in 'text' mode using the default preferred encoding
2017-05-27 09:40:34 -05:00
Joe Rickerby a232b09fee Add automatic deployment option to readme
See #5
2017-04-23 13:25:40 +01:00
Joe Rickerby e3b2de0489 Bump version 2017-04-13 15:24:05 +01:00
Joe Rickerby c3565d5025 Add fancy preamble for the log files 2017-04-13 15:02:04 +01:00
Joe Rickerby 31ac2ba03f Fix compatibility with setup_requires 2017-04-13 14:22:27 +01:00
Joe Rickerby 083ece8bbc Don't ignore dependency wheels, copy them to output if pure wheels 2017-04-13 13:48:57 +01:00
Joe Rickerby bdbbf18e43 Add zfrenchee credit to README
Ref #2
2017-04-12 09:59:58 +01:00
Joe Rickerby 45a7836cc6 Fix copy/paste error 2017-04-12 09:58:46 +01:00
Joe Rickerby d5fa5475d4 Don't audit or delocate wheels that are dependencies of the package
Wheels that are dependencies are also output by `pip wheel`. These can
cause auditwheel trouble, and we're not interested in them anyway
2017-04-12 09:49:38 +01:00
Joe Rickerby 3f6a943b6d Add missing imports 2017-04-11 23:28:13 +01:00
Joe Rickerby c7764d239b Remove coming soon link
Fix #3
2017-04-11 23:24:53 +01:00
Joe Rickerby e072e82252 Add test for CIBW_SKIP 2017-04-11 23:23:59 +01:00
Joe Rickerby cf7f2e18ce Style 2017-04-11 23:23:50 +01:00
Joe Rickerby a2a2a7b382 Add Mac and Windows implementations of CIBW_SKIP 2017-04-11 23:15:19 +01:00
Joe Rickerby ba22589f6f Implement CIBW_SKIP for linux 2017-04-11 22:57:42 +01:00
Joe Rickerby e9bf990fd2 Merge branch 'before_build'
* before_build:
  Add documentation for CIBW_BEFORE_BUILD
  Fix os/sys typo and use an absolute path on *nix too
  Change version file path to absolute on Windows
  Perhaps a filename collision
  Check the path is okay on windows
  remove redundant line
  A little more debugging info for Windows
  Add before_build option, that runs a shell command before 'pip wheel'
  Don't run tests in a subshell so the errexit option applies
  Run the pip version check in the right env

# Conflicts:
#	README.md
2017-04-11 21:54:31 +01:00
Joe Rickerby db09cfc4a9 Update README.md 2017-04-10 22:58:05 +01:00
Joe Rickerby 2c0d6e3db1 Add documentation for CIBW_BEFORE_BUILD 2017-04-10 22:48:12 +01:00
Joe Rickerby 89c8735719 Fix os/sys typo and use an absolute path on *nix too 2017-04-10 22:41:08 +01:00
Joe Rickerby 7a2e951f48 Change version file path to absolute on Windows 2017-04-10 22:36:06 +01:00
Joe Rickerby ff942ad766 Perhaps a filename collision 2017-04-10 22:25:34 +01:00
Joe Rickerby 881cd66fae Check the path is okay on windows 2017-04-10 22:12:26 +01:00
Joe Rickerby 278661df77 remove redundant line 2017-04-10 22:11:10 +01:00
Joe Rickerby 520daa4c23 A little more debugging info for Windows 2017-04-10 21:56:26 +01:00
Joe Rickerby 2679cb7f46 Add before_build option, that runs a shell command before 'pip wheel' 2017-04-10 21:42:12 +01:00
Joe Rickerby 56f9a37165 Don't run tests in a subshell so the errexit option applies 2017-04-10 21:41:05 +01:00
Joe Rickerby 5c91340ad0 Run the pip version check in the right env 2017-04-10 21:40:03 +01:00
Joe Rickerby c53d79557e Bump version 2017-03-31 10:20:43 +01:00
Joe Rickerby 510229177a Remove some debug code that was breaking the build 2017-03-31 09:58:28 +01:00
Joe Rickerby 46868f6cfe Bump version 2017-03-23 15:28:56 +00:00
Joe Rickerby 61138c19d5 Add version lock to the readme, so project will specify that themselves 2017-03-23 15:28:42 +00:00
Joe Rickerby 782e383c10 Fix Travis build 2017-03-23 15:20:03 +00:00
Joe Rickerby fe3038049a Make sure environment variables are str type for Windows 2017-03-23 11:37:13 +00:00
Joe Rickerby 17a2485d25 Add second test project - testing the CIBW_TEST options 2017-03-23 11:20:50 +00:00
Joe Rickerby 747556c417 Add ability for test projects to have custom env vars 2017-03-23 11:20:00 +00:00
Joe Rickerby 21c53fed74 Add CIBW_PLATOFRM environment variable 2017-03-23 11:19:14 +00:00
Joe Rickerby c230a6d3ce Remove old standalone script 2017-03-23 11:18:18 +00:00
Joe Rickerby 32eb1b2dee Add some lovely badges 2017-03-21 09:50:25 +00:00
Joe Rickerby 3372e585f9 Fix module name error 2017-03-21 09:39:28 +00:00
Joe Rickerby 309a2adcda Add a single test project and set to build on travis/appveyor 2017-03-20 23:54:29 +00:00
Joe Rickerby 095a857dcf Readme updates 2017-03-20 23:04:35 +00:00
Joe Rickerby c06cffc652 Update README.md 2017-03-20 09:24:07 +00:00
41 changed files with 1235 additions and 427 deletions
+3
View File
@@ -92,3 +92,6 @@ ENV/
# Rope project settings
.ropeproject
# VSCode project settings
/.vscode
+17
View File
@@ -0,0 +1,17 @@
matrix:
include:
- sudo: required
services:
- docker
- os: osx
script:
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
pip install -r requirements-dev.txt
python ./bin/run_tests.py
else
# linux test requires root to clean up the wheelhouse (docker runs as root)
sudo pip install -r requirements-dev.txt
sudo python ./bin/run_tests.py
fi
+46
View File
@@ -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 -1
View File
@@ -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:
+156 -27
View File
@@ -1,6 +1,8 @@
cibuildwheel
============
[![PyPI](https://img.shields.io/pypi/v/cibuildwheel.svg)](https://pypi.python.org/pypi/cibuildwheel) [![Build Status](https://travis-ci.org/joerick/cibuildwheel.svg?branch=master)](https://travis-ci.org/joerick/cibuildwheel) [![Build status](https://ci.appveyor.com/api/projects/status/wbsgxshp05tt1tif/branch/master?svg=true)](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/master)
Python wheels are great. Building them across **Mac, Linux, Windows**, on **multiple versions of Python**, is not.
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports Travis CI and Appveyor - and it builds and tests your wheels across all of your platforms.
@@ -18,7 +20,7 @@ What does it do?
| Python 3.5 | ✅ | ✅ | ✅ | ✅ | ✅ |
| Python 3.6 | ✅ | ✅ | ✅ | ✅ | ✅ |
- Builds manylinux, macOS and Windows (32 and 64bit) wheels
- Builds manylinux, macOS and Windows (32 and 64bit) wheels using Travis CI and Appveyor
- Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate)
- Runs the library test suite against the wheel-installed version of your library
@@ -42,24 +44,24 @@ Usage
- os: osx
script:
- pip install cibuildwheel
- pip install cibuildwheel==0.5.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
- pip install cibuildwheel==0.5.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.
@@ -111,12 +113,70 @@ Default: `auto`
For `linux` you need Docker running, on Mac or Linux. For `macos`, you need a Mac machine, and note that this script is going to automatically install MacPython on your system, so don't run on your development machine. For `windows`, you need to run in Windows, and it will build and test for all versions of Python at `C:\PythonXX[-x64]`.
| 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` `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-* cp35-*` (don't build on Python 3.4 or Python 3.5)
| Environment variable: `CIBW_ENVIRONMENT`
| ---
Optional.
A space-separated list of environment variables to set during the build. Bash syntax should be used (even on Windows!).
You must set this variable to pass variables to Linux builds (since they execute in a Docker container). It also works for the other platforms.
You can use `$PATH` syntax to insert other variables, or the `$(pwd)` syntax to insert the output of other shell commands.
Example: `CFLAGS="-g -Wall" CXXFLAGS="-Wall"`
Example: `PATH=$PATH:/usr/local/bin`
Example: `BUILD_TIME="$(date)"`
Example: `PIP_EXTRA_INDEX_URL="https://pypi.myorg.com/simple"`
Platform-specific variants also available:
`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX`
In addition to the above, `cibuildwheel` always defines the environment variable `CIBUILDWHEEL=1`. This can be useful for [building wheels with optional extensions](https://github.com/joerick/cibuildwheel/wiki/Building-packages-with-optional-C-extensions).
| Environment variable: `CIBW_BEFORE_BUILD`
| ---
Optional.
A 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`
Example: `yum install -y libffi-dev && {pip} install .`
Platform-specific variants also available:
`CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX`
| Environment variable: `CIBW_TEST_COMMAND`
| ---
Optional.
Shell command to run the tests. The project root should be included in the command as "{project}". The wheel will be installed automatically and available for import from the tests.
Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. The project root should be included in the command as "{project}".
Example: `nosetests {project}/tests`
@@ -130,25 +190,6 @@ 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 🔶
| --- | ---
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.
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`
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 YML syntax
@@ -166,20 +207,100 @@ Example: `cp34-*` (don't build on Python 3.4)
</code></pre></td>
</tr></table>
Delivering to PyPI
------------------
After you've built your wheels, you'll probably want to deliver them to PyPI.
### Manual method
On your development machine, do the following...
```bash
# Clear out your 'dist' folder.
rm -rf dist
# Make a source distribution
python setup.py sdist
# 🏃🏻
# Go and download your wheel files from wherever you put them. Put
# them all into the 'dist' folder.
# Upload using 'twine' (you may need to 'pip install twine')
twine upload dist/*
```
### Semi-automatic method using wheelhouse-uploader
Obviously, manual steps are for chumps, so we can automate this a little by using [wheelhouse-uploader](https://github.com/ogrisel/wheelhouse-uploader).
> 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!
---------------
If your wheel didn't compile, check the list below for some debugging tips.
- A mistake in your config. To quickly test your config without doing a git push and waiting for your code to build on CI, you can run the Linux build in a Docker container. Try `cibuildwheel --platform linux`. You'll have to bring your config into the current environment first.
- A mistake in your config. To quickly test your config without doing a git push and waiting for your code to build on CI, you can run the Linux build in a Docker container. On Mac or Linux, with Docker running, try `cibuildwheel --platform linux`. You'll have to bring your config into the current environment first.
- Missing dependency. You might need to install something on the build machine. You can do this in `.travis.yml` or `appveyor.yml`, with apt-get, brew or whatever Windows uses :P . Given how the Linux build works, we'll probably have to build something into `cibuildwheel`. Let's chat about that over in the issues!
- Windows: missing C feature. The Windows C compiler doesn't support C language features invented after 1990, so you'll have to backport your C code to C90. For me, this mostly involved putting my variable declarations at the top of the function like an animal.
Working examples
----------------
Here are some repos that use cibuildwheel.
- [pyinstrument_cext](https://github.com/joerick/pyinstrument_cext)
- [websockets](https://github.com/aaugustin/websockets)
> Add repo here! Send a PR.
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.
Since `cibuildwheel` runs the wheel through delocate or auditwheel, it will automatically bundle library dependencies. This is similar to static linking, so it might have some licence implications. Check the license for any code you're pulling in to make sure that's allowed.
Changelog
=========
### 0.4.1
- Fixed a bug on Windows where subprocess' output was hidden (#23)
- Fixed a bug on Appveyor where logs would appear in the wrong order due to output buffering (#24, thanks @YannickJadoul!)
### 0.4.0
- Fixed a bug that was increasing the build time by building the wheel twice. This was a problem for large projects that have a long build time. If you're upgrading and you need the old behaviour, use `CIBW_BEFORE_BUILD={pip} install .`, or install exactly the dependencies you need in `CIBW_BEFORE_BUILD`. See #18.
### 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
============
@@ -203,6 +324,13 @@ 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)
- Yannick Jadoul [@YannickJadoul](https://github.com/YannickJadoul)
Credits
-------
@@ -211,6 +339,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
--------
+5
View File
@@ -0,0 +1,5 @@
build_script:
- pip install -r requirements-dev.txt
# the '-u' flag is required so the output is in the correct order.
# See https://github.com/joerick/cibuildwheel/pull/24 for more info.
- python -u ./bin/run_tests.py
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
cd "$(dirname "$0")"
cd ..
CIBW_PLATFORM=linux ./bin/run_test.py $1
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/python
from __future__ import print_function
import os, sys, subprocess, shutil, json
from glob import glob
def single_run(test_project):
# load project settings into environment
env_file = os.path.join(test_project, 'environment.json')
project_env = {}
if os.path.exists(env_file):
with open(env_file) as f:
project_env = json.load(f)
# run the build
env = os.environ.copy()
project_env = {str(k): str(v) for k, v in project_env.items()} # unicode not allowed in env
env.update(project_env)
print('Building %s with environment %s' % (test_project, project_env))
subprocess.check_call(['cibuildwheel', test_project], env=env)
wheels = glob('wheelhouse/*.whl')
print('%s built successfully. %i wheels built.' % (test_project, len(wheels)))
# check some wheels were actually built
assert len(wheels) >= 4
# clean up
shutil.rmtree('wheelhouse')
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("test_project_dir")
args = parser.parse_args()
project_path = os.path.abspath(args.test_project_dir)
if not os.path.exists(project_path):
print('No test project not found.', file=sys.stderr)
exit(2)
single_run(project_path)
print('Project built successfully.')
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/python
from __future__ import print_function
import os, sys, subprocess, shutil, json
from glob import glob
if __name__ == '__main__':
# move cwd to the project root
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
### run the unit tests
subprocess.check_call(['python', '-m', 'pytest', 'unit_test'])
### run the integration tests
test_projects = glob('test/??_*')
if len(test_projects) == 0:
print('No test projects found. Aborting.', file=sys.stderr)
exit(2)
print('Testing projects:', test_projects)
run_test_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'run_test.py')
for project_path in test_projects:
subprocess.check_call([sys.executable, run_test_path, project_path])
print('%d projects built successfully.' % len(test_projects))
-292
View File
@@ -1,292 +0,0 @@
from __future__ import print_function
import argparse, os, subprocess, sys, tempfile, shlex
from collections import namedtuple
from glob import glob
try:
# Python 3
import urllib.request as urllib2
except ImportError:
# Python 2
import urllib2
try:
from shlex import quote as shlex_quote
except ImportError:
from pipes import quote as shlex_quote
def linux_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']:
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 whl in /tmp/linux_wheels/*.whl; do
auditwheel repair "$whl" -w /output
done
# 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
# Install any requirements to run the tests
if [ ! -z "{test_requires}" ]; then
"$PYBIN/pip" install {test_requires}
fi
# Run the tests from a different directory
if [ ! -z {test_command} ]; then
(cd "$HOME" && export PATH=$PYBIN:$PATH && sh -c {test_command})
fi
done
'''.format(
package_name=package_name,
test_requires=' '.join(test_requires),
test_command=shlex_quote(test_command.format(project='/project') if test_command else ''),
)
docker_process = subprocess.Popen([
'docker',
'run',
'--rm',
'-i',
'-v', '%s:/project' % os.path.abspath(project_dir),
'-v', '%s:/output' % os.path.abspath(output_dir),
docker_image,
'/bin/bash'],
stdin=subprocess.PIPE)
docker_process.communicate(bash_script)
if docker_process.returncode != 0:
exit(1)
def windows_build(project_dir, package_name, output_dir, test_command, test_requires):
# 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')
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)
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', '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'),
]
for config in python_configurations:
env = os.environ.copy()
# set up environment variables for run_with_env
env['PYTHON_VERSION'] = config.version
env['PYTHON_ARCH'] = config.arch
env['PATH'] = os.pathsep.join([
config.path,
os.path.join(config.path, 'Scripts'),
env['PATH']
])
# for the logs - check we're running the right version of python
shell(['python', '--version'], env=env)
shell(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)\"'], env=env)
# prepare the Python environment
shell(['pip', 'install', '--disable-pip-version-check', '--user', '--upgrade', 'pip'], env=env)
shell(['pip', 'install', 'wheel'], env=env)
# build the wheel
shell(['pip', 'wheel', project_dir, '-w', output_dir], env=env)
# install the wheel
shell(['pip', 'install', package_name, '--no-index', '-f', output_dir], env=env)
# test the wheel
if test_requires:
shell(['pip', 'install'] + test_requires, env=env)
if test_command:
# run the tests from c:\, with an absolute path in the command
# (this ensures that Python runs the tests against the installed wheel
# and not the repo code)
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)
def macos_build(project_dir, package_name, output_dir, test_command, test_requires):
PythonConfiguration = namedtuple('PythonConfiguration', ['version', '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'),
]
def shell(args, env=None, cwd=None):
# print the command executing for the logs
print('+ ' + ' '.join(shlex_quote(a) for a in args))
return subprocess.check_call(args, env=env, cwd=cwd)
for config in python_configurations:
# download the pkg
shell(['curl', '-L', '-o', '/tmp/Python.pkg', config.url])
# install
shell(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
env = os.environ.copy()
env['PATH'] = os.pathsep.join([
'/Library/Frameworks/Python.framework/Versions/%s/bin' % config.version,
env['PATH'],
])
python = 'python3' if config.version[0] == '3' else 'python2'
pip = 'pip3' if config.version[0] == '3' else 'pip2'
# check what version we're on
shell(['which', python], env=env)
shell([python, '--version'], env=env)
# install pip & wheel
shell([python, '-m', 'ensurepip', '--upgrade'], env=env)
shell(['which', pip], env=env) # todo: remove
shell([pip, '--version'])
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]
# 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)
# test the wheel
if test_requires:
shell([pip, '-v', 'install'] + test_requires, env=env)
if test_command:
# run the tests from $HOME, with an absolute path in the command
# (this ensures that Python runs the tests against the installed wheel
# and not the repo code)
abs_project_dir = os.path.abspath(project_dir)
test_command_absolute = test_command.format(project=abs_project_dir)
shell(['which', 'nosetests'], env=env) # todo: remove
shell(['which', 'nosetests'], cwd=os.environ['HOME'], env=env) # todo: remove
shell(['nosetests', '-v'], env=env) # todo: remove
shell(['nosetests', '-v'], cwd=os.environ['HOME'], env=env) # todo: remove
shell(shlex.split(test_command_absolute), cwd=os.environ['HOME'], env=env)
def main():
parser = argparse.ArgumentParser(
description='Build wheels for all the platforms.',
epilog=('Most options are supplied via environment variables. '
'See https://github.com/joerick/cibuildwheel#options for info.'))
parser.add_argument('--platform',
choices=['auto', 'linux', 'macos', 'windows'],
default='auto',
help=('Platform to build for. For "linux" you need docker running, on Mac '
'or Linux. For "macos", you need a Mac machine, and note that this '
'script is going to automatically install MacPython on your system, '
'so don\'t run on your development machine. For "windows", you need to '
'run in Windows, and it will build and test for all versions of '
'Python at C:\\PythonXX[-x64]. Default: auto.'))
parser.add_argument('--output-dir',
default=os.environ.get('CIBW_OUTPUT_DIR', 'wheelhouse'),
help='Destination folder for the wheels.')
parser.add_argument('project_dir',
default='.',
nargs='?',
help=('Path to the project that you want wheels for. Default: the current '
'directory.'))
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:
if os.environ.get('TRAVIS_OS_NAME') == 'linux':
platform = 'linux'
elif os.environ.get('TRAVIS_OS_NAME') == 'osx':
platform = 'macos'
elif 'APPVEYOR' in os.environ:
platform = 'windows'
else:
print('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)
if platform == 'linux':
linux_build(**build_args)
elif platform == 'windows':
windows_build(**build_args)
elif platform == 'macos':
macos_build(**build_args)
else:
print('cibuildwheel: Unsupported platform %s' % platform, file=sys.stderr)
exit(1)
if __name__ == '__main__':
main()
+1
View File
@@ -0,0 +1 @@
__version__ = '0.5.0'
+104 -39
View File
@@ -1,7 +1,28 @@
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.environment import parse_environment, EnvironmentParseError
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(
@@ -11,7 +32,7 @@ def main():
parser.add_argument('--platform',
choices=['auto', 'linux', 'macos', 'windows'],
default='auto',
default=os.environ.get('CIBW_PLATFORM', 'auto'),
help=('Platform to build for. For "linux" you need docker running, on Mac '
'or Linux. For "macos", you need a Mac machine, and note that this '
'script is going to automatically install MacPython on your system, '
@@ -29,38 +50,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 +60,97 @@ 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', '')
environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform) or ''
try:
environment = parse_environment(environment_config)
except (EnvironmentParseError, ValueError) as e:
print('cibuildwheel: Malformed environment option "%s"' % environment_config, file=sys.stderr)
import traceback
traceback.print_exc(None, sys.stderr)
exit(2)
skip = BuildSkipper(skip_config)
# Add CIBUILDWHEEL environment variable
# This needs to be passed on to the docker container in linux.py
os.environ['CIBUILDWHEEL'] = '1'
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,
environment=environment,
)
print_preamble(platform, build_options)
if not os.path.exists(output_dir):
os.makedirs(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()
+67
View File
@@ -0,0 +1,67 @@
import subprocess, shlex
from collections import namedtuple
import bashlex
NodeExecutionContext = namedtuple('NodeExecutionContext', ['environment', 'input'])
def evaluate(value, environment):
if not value:
# empty string evaluates to empty string
# (but trips up bashlex)
return ''
command_node = bashlex.parsesingle(value)
if len(command_node.parts) != 1:
raise ValueError('"%s" has too many parts' % value)
value_word_node = command_node.parts[0]
return evaluate_node(
value_word_node,
context=NodeExecutionContext(environment=environment, input=value)
)
def evaluate_node(node, context):
if node.kind == 'word':
return evaluate_word_node(node, context=context)
elif node.kind == 'commandsubstitution':
return evaluate_command_node(node.command, context=context)
elif node.kind == 'parameter':
return evaluate_parameter_node(node, context=context)
else:
raise ValueError('Unsupported bash construct: "%s"' % node.word)
def evaluate_word_node(node, context):
word_start = node.pos[0]
word_end = node.pos[1]
word_string = context.input[word_start:word_end]
letters = list(word_string)
for part in node.parts:
part_start = part.pos[0] - word_start
part_end = part.pos[1] - word_start
# Set all the characters in the part to None
for i in range(part_start, part_end):
letters[i] = None
letters[part_start] = evaluate_node(part, context=context)
# remove the None letters and concat
value = ''.join(l for l in letters if l is not None)
# apply bash-like quotes/whitespace treatment
return ' '.join(word.strip() for word in shlex.split(value))
def evaluate_command_node(node, context):
words = [evaluate_node(part, context=context) for part in node.parts]
command = ' '.join(words)
return subprocess.check_output(shlex.split(command), env=context.environment)
def evaluate_parameter_node(node, context):
return context.environment.get(node.value, '')
+80
View File
@@ -0,0 +1,80 @@
import bashlex
from . import bashlex_eval
class EnvironmentParseError(Exception):
pass
def parse_environment(env_string):
env_items = split_env_items(env_string)
assignments = [EnvironmentAssignment(item) for item in env_items]
return ParsedEnvironment(assignments=assignments)
def split_env_items(env_string):
'''Splits space-separated variable assignments into a list of individual assignments.
>>> split_env_items('VAR=abc')
['VAR=abc']
>>> split_env_items('VAR="a string" THING=3')
['VAR="a string"', 'THING=3']
>>> split_env_items('VAR="a string" THING=\\'single "quotes"\\'')
['VAR="a string"', 'THING=\\'single "quotes"\\'']
>>> split_env_items('VAR="dont \\\\"forget\\\\" about backslashes"')
['VAR="dont \\\\"forget\\\\" about backslashes"']
>>> split_env_items('PATH="$PATH;/opt/cibw_test_path"')
['PATH="$PATH;/opt/cibw_test_path"']
>>> split_env_items('PATH2="something with spaces"')
['PATH2="something with spaces"']
'''
if not env_string:
return []
command_node = bashlex.parsesingle(env_string)
result = []
for word_node in command_node.parts:
part_string = env_string[word_node.pos[0]:word_node.pos[1]]
result.append(part_string)
return result
class EnvironmentAssignment(object):
def __init__(self, assignment):
name, equals, value = assignment.partition('=')
if not equals:
raise EnvironmentParseError(assignment)
self.name = name
self.value = value
def evaluated_value(self, environment):
'''Returns the value of this assignment, as evaluated in the environment'''
return bashlex_eval.evaluate(self.value, environment=environment)
def as_shell_assignment(self):
return 'export %s=%s' % (self.name, self.value)
def __repr__(self):
return '%s=%s' % (self.name, self.value)
class ParsedEnvironment(object):
def __init__(self, assignments):
self.assignments = assignments
def as_dictionary(self, prev_environment):
environment = prev_environment.copy()
for assignment in self.assignments:
value = assignment.evaluated_value(environment=environment)
environment[assignment.name] = value
return environment
def as_shell_commands(self):
return [a.as_shell_assignment() for a in self.assignments]
def __repr__(self):
return 'ParsedEnvironment(%r)' % [repr(a) for a in self.assignments]
+90 -16
View File
@@ -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,79 @@ 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, environment):
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
{environment_exports}
for whl in /tmp/linux_wheels/*.whl; do
auditwheel repair "$whl" -w /output
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
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,27 +90,45 @@ 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 ''
),
environment_exports='\n'.join(environment.as_shell_commands()),
)
docker_process = subprocess.Popen([
'docker',
'run',
'--env',
'CIBUILDWHEEL',
'--rm',
'-i',
'-v', '%s:/project' % os.path.abspath(project_dir),
'-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)
+60 -34
View File
@@ -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,71 +7,97 @@ 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, environment):
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):
def call(args, env=None, cwd=None, shell=False):
# print the command executing for the logs
print('+ ' + ' '.join(shlex_quote(a) for a in args))
return subprocess.check_call(args, env=env, cwd=cwd)
if shell:
print('+ %s' % args)
else:
print('+ ' + ' '.join(shlex_quote(a) for a in args))
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
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])
call(['curl', '-L', '-o', '/tmp/Python.pkg', config.url])
# install
shell(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
env = os.environ.copy()
env['PATH'] = os.pathsep.join([
'/Library/Frameworks/Python.framework/Versions/%s/bin' % config.version,
env['PATH'],
])
env = environment.as_dictionary(prev_environment=env)
python = 'python3' if config.version[0] == '3' else 'python2'
pip = 'pip3' if config.version[0] == '3' else 'pip2'
# check what version we're on
shell(['which', python], env=env)
shell([python, '--version'], env=env)
call(['which', python], env=env)
call([python, '--version'], env=env)
# install pip & wheel
shell([python, '-m', 'ensurepip', '--upgrade'], env=env)
shell(['which', pip], env=env) # todo: remove
shell([pip, '--version'])
shell([pip, 'install', 'wheel'], env=env)
shell([pip, 'install', 'delocate'], env=env)
call([python, '-m', 'ensurepip', '--upgrade'], env=env)
call([pip, '--version'], env=env)
call([pip, 'install', 'wheel'], env=env)
call([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)
call(before_build_prepared, env=env, shell=True)
# build the wheel
call([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
call(['delocate-listdeps', built_wheel], env=env)
# rebuild the wheel with shared libraries included and place in output dir
call(['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)
call([pip, 'install', delocated_wheel], env=env)
# test the wheel
if test_requires:
shell([pip, '-v', 'install'] + test_requires, env=env)
call([pip, 'install'] + test_requires, env=env)
if test_command:
# run the tests from $HOME, with an absolute path in the command
# (this ensures that Python runs the tests against the installed wheel
# and not the repo code)
abs_project_dir = os.path.abspath(project_dir)
test_command_absolute = test_command.format(project=abs_project_dir)
shell(['which', 'nosetests'], env=env) # todo: remove
shell(['which', 'nosetests'], cwd=os.environ['HOME'], env=env) # todo: remove
shell(['nosetests', '-v'], env=env) # todo: remove
shell(['nosetests', '-v'], cwd=os.environ['HOME'], env=env) # todo: remove
shell(shlex.split(test_command_absolute), cwd=os.environ['HOME'], env=env)
call(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)
+24
View File
@@ -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)
+49 -16
View File
@@ -1,14 +1,25 @@
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, environment):
# Python under AppVeyor/Windows seems to be buffering by default, giving problems interleaving subprocess call output with unflushed calls to 'print'
sys.stdout.flush()
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# 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 +28,33 @@ 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'),
]
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
@@ -41,6 +64,7 @@ def build(project_dir, package_name, output_dir, test_command, test_requires):
os.path.join(config.path, 'Scripts'),
env['PATH']
])
env = environment.as_dictionary(prev_environment=env)
# for the logs - check we're running the right version of python
shell(['python', '--version'], env=env)
@@ -51,11 +75,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 +97,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)
+2
View File
@@ -0,0 +1,2 @@
-e .
pytest
+7 -1
View File
@@ -1,8 +1,14 @@
[bumpversion]
current_version = 0.1.1
current_version = 0.5.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}
+2 -1
View File
@@ -8,7 +8,8 @@ except ImportError:
setup(
name='cibuildwheel',
version='0.1.1',
version='0.5.0',
install_requires=['bashlex'],
description="Build Python wheels on CI with minimal configuration.",
long_description='For readme please see http://github.com/joerick/cibuildwheel',
author="Joe Rickerby",
+12
View File
@@ -0,0 +1,12 @@
import os
from setuptools import setup, Extension
if os.environ.get('CIBUILDWHEEL', '0') != '1':
raise Exception('CIBUILDWHEEL environment variable is not set to 1')
setup(
name="spam",
ext_modules=[Extension('spam', sources=['spam.c'])],
version="0.1.0",
)
+48
View File
@@ -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)
}
+4
View File
@@ -0,0 +1,4 @@
{
"CIBW_TEST_REQUIRES": "nose",
"CIBW_TEST_COMMAND": "nosetests {project}/test"
}
+7
View File
@@ -0,0 +1,7 @@
from setuptools import setup, Extension
setup(
name="spam",
ext_modules=[Extension('spam', sources=['spam.c'])],
version="0.1.0",
)
+48
View File
@@ -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)
}
+8
View File
@@ -0,0 +1,8 @@
from unittest import TestCase
import spam
class TestSpam(TestCase):
def test_system(self):
self.assertEqual(0, spam.system('python -c "exit(0)"'))
self.assertNotEqual(0, spam.system('python -c "exit(1)"'))
+4
View File
@@ -0,0 +1,4 @@
{
"CIBW_BEFORE_BUILD": "{python} -c \"import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)\" && {python} -c \"import sys; open('/tmp/pythonexecutable.txt', 'w').write(sys.executable)\"",
"CIBW_BEFORE_BUILD_WINDOWS": "{python} -c \"import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)\" && {python} -c \"import sys; open('c:\\pythonexecutable.txt', 'w').write(sys.executable)\""
}
+27
View File
@@ -0,0 +1,27 @@
from setuptools import setup, Extension
import sys, os
if sys.argv[-1] != '--name':
# assert that the Python version as written to pythonversion.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
# check that the executable also was written
executable_file = 'c:\\pythonexecutable.txt' if sys.platform == 'win32' else '/tmp/pythonexecutable.txt'
with open(executable_file) as f:
stored_executable = f.read()
print('stored_executable', stored_executable)
print('sys.executable', sys.executable)
# windows/mac are case insensitive
assert os.path.realpath(stored_executable).lower() == os.path.realpath(sys.executable).lower()
setup(
name="spam",
ext_modules=[Extension('spam', sources=['spam.c'])],
version="0.1.0",
)
+48
View File
@@ -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)
}
+2
View File
@@ -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)]
+3
View File
@@ -0,0 +1,3 @@
{
"CIBW_SKIP": "cp26-* cp33-*"
}
+15
View File
@@ -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",
)
+48
View File
@@ -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)
}
+2
View File
@@ -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)]
+4
View File
@@ -0,0 +1,4 @@
{
"CIBW_ENVIRONMENT": "CIBW_TEST_VAR=\"a b c\" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3=\"$(echo 'test string 3')\" PATH=$PATH:/opt/cibw_test_path",
"CIBW_ENVIRONMENT_WINDOWS": "CIBW_TEST_VAR=\"a b c\" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3=\"$(echo 'test string 3')\" PATH=\"$PATH;/opt/cibw_test_path\""
}
+24
View File
@@ -0,0 +1,24 @@
from setuptools import setup, Extension
import sys, os
if sys.argv[-1] != '--name':
# explode if environment isn't correct, as set in CIBW_ENVIRONMENT
CIBW_TEST_VAR = os.environ.get('CIBW_TEST_VAR')
CIBW_TEST_VAR_2 = os.environ.get('CIBW_TEST_VAR_2')
PATH = os.environ.get('PATH')
if CIBW_TEST_VAR != 'a b c':
raise Exception('CIBW_TEST_VAR should equal "a b c". It was "%s"' % CIBW_TEST_VAR)
if CIBW_TEST_VAR_2 != '1':
raise Exception('CIBW_TEST_VAR_2 should equal "1". It was "%s"' % CIBW_TEST_VAR_2)
if '/opt/cibw_test_path' not in PATH:
raise Exception('PATH should contain "/opt/cibw_test_path". It was "%s"' % PATH)
if '$PATH' in PATH:
raise Exception('$PATH should be expanded in PATH. It was "%s"' % PATH)
setup(
name="spam",
ext_modules=[Extension('spam', sources=['spam.c'])],
version="0.1.0",
)
+48
View File
@@ -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)
}
+2
View File
@@ -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)]
+3
View File
@@ -0,0 +1,3 @@
This folder contains repos that are built by cibuildwheel to test cibuildwheel. Confusing, I know.
Basically, if cibuildwheel can build these projects, we're good.
+84
View File
@@ -0,0 +1,84 @@
from cibuildwheel.environment import parse_environment
def test_basic_parsing():
environment_recipe = parse_environment('VAR=1 VBR=2')
environment_dict = environment_recipe.as_dictionary(
prev_environment={}
)
environment_cmds = environment_recipe.as_shell_commands()
assert environment_dict == {'VAR': '1', 'VBR': '2'}
assert environment_cmds == ['export VAR=1', 'export VBR=2']
def test_quotes():
environment_recipe = parse_environment('A=1 VAR="1 NOT_A_VAR=2" VBR=\'vbr\'')
environment_dict = environment_recipe.as_dictionary(
prev_environment={}
)
environment_cmds = environment_recipe.as_shell_commands()
assert environment_dict == {'A': '1', 'VAR': '1 NOT_A_VAR=2', 'VBR': 'vbr'}
assert environment_cmds == ['export A=1', 'export VAR="1 NOT_A_VAR=2"', 'export VBR=\'vbr\'']
def test_inheritance():
environment_recipe = parse_environment('PATH=$PATH:/usr/local/bin')
environment_dict = environment_recipe.as_dictionary(
prev_environment={'PATH': '/usr/bin'}
)
environment_cmds = environment_recipe.as_shell_commands()
assert environment_dict == {'PATH': '/usr/bin:/usr/local/bin'}
assert environment_cmds == ['export PATH=$PATH:/usr/local/bin']
def test_shell_eval():
environment_recipe = parse_environment('VAR="$(echo "a test" string)"')
environment_dict = environment_recipe.as_dictionary(
prev_environment={}
)
environment_cmds = environment_recipe.as_shell_commands()
assert environment_dict == {'VAR': 'a test string'}
assert environment_cmds == ['export VAR="$(echo "a test" string)"']
def test_shell_eval_and_env():
environment_recipe = parse_environment('VAR="$(echo "$PREV_VAR" string)"')
environment_dict = environment_recipe.as_dictionary(
prev_environment={'PREV_VAR': '1 2 3'}
)
environment_cmds = environment_recipe.as_shell_commands()
assert environment_dict == {'PREV_VAR': '1 2 3', 'VAR': '1 2 3 string'}
assert environment_cmds == ['export VAR="$(echo "$PREV_VAR" string)"']
def test_empty_var():
environment_recipe = parse_environment('CFLAGS=')
environment_dict = environment_recipe.as_dictionary(
prev_environment={'CFLAGS': '-Wall'}
)
environment_cmds = environment_recipe.as_shell_commands()
assert environment_dict == {'CFLAGS': ''}
assert environment_cmds == ['export CFLAGS=']
def test_no_vars():
environment_recipe = parse_environment('')
environment_dict = environment_recipe.as_dictionary(prev_environment={})
environment_cmds = environment_recipe.as_shell_commands()
assert environment_dict == {}
assert environment_cmds == []
def test_no_vars_pass_through():
environment_recipe = parse_environment('')
environment_dict = environment_recipe.as_dictionary(prev_environment={'CIBUILDWHEEL': 'awesome'})
assert environment_dict == {'CIBUILDWHEEL': 'awesome'}