背景
CMake で libPython とリンクするアプリ/ライブラリを作成したいが find_package(Python)
がなんかいろいろややこしい...
例として nanobind https://github.com/wjakob/nanobind で C++ の python モジュールを作るのを想定します.
CMake のバージョンは 3.12 ~ 3.22
を想定します.
主な変更点として
- 3.12 : FindPython サポート
- 3.14 : Import target(
Python3::Interpreter
など) が CMAKE_ROLE project のときのみ生成 - 3.15 :
Python3::Module
import taget, Python3_FIND_FRAMEWORK 対応 - 3.18 : Development.Module, Development.Embed モジュールに細分化
- 3.19 : find_package でバージョン指定可能
があります. 2022/07 時点で最低 3.15 は要求したいところでしょうか.
FindPython?
以前は FindPythonInterp だったようですが, cmake 3.12 からは FindPython
が推奨のようです.
find_package(Python ...)
(FindPython) と find_package(Python3 ...)
(FindPython3) の2つがあります! (ややこしい...)
FindPython は Python3 が見つからなかったら, Python2 を探します.
現時点(2022/07)では Python2 はもう EoL で Python3 も 3.8 あたり以上が dominant(UTF あたりなどいろいろ改善があるバージョン)なので, FindPython3 を使ったほうがよいと思います.
3.19 からは, バージョン指定(単体 or 範囲)もできるようになりました.
find_package(Python 3.8 ...)
など(範囲指定は T.B.W.)
また, FindPython で Python3 が見つかった場合, PYTHON_INCLUDE_DIRS
が定義されますが, PYTHON3_INCUDE_DIRS
は定義されません...(逆も同様)
Module
Interpreter
, Compiler
, Development
があります.
3.18 からは, Development
は Development.Module
, Development.Embed
に細分化されました(Development
だけだと Development.Module
と解釈)
Module は自身のライブラリを Python モジュールとしてビルドする場合
Embed は Python ライブラリ(libpython)をリンクして Python を組み込んだアプリを作る場合につかいます
(したがって Module だと libpython はリンクされないので注意!)
Development.Embed
の場合は Interpreter
で代用できるかもしれませんが, Interpreter
だと python.exe
をさがす(はず)ので, pure な libPython 組み込みアプリを作りたいときは Development.Embed
がいいかもしれません.
Imported target
Python3::Module
などです.
これらはプロジェクトの上位では参照できません!(より正確には, import ターゲット が呼ばれている CMake ファイルの CMAKE_ROLE
が project
である必要がある) Python3_add_library(... MODULE ...)
も同様です.
したがって nanobind(の CMakeLists.txt) を add_subdirectory
で追加しても,
Python3_ADD_LIBRARY: dependent target 'Python3::Module' is not defined.
エラーで cmake bootstrap がコケます...
(GLOBAL
あたりでいけるかもしれませんが)
Python_FIND_FRAMEWORK LAST
macOS あたりだと LAST
を設定しておくとよいようです.
set(Python_FIND_FRAMEWORK LAST)
nanobind 向けの修正
-
find_package(Python3 ...)
を project toplevel で行う. - nanobind/CMakeLists.txt での
find_package(Python ...)
をコメントアウト - https://github.com/wjakob/nanobind/blob/master/cmake/nanobind-config.cmake#L3 のチェックをコメントアウト
- nanobind で
Python_INCLUDE_DIRS
->Python3_INCLUDE_DIRS
に修正
でとりあえずうまくいきました.
もう少しテストしてみて, nanobind 側に PR 送るかもしれません.
おまけ Python も cmake build?
find_package で Python 探すのもめんどいんで(特に CI で回したいときなど), Python 自体もまとめてコンパイルしたいときもありますが, めんどうなことに, (C)Python 自体は cmake 対応しておりません.
python-cmake-buildsystem の試みがありますが, 現時点(2022/07)では 3.10 には対応していません(FFI や内部構造が変わった).
また, tls などを使いたい場合は別途 OpenSSL もビルドする必要があります...(こちらも一応 cmake 対応のがあるけど...). Cmake friendly な LibreSSL という手もありますが, 完全互換というわけではありませんので注意です https://peps.python.org/pep-0644/ (TLS 1.3 関連なモジュールを作りたい以外はあまり影響はないかもではあります)
Custom python の指定
特定の Python を指定したい場合,
set(Python3_FIND_FRAMEWORK NEVER) # Do not search framework python
set(Python3_FIND_STRATEGY LOCATION)
set(Python3_FIND_REGISTRY NEVER) # Windows only
とし, cmake options で -DPython3_EXECUTABLE=/path/to/bin/python
を指定します.
(Windows だと /path/to/Scripts/python.exe
になります)
また, (特に Windows だと?) case sensitive です. Python3_EXECUTABLE
は OK ですが PYTHON3_EXECUTABLE
だと NG.