python-cmake-buildsystem で libpython を自前 C/C++ アプリに組み込む


背景

  • CMake
  • Python インタプリタを自前 CMake + C/C++ アプリにモノリシックに組み込みたい(libpython も含んでの単一バイナリにして配布したいなど)
    • Windows, macOS, Linux クロスプラットフォームでやりたい
  • システムの libpython や, プレビルドバイナリを利用だと, セットアップが面倒だったり, 環境によっては ABI が合わないとか, ビルド環境と実行環境での python バージョン違いとか, コンパイラが合わないとかでうまくビルドできなかったり実行できなかったりの問題に陥ることがあるので避けたい
  • Python コアの組み込み機能を使うくらいで, readline や OpenSSL などやその他外部パッケージを利用する予定はない
    • python でちょっとしたテストのためのスクリプトを実行したい
    • グラフィックス関連の処理で, C++ 世界での変数を値を使って python でスクリプト処理したい
    • などなど.
  • 組み込み用には micropython とか tinypy とかあるが, やはりフル機能の Python が使いたい
    • フル機能であれば, pybind11 と連携して, バインディングも記述しやすくなる.

python-cmake-buildsystem

Python パッケージ自体のビルドシステムは古い configure 形式のままで, cmake のプロジェクトは用意されていません.

Python を CMake でビルドできるようにする python-cmake-buildsystem があります.

Python パッケージを自動ダウンロード + patching もしてくれます.

ただ, オリジナルのは開発止まっているのか, 3.6 までです,

いくつか fork で 3.8, 3.9 対応しているのがあります.

がよさそうでしょうか.
ただ, asv_cmake という独自(?)の設定を読み込んでいますのでそのままではうごきません. CMakeLists.txt で該当部分を消せばいけます.
 

ビルドオプション

cmake で,

-DWITH_STATIC_DEPENDENCIES=On \ # Linux(posix) only
-DBUILD_EXTENSIONS_AS_BUILTIN=On \
-DUSE_SYSTEM_LIBRARIES=OFF

あたりを設定すると, 静的リンク + バイナリが作れるかと思います. いくつか cmake オプションがありますので, 自分の環境に合うようにいろいろ試してみてください.

また, 自前の C/C++ アプリの CMakeLists.txt に add_subdirectory() でこの python-cmake-buildsystem を登録してもよいかもですが, いろいろファイルコピーのインストール処理とかも考えると, 一旦 python-cmake-buildsystem でビルドをしておき, 自前の C/C++ アプリではそのインストールディレクトリを指定, というのが手間がかからないかなと思います.

注意点

C++ から Python を呼ぶ場合, site の設定を無効にしておかないと(or なにかしら site を設定), python インタプリタが, import site ができないエラーが出ます.

site の設定を無効にするには, C++ コードで Python インタプリタ初期化の前に, 以下のように Py_NoSiteFlag グローバル変数を設定します.


Py_NoSiteFlag = 1; // disable import site
// Py_VerboseFlag = 1;
Py_IgnoreEnvironmentFlag = 1;
Py_NoUserSiteDirectory = 1;

Py_InitializeEx(0);
...

参考情報: https://docs.python.org/3.6/c-api/init.html

まとめ

python-cmake-buildsystem で C/C++ アプリで libpython をリンクして, Python コードを呼ぶしくみを構築し, 動作確認しました.

TODO

  • minimal なサンプルコードを用意する
  • pybind11 と連携し, C/C++ <-> Python をよりシームレスに繋ぐ