Skip to content

Commit 1240bd2

Browse files
committed
Convert to generic platform wheel. See scikit-build#71
Update CI configuration to only build one wheel for each Python version.
1 parent bcd451e commit 1240bd2

File tree

5 files changed

+180
-102
lines changed

5 files changed

+180
-102
lines changed

Diff for: .circleci/config.yml

-48
Original file line numberDiff line numberDiff line change
@@ -44,30 +44,14 @@ references:
4444
jobs:
4545

4646
# x64
47-
manylinux-x64_cp27-cp27m:
48-
<<: *x64_build_job
4947
manylinux-x64_cp27-cp27mu:
5048
<<: *x64_build_job
51-
manylinux-x64_cp34-cp34m:
52-
<<: *x64_build_job
53-
manylinux-x64_cp35-cp35m:
54-
<<: *x64_build_job
55-
manylinux-x64_cp36-cp36m:
56-
<<: *x64_build_job
5749
manylinux-x64_cp37-cp37m_upload-sdist:
5850
<<: *x64_build_job
5951

6052
# x86
61-
# manylinux-x86_cp27-cp27m:
62-
# <<: *x86_build_job
6353
# manylinux-x86_cp27-cp27mu:
6454
# <<: *x86_build_job
65-
# manylinux-x86_cp34-cp34m:
66-
# <<: *x86_build_job
67-
# manylinux-x86_cp35-cp35m:
68-
# <<: *x86_build_job
69-
# manylinux-x86_cp36-cp36m:
70-
# <<: *x86_build_job
7155
# manylinux-x86_cp37-cp37m:
7256
# <<: *x86_build_job
7357

@@ -103,66 +87,34 @@ workflows:
10387
build-test-deploy:
10488
jobs:
10589
# x64
106-
- manylinux-x64_cp27-cp27m:
107-
<<: *no_filters
10890
- manylinux-x64_cp27-cp27mu:
10991
<<: *no_filters
110-
- manylinux-x64_cp34-cp34m:
111-
<<: *no_filters
112-
- manylinux-x64_cp35-cp35m:
113-
<<: *no_filters
114-
- manylinux-x64_cp36-cp36m:
115-
<<: *no_filters
11692
- manylinux-x64_cp37-cp37m_upload-sdist:
11793
<<: *no_filters
11894
# x86
119-
# - manylinux-x86_cp27-cp27m:
120-
# <<: *no_filters
12195
# - manylinux-x86_cp27-cp27mu:
12296
# <<: *no_filters
123-
# - manylinux-x86_cp34-cp34m:
124-
# <<: *no_filters
125-
# - manylinux-x86_cp35-cp35m:
126-
# <<: *no_filters
127-
# - manylinux-x86_cp36-cp36m:
128-
# <<: *no_filters
12997
# - manylinux-x86_cp37-cp37m:
13098
# <<: *no_filters
13199

132100
- deploy-master:
133101
requires:
134102
# x64
135-
- manylinux-x64_cp27-cp27m
136103
- manylinux-x64_cp27-cp27mu
137-
- manylinux-x64_cp34-cp34m
138-
- manylinux-x64_cp35-cp35m
139-
- manylinux-x64_cp36-cp36m
140104
- manylinux-x64_cp37-cp37m_upload-sdist
141105
# x86
142-
# - manylinux-x86_cp27-cp27m
143106
# - manylinux-x86_cp27-cp27mu
144-
# - manylinux-x86_cp34-cp34m
145-
# - manylinux-x86_cp35-cp35m
146-
# - manylinux-x86_cp36-cp36m
147107
# - manylinux-x86_cp37-cp37m
148108
filters:
149109
branches:
150110
only: master
151111
- deploy-release:
152112
requires:
153113
# x64
154-
- manylinux-x64_cp27-cp27m
155114
- manylinux-x64_cp27-cp27mu
156-
- manylinux-x64_cp34-cp34m
157-
- manylinux-x64_cp35-cp35m
158-
- manylinux-x64_cp36-cp36m
159115
- manylinux-x64_cp37-cp37m_upload-sdist
160116
# x86
161-
# - manylinux-x86_cp27-cp27m
162117
# - manylinux-x86_cp27-cp27mu
163-
# - manylinux-x86_cp34-cp34m
164-
# - manylinux-x86_cp35-cp35m
165-
# - manylinux-x86_cp36-cp36m
166118
# - manylinux-x86_cp37-cp37m
167119
filters:
168120
tags:

Diff for: .travis.yml

-18
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,6 @@ matrix:
1313
env:
1414
- PYTHON_VERSION=3.7.2
1515

16-
- os: osx
17-
language: generic
18-
env:
19-
- PYTHON_VERSION=3.6.8
20-
21-
- os: osx
22-
language: generic
23-
env:
24-
- PYTHON_VERSION=3.5.6
25-
26-
- os: osx
27-
language: generic
28-
env:
29-
- PYTHON_VERSION=3.4.9
30-
3116
- os: osx
3217
language: generic
3318
env:
@@ -36,9 +21,6 @@ matrix:
3621
cache:
3722
directories:
3823
- $HOME/.pyenv/versions/3.7.2
39-
- $HOME/.pyenv/versions/3.6.8
40-
- $HOME/.pyenv/versions/3.5.6
41-
- $HOME/.pyenv/versions/3.4.9
4224
- $HOME/.pyenv/versions/2.7.15
4325
- $HOME/downloads
4426

Diff for: appveyor.yml

-36
Original file line numberDiff line numberDiff line change
@@ -20,42 +20,6 @@ environment:
2020
PYTHON_ARCH: "64"
2121
BLOCK: "0"
2222

23-
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
24-
PYTHON_DIR: "C:\\Python34"
25-
PYTHON_VERSION: "3.4.x"
26-
PYTHON_ARCH: "32"
27-
BLOCK: "0"
28-
29-
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
30-
PYTHON_DIR: "C:\\Python34-x64"
31-
PYTHON_VERSION: "3.4.x"
32-
PYTHON_ARCH: "64"
33-
BLOCK: "0"
34-
35-
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
36-
PYTHON_DIR: "C:\\Python35"
37-
PYTHON_VERSION: "3.5.x"
38-
PYTHON_ARCH: "32"
39-
BLOCK: "0"
40-
41-
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
42-
PYTHON_DIR: "C:\\Python35-x64"
43-
PYTHON_VERSION: "3.5.x"
44-
PYTHON_ARCH: "64"
45-
BLOCK: "0"
46-
47-
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
48-
PYTHON_DIR: "C:\\Python36"
49-
PYTHON_VERSION: "3.6.x"
50-
PYTHON_ARCH: "32"
51-
BLOCK: "0"
52-
53-
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
54-
PYTHON_DIR: "C:\\Python36-x64"
55-
PYTHON_VERSION: "3.6.x"
56-
PYTHON_ARCH: "64"
57-
BLOCK: "0"
58-
5923
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
6024
PYTHON_DIR: "C:\\Python37"
6125
PYTHON_VERSION: "3.7.x"

Diff for: scikit-ci.yml

+9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ install:
2626
commands:
2727
- python -c "import sys; print(sys.version)"
2828
- python -m pip install --disable-pip-version-check --upgrade pip
29+
- python -m pip install auditwheel
2930
- pip install pytest-virtualenv -f https://github.com/jcfr/pytest-plugins/releases/tag/v1.7.0.dev15 --pre
3031
- pip install -r requirements-dev.txt
3132

@@ -60,6 +61,14 @@ build:
6061
6162
test:
6263
commands:
64+
# Convert to generic platform wheel
65+
- python: |
66+
import glob, sys
67+
sys.path.insert(0, "./scripts")
68+
from convert_to_generic_platform_wheel import convert_to_generic_platform_wheel
69+
wheels = glob.glob("dist/*.whl")
70+
for wheel in wheels:
71+
convert_to_generic_platform_wheel(wheel, remove_original=True)
6372
- python setup.py test
6473

6574
after_test:

Diff for: scripts/convert_to_generic_platform_wheel.py

+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
2+
import argparse
3+
import logging
4+
import os
5+
import sys
6+
7+
from itertools import product
8+
from os.path import (abspath, basename, dirname, isfile, join as pjoin, splitext)
9+
10+
from auditwheel.tools import unique_by_index
11+
from auditwheel.wheeltools import _dist_info_dir, InWheelCtx
12+
from wheel.pkginfo import read_pkg_info, write_pkg_info
13+
from wheel.install import WHEEL_INFO_RE
14+
15+
logger = logging.getLogger(splitext(basename(__file__))[0])
16+
17+
18+
def _to_generic_pyver(pyver_tags):
19+
"""Convert from CPython implementation to generic python version tags
20+
21+
Convert each CPython version tag to the equivalent generic tag.
22+
23+
For example::
24+
25+
cp35 -> py3
26+
cp27 -> py2
27+
28+
See https://www.python.org/dev/peps/pep-0425/#python-tag
29+
"""
30+
return ['py%s' % tag[2] if tag.startswith('cp') else tag for tag in pyver_tags]
31+
32+
33+
def _convert_to_generic_platform_wheel(wheel_ctx):
34+
"""Switch to generic python tags and remove ABI tags from a wheel
35+
36+
Convert implementation specific python tags to their generic equivalent and
37+
remove all ABI tags from wheel_ctx's filename and ``WHEEL`` file.
38+
39+
Parameters
40+
----------
41+
wheel_ctx : InWheelCtx
42+
An open wheel context
43+
"""
44+
45+
abi_tags = ['none']
46+
47+
info_fname = pjoin(_dist_info_dir(wheel_ctx.path), 'WHEEL')
48+
info = read_pkg_info(info_fname)
49+
50+
# Check what tags we have
51+
if wheel_ctx.out_wheel is not None:
52+
out_dir = dirname(wheel_ctx.out_wheel)
53+
wheel_fname = basename(wheel_ctx.out_wheel)
54+
else:
55+
out_dir = '.'
56+
wheel_fname = basename(wheel_ctx.in_wheel)
57+
58+
# Update wheel filename
59+
parsed_fname = WHEEL_INFO_RE(wheel_fname)
60+
fparts = parsed_fname.groupdict()
61+
original_platform_tags = fparts['plat'].split('.')
62+
63+
original_abi_tags = fparts['abi'].split('.')
64+
logger.debug('Previous ABI tags: %s', ', '.join(original_abi_tags))
65+
if abi_tags != original_abi_tags:
66+
logger.debug('New ABI tags ....: %s', ', '.join(abi_tags))
67+
fparts['abi'] = '.'.join(abi_tags)
68+
else:
69+
logger.debug('No ABI tags change needed.')
70+
71+
original_pyver_tags = fparts['pyver'].split('.')
72+
logger.debug('Previous pyver tags: %s', ', '.join(original_pyver_tags))
73+
pyver_tags = _to_generic_pyver(original_pyver_tags)
74+
if pyver_tags != original_pyver_tags:
75+
logger.debug('New pyver tags ....: %s', ', '.join(pyver_tags))
76+
fparts['pyver'] = '.'.join(pyver_tags)
77+
else:
78+
logger.debug('No pyver change needed.')
79+
80+
_, ext = splitext(wheel_fname)
81+
fparts['ext'] = ext
82+
out_wheel_fname = "{namever}-{pyver}-{abi}-{plat}{ext}".format(**fparts)
83+
84+
logger.info('Previous filename: %s', wheel_fname)
85+
if out_wheel_fname != wheel_fname:
86+
logger.info('New filename ....: %s', out_wheel_fname)
87+
else:
88+
logger.info('No filename change needed.')
89+
90+
out_wheel = pjoin(out_dir, out_wheel_fname)
91+
92+
# Update wheel tags
93+
in_info_tags = [tag for name, tag in info.items() if name == 'Tag']
94+
logger.info('Previous WHEEL info tags: %s', ', '.join(in_info_tags))
95+
96+
# Python version, C-API version combinations
97+
pyc_apis = []
98+
for tag in in_info_tags:
99+
py_ver = '.'.join(_to_generic_pyver(tag.split('-')[0].split('.')))
100+
abi = 'none'
101+
pyc_apis.append('-'.join([py_ver, abi]))
102+
# unique Python version, C-API version combinations
103+
pyc_apis = unique_by_index(pyc_apis)
104+
105+
# Set tags for each Python version, C-API combination
106+
updated_tags = ['-'.join(tup) for tup in product(pyc_apis, original_platform_tags)]
107+
108+
if updated_tags != in_info_tags:
109+
del info['Tag']
110+
for tag in updated_tags:
111+
info.add_header('Tag', tag)
112+
113+
logger.info('New WHEEL info tags ....: %s', ', '.join(info.get_all('Tag')))
114+
write_pkg_info(info_fname, info)
115+
else:
116+
logger.info('No WHEEL info change needed.')
117+
return out_wheel
118+
119+
120+
def convert_to_generic_platform_wheel(wheel_path, out_dir='./dist/', remove_original=False, verbose=0):
121+
logging.disable(logging.NOTSET)
122+
if verbose >= 1:
123+
logging.basicConfig(level=logging.DEBUG)
124+
else:
125+
logging.basicConfig(level=logging.INFO)
126+
127+
wheel_fname = basename(wheel_path)
128+
out_dir = abspath(out_dir)
129+
130+
with InWheelCtx(wheel_path) as ctx:
131+
ctx.out_wheel = pjoin(out_dir, wheel_fname)
132+
ctx.out_wheel = _convert_to_generic_platform_wheel(ctx)
133+
134+
if remove_original:
135+
logger.info('Removed original wheel %s' % wheel_path)
136+
os.remove(wheel_path)
137+
138+
139+
def main():
140+
p = argparse.ArgumentParser(description='Convert wheel to be independent of python implementation and ABI')
141+
p.set_defaults(prog=basename(sys.argv[0]))
142+
p.add_argument("-v",
143+
"--verbose",
144+
action='count',
145+
dest='verbose',
146+
default=0,
147+
help='Give more output. Option is additive')
148+
149+
p.add_argument('WHEEL_FILE', help='Path to wheel file.')
150+
p.add_argument('-w',
151+
'--wheel-dir',
152+
dest='WHEEL_DIR',
153+
type=abspath,
154+
help='Directory to store updated wheels (default: "dist/")',
155+
default='dist/')
156+
p.add_argument("-r",
157+
"--remove-original",
158+
dest='remove_original',
159+
action='store_true',
160+
help='Remove original wheel')
161+
162+
args = p.parse_args()
163+
164+
if not isfile(args.WHEEL_FILE):
165+
p.error('cannot access %s. No such file' % args.WHEEL_FILE)
166+
167+
convert_to_generic_platform_wheel(args.WHEEL_FILE, args.WHEEL_DIR, args.remove_original, args.verbose)
168+
169+
170+
if __name__ == '__main__':
171+
main()

0 commit comments

Comments
 (0)