CMake FindPython のメモ

## 背景

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?

https://qiita.com/syoyo/items/137599ec1849d6d8c7b0

以前は FindPythonInterp だったようですが, cmake 3.12 からは `FindPython` が推奨のようです.
 
`find_package(Python ...)`(FindPython) と `find_package(Python3 ...)`(FindPython3) の２つがあります! (ややこしい...)

https://cmake.org/cmake/help/latest/module/FindPython.html

https://cmake.org/cmake/help/latest/module/FindPython3.html

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` などです.

https://cmake.org/cmake/help/latest/module/FindPython3.html#imported-targets

これらはプロジェクトの上位では参照できません!(より正確には, import ターゲット が呼ばれている CMake ファイルの `CMAKE_ROLE` が `project` である必要がある) `Python3_add_library(... MODULE ...)` も同様です.

https://qiita.com/osamu0329/items/3e9868e83d3745b8c8b6

したがって 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)` 

https://github.com/actions/setup-python/issues/121

## 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 対応しておりません.

https://qiita.com/syoyo/items/5a935fdcbdf89e0a2635

https://github.com/python-cmake-buildsystem/python-cmake-buildsystem

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.