はじめに
PyInstallerでは生成物の肥大化を防ぐため、専用の仮想環境を用意して、その中でビルドしようという話があります。
しかし、面倒です。
普段はDocker環境で開発していることが多いので、わざわざビルドのためだけに仮想環境を作りたくないですね。
ということで以下の方法により、ローカル環境を汚すことなく、最小サイズのExeファイルただ1つ吐き出させることができます。
Mac用の実行ファイルはMac上でしか生成できないらしいので(参考)、
本記事はWindowsまたはLinuxユーザ向けの情報となります。
ここではWindowsでの方法を載せますが、Linuxの場合もほぼ同じです。
下記の通り読み替えてください。
Windows | Linux |
---|---|
pyinstaller-windows | pyinstaller-linux |
main.exe | main |
手順
https://github.com/cdrx/docker-pyinstaller
※詳細は上記を参照されるとし、アレンジした使い方を述べます。
現在のディレクトリ構成は次のようになっています。
.
├── main.py
└── requirements.txt
下記のワンライナーを実行します。(WSLで実行しました。)
docker run --rm -v "$(pwd):/src/" cdrx/pyinstaller-windows -c \
"pip install -r requirements.txt && \
pyinstaller main.py --onedir --onefile --clean && \
mv dist/main.exe main.exe && \
rm -rf __pycache__/ build/ dist/ main.spec"
すると、Exeファイルが生成されます。
.
├── main.exe
├── main.py
└── requirements.txt
簡単な解説
docker run --rm -v "$(pwd):/src/" cdrx/pyinstaller-windows
cdrx/pyinstaller-windows
イメージから使い捨てのコンテナを立て、カレントディレクトリをそのコンテナにマウントします。
"pip install -r requirements.txt && \
pyinstaller main.py --onedir --onefile --clean && \
mv dist/main.exe main.exe && \
rm -rf __pycache__/ build/ dist/ main.spec"
全体の流れとしては、
- pipで依存関係をインストール
- PyInstallerでExe化
- Exeファイルの場所を移動
- PyInstallerによって生成されたゴミを削除
requirements.txt
が不要なら、pip install -r requirements.txt && \
は削除しておきます。
その他の依存関係があれば、pyinstaller ...
より前でインストールします。(/src/
にホスト側のカレントディレクトリをマウントしているので、わざわざインストールするモノもなかろうかと思いますが)
実はこのあたり、単に-c "/entrypoint.sh"
とだけ書いてもいいのですが、個人的なこだわりでアレンジしています。
ちょっとしたコツ
開発・実行環境における環境差について
まず、当然ながらmain.py
はWindowsで動くプログラムである必要があります。
具体的には、下記のコードはDocker環境(Linux)では動くものの、Windows上では動作しません。
with open("data.json", "r") as f:
data = json.load(f)
実際には、encoding
を指定する必要があります。
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)
CLIアプリではひと工夫
プログラム終了時にはその旨が黒い画面に表示され続けると親切です。
急に画面が消えると、正常終了したかどうか不安になりますから。
何も工夫しなければそのまま閉じてしまうので、プログラムに一手間加えます。
def main():
print("hello!")
if __name__ == "__main__":
main()
import subprocess
subprocess.call('PAUSE', shell=True)
hello!
続行するには何かキーを押してください . . .
実行中にコンソールを表示したくない場合
GUIアプリの場合など、稀に該当すると思います。
この場合、pyinstallerの引数に--noconsole
を追加します。
docker run --rm -v "$(pwd):/src/" cdrx/pyinstaller-windows -c \
"pip install -r requirements.txt && \
pyinstaller main.py --onedir --onefile --clean --noconsole && \
mv dist/main.exe main.exe && \
rm -rf __pycache__/ build/ dist/ main.spec"