先日の記事 で紹介した、 manylinux1 wheel を作ってみます。
manylinux1 docker image
ビルド環境を Docker image として公開してくれています。
docker pull quay.io/pypa/manylinux1_x86_64; docker pull quay.io/pypa/manylinux1_i686
しておきましょう。
なお、このイメージは CentOS 5 をベースにビルドスクリプトを実行しているだけなので、 vagrant 等で同じ環境を作るのは簡単そうです。
作業ディレクトリを作って docker 内で bash を起動します。
$ mkdir docker $ docker run --rm -ti -v `pwd`/docker:/docker -w /docker quay.io/pypa/manylinux1_x86_64 bash
このように実行すると、bash を抜けるときにイメージが消えますが、ボリュームに指定した docker ディレクトリの内容は残るので便利です。
wheel をビルドしてみる
/opt/python
配下に各種 Python が用意されています。 Python 2.6 と 2.7 は、 utf-16 の narrow build (cp26m, cp27m) と utf-32 の wide build (cp26mu, cp27mu) があります。
[root@c3f621332eef docker]# ls /opt/python/ cp26-cp26m cp26-cp26mu cp27-cp27m cp27-cp27mu cp33-cp33m cp34-cp34m cp35-cp35m
試しに msgpack の最新リリース版をビルドしてみます。
[root@89ab8fbbfbc0 docker]# curl 'https://pypi.python.org/packages/a3/fb/bcf568236ade99903ef3e3e186e2d9252adbf000b378de596058fb9df847/msgpack-python-0.4.7.tar.gz' -o msgpack-python-0.4.7.tar.gz [root@89ab8fbbfbc0 docker]# tar xf msgpack-python-0.4.7.tar.gz [root@89ab8fbbfbc0 docker]# cd msgpack-python-0.4.7 [root@89ab8fbbfbc0 msgpack-python-0.4.7]# /opt/python/cp35-cp35m/bin/python setup.py bdist_wheel ... [root@89ab8fbbfbc0 msgpack-python-0.4.7]# /opt/python/cp34-cp34m/bin/python setup.py bdist_wheel ... [root@89ab8fbbfbc0 msgpack-python-0.4.7]# /opt/python/cp27-cp27m/bin/python setup.py bdist_wheel ... [root@89ab8fbbfbc0 msgpack-python-0.4.7]# rm -rf build [root@89ab8fbbfbc0 msgpack-python-0.4.7]# /opt/python/cp27-cp27mu/bin/python setup.py bdist_wheel
Python 2.7 や 2.6 のビルド時は、 rm -rf build
をしておかないと、 narrow / wide ビルドが違っても build/ ディレクトリ配下にできるファイルにはタグがなくて再利用されてしまうので注意が必要です。
これで dist ディレクトリに、通常の linux 用 wheel ができます。しかしこのままでは PyPI にはアップロードできません。通常の linux wheel は、ローカルや同じ環境のマシンで使い回すためにしか使えません。
manylinux1 wheel に変換する
Docker イメージには auditwheel コマンドが用意されています。このコマンドで、 wheel 内のバイナリが、決められた以外の外部ライブラリに依存してないかなどのチェックが行われます。 (auditwheel も manylinux1 もまだ新しいツールなので、チェックが通ったから安全とは限りません。たとえば上で説明した narrow / wide ビルドミスは現状ではチェックが漏れています。)
auditwheel show
コマンドでチェック結果が表示され、 auditwheel repair
コマンドは依存してる外部ライブラリをバンドルして RPATH を設定するなどの黒魔術を施して linux wheel を manylinux1 wheel にリネームしてくれます。 repair コマンドは wheelhouse ディレクトリに wheel を生成します。
(repair コマンドの黒魔術が必要ない場合は、 上の手順で bdist_wheel
するときに -p manylinux1_x86_64
オプションを使えば良さそうです。)
[root@0018d7cfd7df dist]# auditwheel show msgpack_python-0.4.7-cp27-cp27m-linux_x86_64.whl msgpack_python-0.4.7-cp27-cp27m-linux_x86_64.whl is consistent with the following platform tag: "manylinux1_x86_64". The wheel references the following external versioned symbols in system-provided shared libraries: GLIBC_2.2.5. The following external shared libraries are required by the wheel: { "libc.so.6": "/lib64/libc-2.5.so", "libgcc_s.so.1": "/lib64/libgcc_s-4.1.2-20080825.so.1", "libm.so.6": "/lib64/libm-2.5.so", "libpthread.so.0": "/lib64/libpthread-2.5.so", "libstdc++.so.6": "/usr/lib64/libstdc++.so.6.0.8" } [root@c89bce940544 dist]# for i in *.whl; do auditwheel repair $i; done Repairing msgpack_python-0.4.7-cp27-cp27m-linux_x86_64.whl Previous filename tags: linux_x86_64 New filename tags: manylinux1_x86_64 Previous WHEEL info tags: cp27-cp27m-linux_x86_64 New WHEEL info tags: cp27-cp27m-manylinux1_x86_64 Fixed-up wheel written to /docker/msgpack-python-0.4.7/dist/wheelhouse/msgpack_python-0.4.7-cp27-cp27m-manylinux1_x86_64.whl Repairing msgpack_python-0.4.7-cp27-cp27mu-linux_x86_64.whl Previous filename tags: linux_x86_64 New filename tags: manylinux1_x86_64 Previous WHEEL info tags: cp27-cp27mu-linux_x86_64 New WHEEL info tags: cp27-cp27mu-manylinux1_x86_64 Fixed-up wheel written to /docker/msgpack-python-0.4.7/dist/wheelhouse/msgpack_python-0.4.7-cp27-cp27mu-manylinux1_x86_64.whl Repairing msgpack_python-0.4.7-cp34-cp34m-linux_x86_64.whl Previous filename tags: linux_x86_64 New filename tags: manylinux1_x86_64 Previous WHEEL info tags: cp34-cp34m-linux_x86_64 New WHEEL info tags: cp34-cp34m-manylinux1_x86_64 Fixed-up wheel written to /docker/msgpack-python-0.4.7/dist/wheelhouse/msgpack_python-0.4.7-cp34-cp34m-manylinux1_x86_64.whl Repairing msgpack_python-0.4.7-cp35-cp35m-linux_x86_64.whl Previous filename tags: linux_x86_64 New filename tags: manylinux1_x86_64 Previous WHEEL info tags: cp35-cp35m-linux_x86_64 New WHEEL info tags: cp35-cp35m-manylinux1_x86_64 Fixed-up wheel written to /docker/msgpack-python-0.4.7/dist/wheelhouse/msgpack_python-0.4.7-cp35-cp35m-manylinux1_x86_64.whl
PyPI にアップロード
docker で実行していた bash を抜けると、 docker イメージは消えますが、ボリュームの中に wheel が残っています。これを twine を使ってアップロードします。
$ pip install twine $ cd docker/msgpack-python-0.4.7/dist/wheelhouse/ $ ls msgpack_python-0.4.7-cp27-cp27m-manylinux1_x86_64.whl msgpack_python-0.4.7-cp34-cp34m-manylinux1_x86_64.whl msgpack_python-0.4.7-cp27-cp27mu-manylinux1_x86_64.whl msgpack_python-0.4.7-cp35-cp35m-manylinux1_x86_64.whl $ twine upload *.whl Uploading distributions to https://pypi.python.org/pypi Uploading msgpack_python-0.4.7-cp27-cp27m-manylinux1_x86_64.whl Uploading msgpack_python-0.4.7-cp27-cp27mu-manylinux1_x86_64.whl Uploading msgpack_python-0.4.7-cp34-cp34m-manylinux1_x86_64.whl Uploading msgpack_python-0.4.7-cp35-cp35m-manylinux1_x86_64.whl
あとは同じ手順を i686 の方の docker でもすれば x86 対応も簡単そうです。
終わりに
msgpack は libstdc++ に依存してしまっていますが、これは PEP 513 で定義されている Core shared library に含まれているので、多分問題ないと思います。
一方 mysqlclient-python の場合、 libmysqlclient 経由で ssl などに依存していて、これは core shared library に含まれていないので、 static link や bundle が必要になります。特に ssl のようにセキュリティアップデートが頻繁にあるライブラリの場合は wheel 提供者が責任をもって追随アップデートする必要がありそうで面倒です。
Core shared library に含まれていないライブラリを利用している場合は、 PEP 513 を良く読んで、ちゃんと理解できないのであれば、 manylinux1 wheel の提供は危険です。 wheel builders という ML ができたので、そこで相談してみるのが良いと思います。
追記: sdist から wheel を作るより簡単な手順
上の手順では sdist の tar.gz を展開してから setup.py を実行していましたが、 pip wheel
コマンドを使うとこの流れを自動化できます。
作業ディレクトリも毎回作り直されるので、 build ディレクトリを消し忘れて narrow / wide 互換性問題を起こすことも無いはずです。
bash-3.2# for i in cp35-cp35m cp34-cp34m cp27-cp27m cp27-cp27mu; do /opt/python/$i/bin/pip wheel --build-option='-pmanylinux1_i686' msgpack-python-0.4.7.tar.gz ; done; /opt/_internal/cpython-3.5.1/lib/python3.5/site-packages/pip/commands/wheel.py:126: UserWarning: Disabling all use of wheels due to the use of --build-options / --global-options / --install-options. cmdoptions.check_install_build_global(options) Processing ./msgpack-python-0.4.7.tar.gz Building wheels for collected packages: msgpack-python Running setup.py bdist_wheel for msgpack-python ... done Stored in directory: /docker Successfully built msgpack-python ... ... Successfully built msgpack-python bash-3.2# ls -la total 1108 drwxr-xr-x 1 1000 ftp 238 Apr 27 08:52 . drwxr-xr-x 39 root root 4096 Apr 27 08:46 .. -rw-r--r-- 1 1000 ftp 241090 Apr 27 08:51 msgpack_python-0.4.7-cp27-cp27m-manylinux1_i686.whl -rw-r--r-- 1 1000 ftp 241106 Apr 27 08:52 msgpack_python-0.4.7-cp27-cp27mu-manylinux1_i686.whl -rw-r--r-- 1 1000 ftp 261276 Apr 27 08:51 msgpack_python-0.4.7-cp34-cp34m-manylinux1_i686.whl -rw-r--r-- 1 1000 ftp 256676 Apr 27 08:51 msgpack_python-0.4.7-cp35-cp35m-manylinux1_i686.whl -rw-r--r-- 1 1000 ftp 126251 Apr 27 06:52 msgpack-python-0.4.7.tar.gz
ここまで行けば、 docker で bash を実行する代わりに、 wheel をビルドするスクリプトを直接実行できそうですね。