diff --git a/README.md b/README.md index e24a4d9b..f3299909 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,8 @@ All being well, you should get wheels delivered to you in a few minutes. | | `CIBW_SKIP` | Skip certain Python versions | | **Build environment** | `CIBW_ENVIRONMENT` | Set environment variables needed during the build | | | `CIBW_BEFORE_BUILD` | Execute a shell command preparing each wheel's build | +| | `CIBW_MANYLINUX1_X86_64_IMAGE` | Specify an alternative manylinx1 x86_64 docker image | +| | `CIBW_MANYLINUX1_I686_IMAGE` | Specify an alternative manylinux1 i686 docker image | | **Tests** | `CIBW_TEST_COMMAND` | Execute a shell command to test all built wheels | | | `CIBW_TEST_REQUIRES` | Install Python dependencies before running the tests | @@ -94,6 +96,7 @@ A more detailed description of the options, the allowed values, and some example Linux wheels are built in the [`manylinux1` docker images](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 513](https://www.python.org/dev/peps/pep-0513/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account: - Progams and libraries cannot be installed on the Travis CI Ubuntu host with `apt-get`, but can be installed inside of the Docker image using `yum` or manually. The same goes for environment variables that are potentially needed to customize the wheel building. `cibuildwheel` supports this by providing the `CIBW_ENVIRONMENT` and `CIBW_BEFORE_BUILD` options to setup the build environment inside the running Docker image. See [below](#options) for details on these options. - The project directory is mounted in the running Docker instance as `/project`, the output directory for the wheels as `/output`. In general, this is handled transparently by `cibuildwheel`. For a more finegrained level of control however, the root of the host file system is mounted as `/host`, allowing for example to access shared files, caches, etc. on the host file system. +- Alternative dockers images can be specified with the `CIBW_MANYLINUX1_X86_64_IMAGE` and `CIBW_MANYLINUX1_I686_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [below](#options) for more details. Options @@ -202,6 +205,15 @@ 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 variables: `CIBW_MANYLINUX1_X86_64_IMAGE` and `CIBW_MANYLINUX1_I686_IMAGE` +| --- + +Optional. + +An alternative docker image to be used for building [`manylinux1`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the official images, [`quay.io/pypa/manylinux1_x86_64`](https://quay.io/pypa/manylinux1_i686) and [`quay.io/pypa/manylinux1_i686`](https://quay.io/pypa/manylinux1_i686). + +Beware to specify a valid docker image that can be used the same as the official, default docker images: all necessary Python and pip versions need to be present in `/opt/python/`, and the `auditwheel` tool needs to be present for `cibuildwheel` to work. Apart from that, the architecture and relevant shared system libraries need to be manylinux1-compatible in order to produce valid `manylinux1` wheels (see https://github.com/pypa/manylinux and [PEP 513](https://www.python.org/dev/peps/pep-0513/) for more details). + | Environment variable: `CIBW_TEST_COMMAND` | --- diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index 1a157495..b8f1ac07 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -122,6 +122,18 @@ def main(): environment=environment, ) + if platform == 'linux': + manylinux1_x86_64_image = os.environ.get('CIBW_MANYLINUX1_X86_64_IMAGE', None) + manylinux1_i686_image = os.environ.get('CIBW_MANYLINUX1_I686_IMAGE', None) + + build_options.update( + manylinux1_images={'x86_64': manylinux1_x86_64_image, 'i686': manylinux1_i686_image}, + ) + elif platform == 'macos': + pass + elif platform == 'windows': + pass + print_preamble(platform, build_options) if not os.path.exists(output_dir): diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py index 68b0349e..cd7a7136 100644 --- a/cibuildwheel/linux.py +++ b/cibuildwheel/linux.py @@ -9,7 +9,7 @@ except ImportError: from pipes import quote as shlex_quote -def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, skip, environment): +def build(project_dir, package_name, output_dir, test_command, test_requires, before_build, skip, environment, manylinux1_images): try: subprocess.check_call(['docker', '--version']) except: @@ -38,8 +38,8 @@ def build(project_dir, package_name, output_dir, test_command, test_requires, be 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'), + ('manylinux1_x86_64', manylinux1_images.get('x86_64') or 'quay.io/pypa/manylinux1_x86_64'), + ('manylinux1_i686', manylinux1_images.get('i686') or 'quay.io/pypa/manylinux1_i686'), ] for platform_tag, docker_image in platforms: diff --git a/test/06_docker_images/environment.json b/test/06_docker_images/environment.json new file mode 100644 index 00000000..d91a26ef --- /dev/null +++ b/test/06_docker_images/environment.json @@ -0,0 +1,4 @@ +{ + "CIBW_MANYLINUX1_X86_64_IMAGE": "dockcross/manylinux-x64", + "CIBW_MANYLINUX1_I686_IMAGE": "dockcross/manylinux-x86" +} diff --git a/test/06_docker_images/setup.py b/test/06_docker_images/setup.py new file mode 100644 index 00000000..d341d5ff --- /dev/null +++ b/test/06_docker_images/setup.py @@ -0,0 +1,15 @@ +import os, sys + +from setuptools import setup, Extension + +if sys.argv[-1] != '--name': + # check that we're running in the correct docker image as specified in the + # environment options CIBW_MANYLINUX1_*_IMAGE + if 'linux' in sys.platform and not os.path.exists('/dockcross'): + raise Exception('/dockcross directory not found. Is this test running in the correct docker image?') + +setup( + name="spam", + ext_modules=[Extension('spam', sources=['spam.c'])], + version="0.1.0", +) diff --git a/test/06_docker_images/spam.c b/test/06_docker_images/spam.c new file mode 100644 index 00000000..d1ab0f22 --- /dev/null +++ b/test/06_docker_images/spam.c @@ -0,0 +1,48 @@ +#include + +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) +}