Skip to content

Commit ee2a908

Browse files
authored
Merge pull request #155 from YannickJadoul/manylinux2010-alternative
Adding support for building manylinux2010 wheels - alternative
2 parents 5f416b1 + 31d8937 commit ee2a908

File tree

9 files changed

+187
-43
lines changed

9 files changed

+187
-43
lines changed

README.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,8 @@ All being well, you should get wheels delivered to you in a few minutes.
221221
| **Build parameters** | `CIBW_BUILD_VERBOSITY` | Increase or decrease the output of `pip wheel` |
222222
| **Build environment** | `CIBW_ENVIRONMENT` | Set environment variables needed during the build |
223223
| | `CIBW_BEFORE_BUILD` | Execute a shell command preparing each wheel's build |
224-
| | `CIBW_MANYLINUX1_X86_64_IMAGE` | Specify an alternative manylinx1 x86_64 docker image |
225-
| | `CIBW_MANYLINUX1_I686_IMAGE` | Specify an alternative manylinux1 i686 docker image |
224+
| | `CIBW_MANYLINUX_X86_64_IMAGE` | Specify an alternative manylinux x86_64 docker image |
225+
| | `CIBW_MANYLINUX_I686_IMAGE` | Specify an alternative manylinux i686 docker image |
226226
| **Tests** | `CIBW_TEST_COMMAND` | Execute a shell command to test all built wheels |
227227
| | `CIBW_TEST_REQUIRES` | Install Python dependencies before running the tests |
228228
| | `CIBW_TEST_EXTRAS` | Install Python dependencies before running the tests using ``extras_require``|
@@ -231,10 +231,10 @@ A more detailed description of the options, the allowed values, and some example
231231
232232
### Linux builds on Docker
233233
234-
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:
234+
Linux wheels are built in the [`manylinux` docker images](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 571](https://www.python.org/dev/peps/pep-0571/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account:
235235
- Programs 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.
236236
- 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. Note that this is not available on CircleCI due to their Docker policies.
237-
- 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.
237+
- Alternative dockers images can be specified with the `CIBW_MANYLINUX_X86_64_IMAGE` and `CIBW_MANYLINUX_I686_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [below](#options) for more details.
238238
239239
240240
Options
@@ -291,17 +291,19 @@ For `linux` you need Docker running, on Mac or Linux. For `macos`, you need a Ma
291291
292292
Optional.
293293
294-
Space-separated list of builds to build and skip. Each build has an identifier like `cp27-manylinux1_x86_64` or `cp34-macosx_10_6_intel` - you can list specific ones to build and `cibuildwheel` will only build those, and/or list ones to skip and `cibuildwheel` won't try to build them.
294+
Space-separated list of builds to build and skip. Each build has an identifier like `cp27-manylinux_x86_64` or `cp34-macosx_10_6_intel` - you can list specific ones to build and `cibuildwheel` will only build those, and/or list ones to skip and `cibuildwheel` won't try to build them.
295295
296296
When both options are specified, both conditions are applied and only builds with a tag that matches `CIBW_BUILD` and does not match `CIBW_SKIP` will be built.
297297
298-
The format is `python_tag-platform_tag`. The tags are as defined in [PEP 0425](https://www.python.org/dev/peps/pep-0425/#details).
298+
The format is `python_tag-platform_tag`. The tags are similar but not identical to the ones defined in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
299299
300300
Python tags look like `cp27` `cp34` `cp35` `cp36` `cp37`
301301
302-
Platform tags look like `macosx_10_6_intel` `manylinux1_x86_64` `manylinux1_i686` `win32` `win_amd64`
302+
Platform tags look like `macosx_10_6_intel` `manylinux_x86_64` `manylinux_i686` `win32` `win_amd64`
303303
304-
You can also use shell-style globbing syntax (as per `fnmatch`)
304+
You can also use shell-style globbing syntax (as per `fnmatch`).
305+
306+
The list of supported and currently selected build identifiers can be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`.
305307
306308
Examples:
307309
- Only build on Python 3.6: `CIBW_BUILD`:`cp36-*`
@@ -311,7 +313,7 @@ Examples:
311313
- Skip Python 2.7 on 32-bit Windows: `CIBW_SKIP`:`cp27-win32`
312314
- Skip Python 3.4 and Python 3.5: `CIBW_SKIP`:`cp34-* cp35-*`
313315
- Skip Python 3.6 on Linux: `CIBW_SKIP`:`cp36-manylinux*`
314-
- Only build on Python 3 and skip 32-bit builds: `CIBW_BUILD`:`cp3?-*` and `CIBW_SKIP`:`*-win32 *-manylinux1_i686`
316+
- Only build on Python 3 and skip 32-bit builds: `CIBW_BUILD`:`cp3?-*` and `CIBW_SKIP`:`*-win32 *-manylinux_i686`
315317
316318
***
317319
@@ -370,15 +372,20 @@ Platform-specific variants also available:
370372
371373
***
372374
373-
| Environment variables: `CIBW_MANYLINUX1_X86_64_IMAGE` and `CIBW_MANYLINUX1_I686_IMAGE`
375+
| Environment variables: `CIBW_MANYLINUX_X86_64_IMAGE` and `CIBW_MANYLINUX_I686_IMAGE`
374376
| ---
375377
376378
Optional.
377379
378-
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).
380+
An alternative Docker image to be used for building [`manylinux`](https://github.com/pypa/manylinux) wheels. `cibuildwheel` will then pull these instead of the default images, [`quay.io/pypa/manylinux2010_x86_64`](https://quay.io/pypa/manylinux2010_x86_64) and [`quay.io/pypa/manylinux2010_i686`](https://quay.io/pypa/manylinux2010_i686).
381+
382+
The value of this option can either be set to `manylinux1` or `manylinux2010` to use the [official `manylinux` images](https://github.com/pypa/manylinux), or any other valid Docker image name.
383+
384+
Beware to specify a valid Docker image that can be used in the same way 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- or manylinux2010-compatible in order to produce valid `manylinux1`/`manylinux2010` wheels (see https://github.com/pypa/manylinux, [PEP 513](https://www.python.org/dev/peps/pep-0513/), and [PEP 571](https://www.python.org/dev/peps/pep-0571/) for more details).
379385
380-
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).
386+
Note that `auditwheel` detects the version of the `manylinux` standard in the Docker image through the `AUDITWHEEL_PLAT` environment variable, as `cibuildwheel` has no way of detecting the correct `--plat` command line argument to pass to `auditwheel` for a custom image. If a Docker image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT="manylinux2010_$(uname -m)"`).
381387
388+
Example: `manylinux1`
382389
Example: `dockcross/manylinux-x64`
383390
Example: `dockcross/manylinux-x86`
384391

cibuildwheel/__main__.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ def main():
5454

5555
args = parser.parse_args()
5656

57+
detect_obsolete_options()
58+
5759
if args.platform != 'auto':
5860
platform = args.platform
5961
else:
@@ -141,11 +143,17 @@ def main():
141143
)
142144

143145
if platform == 'linux':
144-
manylinux1_x86_64_image = os.environ.get('CIBW_MANYLINUX1_X86_64_IMAGE', None)
145-
manylinux1_i686_image = os.environ.get('CIBW_MANYLINUX1_I686_IMAGE', None)
146+
manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010')
147+
manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux2010')
148+
149+
default_manylinux_images_x86_64 = {'manylinux1': 'quay.io/pypa/manylinux1_x86_64',
150+
'manylinux2010': 'quay.io/pypa/manylinux2010_x86_64'}
151+
default_manylinux_images_i686 = {'manylinux1': 'quay.io/pypa/manylinux1_i686',
152+
'manylinux2010': 'quay.io/pypa/manylinux2010_i686'}
146153

147154
build_options.update(
148-
manylinux1_images={'x86_64': manylinux1_x86_64_image, 'i686': manylinux1_i686_image},
155+
manylinux_images={'x86_64': default_manylinux_images_x86_64.get(manylinux_x86_64_image) or manylinux_x86_64_image,
156+
'i686': default_manylinux_images_i686.get(manylinux_i686_image) or manylinux_i686_image},
149157
)
150158
elif platform == 'macos':
151159
pass
@@ -170,6 +178,26 @@ def main():
170178
raise Exception('Unsupported platform')
171179

172180

181+
def detect_obsolete_options():
182+
# Check the old 'MANYLINUX1_*_IMAGE' options
183+
for (deprecated, alternative) in [('CIBW_MANYLINUX1_X86_64_IMAGE', 'CIBW_MANYLINUX_X86_64_IMAGE'),
184+
('CIBW_MANYLINUX1_I686_IMAGE', 'CIBW_MANYLINUX_I686_IMAGE')]:
185+
if deprecated in os.environ:
186+
print("'{}' has been deprecated, and will be removed in a future release. Use the option '{}' instead.".format(deprecated, alternative))
187+
if alternative not in os.environ:
188+
print("Using value of option '{}' as replacement for '{}'".format(deprecated, alternative))
189+
os.environ[alternative] = os.environ[deprecated]
190+
else:
191+
print("Option '{}' is not empty. Please unset '{}'".format(alternative, deprecated))
192+
exit(2)
193+
194+
# Check for 'manylinux1' in the 'CIBW_BUILD' and 'CIBW_SKIP' options
195+
for deprecated in ['CIBW_BUILD', 'CIBW_SKIP']:
196+
if deprecated in os.environ and 'manylinux1' in os.environ[deprecated]:
197+
print("Build identifiers with 'manylinux1' been deprecated. Replacing all occurences of 'manylinux1' by 'manylinux' in the option '{}'".format(deprecated))
198+
os.environ[deprecated] = os.environ[deprecated].replace('manylinux1', 'manylinux')
199+
200+
173201
def print_preamble(platform, build_options):
174202
print(textwrap.dedent('''
175203
_ _ _ _ _ _ _

cibuildwheel/linux.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,25 @@
1212
def get_python_configurations(build_selector):
1313
PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path'])
1414
python_configurations = [
15-
PythonConfiguration(identifier='cp27-manylinux1_x86_64', path='/opt/python/cp27-cp27m'),
16-
PythonConfiguration(identifier='cp27-manylinux1_x86_64', path='/opt/python/cp27-cp27mu'),
17-
PythonConfiguration(identifier='cp34-manylinux1_x86_64', path='/opt/python/cp34-cp34m'),
18-
PythonConfiguration(identifier='cp35-manylinux1_x86_64', path='/opt/python/cp35-cp35m'),
19-
PythonConfiguration(identifier='cp36-manylinux1_x86_64', path='/opt/python/cp36-cp36m'),
20-
PythonConfiguration(identifier='cp37-manylinux1_x86_64', path='/opt/python/cp37-cp37m'),
21-
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27m'),
22-
PythonConfiguration(identifier='cp27-manylinux1_i686', path='/opt/python/cp27-cp27mu'),
23-
PythonConfiguration(identifier='cp34-manylinux1_i686', path='/opt/python/cp34-cp34m'),
24-
PythonConfiguration(identifier='cp35-manylinux1_i686', path='/opt/python/cp35-cp35m'),
25-
PythonConfiguration(identifier='cp36-manylinux1_i686', path='/opt/python/cp36-cp36m'),
26-
PythonConfiguration(identifier='cp37-manylinux1_i686', path='/opt/python/cp37-cp37m'),
15+
PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'),
16+
PythonConfiguration(identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
17+
PythonConfiguration(identifier='cp34-manylinux_x86_64', path='/opt/python/cp34-cp34m'),
18+
PythonConfiguration(identifier='cp35-manylinux_x86_64', path='/opt/python/cp35-cp35m'),
19+
PythonConfiguration(identifier='cp36-manylinux_x86_64', path='/opt/python/cp36-cp36m'),
20+
PythonConfiguration(identifier='cp37-manylinux_x86_64', path='/opt/python/cp37-cp37m'),
21+
PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27m'),
22+
PythonConfiguration(identifier='cp27-manylinux_i686', path='/opt/python/cp27-cp27mu'),
23+
PythonConfiguration(identifier='cp34-manylinux_i686', path='/opt/python/cp34-cp34m'),
24+
PythonConfiguration(identifier='cp35-manylinux_i686', path='/opt/python/cp35-cp35m'),
25+
PythonConfiguration(identifier='cp36-manylinux_i686', path='/opt/python/cp36-cp36m'),
26+
PythonConfiguration(identifier='cp37-manylinux_i686', path='/opt/python/cp37-cp37m'),
2727
]
2828

2929
# skip builds as required
3030
return [c for c in python_configurations if build_selector(c.identifier)]
3131

3232

33-
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, environment, manylinux1_images):
33+
def build(project_dir, output_dir, test_command, test_requires, test_extras, before_build, build_verbosity, build_selector, environment, manylinux_images):
3434
try:
3535
subprocess.check_call(['docker', '--version'])
3636
except:
@@ -42,8 +42,8 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
4242

4343
python_configurations = get_python_configurations(build_selector)
4444
platforms = [
45-
('manylinux1_x86_64', manylinux1_images.get('x86_64') or 'quay.io/pypa/manylinux1_x86_64'),
46-
('manylinux1_i686', manylinux1_images.get('i686') or 'quay.io/pypa/manylinux1_i686'),
45+
('manylinux_x86_64', manylinux_images['x86_64']),
46+
('manylinux_i686', manylinux_images['i686']),
4747
]
4848

4949
for platform_tag, docker_image in platforms:
@@ -62,9 +62,9 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
6262
for PYBIN in {pybin_paths}; do
6363
# Setup
6464
rm -rf /tmp/built_wheel
65-
rm -rf /tmp/delocated_wheel
65+
rm -rf /tmp/delocated_wheels
6666
mkdir /tmp/built_wheel
67-
mkdir /tmp/delocated_wheel
67+
mkdir /tmp/delocated_wheels
6868
6969
if [ ! -z {before_build} ]; then
7070
PATH="$PYBIN:$PATH" sh -c {before_build}
@@ -79,11 +79,11 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
7979
# the first element
8080
if [[ "$built_wheel" == *none-any.whl ]]; then
8181
# pure python wheel - just copy
82-
mv "$built_wheel" /tmp/delocated_wheel
82+
mv "$built_wheel" /tmp/delocated_wheels
8383
else
84-
auditwheel repair "$built_wheel" -w /tmp/delocated_wheel
84+
auditwheel repair "$built_wheel" -w /tmp/delocated_wheels
8585
fi
86-
delocated_wheel=(/tmp/delocated_wheel/*.whl)
86+
delocated_wheels=(/tmp/delocated_wheels/*.whl)
8787
8888
if [ ! -z {test_command} ]; then
8989
# Set up a virtual environment to install and test from, to make sure
@@ -100,7 +100,12 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
100100
echo "Running tests using `which python`"
101101
102102
# Install the wheel we just built
103-
pip install "$delocated_wheel"{test_extras}
103+
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
104+
# conforms to multiple manylinux standards. These multiple versions of the wheel are
105+
# functionally the same, differing only in name, wheel metadata, and possibly include
106+
# different external shared libraries. so it doesn't matter which one we run the tests on.
107+
# Let's just pick the first one.
108+
pip install "${{delocated_wheels[0]}}"{test_extras}
104109
105110
# Install any requirements to run the tests
106111
if [ ! -z "{test_requires}" ]; then
@@ -118,8 +123,8 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef
118123
fi
119124
120125
# we're all done here; move it to output
121-
mv "$delocated_wheel" /output
122-
chown {uid}:{gid} "/output/$(basename "$delocated_wheel")"
126+
mv "${{delocated_wheels[@]}}" /output
127+
for delocated_wheel in "${{delocated_wheels[@]}}"; do chown {uid}:{gid} "/output/$(basename "$delocated_wheel")"; done
123128
done
124129
'''.format(
125130
pybin_paths=' '.join(c.path+'/bin' for c in platform_configs),

test/01_basic/cibuildwheel_test.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ def test():
1717
def test_build_identifiers():
1818
# check that the number of expected wheels matches the number of build
1919
# identifiers
20-
expected_wheels = utils.expected_wheels('spam', '0.1.0')
20+
# after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
21+
# can be multiple wheels for each wheel, though, so we need to limit
22+
# the expected wheels
23+
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
24+
if not '-manylinux' in w or '-manylinux1' in w]
2125
build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir)
2226
assert len(expected_wheels) == len(build_identifiers)

test/06_docker_images/cibuildwheel_test.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ def test():
55
project_dir = os.path.dirname(__file__)
66

77
if utils.platform != 'linux':
8-
pytest.skip('the docker test is only relevant to the linux build')
8+
pytest.skip('the test is only relevant to the linux build')
99

1010
utils.cibuildwheel_run(project_dir, add_env={
11-
'CIBW_MANYLINUX1_X86_64_IMAGE': 'dockcross/manylinux-x64',
12-
'CIBW_MANYLINUX1_I686_IMAGE': 'dockcross/manylinux-x86',
11+
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
12+
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux1-x86',
13+
'CIBW_BEFORE_BUILD': '/opt/python/cp36-cp36m/bin/pip install -U auditwheel', # Currently necessary on dockcross images to get auditwheel 2.1 supporting AUDITWHEEL_PLAT
14+
'CIBW_ENVIRONMENT': 'AUDITWHEEL_PLAT=`if [ $(uname -i) == "x86_64" ]; then echo "manylinux2010_x86_64"; else echo "manylinux1_i686"; fi`',
1315
})
1416

1517
# also check that we got the right wheels built
16-
expected_wheels = utils.expected_wheels('spam', '0.1.0')
18+
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0')
19+
if '-manylinux2010_i686' not in w]
1720
actual_wheels = os.listdir('wheelhouse')
1821
assert set(actual_wheels) == set(expected_wheels)

0 commit comments

Comments
 (0)