「Pythonその他 - 実行ファイル」の版間の差分

提供: MochiuWiki : SUSE, EC, PCB

ページの作成:「== 概要 == Pythonプロジェクトを実行ファイルに変換する手順を記載する。<br> <br><br> == 実行ファイルの作成 == Pythonプロジェクト…」
 
 
(同じ利用者による、間の1版が非表示)
1行目: 1行目:
== 概要 ==
== 概要 ==
Pythonプロジェクトを実行ファイルに変換する手順を記載する。<br>
Pythonスクリプトを実行ファイル化することにより、Pythonがインストールされていない環境でもプログラムを実行できるようになる。<br>
実行ファイル化のツールとして広く使用されており信頼性の高いPyInstallerやNuitkaが存在する。<br>
<br>
PyInstallerの主な特徴を以下に示す。<br>
* 単一ファイルまたはフォルダ形式での出力
* Windows、Linux、MacOSに対応
* 依存モジュールの自動検出
* データファイルやリソースの組み込み
* GUIアプリケーションのサポート
<br><br>
 
== 複数ファイルで構成されるプロジェクトの構造 ==
実際の開発では、単一ファイルではなく複数のモジュールに分割されたプロジェクト構造が一般的である。<br>
<br>
==== 想定するプロジェクト構造 ====
この構造において、<u>main.py</u> ファイルがエントリーポイントとなり、他のモジュールをimportして使用する。<br>
<br>
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
<br>
==== エントリーポイント (main.py) の例 ====
<syntaxhighlight lang="python">
#!/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()
</syntaxhighlight>
<br><br>
 
== Windows ==
==== 環境構築 ====
コマンドプロンプトまたはPowerShellを開き、Pythonのバージョンを確認する。<br>
python --version
pip --version
<br>
<code>python</code> コマンドが認識されない場合は、Pythonのインストール時に[Add Python to PATH]にチェックを入力し忘れている可能性がある。<br>
その場合は環境変数を手動で設定、または、Pythonを再インストールする。<br>
<br>
==== 仮想環境の作成 ====
プロジェクトフォルダに移動して仮想環境を作成する。<br>
仮想環境を使用することで、実行ファイルに不要なパッケージが含まれることを防ぎ、ファイルサイズを最小限に抑えられる。<br>
cd C:\path\to\my_project
python -m venv venv
venv\Scripts\activate
<br>
プロンプトの先頭に <u>(venv)</u> と表示されれば、仮想環境がアクティブになっている。<br>
<br>
==== 依存パッケージとPyInstallerのインストール ====
pip install pyinstaller
pip install -r requirements.txt
<br>
==== 基本的なビルドコマンド ====
複数ファイルのプロジェクトでも、指定するのはエントリーポイントのファイルだけである。<br>
PyInstallerは、そのファイルからimportされている全てのモジュールを自動的に追跡して、実行ファイルに含める。<br>
pyinstaller main.py
<br>
このコマンドにより、<u>main.py</u> がimportしている <u>src.core.engine</u>、<u>src.ui.main_window</u> 等のモジュールは自動的に検出されて、実行ファイルに組み込まれる。<br>
<br>
==== 生成されるディレクトリとファイル ====
<center>
{| class="wikitable"
|+ ビルド時に生成されるディレクトリとファイル
|-
! ディレクトリ / ファイル !! 説明
|-
| build/ || PyInstallerが作業中に使用する一時ファイルの格納場所。<br>配布時には不要。
|-
| dist/ || 生成された実行ファイルと依存ファイルの格納場所。
|-
| your_script.spec || PyInstallerの設定ファイル。<br>カスタマイズに使用する。
|}
</center>
<br>
==== データファイルとリソースの追加 ====
Pythonソースコード以外のファイル (設定ファイル、画像、JSONデータ等) は自動的には含まれない。<br>
<code>--add-data</code> オプションで明示的に指定する必要がある。<br>
pyinstaller --onefile ^
    --add-data "config.ini;." ^
    --add-data "resources;resources" ^
    main.py
<br>
<code>--add-data</code> オプションの書式は、<code>"元のパス;実行ファイル内での配置先パス"</code>となる。<br>
Windowsではセミコロン (<code>;</code>) を区切り文字として使用する。<br>
<br>
上記の例では、config.iniファイルは実行ファイルと同じ階層に配置され、resourcesフォルダはその中身ごとresourcesという名前で配置される。<br>
<br>
==== GUIアプリケーション向けオプション ====
GUIアプリケーションの場合は、コンソールウィンドウを非表示にし、アイコンを設定する。<br>
pyinstaller --onefile --noconsole ^
    --icon=resources\images\app_icon.ico ^
    --name=MyApplication ^
    --add-data "config.ini;." ^
    --add-data "resources;resources" ^
    main.py
<br>
<center>
{| class="wikitable"
|+ 主なオプション
|-
! オプション !! 説明
|-
| --onefile || 単一の実行ファイルにまとめる
|-
| --noconsole || コンソールウィンドウを非表示 (GUIアプリ向け)
|-
| --windowed || --noconsoleと同じ
|-
| --icon || アイコンファイルを指定 (.ico形式)
|-
| --name || 出力ファイル名を指定
|-
| --add-data || データファイルを追加
|-
| --hidden-import || 自動検出されないモジュールを追加
|}
</center>
<br>
==== specファイルを使用した詳細設定 ====
複雑なプロジェクトでは、specファイルを使用して設定を管理することを推奨する。<br>
まず、初回のビルドでspecファイルを生成する。<br>
pyinstaller --onefile --noconsole --name=MyApplication main.py
<br>
生成されたMyApplication.specファイルを開いて、必要に応じて編集する。<br>
<syntaxhighlight lang="python">
# -*- 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',
)
</syntaxhighlight>
<br>
specファイルを編集した後は、以下に示すコマンドを実行してビルドする。<br>
pyinstaller MyApplication.spec
<br>
==== Hidden Importsの対処 ====
PyInstallerは静的解析によってimportを検出するが、以下のようなケースでは自動検出できないことがある。<br>
<br>
* 動的インポートを使用している場合
*: <code>importlib.import_module()</code> 等
* 文字列からモジュールを参照している場合
* 一部の複雑なライブラリ
*: pandas、scipy、scikit-learn、PIL等のサブモジュール
<br>
エラーメッセージに <u>ModuleNotFoundError</u> が含まれている場合は、そのモジュールを <code>--hidden-import</code> オプションで追加する。<br>
pyinstaller --onefile --hidden-import=PIL._tkinter_finder main.py
<br>
または、specファイルの <u>hiddenimports</u> リストに追加する。<br>
<br>
==== Windows用ビルドスクリプト ====
ビルド作業を効率化するため、バッチファイルを作成することを推奨する。<br>
<syntaxhighlight lang="batch">
@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
</syntaxhighlight>
<br><br>
<br><br>


== 実行ファイルの作成 ==
== RHEL / SUSE ==
Pythonプロジェクトを実行ファイルに変換するには、pyinstallerを使用する。<br>
==== 前提条件 ====
PyInstallerはクロスコンパイルをサポートしていない。<br>
Linux向けの実行ファイルを作成するには、Linux環境でPyInstallerを実行する必要がある。<br>
<br>
もし、Windows環境からLinux向けの実行ファイルを作成する場合は、WSL2、Docker、仮想マシンを使用する。<br>
<br>
==== 環境構築 ====
===== RHEL =====
まず、Pythonと開発ツールをインストールする。<br>
# RHEL
sudo dnf install python3 python3-pip python3-devel
sudo dnf groupinstall "Development Tools"
<br>
RHELでは、デフォルトのPythonバージョンがシステムによって異なる。<br>
例えば、Python 3.9以上を使用する場合は、以下に示すようにインストールする。<br>
# Python 3.11を使用する場合
sudo dnf install python3.11 python3.11-pip python3.11-devel
<br>
===== SUSE =====
まず、Pythonと開発ツールをインストールする。<br>
devel_basisパターンには、gcc、make等の基本的な開発ツールが含まれている。<br>
<br>
# SUSE
sudo zypper install -t pattern devel_basis
sudo zypper install python3 python3-pip python3-devel
<br>
==== 仮想環境の作成 ====
プロジェクトディレクトリに移動して、仮想環境を作成する。<br>
cd /path/to/my_project
python3 -m venv venv
source venv/bin/activate
<br>
<br>
まず、pyinstallerをインストールするため、以下のコマンドを実行する。<br>
特定バージョンのPythonを使用する場合は、そのバージョンを明示的に指定する。<br>
python3.11 -m venv venv
source venv/bin/activate
<br>
==== 依存パッケージとPyInstallerのインストール ====
pip install --upgrade pip
  pip install pyinstaller
  pip install pyinstaller
pip install -r requirements.txt
<br>
==== 実行ファイルの作成 ====
基本的なコマンド構文はWindowsと同様である、<code>--add-data</code> オプションの区切り文字がコロン (<code>:</code>) になることに注意する。<br>
# 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
<br>
<center>
{| class="wikitable"
|+ PyInstallerによるビルドと実行時の環境要件
|-
! 項目 !! 説明
|-
| ビルド時 || PyInstallerのビルドプロセス自体はコマンドラインで実行するため、ディスプレイサーバー (X11 / Wayland) は不要
|-
| 実行時 || 使用するGUIツールキットに依存する
|}
</center>
<br>
下表に、GUIツールキットのWayland対応状況を示す。<br>
<br>
<center>
{| class="wikitable"
|+ Python GUIツールキットのWayland対応状況 (2025年1月時点)
|-
! ツールキット !! Wayland対応 !! 備考
|-
| Qt5 / Qt6 || ネイティブサポート || qt5-wayland / qt6-wayland パッケージのインストールが必要となる。<br>環境変数 <code>QT_QPA_PLATFORM=wayland</code> で明示的に指定可能
|-
| PyQt5 / PyQt6 / PySide6 || ネイティブサポート || QtのPythonバインディングのため、Qt同様にWaylandをネイティブサポート
|-
| GTK3 / GTK4 || ネイティブサポート || GTK3はバージョン3.20以降で完全サポート<br>GTK4はWayland優先設計
|-
| Kivy || SDL2経由でサポート || OpenGL ES 2.0とSDL2上に構築<br>SDL2がWayland対応のため、間接的にサポート
|-
| wxWidgets (wxPython) || GTK経由でサポート || LinuxではGTKバックエンドを使用するため、GTK経由でWaylandをサポート<br>ただし、クリップボードやドラッグ&ドロップ等で一部制限あり
|-
| Tkinter || 非対応 (XWayland経由で動作) || Tcl/TkがX11ベースで設計されているため、ネイティブ非対応<br>XWayland互換レイヤー経由で動作可能<br><br>将来的なWayland対応は検討中だが、現時点では未実装
|}
</center>
<br>
==== specファイルを使用した設定 ====
Linuxでのspecファイルは、パス区切り文字がスラッシュになる点を除き、Windowsとほぼ同じ構造である。<br>
<syntaxhighlight lang="python">
# -*- 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
)
</syntaxhighlight>
<br>
==== 実行権限の付加と動作確認 ====
# 実行権限の付加
ls -la dist/MyApplication
chmod u+x dist/MyApplication    # 必要な場合
# 動作確認
./dist/MyApplication
<br>
==== GLIBCバージョンの互換性について ====
PyInstallerで生成されたバイナリは、ビルド環境のGLIBCバージョンに依存する。<br>
新しいGLIBCでビルドしたバイナリは、古いGLIBCを持つシステムでは動作しない可能性がある。<br>
<br>
配布先の環境で確実に動作させるためには、以下に示す事柄を考慮する。<br>
* ターゲット環境と同じ、あるいは、それより古いバージョンのディストリビューションんでビルドする。
*: 例 : RHEL 9向けの実行ファイルを作成する場合は、RHEL 9環境でビルドする。
<br>
現在のGLIBCバージョンは以下に示すコマンドで確認できる。<br>
ldd --version
# または
rpm -q glibc
<br>
==== 共有ライブラリの依存関係の確認 ====
生成されたバイナリが依存している共有ライブラリを確認するには、<code>ldd</code> コマンドを使用する。<br>
ldd dist/MyApplication
<br>
<u>"not found:</u> と表示されるライブラリがある場合、そのバイナリはターゲット環境で動作しない可能性がある。<br>
<br>
==== Linux向けビルドスクリプト ====
<syntaxhighlight lang="bash">
#!/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"
</syntaxhighlight>
<br>
スクリプトに実行権限を付与する。<br>
chmod u+x build_scripts/build_linux.sh
<br><br>
== Nuitkaを使用した高度なコンパイル ==
Nuitkaは、PythonコードをC言語にコンパイルしてから実行ファイルを生成するツールである。<br>
PyInstallerと比較して、実行速度が向上し、リバースエンジニアリングへの耐性も高くなる。<br>
<br>
<br>
pyinstallerを使用して、実行ファイルを作成する。<br>
==== Nuitkaのインストール ====
なお、–-onefileオプションと-–noconsoleオプションは省略可能である。<br>
  pip install nuitka
* –-onefile
*: 関連するファイルを1つにまとめて実行ファイルを作成する。
* -–noconsole
*: コンソール画面を表示しない。
  pyinstaller <Pythonファイル名> [--onefile] [--noconsole]
<br>
<br>
Pythonプロジェクトディレクトリに、以下に示す2つのディレクトリが作成される。<br>
Windows環境では、Visual Studio Build Tools または MinGW-w64 が必要である。<br>
* buildディレクトリ
Linux環境では、GCCと関連ツールが必要である。<br>
* distディレクトリ
# RHEL
*: 実行ファイル
sudo dnf install gcc gcc-c++
# SUSE
sudo zypper install gcc gcc-c++
<br>
<br>
<u>実行ファイルは、Pythonがインストールされていない環境でも実行できる。</u><br>
==== 基本的な使用方法 ====
# 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
<br>
Nuitkaは初回ビルド時に時間が掛かるが、生成される実行ファイルは一般的にPyInstallerよりも高速に動作する。<br>
<br><br>
<br><br>
== トラブルシューティング ==
==== ModuleNotFoundErrorが発生する ====
* 原因
*: PyInstallerが動的インポートや特定のライブラリのサブモジュールを検出できていない。
* 対処法
*: エラーメッセージに表示されているモジュールを <code>--hidden-import</code> オプションで追加する。
<br>
pyinstaller --onefile --hidden-import=missing_module main.py
<br>
よく問題になるライブラリと対処例を以下に示す。<br>
<center>
{| class="wikitable"
|+ Hidden Importが必要になることが多いライブラリ
|-
! ライブラリ !! 追加が必要なモジュールの例
|-
| pandas || pandas._libs.tslibs.timedeltas
|-
| PIL / Pillow || PIL._tkinter_finder
|-
| scikit-learn || --collect-submodules=sklearn
|}
</center>
<br>
==== ファイルが見つからないエラー ====
* 原因
*: データファイル (設定ファイル、画像等) が実行ファイルに含まれていない、または、パスの解決が正しくない。
* 対処法
*: <code>--add-data</code> オプションでファイルを正しく含めているか確認する。
*: ソースコード内で <code>get_resource_path()</code> メソッドを使用してパスを解決しているかどうかを確認する。
<br>
==== 実行ファイルのサイズが大きすぎる ====
対処法を以下に示す。<br>
<br>
* 仮想環境を使用して、必要なパッケージのみをインストールした環境でビルドする。
* specファイルの <u>excludes</u> リストに、使用していないモジュールを追加する。
* UPX圧縮を使用する。
*: もしUPXがインストールされていれば、PyInstallerは自動的に使用する。
*: <br>
*: UPXのインストール手順を以下に示す。
*: <syntaxhighlight lang="text">
# RHEL
sudo dnf install upx
# SUSE
sudo zypper install upx
</syntaxhighlight>
<br>
==== "GLIBC_X.XX not found" エラー ====
* 原因
*: ビルド環境のGLIBCのバージョンが、実行環境より新しい。
* 対処法
*: ターゲット環境と同じか、それより古いバージョンのLinuxでビルドする。
*: Dockerを使用して古い環境でビルドする方法も効果的である。
*: <br>
*: Dockerを使用したビルド方法を以下に示す。
*: <syntaxhighlight lang="text">
# 例: openSUSE Leap 15.1のDockerコンテナでビルド
docker run -it -v $(pwd):/app opensuse/leap:15.1 bash
# コンテナ内でPythonとPyInstallerをセットアップしてビルド
# ...略
</syntaxhighlight>
<br><br>
== 手法の選択指針 ==
<center>
{| class="wikitable"
|+ ツール選択の指針
|-
! 用途 !! 推奨ツール !! 備考
|-
| 通常の用途で手軽に実行ファイルを作成したい || PyInstaller || 豊富な情報があり、トラブルシューティングも容易
|-
| 実行速度を重視する || Nuitka || ビルド環境の準備とビルド時間が増加する
|-
| コードの難読化を強化したい || Nuitka || C言語へのコンパイルにより難読化される
|-
| クロスプラットフォーム対応が必要 || PyInstaller || 各OSの環境でそれぞれビルドを行う必要がある
|}
</center>
<br>
クロスプラットフォーム対応が必要な場合は、各OSの環境 (実機、VM、Docker、WSL2等) でそれぞれビルドを行う必要がある。<br>
CI/CDパイプライン (GitHub Actions等) を活用すると、複数プラットフォーム向けのビルドを自動化できる。<br>
<br><br>
== 外部リンク ==
* [https://pyinstaller.org/en/stable/ PyInstaller公式ドキュメント]
* [https://nuitka.net/ Nuitka公式サイト]
* [https://github.com/pyinstaller/pyinstaller PyInstaller GitHub]
<br><br>
{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Python,PyInstaller,Nuitka,exe,実行ファイル,Windows,Linux,RHEL,SUSE,openSUSE,パッケージング,配布,デプロイ
|description={{PAGENAME}} - PythonスクリプトをWindows exeやLinux実行ファイルに変換する方法 | This page is {{PAGENAME}} in our wiki about Python packaging and deployment
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}


__FORCETOC__
__FORCETOC__
[[カテゴリ:Python]]
[[カテゴリ:Python]]

2026年1月28日 (水) 15:38時点における最新版

概要

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.enginesrc.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によるビルドと実行時の環境要件
項目 説明
ビルド時 PyInstallerのビルドプロセス自体はコマンドラインで実行するため、ディスプレイサーバー (X11 / Wayland) は不要
実行時 使用するGUIツールキットに依存する


下表に、GUIツールキットのWayland対応状況を示す。

Python GUIツールキットのWayland対応状況 (2025年1月時点)
ツールキット 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


よく問題になるライブラリと対処例を以下に示す。

Hidden Importが必要になることが多いライブラリ
ライブラリ 追加が必要なモジュールの例
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等) を活用すると、複数プラットフォーム向けのビルドを自動化できる。


外部リンク