diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index ab1aa654..7099bc8f 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -48,26 +48,34 @@ def strtobool(val):
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.'))
+ 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=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, '
- '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. 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. 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',
+ parser.add_argument('package_dir',
default='.',
nargs='?',
- help=('Path to the project that you want wheels for. Default: the current '
- 'directory.'))
+ help='''
+ Path to the package that you want wheels for. Must be a subdirectory of
+ the working directory. When set, the working directory is still
+ considered the 'project' and is copied into the Docker container on
+ Linux. Default: the working directory.
+ ''')
parser.add_argument('--print-build-identifiers',
action='store_true',
@@ -104,7 +112,7 @@ def main():
test_command = get_option_from_environment('CIBW_TEST_COMMAND', platform=platform)
test_requires = get_option_from_environment('CIBW_TEST_REQUIRES', platform=platform, default='').split()
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
- project_dir = args.project_dir
+ package_dir = args.package_dir
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
build_verbosity = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
@@ -147,8 +155,8 @@ def main():
# This needs to be passed on to the docker container in linux.py
os.environ['CIBUILDWHEEL'] = '1'
- if not os.path.exists(os.path.join(project_dir, 'setup.py')):
- print('cibuildwheel: Could not find setup.py at root of project', file=sys.stderr)
+ if not os.path.exists(os.path.join(package_dir, 'setup.py')):
+ print('cibuildwheel: Could not find setup.py at root of package', file=sys.stderr)
exit(2)
if args.print_build_identifiers:
@@ -189,7 +197,7 @@ def main():
manylinux_images = None
build_options = BuildOptions(
- project_dir=project_dir,
+ package_dir=package_dir,
output_dir=output_dir,
test_command=test_command,
test_requires=test_requires,
@@ -201,7 +209,7 @@ def main():
environment=environment,
before_test=before_test,
dependency_constraints=dependency_constraints,
- manylinux_images=manylinux_images
+ manylinux_images=manylinux_images,
)
# Python is buffering by default when running on the CI platforms, giving problems interleaving subprocess call output with unflushed calls to 'print'
diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py
index a28c0a49..04b2a620 100644
--- a/cibuildwheel/linux.py
+++ b/cibuildwheel/linux.py
@@ -96,12 +96,18 @@ def build(options: BuildOptions):
('pp', 'manylinux_x86_64', options.manylinux_images['pypy_x86_64']),
]
+ if not os.path.realpath(options.package_dir).startswith(os.path.realpath('.')):
+ raise Exception('package_dir must be inside the working directory')
+
+ container_package_dir = os.path.join('/project', os.path.relpath(options.package_dir, '.'))
+
for implementation, platform_tag, docker_image in platforms:
platform_configs = [c for c in python_configurations if c.identifier.startswith(implementation) and c.identifier.endswith(platform_tag)]
if not platform_configs:
continue
container_name = 'cibuildwheel-{}'.format(uuid.uuid4())
+
try:
call(['docker', 'create',
'--env', 'CIBUILDWHEEL',
@@ -111,9 +117,7 @@ def build(options: BuildOptions):
docker_image,
'/bin/bash'])
- call(['docker', 'cp',
- os.path.abspath(options.project_dir) + '/.',
- container_name + ':/project'])
+ call(['docker', 'cp', '.', container_name + ':/project'])
call(['docker', 'start', container_name])
@@ -165,7 +169,7 @@ def build(options: BuildOptions):
# Build the wheel
rm -rf /tmp/built_wheel
mkdir /tmp/built_wheel
- pip wheel . -w /tmp/built_wheel --no-deps {build_verbosity_flag}
+ pip wheel {package_dir} -w /tmp/built_wheel --no-deps {build_verbosity_flag}
built_wheel=(/tmp/built_wheel/*.whl)
# repair the wheel
@@ -234,13 +238,14 @@ def build(options: BuildOptions):
done
'''.format(
config_python_bin=config.path + '/bin',
+ package_dir=container_package_dir,
test_requires=' '.join(options.test_requires),
test_extras=options.test_extras,
test_command=shlex.quote(
- prepare_command(options.test_command, project='/project') if options.test_command else ''
+ prepare_command(options.test_command, project='/project', package=container_package_dir) if options.test_command else ''
),
before_build=shlex.quote(
- prepare_command(options.before_build, project='/project') if options.before_build else ''
+ prepare_command(options.before_build, project='/project', package=container_package_dir) if options.before_build else ''
),
build_verbosity_flag=' '.join(get_build_verbosity_extra_flags(options.build_verbosity)),
repair_command=shlex.quote(
@@ -250,7 +255,7 @@ def build(options: BuildOptions):
uid=os.getuid(),
gid=os.getgid(),
before_test=shlex.quote(
- prepare_command(options.before_test, project='/project') if options.before_test else ''
+ prepare_command(options.before_test, project='/project', package=container_package_dir) if options.before_test else ''
),
dependency_install_flags='-c /constraints.txt' if options.dependency_constraints else '',
)
@@ -268,12 +273,12 @@ def build(options: BuildOptions):
call(['docker', 'rm', '--force', '-v', container_name])
-def troubleshoot(project_dir, error):
+def troubleshoot(package_dir, error):
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
# the bash script failed
print('Checking for common errors...')
so_files = []
- for root, dirs, files in os.walk(project_dir):
+ for root, dirs, files in os.walk(package_dir):
for name in files:
_, ext = os.path.splitext(name)
if ext == '.so':
diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py
index 798cb493..599e9b23 100644
--- a/cibuildwheel/macos.py
+++ b/cibuildwheel/macos.py
@@ -168,7 +168,6 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
def build(options: BuildOptions):
- abs_project_dir = os.path.abspath(options.project_dir)
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
@@ -186,14 +185,14 @@ def build(options: BuildOptions):
# run the before_build command
if options.before_build:
- before_build_prepared = prepare_command(options.before_build, project=abs_project_dir)
+ before_build_prepared = prepare_command(options.before_build, project='.', package=options.package_dir)
call(before_build_prepared, env=env, shell=True)
# build the wheel
if os.path.exists(built_wheel_dir):
shutil.rmtree(built_wheel_dir)
os.makedirs(built_wheel_dir)
- call(['pip', 'wheel', abs_project_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
+ call(['pip', 'wheel', options.package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
# repair the wheel
@@ -229,7 +228,7 @@ def build(options: BuildOptions):
call(['which', 'python'], env=virtualenv_env)
if options.before_test:
- before_test_prepared = prepare_command(options.before_test, project=abs_project_dir)
+ before_test_prepared = prepare_command(options.before_test, project='.', package=options.package_dir)
call(before_test_prepared, env=virtualenv_env, shell=True)
# install the wheel
@@ -242,7 +241,11 @@ def build(options: BuildOptions):
# 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)
- test_command_prepared = prepare_command(options.test_command, project=abs_project_dir)
+ test_command_prepared = prepare_command(
+ options.test_command,
+ project=os.path.abspath('.'),
+ package=os.path.abspath(options.package_dir)
+ )
call(test_command_prepared, cwd=os.environ['HOME'], env=virtualenv_env, shell=True)
# clean up
diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py
index 03fe6af5..64f561a1 100644
--- a/cibuildwheel/util.py
+++ b/cibuildwheel/util.py
@@ -107,7 +107,7 @@ class DependencyConstraints:
BuildOptions = NamedTuple("BuildOptions", [
- ("project_dir", str),
+ ("package_dir", str),
("output_dir", str),
("test_command", Optional[str]),
("test_requires", List[str]),
diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py
index db5fc48a..e6f14ed9 100644
--- a/cibuildwheel/windows.py
+++ b/cibuildwheel/windows.py
@@ -143,7 +143,6 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
def build(options: BuildOptions):
- abs_project_dir = os.path.abspath(options.project_dir)
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
@@ -165,14 +164,14 @@ def build(options: BuildOptions):
# run the before_build command
if options.before_build:
- before_build_prepared = prepare_command(options.before_build, project=abs_project_dir)
+ before_build_prepared = prepare_command(options.before_build, project='.', package=options.package_dir)
shell([before_build_prepared], env=env)
# build the wheel
if os.path.exists(built_wheel_dir):
shutil.rmtree(built_wheel_dir)
os.makedirs(built_wheel_dir)
- shell(['pip', 'wheel', abs_project_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
+ shell(['pip', 'wheel', options.package_dir, '-w', built_wheel_dir, '--no-deps'] + get_build_verbosity_extra_flags(options.build_verbosity), env=env)
built_wheel = glob(os.path.join(built_wheel_dir, '*.whl'))[0]
# repair the wheel
@@ -213,7 +212,11 @@ def build(options: BuildOptions):
shell(['which', 'python'], env=virtualenv_env)
if options.before_test:
- before_test_prepared = prepare_command(options.before_test, project=abs_project_dir)
+ before_test_prepared = prepare_command(
+ options.before_test,
+ project='.',
+ package=options.package_dir
+ )
shell([before_test_prepared], env=virtualenv_env)
# install the wheel
@@ -226,7 +229,11 @@ def build(options: BuildOptions):
# 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)
- test_command_prepared = prepare_command(options.test_command, project=abs_project_dir)
+ test_command_prepared = prepare_command(
+ options.test_command,
+ project=os.path.abspath('.'),
+ package=os.path.abspath(options.package_dir)
+ )
shell([test_command_prepared], cwd='c:\\', env=virtualenv_env)
# clean up
diff --git a/docs/options.md b/docs/options.md
index c17e4933..8ecaaf23 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -199,7 +199,7 @@ A shell command to run before building the wheel. This option allows you to run
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`; `cibuildwheel` makes sure the right version of Python and pip will be executed. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
+The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. The placeholder `{package}` can be used here; it will be replaced by the path to the package being built by `cibuildwheel`.
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
@@ -217,8 +217,11 @@ CIBW_BEFORE_BUILD: pip install pybind11
# chain commands using &&
CIBW_BEFORE_BUILD: yum install -y libffi-dev && pip install .
-# run a script that's inside your repo
+# run a script that's inside your project
CIBW_BEFORE_BUILD: bash scripts/prepare_for_build.sh
+
+# if cibuildwheel is called with a package_dir argument, it's available as {package}
+CIBW_BEFORE_BUILD: "{package}/bin/prepare_for_build.sh"
```
@@ -348,7 +351,10 @@ CIBW_DEPENDENCY_VERSIONS: ./constraints.txt
### `CIBW_TEST_COMMAND` {: #test-command}
> Execute a shell command to test each built wheel
-Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
+Shell command to run tests after the build. The wheel will be installed automatically and available for import from the tests. To ensure the wheel is imported by your tests (instead of your source copy), tests are run from a different directory. Use the placeholders `{project}` and `{package}` when specifying paths in your project.
+
+- `{project}` is an absolute path to the project root - the working directory where cibuildwheel was called.
+- `{package}` is the path to the package being built - the `package_dir` argument supplied to cibuildwheel on the command line.
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
@@ -361,8 +367,8 @@ Platform-specific variants also available:
# run the project tests against the installed wheel using `nose`
CIBW_TEST_COMMAND: nosetests {project}/tests
-# run the project tests using `pytest`
-CIBW_TEST_COMMAND: pytest {project}/tests
+# run the package tests using `pytest`
+CIBW_TEST_COMMAND: pytest {package}/tests
```
@@ -411,7 +417,7 @@ CIBW_TEST_EXTRAS: test,qt
A shell command to run in **each** test virtual environment, before your wheel is installed and tested. This is useful if you need to install a non pip package, change values of environment variables
or perform multi step pip installation (e.g. installing `scikit-build` or `cython` before install test package)
-The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. `{project}` can be used as a placeholder for the absolute path to the project's root and will be replaced by `cibuildwheel`.
+The active Python binary can be accessed using `python`, and pip with `pip`; `cibuildwheel` makes sure the right version of Python and pip will be executed. The placeholder `{package}` can be used here; it will be replaced by the path to the package being built by `cibuildwheel`.
The command is run in a shell, so you can write things like `cmd1 && cmd2`.
@@ -456,13 +462,16 @@ CIBW_BUILD_VERBOSITY: 1
```text
usage: cibuildwheel [-h] [--platform {auto,linux,macos,windows}]
[--output-dir OUTPUT_DIR] [--print-build-identifiers]
- [project_dir]
+ [package_dir]
Build wheels for all the platforms.
positional arguments:
- project_dir Path to the project that you want wheels for.
- Default: the current directory.
+ package_dir Path to the package that you want wheels for. Must be
+ a subdirectory of the working directory. When set, the
+ working directory is still considered the 'project'
+ and is copied into the Docker container on Linux.
+ Default: the working directory.
optional arguments:
-h, --help show this help message and exit
@@ -473,13 +482,12 @@ optional arguments:
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].
+ for all versions of Python. Default: auto.
--output-dir OUTPUT_DIR
Destination folder for the wheels.
--print-build-identifiers
Print the build identifiers matched by the current
invocation and exit.
-
```