目的
C++ と cmake で作った実行ファイルを、pipでインストールできる形式にします。
加えて、pythonのライブラリも添付します。
なお、PyPIへのアップロード・配布は考慮していません。
作り方
pythonの準備
skbuildを使うので、
pip install scikit-build でインストールしておきます。
python 3.8.1 で動作確認しています。
pipでインストールするC++のプログラムの準備
練習用C++プログラムを準備します。既存の、C++ & cmakeでビルドするプログラムがあれば、ここはスキップします。
まず、練習用C++プログラムの本体です。
[aaa.cpp]
| #include <iostream>
int main(int argc, char *argv[]){
std::cout << "aaa bbb ccc" << std::endl;
for(int i=0; i<argc; ++i){
std::cout << argv[i] << std::endl;
}
return 0;
}
|
文字列を表示するだけのプログラムです。
これをビルドするCMakeLists.txtを作成します。
[CMakeLists.txt]
| cmake_minimum_required(VERSION 3.11.0)
project(projAAA)
add_executable(aaa aaa.cpp)
install(TARGETS aaa RUNTIME DESTINATION .)
|
ここでは aaa という名前のコマンドを作成しています。C++やcmakeの説明は省略します。
setup.py
配布パッケージを作成するためのPythonスクリプト setup.py を作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | #!/usr/bin/env python
# -*- coding: utf-8 -*-
from skbuild import setup
setup(name="bdist-template", # パッケージの名前。pipでインストール後、pip list で表示される名前
version="0.0.0",
author="Your NAME",
author_email="your@name.com",
packages=["bdist_example"], # importするときに使う名前。nameと一致してなくても良い。
# 同じ方がユーザーには親切かも。
cmake_install_dir="bdist_example/bin", # 最初のディレクトリ名はpackagesで指定した名前と同じにする
entry_points={
"console_scripts": [
"cmdaaa=bdist_example:python_function_aaa"
]
# cmdaaa がpip install後にシェルから使えるコマンド名になる
# =と:の間は、packagesで指定した名前と同じにする
# :の右側は後ほど説明
},
url="http://yourname.abc",
download_url="https://yourname.abc/download",
description="Test package", # このパッケージの短い説明文
long_description="Test package !!!", # このパッケージの詳細な説明文
classifiers=[
"Programming Language :: C++",
'Programming Language :: Python'
],
license="???" # ライセンス名を記載(GPLとかApacheとか)
)
|
__init__.py
pipでインストールする場合、C++でビルドした実行ファイルをそのまま実行するのではなく、Pythonを一旦経由するので、
そのためのスクリプトを準備します。
まず、 setup.py のpackagesで指定した名前(ここではbdist_example)のディレクトリを作成します。
そこに次のような内容を含む __init__.py を作成します。
[bdist_example/__init__.py]
| import os
import subprocess
import sys
CMAKE_BIN_DIR = os.path.join(os.path.dirname(__file__), "bin")
def python_function_aaa():
raise SystemExit(subprocess.call([os.path.join(CMAKE_BIN_DIR, "aaa")] + sys.argv[1:]))
|
"aaa"の部分を CMakeLists.txt で書いたプログラム名と一致させます。
python_function_aaa は setup.py の console_scripts の部分の : の右側部分に書いた名前と一致させます。
同梱するスクリプト
次のスクリプトも同梱するようにします。バイナリのみの配布であれば、ここはスキップします。
[bdist_example/aaalib.py]
| def print_aaaaa():
print("aaaaa")
|
.whlファイルの作成
ここまでそろうとパッケージを作ることができます。
$ python setup.py bdist_wheel
を実行すると、distディレクトリの中に.whlファイルが作成されます。
インストール
作成した.whlを次のようにしてインストールします。
$ pip install dist/bdist_template-0.0.0-cp38-cp38-linux_x86_64.whl
Processing ./dist/bdist_template-0.0.0-cp38-cp38-linux_x86_64.whl
Installing collected packages: bdist-template
Successfully installed bdist-template-0.0.0
インストールされたことの確認は、
$ pip list
Package Version
-------------- -------
bdist-template 0.0.0
packaging 20.1
pip 20.0.2
pyparsing 2.4.6
scikit-build 0.10.0
setuptools 41.2.0
six 1.14.0
wheel 0.34.2
でできます。bdist-templateがインストールされていることがわかります。
動作確認
先程作成したコマンドが呼び出せることを確認します。
$ cmdaaa x y z
aaa bbb ccc
/home/user/.pyenv/versions/3.8.1/envs/py38/lib/python3.8/site-packages/bdist_example/bin/aaa
x
y
z
C++でビルドしたコマンドそのものは aaa でしたが、setup.pyで書いたコマンド名 cmdaaa が有効になっていることが確認できました。
同梱したスクリプトをpythonから実行してみます。
>>> import bdist_example.aaalib
>>> bdist_example.aaalib.print_aaaaa()
aaaaa
おまけ1
データを同梱したい場合は、次のようなコードを setup.py のsetup関数に渡す引数に追加します。
setup(...
include_package_data=True,
package_data={
"bdist_example": ["data/*.dat"],
}
)
ここでは、bdist_example/data/*.dat にデータがあると仮定しています。
おまけ2
.whlのファイル名でインストールできるpythonのバージョンが制限されるので、
$ mv bdist_template-0.0.0-{cp38-cp38,py3-none}-linux_x86_64.whl
のようにリネームしておくと、Python3ならインストールできるようになります。
ファイル名での制限を外すと、動作しないバージョンのPythonにインストールできてしまうので、代わりに、
setup(...
python_requires='~=3.6'
)
のように指定しておきます。バージョン指定の書き方は
https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
が参考になります。
まとめ
skbuild により、 setup.py と __init__.py を準備するだけで .whl ファイルを作ることができました。
参考文献
scikit-build (skbuild) のドキュメント
https://scikit-build.readthedocs.io/en/latest/usage.html#basic-usage
cmake-python-distributions のコード
https://github.com/scikit-build/cmake-python-distributions
プロジェクトのパッケージングと配布
https://python-packaging-user-guide-ja.readthedocs.io/ja/latest/distributing.html
https://packaging.python.org/guides/distributing-packages-using-setuptools/