3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

C++14, C++17 Python C/C++ モジュールを cmake build しパッケージングして pypi にアップロードするメモ(2022 年夏版)

Last updated at Posted at 2022-08-09

とりあえずのまとめ

scikit-build を使い setup.py を記述.

  • C++14 : manylinux2014 or manylinux_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

auditwheelmanylinux1(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 対応 :tada:

cibuildwheel 最近(2022/07) manylinux_2_28 対応しました. これで C++17 アプリのパッケージングが楽になりますね!

TODO

  • setup.py とか MANIFEST.in とか, python build 関連を別フォルダにする方法を探す.
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?