Compare commits

..
56 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
27 changed files with 684 additions and 153 deletions
+3
View File
@@ -92,3 +92,6 @@ ENV/
# Rope project settings
.ropeproject
# VSCode project settings
/.vscode
+4 -4
View File
@@ -8,10 +8,10 @@ matrix:
script:
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
pip install .
python ./run_tests.py
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 .
sudo python ./run_tests.py
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/
+85 -39
View File
@@ -44,24 +44,24 @@ Usage
- os: osx
script:
- pip install cibuildwheel==0.3.0
- 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==0.3.0
- 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.
@@ -113,39 +113,6 @@ 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_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.
Example: `nosetests {project}/tests`
| Environment variable: `CIBW_TEST_REQUIRES`
| ---
Optional.
Space-separated list of dependencies required for running the tests.
Example: `pytest`
Example: `nose==1.3.7 moto==0.4.31`
| Environment variable: `CIBW_BEFORE_BUILD`
| ---
Optional.
Shell command to run before building the wheel. This option allows you to run a command in **each** Python environment before the `pip wheel` command. This is useful if you need to set up some dependency so it's available during the build.
The active Python binary can be accessed using `{python}`, and pip with `{pip}`. These are useful when you need to write `python3` or `pip3` on a Python 3.x build.
Example: `{pip} install pybind11`
Platform-specific variants also available:
`CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX`
| Environment variable: `CIBW_SKIP`
| ---
@@ -161,10 +128,68 @@ Platform tags look like `macosx_10_6_intel` `manylinux1_x86_64` `manylinux1_i386
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-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 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`
| Environment variable: `CIBW_TEST_REQUIRES`
| ---
Optional.
Space-separated list of dependencies required for running the tests.
Example: `pytest`
Example: `nose==1.3.7 moto==0.4.31`
--
#### Example YML syntax
@@ -232,17 +257,31 @@ 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
@@ -285,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
-------
+4 -2
View File
@@ -1,3 +1,5 @@
build_script:
- pip install .
- python ./run_tests.py
- 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))
+1 -1
View File
@@ -1 +1 @@
__version__ = '0.3.0'
__version__ = '0.5.0'
+21 -2
View File
@@ -3,6 +3,7 @@ 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):
@@ -59,7 +60,7 @@ 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.',
@@ -72,12 +73,26 @@ def main():
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'])
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:
@@ -102,10 +117,14 @@ def main():
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':
cibuildwheel.linux.build(**build_options)
elif platform == 'windows':
+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]
+43 -20
View File
@@ -1,5 +1,5 @@
from __future__ import print_function
import os, subprocess
import os, subprocess, sys
from collections import namedtuple
from .util import prepare_command
@@ -9,7 +9,15 @@ except ImportError:
from pipes import quote as shlex_quote
def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, skip):
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'),
@@ -18,8 +26,6 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
PythonConfiguration(identifier='cp34-manylinux1_x86_64', path='/opt/python/cp34-cp34m'),
PythonConfiguration(identifier='cp35-manylinux1_x86_64', path='/opt/python/cp35-cp35m'),
PythonConfiguration(identifier='cp36-manylinux1_x86_64', path='/opt/python/cp36-cp36m'),
PythonConfiguration(identifier='cp26-manylinux1_i686', path='/opt/python/cp26-cp26m'),
PythonConfiguration(identifier='cp26-manylinux1_i686', path='/opt/python/cp26-cp26mu'),
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27m'),
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27mu'),
PythonConfiguration(identifier='cp33-manylinux1_i686', path='/opt/python/cp33-cp33m'),
@@ -38,37 +44,44 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
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
{environment_exports}
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
# install the package first to take care of dependencies
"$PYBIN/pip" install .
# Build that wheel
"$PYBIN/pip" wheel . -w /tmp/built_wheel --no-deps
built_wheel=(/tmp/built_wheel/*.whl)
"$PYBIN/pip" wheel --no-deps . -w /tmp/linux_wheels
done
for whl in /tmp/linux_wheels/*.whl; do
if [[ "$whl" == *none-any.whl ]]; then
# pure python wheel - just copy to the output
cp "$whl" /output
# 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 "$whl" -w /output
auditwheel repair "$built_wheel" -w /tmp/delocated_wheel
fi
done
delocated_wheel=(/tmp/delocated_wheel/*.whl)
# Install packages and test
for PYBIN in {pybin_paths}; do
# Install the wheel we just built
"$PYBIN/pip" install {package_name} \
--upgrade --force-reinstall --no-deps --no-index -f /output
"$PYBIN/pip" install "$delocated_wheel"
# Install any requirements to run the tests
if [ ! -z "{test_requires}" ]; then
@@ -81,6 +94,9 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
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,
@@ -92,11 +108,14 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
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),
@@ -105,7 +124,11 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
'/bin/bash'],
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 -31
View File
@@ -1,5 +1,5 @@
from __future__ import print_function
import os, subprocess, shlex, sys
import os, subprocess, shlex, sys, shutil
from collections import namedtuple
from glob import glob
try:
@@ -10,7 +10,7 @@ except ImportError:
from .util import prepare_command
def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, skip):
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', identifier='cp27-macosx_10_6_intel', url='https://www.python.org/ftp/python/2.7.13/python-2.7.13-macosx10.6.pkg'),
@@ -19,10 +19,14 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
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):
@@ -30,62 +34,70 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
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([pip, '--version'], env=env)
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)
# 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')
# 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)
call(before_build_prepared, env=env, shell=True)
# install the package first to take care of dependencies
shell([pip, 'install', project_dir], env=env)
# build the wheel
call([pip, 'wheel', project_dir, '-w', '/tmp/built_wheel', '--no-deps'], env=env)
built_wheel = glob('/tmp/built_wheel/*.whl')[0]
# build the wheel to temp dir
temp_wheel_dir = '/tmp/tmpwheel%s' % config.version
shell([pip, 'wheel', project_dir, '-w', temp_wheel_dir, '--no-deps'], env=env)
temp_wheel = glob(temp_wheel_dir+'/*.whl')[0]
if temp_wheel.endswith('none-any.whl'):
# pure python wheel - just copy to output_dir
shell(['cp', temp_wheel, output_dir], env=env)
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', temp_wheel], env=env)
call(['delocate-listdeps', built_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)
call(['delocate-wheel', '-w', '/tmp/delocated_wheel', built_wheel], env=env)
delocated_wheel = glob('/tmp/delocated_wheel/*.whl')[0]
# now install the package from the generated wheel
shell([pip, 'install', package_name, '--upgrade', '--force-reinstall',
'--no-deps', '--no-index', '--find-links', output_dir], env=env)
# install the wheel
call([pip, 'install', delocated_wheel], env=env)
# test the wheel
if test_requires:
shell([pip, '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(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)
+22 -9
View File
@@ -1,15 +1,20 @@
from __future__ import print_function
import os, tempfile, subprocess, sys
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, before_build, skip):
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):
@@ -37,11 +42,19 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
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,6 +64,7 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
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)
@@ -66,16 +80,12 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
before_build_prepared = prepare_command(before_build, python='python', pip='pip')
shell([before_build_prepared], env=env)
# install the package first to take care of dependencies
shell(['pip', 'install', project_dir], env=env)
# build the wheel
shell(['pip', 'wheel', project_dir, '-w', output_dir, '--no-deps'], 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, '--upgrade',
'--force-reinstall', '--no-deps', '--no-index', '-f',
output_dir], env=env)
shell(['pip', 'install', built_wheel], env=env)
# test the wheel
if test_requires:
@@ -87,3 +97,6 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be
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
-39
View File
@@ -1,39 +0,0 @@
from __future__ import print_function
import os, sys, subprocess, shutil, json
from glob import glob
# move cwd to the project root
os.chdir(os.path.dirname(os.path.abspath(__file__)))
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)
for project_path in test_projects:
# load project settings into environment
env_file = os.path.join(project_path, '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' % (project_path, project_env))
subprocess.check_call(['cibuildwheel', project_path], env=env)
wheels = glob('wheelhouse/*.whl')
print('%s built successfully. %i wheels built.' % (project_path, len(wheels)))
# check some wheels were actually built
assert len(wheels) >= 4
# clean up
shutil.rmtree('wheelhouse')
print('%d projects built successfully.' % len(test_projects))
+1 -1
View File
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.3.0
current_version = 0.5.0
commit = True
tag = True
message = Bump version
+2 -1
View File
@@ -8,7 +8,8 @@ except ImportError:
setup(
name='cibuildwheel',
version='0.3.0',
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",
+5
View File
@@ -1,5 +1,10 @@
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'])],
+2 -2
View File
@@ -1,4 +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)\""
"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)\""
}
+11 -2
View File
@@ -1,8 +1,8 @@
from setuptools import setup, Extension
import sys
import sys, os
if sys.argv[-1] != '--name':
# assert that the Python version as written to version.txt in the CIBW_BEFORE_BUILD step
# 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:
@@ -11,6 +11,15 @@ if sys.argv[-1] != '--name':
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'])],
+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)]
+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'}