26
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Docker環境のPyInstallerでキレイにExe化する

Last updated at Posted at 2021-05-26

はじめに

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

簡単な解説

1行目
docker run --rm -v "$(pwd):/src/" cdrx/pyinstaller-windows

cdrx/pyinstaller-windowsイメージから使い捨てのコンテナを立て、カレントディレクトリをそのコンテナにマウントします。

2行目以降
  "pip install -r requirements.txt && \
  pyinstaller main.py --onedir --onefile --clean && \
  mv dist/main.exe main.exe && \
  rm -rf __pycache__/ build/ dist/ main.spec"

全体の流れとしては、

  1. pipで依存関係をインストール
  2. PyInstallerでExe化
  3. Exeファイルの場所を移動
  4. PyInstallerによって生成されたゴミを削除

requirements.txtが不要なら、pip install -r requirements.txt && \は削除しておきます。

その他の依存関係があれば、pyinstaller ...より前でインストールします。(/src/にホスト側のカレントディレクトリをマウントしているので、わざわざインストールするモノもなかろうかと思いますが)

実はこのあたり、単に-c "/entrypoint.sh"とだけ書いてもいいのですが、個人的なこだわりでアレンジしています。

ちょっとしたコツ

開発・実行環境における環境差について

まず、当然ながらmain.pyはWindowsで動くプログラムである必要があります。
具体的には、下記のコードはDocker環境(Linux)では動くものの、Windows上では動作しません。

main.py
with open("data.json", "r") as f:
    data = json.load(f)

実際には、encodingを指定する必要があります。

main.py
with open("data.json", "r", encoding="utf-8") as f:
    data = json.load(f)

CLIアプリではひと工夫

プログラム終了時にはその旨が黒い画面に表示され続けると親切です。
急に画面が消えると、正常終了したかどうか不安になりますから。
何も工夫しなければそのまま閉じてしまうので、プログラムに一手間加えます。

main.py
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"
26
32
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?