Pythonその他 - 実行ファイル
概要
Pythonスクリプトを実行ファイル化することにより、Pythonがインストールされていない環境でもプログラムを実行できるようになる。
実行ファイル化のツールとして広く使用されており信頼性の高いPyInstallerやNuitkaが存在する。
PyInstallerの主な特徴を以下に示す。
- 単一ファイルまたはフォルダ形式での出力
- Windows、Linux、MacOSに対応
- 依存モジュールの自動検出
- データファイルやリソースの組み込み
- GUIアプリケーションのサポート
複数ファイルで構成されるプロジェクトの構造
実際の開発では、単一ファイルではなく複数のモジュールに分割されたプロジェクト構造が一般的である。
想定するプロジェクト構造
この構造において、main.py ファイルがエントリーポイントとなり、他のモジュールをimportして使用する。
my_project/
├── main.py # エントリーポイント (メインスクリプト)
├── requirements.txt # 依存パッケージ一覧
├── config.ini # 設定ファイル (データファイル)
├── resources/ # リソースフォルダ
│ ├── images/
│ │ └── logo.png
│ └── data/
│ └── default_settings.json
├── src/ # ソースコードのメインパッケージ
│ ├── __init__.py
│ ├── core/ # コア機能モジュール
│ │ ├── __init__.py
│ │ ├── engine.py
│ │ └── processor.py
│ ├── ui/ # UI関連モジュール
│ │ ├── __init__.py
│ │ ├── main_window.py
│ │ └── dialogs.py
│ └── utils/ # ユーティリティモジュール
│ ├── __init__.py
│ ├── file_handler.py
│ └── logger.py
└── tests/ # テストコード (実行ファイルには含めない)
├── __init__.py
└── test_engine.py
エントリーポイント (main.py) の例
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
アプリケーションのエントリーポイント
PyInstallerでexe化する際は、このファイルを指定する
"""
import sys
import os
def get_base_path():
"""
実行ファイルのベースパスを取得する。
PyInstallerでパッケージ化された場合と、通常のPython実行の場合で
パスの取得方法が異なるため、この関数で吸収する。
"""
if getattr(sys, 'frozen', False):
# PyInstallerで実行されている場合
# sys._MEIPASS は一時展開フォルダのパス (--onefileの場合)
# sys.executable は実行ファイル自体のパス
return os.path.dirname(sys.executable)
else:
# 通常のPython実行の場合
return os.path.dirname(os.path.abspath(__file__))
def get_resource_path(relative_path):
"""
リソースファイルへのパスを取得する。
--add-data で含めたファイルにアクセスする際に使用する。
"""
if getattr(sys, 'frozen', False):
# PyInstallerの一時展開フォルダ内のパス
base_path = sys._MEIPASS
else:
base_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base_path, relative_path)
def main():
"""メイン関数"""
# リソースファイルへのアクセス例
config_path = get_resource_path('config.ini')
logo_path = get_resource_path(os.path.join('resources', 'images', 'logo.png'))
# 各モジュールのimport
from src.core.engine import Engine
from src.ui.main_window import MainWindow
from src.utils.logger import setup_logging
# ロギングの設定
setup_logging()
# アプリケーションの起動
engine = Engine()
window = MainWindow(engine)
window.run()
if __name__ == '__main__':
main()
Windows
環境構築
コマンドプロンプトまたはPowerShellを開き、Pythonのバージョンを確認する。
python --version pip --version
python コマンドが認識されない場合は、Pythonのインストール時に[Add Python to PATH]にチェックを入力し忘れている可能性がある。
その場合は環境変数を手動で設定、または、Pythonを再インストールする。
仮想環境の作成
プロジェクトフォルダに移動して仮想環境を作成する。
仮想環境を使用することで、実行ファイルに不要なパッケージが含まれることを防ぎ、ファイルサイズを最小限に抑えられる。
cd C:\path\to\my_project python -m venv venv venv\Scripts\activate
プロンプトの先頭に (venv) と表示されれば、仮想環境がアクティブになっている。
依存パッケージとPyInstallerのインストール
pip install pyinstaller pip install -r requirements.txt
基本的なビルドコマンド
複数ファイルのプロジェクトでも、指定するのはエントリーポイントのファイルだけである。
PyInstallerは、そのファイルからimportされている全てのモジュールを自動的に追跡して、実行ファイルに含める。
pyinstaller main.py
このコマンドにより、main.py がimportしている src.core.engine、src.ui.main_window 等のモジュールは自動的に検出されて、実行ファイルに組み込まれる。
生成されるディレクトリとファイル
| ディレクトリ / ファイル | 説明 |
|---|---|
| build/ | PyInstallerが作業中に使用する一時ファイルの格納場所。 配布時には不要。 |
| dist/ | 生成された実行ファイルと依存ファイルの格納場所。 |
| your_script.spec | PyInstallerの設定ファイル。 カスタマイズに使用する。 |
データファイルとリソースの追加
Pythonソースコード以外のファイル (設定ファイル、画像、JSONデータ等) は自動的には含まれない。
--add-data オプションで明示的に指定する必要がある。
pyinstaller --onefile ^ --add-data "config.ini;." ^ --add-data "resources;resources" ^ main.py
--add-data オプションの書式は、"元のパス;実行ファイル内での配置先パス"となる。
Windowsではセミコロン (;) を区切り文字として使用する。
上記の例では、config.iniファイルは実行ファイルと同じ階層に配置され、resourcesフォルダはその中身ごとresourcesという名前で配置される。
GUIアプリケーション向けオプション
GUIアプリケーションの場合は、コンソールウィンドウを非表示にし、アイコンを設定する。
pyinstaller --onefile --noconsole ^ --icon=resources\images\app_icon.ico ^ --name=MyApplication ^ --add-data "config.ini;." ^ --add-data "resources;resources" ^ main.py
| オプション | 説明 |
|---|---|
| --onefile | 単一の実行ファイルにまとめる |
| --noconsole | コンソールウィンドウを非表示 (GUIアプリ向け) |
| --windowed | --noconsoleと同じ |
| --icon | アイコンファイルを指定 (.ico形式) |
| --name | 出力ファイル名を指定 |
| --add-data | データファイルを追加 |
| --hidden-import | 自動検出されないモジュールを追加 |
specファイルを使用した詳細設定
複雑なプロジェクトでは、specファイルを使用して設定を管理することを推奨する。
まず、初回のビルドでspecファイルを生成する。
pyinstaller --onefile --noconsole --name=MyApplication main.py
生成されたMyApplication.specファイルを開いて、必要に応じて編集する。
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
# データファイルの定義
# 形式: (元のパス, 実行ファイル内での配置先)
added_files = [
('config.ini', '.'),
('resources/images', 'resources/images'),
('resources/data', 'resources/data'),
]
# 自動検出されないモジュールがある場合に追加
hidden_imports = [
# 例: 動的インポートを使用している場合
# 'src.plugins.optional_module',
]
# 除外するモジュール (ファイルサイズ削減のため)
excludes = [
'tkinter', # GUIフレームワークを使わない場合
'unittest', # テストフレームワーク
'pytest',
]
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=added_files,
hiddenimports=hidden_imports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=excludes,
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='MyApplication',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True, # UPX圧縮を使用 (インストール済みの場合)
upx_exclude=[],
runtime_tmpdir=None,
console=False, # コンソールウィンドウを非表示
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='resources\\images\\app_icon.ico',
)
specファイルを編集した後は、以下に示すコマンドを実行してビルドする。
pyinstaller MyApplication.spec
Hidden Importsの対処
PyInstallerは静的解析によってimportを検出するが、以下のようなケースでは自動検出できないことがある。
- 動的インポートを使用している場合
importlib.import_module()等
- 文字列からモジュールを参照している場合
- 一部の複雑なライブラリ
- pandas、scipy、scikit-learn、PIL等のサブモジュール
エラーメッセージに ModuleNotFoundError が含まれている場合は、そのモジュールを --hidden-import オプションで追加する。
pyinstaller --onefile --hidden-import=PIL._tkinter_finder main.py
または、specファイルの hiddenimports リストに追加する。
Windows用ビルドスクリプト
ビルド作業を効率化するため、バッチファイルを作成することを推奨する。
@echo off
setlocal
REM 仮想環境のアクティベート
call venv\Scripts\activate
REM 出力ディレクトリの作成
if not exist dist\windows mkdir dist\windows
REM PyInstallerの実行
pyinstaller --onefile --noconsole ^
--distpath dist\windows ^
--workpath build\windows ^
--add-data "config.ini;." ^
--add-data "resources;resources" ^
--icon=resources\images\app_icon.ico ^
--name=MyApplication ^
main.py
echo.
echo ビルドが完了しました。出力先: dist\windows\MyApplication.exe
pause
RHEL / SUSE
前提条件
PyInstallerはクロスコンパイルをサポートしていない。
Linux向けの実行ファイルを作成するには、Linux環境でPyInstallerを実行する必要がある。
もし、Windows環境からLinux向けの実行ファイルを作成する場合は、WSL2、Docker、仮想マシンを使用する。
環境構築
RHEL
まず、Pythonと開発ツールをインストールする。
# RHEL sudo dnf install python3 python3-pip python3-devel sudo dnf groupinstall "Development Tools"
RHELでは、デフォルトのPythonバージョンがシステムによって異なる。
例えば、Python 3.9以上を使用する場合は、以下に示すようにインストールする。
# Python 3.11を使用する場合 sudo dnf install python3.11 python3.11-pip python3.11-devel
SUSE
まず、Pythonと開発ツールをインストールする。
devel_basisパターンには、gcc、make等の基本的な開発ツールが含まれている。
# SUSE sudo zypper install -t pattern devel_basis sudo zypper install python3 python3-pip python3-devel
仮想環境の作成
プロジェクトディレクトリに移動して、仮想環境を作成する。
cd /path/to/my_project python3 -m venv venv source venv/bin/activate
特定バージョンのPythonを使用する場合は、そのバージョンを明示的に指定する。
python3.11 -m venv venv source venv/bin/activate
依存パッケージとPyInstallerのインストール
pip install --upgrade pip pip install pyinstaller pip install -r requirements.txt
実行ファイルの作成
基本的なコマンド構文はWindowsと同様である、--add-data オプションの区切り文字がコロン (:) になることに注意する。
# CLIアプリケーションの場合 pyinstaller --onefile \ --add-data "config.ini:." \ --add-data "resources:resources" \ main.py # GUIアプリケーションの場合 pyinstaller --onefile --noconsole \ --add-data "config.ini:." \ --add-data "resources:resources" \ --name=MyApplication \ main.py
| 項目 | 説明 |
|---|---|
| ビルド時 | PyInstallerのビルドプロセス自体はコマンドラインで実行するため、ディスプレイサーバー (X11 / Wayland) は不要 |
| 実行時 | 使用するGUIツールキットに依存する |
下表に、GUIツールキットのWayland対応状況を示す。
| ツールキット | Wayland対応 | 備考 |
|---|---|---|
| Qt5 / Qt6 | ネイティブサポート | qt5-wayland / qt6-wayland パッケージのインストールが必要となる。 環境変数 QT_QPA_PLATFORM=wayland で明示的に指定可能
|
| PyQt5 / PyQt6 / PySide6 | ネイティブサポート | QtのPythonバインディングのため、Qt同様にWaylandをネイティブサポート |
| GTK3 / GTK4 | ネイティブサポート | GTK3はバージョン3.20以降で完全サポート GTK4はWayland優先設計 |
| Kivy | SDL2経由でサポート | OpenGL ES 2.0とSDL2上に構築 SDL2がWayland対応のため、間接的にサポート |
| wxWidgets (wxPython) | GTK経由でサポート | LinuxではGTKバックエンドを使用するため、GTK経由でWaylandをサポート ただし、クリップボードやドラッグ&ドロップ等で一部制限あり |
| Tkinter | 非対応 (XWayland経由で動作) | Tcl/TkがX11ベースで設計されているため、ネイティブ非対応 XWayland互換レイヤー経由で動作可能 将来的なWayland対応は検討中だが、現時点では未実装 |
specファイルを使用した設定
Linuxでのspecファイルは、パス区切り文字がスラッシュになる点を除き、Windowsとほぼ同じ構造である。
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
# データファイルの定義 (Linux版)
# パス区切り文字は / を使用
added_files = [
('config.ini', '.'),
('resources/images', 'resources/images'),
('resources/data', 'resources/data'),
]
hidden_imports = []
excludes = [
'tkinter',
'unittest',
'pytest',
]
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=added_files,
hiddenimports=hidden_imports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=excludes,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='MyApplication',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True, # CLIアプリの場合はTrue、GUIの場合はFalse
)
実行権限の付加と動作確認
# 実行権限の付加 ls -la dist/MyApplication chmod u+x dist/MyApplication # 必要な場合 # 動作確認 ./dist/MyApplication
GLIBCバージョンの互換性について
PyInstallerで生成されたバイナリは、ビルド環境のGLIBCバージョンに依存する。
新しいGLIBCでビルドしたバイナリは、古いGLIBCを持つシステムでは動作しない可能性がある。
配布先の環境で確実に動作させるためには、以下に示す事柄を考慮する。
- ターゲット環境と同じ、あるいは、それより古いバージョンのディストリビューションんでビルドする。
- 例 : RHEL 9向けの実行ファイルを作成する場合は、RHEL 9環境でビルドする。
現在のGLIBCバージョンは以下に示すコマンドで確認できる。
ldd --version # または rpm -q glibc
共有ライブラリの依存関係の確認
生成されたバイナリが依存している共有ライブラリを確認するには、ldd コマンドを使用する。
ldd dist/MyApplication
"not found: と表示されるライブラリがある場合、そのバイナリはターゲット環境で動作しない可能性がある。
Linux向けビルドスクリプト
#!/usr/bin/env sh
# 仮想環境のアクティベート
source venv/bin/activate
# 出力ディレクトリの作成
mkdir -p dist/linux
# PyInstallerの実行
pyinstaller --onefile \
--distpath dist/linux \
--workpath build/linux \
--add-data "config.ini:." \
--add-data "resources:resources" \
--name=MyApplication \
main.py
echo ""
echo "ビルドが完了しました。出力先: dist/linux/MyApplication"
スクリプトに実行権限を付与する。
chmod u+x build_scripts/build_linux.sh
Nuitkaを使用した高度なコンパイル
Nuitkaは、PythonコードをC言語にコンパイルしてから実行ファイルを生成するツールである。
PyInstallerと比較して、実行速度が向上し、リバースエンジニアリングへの耐性も高くなる。
Nuitkaのインストール
pip install nuitka
Windows環境では、Visual Studio Build Tools または MinGW-w64 が必要である。
Linux環境では、GCCと関連ツールが必要である。
# RHEL sudo dnf install gcc gcc-c++ # SUSE sudo zypper install gcc gcc-c++
基本的な使用方法
# CLIアプリケーションの場合 python -m nuitka --standalone --onefile your_script.py # GUIアプリケーションの場合 python -m nuitka --standalone --onefile --windows-disable-console --windows-icon-from-ico=icon.ico your_script.py
Nuitkaは初回ビルド時に時間が掛かるが、生成される実行ファイルは一般的にPyInstallerよりも高速に動作する。
トラブルシューティング
ModuleNotFoundErrorが発生する
- 原因
- PyInstallerが動的インポートや特定のライブラリのサブモジュールを検出できていない。
- 対処法
- エラーメッセージに表示されているモジュールを
--hidden-importオプションで追加する。
- エラーメッセージに表示されているモジュールを
pyinstaller --onefile --hidden-import=missing_module main.py
よく問題になるライブラリと対処例を以下に示す。
| ライブラリ | 追加が必要なモジュールの例 |
|---|---|
| pandas | pandas._libs.tslibs.timedeltas |
| PIL / Pillow | PIL._tkinter_finder |
| scikit-learn | --collect-submodules=sklearn |
ファイルが見つからないエラー
- 原因
- データファイル (設定ファイル、画像等) が実行ファイルに含まれていない、または、パスの解決が正しくない。
- 対処法
--add-dataオプションでファイルを正しく含めているか確認する。- ソースコード内で
get_resource_path()メソッドを使用してパスを解決しているかどうかを確認する。
実行ファイルのサイズが大きすぎる
対処法を以下に示す。
- 仮想環境を使用して、必要なパッケージのみをインストールした環境でビルドする。
- specファイルの excludes リストに、使用していないモジュールを追加する。
- UPX圧縮を使用する。
- もしUPXがインストールされていれば、PyInstallerは自動的に使用する。
- UPXのインストール手順を以下に示す。
# RHEL sudo dnf install upx # SUSE sudo zypper install upx
"GLIBC_X.XX not found" エラー
- 原因
- ビルド環境のGLIBCのバージョンが、実行環境より新しい。
- 対処法
- ターゲット環境と同じか、それより古いバージョンのLinuxでビルドする。
- Dockerを使用して古い環境でビルドする方法も効果的である。
- Dockerを使用したビルド方法を以下に示す。
# 例: openSUSE Leap 15.1のDockerコンテナでビルド docker run -it -v $(pwd):/app opensuse/leap:15.1 bash # コンテナ内でPythonとPyInstallerをセットアップしてビルド # ...略
手法の選択指針
| 用途 | 推奨ツール | 備考 |
|---|---|---|
| 通常の用途で手軽に実行ファイルを作成したい | PyInstaller | 豊富な情報があり、トラブルシューティングも容易 |
| 実行速度を重視する | Nuitka | ビルド環境の準備とビルド時間が増加する |
| コードの難読化を強化したい | Nuitka | C言語へのコンパイルにより難読化される |
| クロスプラットフォーム対応が必要 | PyInstaller | 各OSの環境でそれぞれビルドを行う必要がある |
クロスプラットフォーム対応が必要な場合は、各OSの環境 (実機、VM、Docker、WSL2等) でそれぞれビルドを行う必要がある。
CI/CDパイプライン (GitHub Actions等) を活用すると、複数プラットフォーム向けのビルドを自動化できる。
外部リンク