[備忘録]PyInstallerを使ってpythonスクリプトを実行ファイル化する
[筆者の環境]
OS: macOS Mojave 10.14.6
エディタ: VSCode 1.74.3
Homebrew 3.6.18
筆者のpythonの開発環境については以下の記事を参照ください。
0. 前提
パッケージやライブラリを使用するものだけに厳選するため、仮想環境を構築する。ここではpython標準のvenvを使用する。
venvの導入とactivate
pythonのパッケージ管理を行うためのツール。異なるプロジェクトで同一パッケージの異なるバージョンをそれぞれ使いたいときなどに有効。
$ python3 -m venv .env
以上のコマンドで仮想環境の初期化ができ、ワーキングディレクトリに仮想環境の設定や、仮想環境にインストールしたパッケージを保管するための階層ができる。ここでは「.env」という名前の仮想環境を初期化したので現在のディレクトリに.envというフォルダが生成される。
仮想環境の有効化は以下のように行う。
$ source .env/bin/activate
先ほど初期化した際に生成された階層にactivateに必要な実行ファイルが格納されている。こちらを使用して仮想環境を有効化する。有効化できると、コンソールに、(.env)のように現在の環境名が表示される。
使用するパッケージのインストール
パッケージのインストールは以下のようにpipを使用する。
$ python3 -m pip install numpy
依存関係のあるパッケージなども自動で探してインストールしてきてくれる。
[追記]
scipyをpipでインストールする際に、筆者の環境だとエラーが起きた。(詳細は以下参照)
openblasをpipが見つけられないために起きているエラーのようで、以下のように、~/.bashrcに書き込みパスを通してやることで解決した。
export LDFLAGS="-L/usr/local/opt/openblas/lib"
export CPPFLAGS="-I/usr/local/opt/openblas/include"
export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig"
1. PyInstallerの導入
仮想環境をアクティブにし、PyInstallerを導入する。
$ pip install pyinstaller
今回は以下のようなディレクトリ構成のプロジェクトをビルドする。
---/
|-.env
| |-bin/
| |-include/
| |-lib/
|
|-main.py
|
|-libs/
|-mymodule01.py
|-mymodule02.py
2. 組み込むモジュールの設定
PyInstallerはインストールされているパッケージの依存関係のあるものまで、基本的には探して自動でバイナリに変換してくれる。ただし、numpyなどのライブラリや自作モジュールなどはその限りでないので、こちらで組み込むライブラリを指定してやる必要がある。
全体の流れは「setuptoolsを用いて.pyファイルから.soファイルにビルド→PyInstallerのhookを使用して全体をビルド」のように行う。
setuptoolsを用いたライブラリのビルド
setup.pyファイルをmain.pyと同じ階層に用意する。中身は以下のように記述する。
記述し、以下のコマンドを実行するとlibsの階層にsoファイル(共有ライブラリファイル)が生成される。
$ python3 setup.py build_ext --inplace
PyInstallerに読み込ませるための準備
PyInstallerフォルダを以下のように作成し、/libs以下のsoファイルをコピーする。その後、init.pyファイルを用意し、/pyinstaller配下にhooksフォルダを作成する。
---/
|-.env
| |-bin/
| |-include/
| |-lib/
|
|-main.py
|
|-pyinstaller/
|-libs/
| |-__init__.py
| |-mymodule01.cpython-310-darwin.so
| |-mymodule02.cpython-310-darwin.so
|
|-hooks/
|-hook-libs.py
hookの設定
前述の通り、PyImstallerは基本的に依存関係まで検索したのちにビルドを行ってくれるが、自作モジュールの場合などはその依存先を明示的に書かなくてはならない。このために使用するのがhookである。
hook-libs.pyは以下のように記述する。
ここではmymodule01とmymodule02でnumpy、scipy、pyloudnormを使用していた場合の例を挙げている。依存関係のあるライブラリやパッケージの名前を記述するだけで良い。
3. PyInstallerの実行
あとは以下のコマンドを実行し、ビルドをする。
$ pyinstaller --windowed --additional-hooks-dir ./hooks main.py --argv-emulation --osx-bundle-identifier ryo.test01
--windowedは実行時にコンソールを起動させないオプション。--additional-hooks-dirオプションでhookの設定をしたファイルのディレクトリ(./hooks)を指定。--argv-emulationはsys.argvが空だった場合でも、ファイルパスが格納されるようにするオプション。--osx-bundle-identifierではバンドルIDを設定できる(macOS向け)。macOSでは非推奨のオプション設定などもあるため、その他の詳細は公式のドキュメントを参照するとよい。
ビルドが無事に終わると、/dist配下に実行ファイルが生成される。最終的なディレクトリ構成は以下のようになる。
---/
|-.env
| |-bin/
| |-include/
| |-lib/
|
|-build/
| |-...(省略)
|
|-dist/
| |-main/
| | |-...(省略)
| |
| |-main.app
|
|-main.py
|
|-pyinstaller/
|-libs/
| |-__init__.py
| |-mymodule01.cpython-310-darwin.so
| |-mymodule02.cpython-310-darwin.so
|
|-hooks/
|-hook-libs.py