とりあえずのまとめ
scikit-build を使い setup.py
を記述.
- C++14 :
manylinux2014
ormanylinux_2_24
ターゲットにして作る-
manylinux2014
がより広く使えるが, コンパイラ環境はやや古い
-
- C++17 :
manylinux_2_28
ターゲットにして作る
manylinux_X_Y
の場合, インストール側は pip 20.3 必要です!
背景
今まで?だと setup.py
で Python モジュールをビルドとパッケージングしていましたが, 最近は pyproject.toml で build
みたいな流れがあります. しかし試したところ微妙な感じでした.
結局は今までどおり setup.py
でやりますが, scikit-build
を使ってよろしくやります.
例は tinyusdz を参照ください.
build?
C/C++ で cmake ベースで Python のモジュールビルドするものは, 最近は build
というモジュールでビルド環境整えるのが流行りのようです.
ただ, verbose モードがなかったり,
基本的には backend(e.g. setup.py
) へ処理を delegate しているだけっぽい
のと, ドキュメントも微妙
なので, 基本は python setup.py
とかでビルド確認して, 最後 python -m build
で処理するようにしたほうがよさそうです!
build はここがよくない
python-build
は venv 環境を作ってビルドするので, 最初の起動でいくらか時間かかります. またなにかビルド中にエラーがあったときに原因を突き止めるのが面倒いです.
↑にあるように clean コマンドが無いので, backend の生成したファイルが残っているとビルドが期待しないときがあります.
著者の場合は MANIFEST ファイル関連でいろいろハマりました. repo にファイルを追加して MANIFEST を更新しても(中間生成されたファイルを見てしまうため)反映されませんでした.
python -m build
ではキャッシュしたのを見に行くのがデフォルトのようで, 一度 python -m build
するまえに, backend 側でいろいろ中間で生成されたファイルは先に消しておく必要がありました.
また, -n
で venv 環境ではない環境でビルドすることもできますので, 開発中はこちらを使ったほうがよいでしょう.
scikit-build
cmake ビルドがやりやすくなるっぽい scikit-build を使います.
scikit-build では setup.py
もまだ必要でした.
cmake ビルドについては, デフォルトでは pyproject.toml
のあるディレクトリの CMakeLists.txt
を見にいきます.
ファイルが足りない...?
python setup.py sdist
で sdist 作っても, あとから git repo にファイルを追加すると何故か見つけてくれないエラーに遭遇しました.
原因としては, ソースパッケージに含めるファイルは MANIFEST.in
で管理しますが, これがないと git archive あたりから自動生成していたためでした...
MANIFEST.in
消したり(自動生成で良い場合) or MANIFEST.in
用意して対応します!
pybind11, nanobind?
pybind11, nanobind の場合でも, cmake で install ターゲットを記述しておけば .so
含んでパッケージングしてくれるようです.
# CMakeLists.txt
nanobind_add_module(${BUILD_TARGET_PY} ${TINYUSDZ_PYTHON_BINDING_SOURCES})
target_link_libraries(${BUILD_TARGET_PY} PRIVATE ${TINYUSDZ_TARGET_STATIC})
# strip "py" prefix
set_target_properties(${BUILD_TARGET_PY} PROPERTIES OUTPUT_NAME "tinyusdz")
# For pypi packaging
install(TARGETS ${BUILD_TARGET_PY} LIBRARY DESTINATION tinyusdz)
__init__.py
を提供しておくとより確実なのかしらん?
ファイル構成
python build 関連のファイルは, 最低限以下のような感じになります.
MANIFEST.in
pyproject.toml
setup.py
CMakeLists.txt
<python_folder>
<python_folder>
は, package に含めたい native ではない Python コードを含んだフォルダです.
manylinux でのビルド
PyPI へのバイナリパッケージ(whl)のアップロードは manylinux 構成でビルドが必要です.
(glibc ランタイムとか合わせていろいろな Linux ディストリビューション環境で動かせるようにするため)
docker 経由でビルドが推奨のようです.
ただ, 手元でビルドと wheel 生成確認したいとき, グラフィックス系アプリで Docker と相性よくない(ネイティブで GL 関連のライブラリと一緒にビルドしたいとか)や, WSL みたいに Docker 使えない(使えるようにセットアップがめんどくさすぎる)環境でビルドする場合, manylinux
環境を最新に絞って対応でとりあえずいけます.
(Ubuntu 20.04 gcc-9 では manylinux_2_24
はいけた)
C++14, C++17?
C++ 対応状況はこんな感じです.
-
manylinux_2_5_x86_64 (aliased by manylinux1_x86_64)
: だいたいの C++11 が使える(gcc-4.8
ベースなので regex あたり微妙に使えないものがある) -
manylinux_2_12_x86_64 (aliased by manylinux2010_x86_64)
: gcc-8. きちんと C++11 が使える(はず) -
manylinux2014
(manylinux_2_17
): GCC-10. C++14 が使える -
manylinux_2_24
: gcc-6 ベース. より確実に C++14 が使える -
manylinux_2_28
: gcc-11 ベース. C++17 が使える(filesystem
はきちんと使えるかは不明)
最近は manylinux_X_Y
でバージョン付けするようになっています.
(manylinux2014
が gcc-10 で 2_24
だと gcc-6 ですが, glibc ランタイムとしては manylinux2014
のほうが古いディストリビューションをサポートしているため glibc バージョンは低いです)
2022/08 時点でよく使われているっぽい? である manylinux_2_17
(manylinux2014
)では C++14 まで(gcc-10)のサポートのため, nanobind(C++17)がコンパイルできないです!
gcc-8 インストールし, stdc++
を static link する手もありますが,
あんまり古い環境をサポートする必要がなければ,
2.28
(gcc-11 が使える)を使うのがよいかなと思います!
ただ, 2.28
の場合はクライアントにインストールするときに pip 20.3 以上である必要があります.
auditwheel
ビルドしただけでは, PyPi は受け付けてくれず manylinux 環境に合うようにしないといけません.
auditwheel repair
でビルドした binary wheel が manylinux 環境に合うように修正します.
$ auditwheel repair -h
usage: auditwheel repair [-h] [--plat PLATFORM] [-L LIB_SDIR] [-w WHEEL_DIR] [--no-update-tags] [--strip]
[--only-plat]
WHEEL_FILE
Vendor in external shared library dependencies of a wheel.
positional arguments:
WHEEL_FILE Path to wheel file.
optional arguments:
-h, --help show this help message and exit
--plat PLATFORM Desired target platform. See the available platforms under the PLATFORMS section
below. (default: "manylinux_2_5_x86_64")
-L LIB_SDIR, --lib-sdir LIB_SDIR
Subdirectory in packages to store copied libraries. (default: ".libs")
-w WHEEL_DIR, --wheel-dir WHEEL_DIR
Directory to store delocated wheels (default: "wheelhouse/")
--no-update-tags Do not update the wheel filename tags and WHEEL info to match the repaired platform
tag.
--strip Strip symbols in the resulting wheel
--only-plat Do not check for higher policy compatibility
PLATFORMS:
These are the possible target platform tags, as specified by PEP 600.
Note that old, pre-PEP 600 tags are still usable and are listed as aliases
below.
- linux_x86_64
- manylinux_2_5_x86_64 (aliased by manylinux1_x86_64)
- manylinux_2_12_x86_64 (aliased by manylinux2010_x86_64)
- manylinux_2_17_x86_64 (aliased by manylinux2014_x86_64)
- manylinux_2_24_x86_64
- manylinux_2_27_x86_64
- manylinux_2_28_x86_64
- manylinux_2_31_x86_64
auditwheel
は manylinux1
(manylinux_2_5
)がデフォルトになっていますので, --plat
で manylinux バージョン指定しましょう.
auditwheel -v
で 2_28 など表示してくれない場合, conda で最新 python 環境作るのが推奨ですが,
auditwheel-symbols
を使うのも手かもしれません.
また, Ubuntu 20.04 標準の gcc-9 では, manylinux_2_24
は行けました.
auditwheel を使わない方法
pytorch では auditwheel を使わず, 自前で処理しています.
CI + Pypi へのアップロード
twine つかっていつもどおりできます!
CI で一括ビルドする場合, cibuildwheel がよいでしょう.
cibuildwheel で python bwheel(C++ モジュール含む) を CI で一括ビルドし PyPI へアップロードするメモ
https://qiita.com/syoyo/items/97f35b4d5c40761cc314
GitHub Actions と cibuildwheel を使ってarm64対応 wheel をビルドする
https://qiita.com/methane/items/41c7d92d603552f2ef33
を参照ください.
aarch64 対応も考えると github actions を使うのがいいかもしれません.
manylinux_2_28 対応
cibuildwheel 最近(2022/07) manylinux_2_28
対応しました. これで C++17 アプリのパッケージングが楽になりますね!
TODO
- setup.py とか MANIFEST.in とか, python build 関連を別フォルダにする方法を探す.