-
Notifications
You must be signed in to change notification settings - Fork 14
/
make-wheel-builder
executable file
·291 lines (234 loc) · 10 KB
/
make-wheel-builder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#!/usr/bin/env python
""" Script to create a wheel builder for a project
"""
from __future__ import print_function
import os
from os.path import isdir
import argparse # Python 2.6, really?
from subprocess import check_call
TERRYFY_URL = 'git://github.com/MacPython/terryfy.git'
LATEST_PY27 = "2.7.10"
LATEST_PY33 = "3.3.5"
LATEST_PY34 = "3.4.3"
LATEST_PY35 = "3.5.1"
VERSION_BIT_NO_NUMPY = \
""" - VERSION={}
- VERSION={}
- VERSION={}
- VERSION={}""".format(
LATEST_PY27, LATEST_PY33, LATEST_PY34, LATEST_PY35)
VERSION_BIT_NUMPY = \
""" - VERSION={} NUMPY_VERSION=1.6.2 # tune to taste
- VERSION={} NUMPY_VERSION=1.7.1
- VERSION={} NUMPY_VERSION=1.7.1
- VERSION={} NUMPY_VERSION=1.7.1""".format(
LATEST_PY27, LATEST_PY33, LATEST_PY34, LATEST_PY35)
TRAVIS_TEMPLATE = \
"""language:
- objective-c
env:
global:
- BUILD_COMMIT='latest-tag' # comment out to build version in submodule
- REPO_DIR={project_dir}
matrix:
{version_bit}
install:
- source terryfy/travis_tools.sh
- get_python_environment macpython $VERSION venv
# Update to latest wheel package for Python 3.5
- pip install -U wheel
- pip install {pip_depends}
- if [ -n "$BUILD_COMMIT" ]; then
checkout_commit $REPO_DIR $BUILD_COMMIT;
fi
- cd {project_dir}
- python setup.py bdist_wheel
- delocate-listdeps dist/*.whl # lists library dependencies
- delocate-wheel dist/*.whl # copies library dependencies into wheel
- delocate-addplat --rm-orig -x 10_9 -x 10_10 dist/*.whl
- pip install dist/*.whl
- cd ..
script:
# install dependencies for tests e.g - pip install nose
- mkdir tmp_for_test
- cd tmp_for_test
# Run the tests
- cd ..
before_deploy:
- cd $REPO_DIR/dist
deploy:
provider: cloudfiles
username: travis-worker
api_key:
secure: ALZrkCwjz3nY/9GfTNPgVQHUmUpCc5SnZTaqIUlJrhVyOBu6aktXaRmQi+sTXzFn7a/PQsYDnoZ8nibCZ472oUVDGUOSbtjIEkPodU+aisLN2dSLNueQsUU7Dmt0SaeyJVGovzfyUiavIYLuAfOtCfUKrwAsi7XTVNQpdyWCQDs=
region: ORD
container: wheels
skip_cleanup: true
"""
GITIGNORE_TEMPLATE = \
"""*.pyc
tmp_for_test/
working/
build/
"""
README_TEMPLATE = \
"""##################################
Building and uploading {project_dir} wheels
##################################
*******
For OSX
*******
We automate OSX wheel building using this custom github repository that builds
on the travis-ci OSX machines.
The travis-ci interface for the builds is :
https://travis-ci.org/MacPython/{project_dir}-wheels
The driving github repository is :
https://github.com/MacPython/{project_dir}-wheels
How it works
============
The wheel-building repository:
* does a fresh build of the required C / C++ libraries;
* builds a {project_dir} wheel, linking against these fresh builds;
* processes the wheel using [delocate](https://pypi.python.org/pypi/delocate).
`delocate` copies the required dynamic libraries into the wheel and relinks
the extension modules against the copied libraries;
* uploads the built wheel to http://wheels.scipy.org (a Rackspace container
kindly donated by Rackspace to scikit-learn).
The resulting wheel is therefore self-contained and does not need any external
dynamic libraries apart from those provided as standard by OSX.
The ``.travis.yml`` file in this repository has a line containing the API key
for the Rackspace container encrypted with an RSA key that is unique to the
repository - see http://docs.travis-ci.com/user/encryption-keys. This
encrypted key gives the travis build permission to upload to the Rackspace
directory pointed to by http://wheels.scipy.org.
Triggering a build
==================
You will need write permission to the github repository to trigger new builds
on the travis-ci interface. Contact us on the mailing list if you need this.
You can trigger a build by:
* making a commit to the `{project_dir}-wheels` repository (e.g. with `git
commit --allow-empty`); or
* clicking on the circular arrow icon towards the top right of the travis-ci
page, to rerun the previous build.
In general, it is better to trigger a build with a commit, because this makes
a new set of build products and logs, keeping the old ones for reference.
Keeping the old build logs helps us keep track of previous problems and
successful builds.
Which {project_dir} commit does the repository build?
============================================
By default, the `{project_dir}-wheels` repository is usually set up to build
the latest git tag. By "latest" we mean the tag on the branch most recently
branched from master - see http://stackoverflow.com/a/24557377/1939576. To
check whether you are building the latest tag have a look around line 5 of
`.travis.yml` in the `{project_dir}-wheels` repository. You should see something
like::
- BUILD_COMMIT='latest-tag'
If this is commented out, then the repository is set up to build the current
commit in the `{project_dir}` submodule of the repository. If it is set to
another value then it will be specifying a commit to build.
You can therefore build any arbitrary commit by specifying the commit hash or
branch name or tag name in this line of the `.travis.yml` file.
Uploading the built wheels to pypi
==================================
Be careful, http://wheels.scipy.org points to a container on a distributed
content delivery network. It can take up to 15 minutes for the new wheel file
to get updated into the container at http://wheels.scipy.org.
When the wheels are updated, you can of course just download them to your
machine manually, and then upload them manually to pypi, or by using
twine_. You can also use a script for doing this, housed at :
https://github.com/MacPython/terryfy/blob/master/wheel-uploader
You'll need twine and `beautiful soup 4 <bs4>`_.
You will typically have a directory on your machine where you store wheels,
called a `wheelhouse`. The typical call for `wheel-uploader` would then
be something like::
wheel-uploader -v -w ~/wheelhouse {project_dir} 1.0.3
where:
* `-v` means give verbose messages;
* `-w ~/wheelhouse` means download the wheels from https://wheels.scipy.org to
the directory `~/wheelhouse`;
* `{project_dir}` is the root name of the wheel(s) to download / upload;
* `1.0.3` is the version to download / upload.
So, in this case, `wheel-uploader` will download all wheels starting with
`{project_dir}-1.0.3-` from http://wheels.scipy.org to `~/wheelhouse`, then upload
them to pypi.
Of course, you will need permissions to upload to pypi, for this to work.
{numpy_bit}
.. _twine: https://pypi.python.org/pypi/twine
.. _bs4: https://pypi.python.org/pypi/beautifulsoup4
"""
NUMPY_BIT_TEMPLATE = \
"""Maintaining the build repo
==========================
* Check minimum numpy versions to build against in ``.travis.yml`` file. You
need to build against the earliest numpy that {project_dir} is compatible with; see
`forward, backward numpy compatibility
<http://stackoverflow.com/questions/17709641/valueerror-numpy-dtype-has-the-wrong-size-try-recompiling/18369312#18369312>`_
"""
def repo2project_dir(url):
""" Get project name from git repo
"""
if url.endswith('.git'):
url = url[:-4]
return url.split('/')[-1]
def main():
parser = argparse.ArgumentParser(
description='Create project to build wheels on travis')
parser.add_argument('github_url',
help = 'Github repo URL')
parser.add_argument('--needs-numpy', action='store_true')
args = parser.parse_args()
project_dir = repo2project_dir(args.github_url)
wheels_project = project_dir + '-wheels'
os.mkdir(wheels_project)
os.chdir(wheels_project)
check_call(['git', 'init'])
check_call(['git', 'submodule', 'add', args.github_url])
project_dir = repo2project_dir(args.github_url)
if not isdir(project_dir):
raise RuntimeError('Expecting {} directory'.format(project_dir))
check_call(['git', 'submodule', 'add', TERRYFY_URL])
version_bit = (VERSION_BIT_NUMPY if args.needs_numpy
else VERSION_BIT_NO_NUMPY)
pip_depends = 'delocate'
if args.needs_numpy:
pip_depends += ' numpy==$NUMPY_VERSION'
with open('.travis.yml', 'wt') as fobj:
fobj.write(TRAVIS_TEMPLATE.format(project_dir=project_dir,
version_bit=version_bit,
pip_depends=pip_depends))
with open('.gitignore', 'wt') as fobj:
fobj.write(GITIGNORE_TEMPLATE)
if args.needs_numpy:
numpy_bit = NUMPY_BIT_TEMPLATE.format(project_dir=project_dir)
else:
numpy_bit = ''
with open('README.rst', 'wt') as fobj:
fobj.write(README_TEMPLATE.format(project_dir=project_dir,
numpy_bit=numpy_bit))
check_call(['git', 'add', '*', '.gitignore', '.travis.yml'])
check_call(['git', 'commit', '-m', 'Initial commit'])
print('Created ' + wheels_project)
print(
"""Now push to github, setup travis building and encrypt the rackspace key.
I use "hub" [1] and the travis command line client [2], but you can do most of
this with the web GUIs.
cd {wheels_project}
hub create <your-username>/{wheels_project} # create project on github
travis sync # sync list of github repos known to travis
travis enable # enable travis testing of this repo
travis encrypt <the rackspace API key> # see README about this key
The last line generates something like:
secure: "fC8a017zdgsbkxZRcJrIrEa35LyzJJEyxTHkbMG/gVJKLhzMLKdw7F6FnsUyxu/RHA5FHE5D2S7X471dNU/xfCYJjWkzvHlrZrwQ5vwJ0KGIRE46KiO0URBGoGFQWI1WPhOeirIJ0ZLaahTYLbXYnQRhM2mOjCmIY74jPSTrrvg="
Replace the current .travis.yml 'secure:' line with the result and commit.
You might also want to fill in the test command for the project in the
.travis.yml file (see comment placeholder)
Finally, push to github to initiate the first travis build.
If your package has external library dependencies, there are some utilities to
help you build them; see
https://github.com/matthew-brett/h5py-wheels/blob/master/.travis.yml for an
example of their use.
[1] https://github.com/github/hub
[2] https://github.com/travis-ci/travis.rb
""".format(wheels_project=wheels_project))
if __name__ == '__main__':
main()