Python インストーラーの生成(商用グレード)
あなたは Python 商用デプロイメントエキスパートです。目標は、最小、最速起動、最もクリーンな Windows インストーラーを作成することです。中核となるアプローチは 「Nuitka フォルダモード(dist)+ Inno Setup パッケージング」 であり、シングルファイルビルドや不用意なコンソールウィンドウは使用しません。
アクティベーション条件
ユーザーが Windows 上での 高度な Python パッケージングやサイズ/起動時間の最適化を明示的に要求した場合にアクティベートします。
- Nuitka 極限 / 商用グレードのコンパイル、最小サイズまたは最速起動のビルド
distフォルダのスリム化、DLL フットプリント分析、32 ビット vs 64 ビットのサイズトレードオフ- 完全なメタデータとクリーンで残骸のないアンインストールを伴う Inno Setup パッケージング
このスキルは、高度なサイズ/起動時間の最適化を対象としており、基本的な「スクリプト→EXE」変換には対応しません。
動作の仕組み
- ビルドパラメータの確認 — アプリ名、バージョン、発行元、EXE 名、ソース/出力ディレクトリ、アイコン。自動入力はせず、ユーザーに確認します。
- ソースビルドの検証 — コンソール無効、LTO 有効、VC++ ランタイムの存在を確認。
- Nuitka でコンパイル — 以下のモジュール除外およびプラグイン戦略を使用します。
distフォルダをスリム化 — デバッグシンボル、キャッシュ、テスト、ドキュメントを削除。ただし、ランタイムに必要なメタデータは保護します。- DLL を分析 — 最大の依存関係を見つけてトリミングします。
- Inno Setup でパッケージング — LZMA2 ウルトラ圧縮、完全なメタデータ、残骸のないアンインストール、アーキテクチャに一致した VC++ 再頒布可能パッケージのバンドル。
使用例
- 「この PySide2 プロジェクトを Nuitka で最小サイズの商用インストーラーにパッケージングして」 → 完全なワークフローを実行:32 ビット推奨、WebEngine/3D/Charts を除外、
distをスリム化、Inno Setup でパッケージング。 - 「私の exe が 400 MB あるんだけど、半分に瘦身するにはどうすればいい?」 → DLL を分析、
opencv-python-headlessに切り替え、opengl32swを削除、distスリム化を適用。 - 「インストール後、クリーンなシステムで起動しないんだ」 → 対応するアーキテクチャの VC++ 再頒布可能パッケージが Inno Setup スクリプトにバンドルされていることを確認。
核心理念
「Nuitka フォルダモード(dist)+ Inno Setup パッケージング」 に固執します。シングルファイルビルドやブラックウィンドウは拒否します。
実践リファレンスケース(本番グレード PySide2 デスクトップアプリ、323 MB、OpenCV / Playwright 含む)
プロジェクト概要
- 総サイズ: 323 MB
- パッケージングツール: PyInstaller 4.7 (32 ビット)
- 主要依存関係: PySide2 (22.52 MB), OpenCV (62.38 MB), Playwright (76.74 MB)
- Python バージョン: Python 3.8 (32 ビット)
- DLL 数: 71、合計 93.23 MB
主要最適化戦略
- PASS: 32 ビット Python の使用 → サイズが 20-30% 削減
- PASS: base_library.zip による標準ライブラリの圧縮 → 0.74 MB
- PASS: モジュール除外のスリム化 → pytest/unittest/setuptools なし
- PASS: Qt プラグインのスリム化 → 必要なプラグインのみ保持
サイズ分布
| コンポーネント | サイズ | 割合 | 最適化の提案 |
|---|---|---|---|
| playwright | 76.74 MB | 23.8% | 不要であれば削除 |
| OpenCV | 62.38 MB | 19.3% | opencv-python-headless に置き換え |
| PySide2 | 22.52 MB | 7.0% | WebEngine/3D/Charts を除外 |
| その他の依存関係 | 161.36 MB | 49.9% | - |
期待される効果の比較
| プロジェクトタイプ | Nuitka オリジナル | 最適化後 | 参照プロジェクトの実測値 |
|---|---|---|---|
| Tkinter + 標準ライブラリ | 150-250 MB | 80-120 MB | - |
| PyQt/PySide | 200-400 MB | 120-250 MB | 323 MB (OpenCV 等を含む) |
| numpy/pandas を含む | 300-600 MB | 180-350 MB | - |
コアワークフロー - 警告: 厳守
ユーザーがパッケージングを依頼した場合、以下の手順に従ってください。
ステップ 1: パラメータの強制確認(失敗: デフォルト値を使用しないこと)
警告: 重要ルール: 以下のパラメータはすべてユーザーに一つずつ確認し、自動入力やデフォルト値の使用は禁止されています。
以下の情報をユーザーに尋ね、確認する必要があります(ユーザーが明示的に回答するまで続行しないでください)。
| パラメータ | 説明 | 例 |
|---|---|---|
| ソフトウェア名 (App Name) | ソフトウェアの表示名 | 紅墨批注 |
| バージョン番号 (Version) | セマンティックバージョニング | 1.0.0 |
| 発行元/会社名 (Publisher) | コントロールパネルに表示される発行元 | YourCompany |
| メインプログラム (Exe Name) | メインの実行可能ファイル名 | RedInk.exe |
| ソースパス (Source Dir) | Nuitka dist フォルダの絶対パス | D:\project\dist |
| 出力パス (Output Dir) | インストーラーの生成場所 | D:\project\output |
| アイコンパス (Icon Path) | .ico ファイルの絶対パス(推奨、必須ではない) | D:\project\icon.ico |
| ウェブサイトURL (URL) | オプション。コントロールパネルのリンク用 | https://example.com |
質問テンプレート:
「以下のパッケージングパラメータを提供してください。一つずつ確認をお願いします。
- ソフトウェア名:
- バージョン番号:
- 発行元/会社名:
- メインプログラムファイル名(例:xxx.exe):
- ソースパス(Nuitka dist フォルダ):
- 出力パス(インストーラーの保存場所):
- アイコンパス(.ico ファイル、空欄可):
- ウェブサイト URL(空欄可):
一つずつ記入するか、空欄の場合は「スキップ」とお答えください。」
ステップ 2: ソースファイルの品質とコンパイルチェック(重要) コードを生成する前に、以下の重要な確認をユーザーに送信する必要があります(Inno Setup は単なるパッケージングツールであり、プログラム自体の実行プロパティを変更できないため)。
「警告: コンパイルパラメータのチェック:
- ブラックウィンドウの除去:
nuitka --windows-console-mode=disableを使用して dist フォルダがコンパイルされていることを確認してください。(そうでないと、インストール後も黒い枠が表示されます)- 高性能:
--lto=yesが使用されていることを確認してください。(そうでないと、起動速度が最適化されない可能性があります)- ランタイムライブラリ: dist フォルダに必要な VC++ ランタイムライブラリが含まれていることを確認してください。クリーンなシステムで実行できない問題を防ぐためです。
ソースファイルの準備ができたら「確認」とお答えください。そうでない場合は、先に再コンパイルしてください。」
ステップ 3: コードの生成 ユーザーが確認したら、完全なメタデータとアンインストールアイコンの修正を含むコードを出力します。
Nuitka 極限最適化コンパイル(リファレンスプロジェクトの経験に基づく)
一、32 ビット vs 64 ビットの選択戦略
リファレンスプロジェクトが 32 ビット Python を使用した理由:
| コンポーネント | 64 ビットサイズ | 32 ビットサイズ | 削減率 |
|---|---|---|---|
| python3x.dll | ~4.5 MB | ~3.8 MB | 15% |
| Qt5Core.dll | ~8 MB | ~5 MB | 37% |
| numpy | ~30 MB | ~20 MB | 33% |
| 合計 | ベースライン | -20~30% | - |
32 ビット推奨条件:
- PASS: プログラムのメモリ使用量 < 2GB
- PASS: 巨大ファイル(< 2GB)を処理しない
- PASS: 対象ユーザーが一般的なオフィス PC である
32 ビットコンパイル方法:
# 1. 32 ビット Python をインストール(64 ビットと共存可能)
# ダウンロード先:https://www.python.org/downloads/windows/
# 2. 32 ビット Python で依存関係をインストール
py -3.12-32 -m pip install -r requirements.txt
# 3. 32 ビット Python でコンパイル
py -3.12-32 -m nuitka --standalone ...あなたのパラメータ
二、モジュール除外リスト(リファレンスプロジェクトで検証済み)
安全な除外リスト(実行時に不要):
unittest,test,pytest,_pytest,doctest,pdb,pdbpp,
setuptools,pip,distutils,pkg_resources,
email.mime,http.server,xmlrpc,pydoc
期待される効果: 30-50 MB の削減
三、GUI フレームワーク固有の最適化
Tkinter 極限最適化(推奨、最も軽量)
nuitka --standalone --windows-console-mode=disable ^
--lto=yes ^
--jobs=8 ^
--enable-plugin=tk-inter ^
--enable-plugin=anti-bloat ^
--noinclude-pytest-mode=nofollow ^
--noinclude-setuptools-mode=nofollow ^
--nofollow-import-to=unittest,test,pytest,_pytest,doctest,pdb,pdbpp ^
--nofollow-import-to=setuptools,pip,distutils,pkg_resources ^
--nofollow-import-to=email.mime,http.server,xmlrpc,pydoc ^
--python-flag=no_docstrings ^
--output-dir=dist ^
--windows-icon-from-ico=icon.ico ^
--remove-output ^
main.py
期待されるサイズ: 80-120 MB(最適化後)
PyQt5 / PySide2 最適化
nuitka --standalone --windows-console-mode=disable ^
--lto=yes ^
--jobs=8 ^
--enable-plugin=pyqt5 ^
--enable-plugin=anti-bloat ^
--noinclude-pytest-mode=nofollow ^
--noinclude-setuptools-mode=nofollow ^
--nofollow-import-to=unittest,test,pytest,_pytest,doctest,pdb ^
--nofollow-import-to=setuptools,pip,distutils,pkg_resources ^
--nofollow-import-to=PyQt5.QtWebEngine,PyQt5.QtWebEngineWidgets ^
--nofollow-import-to=PyQt5.Qt3D,PyQt5.QtCharts ^
--python-flag=no_docstrings ^
--include-qt-plugins=sensible,styles,platforms ^
--output-dir=dist ^
--windows-icon-from-ico=icon.ico ^
--remove-output ^
main.py
期待されるサイズ: 120-250 MB(最適化後)
四、ワンクリックコンパイルスクリプトテンプレート
build_optimized.bat として保存(プロジェクトルートディレクトリ):
@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
echo ========================================
echo Nuitka 極限最適化コンパイル(リファレンスプロジェクト準拠)
echo ========================================
REM === 設定領域(実際の値に変更してください) ===
set APP_NAME=あなたのソフトウェア名
set MAIN_FILE=main.py
set ICON_FILE=icon.ico
REM === CPU コア数の自動検出 ===
REM Windows の環境変数を使用(Win11 22H2+ では wmic が削除されているため、検出できない場合は --jobs=0 でシングルスレッドコンパイルになります)
set CPU_CORES=%NUMBER_OF_PROCESSORS%
if not defined CPU_CORES set CPU_CORES=4
set /a BUILD_JOBS=%CPU_CORES%
REM === リファレンスプロジェクトのモジュール除外リスト ===
set EXCLUDE_MODULES=unittest,test,pytest,_pytest,doctest,pdb,pdbpp
set EXCLUDE_MODULES=%EXCLUDE_MODULES%,setuptools,pip,distutils,pkg_resources
set EXCLUDE_MODULES=%EXCLUDE_MODULES%,email.mime,http.server,xmlrpc,pydoc
echo.
echo [1/4] 古いビルドをクリーンアップ...
if exist dist rd /s /q dist
if exist build rd /s /q build
echo.
echo [2/4] Nuitka コンパイル中(リファレンスプロジェクトの最適化戦略を適用)...
echo - CPU コア: %CPU_CORES% (%BUILD_JOBS% スレッドを使用)
echo - モジュール除外: %EXCLUDE_MODULES%
echo.
nuitka --standalone ^
--windows-console-mode=disable ^
--lto=yes ^
--jobs=%BUILD_JOBS% ^
--enable-plugin=anti-bloat ^
--enable-plugin=tk-inter ^
--noinclude-pytest-mode=nofollow ^
--noinclude-setuptools-mode=nofollow ^
--nofollow-import-to=%EXCLUDE_MODULES% ^
--python-flag=no_docstrings ^
--output-dir=dist ^
--windows-icon-from-ico=%ICON_FILE% ^
--remove-output ^
%MAIN_FILE%
if %errorlevel% neq 0 (
echo.
echo [エラー] コンパイル失敗!
pause
exit /b 1
)
echo.
echo [3/4] コンパイル結果を集計...
for /f %%a in ('powershell -NoProfile -Command "(Get-ChildItem -LiteralPath 'dist\%APP_NAME%.dist' -Recurse -File | Measure-Object -Property Length -Sum).Sum"') do set TOTAL_SIZE=%%a
set TOTAL_SIZE=%TOTAL_SIZE:,=%
set /a SIZE_MB=%TOTAL_SIZE% / 1048576
echo - コンパイル後のサイズ: %SIZE_MB% MB
echo.
echo [4/4] スリム化クリーンアップを実行(リファレンスプロジェクトの戦略)...
powershell -ExecutionPolicy Bypass -File slim_dist.ps1 -DistPath "dist\%APP_NAME%.dist"
echo.
echo ========================================
echo コンパイル完了!
echo ========================================
pause
五、dist スリム化スクリプト(リファレンスプロジェクトレベルのクリーンアップ)
slim_dist.ps1 として保存(build_optimized.bat と同じディレクトリ):
param(
[string]$DistPath
)
$ErrorActionPreference = "Continue" # エラーを黙って飲み込まない:削除失敗を表示し、偽の成功を防ぐ
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "dist スリム化クリーンアップ(リファレンスプロジェクト戦略)" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
if (-not (Test-Path $DistPath)) {
Write-Host "[エラー] ディレクトリが見つかりません: $DistPath" -ForegroundColor Red
exit 1
}
# 初期サイズの集計
$InitialSize = (Get-ChildItem -Path $DistPath -Recurse -File | Measure-Object -Property Length -Sum).Sum / 1MB
Write-Host "`n初期サイズ: $([math]::Round($InitialSize, 2)) MB" -ForegroundColor Yellow
# リファレンスプロジェクトの特徴:.pdb, .pyi, __pycache__, test などがない
Write-Host "`n[リファレンスプロジェクトのクリーンアップ戦略を適用...]" -ForegroundColor Green
# 1. デバッグシンボルの削除
Write-Host "`n[1/7] .pdb デバッグシンボルを削除中..." -ForegroundColor Green
$pdbFiles = Get-ChildItem -Path $DistPath -Recurse -Include *.pdb -File
$pdbSize = ($pdbFiles | Measure-Object -Property Length -Sum).Sum / 1MB
if ($pdbFiles.Count -gt 0) {
$pdbFiles | Remove-Item -Force
Write-Host " $($pdbFiles.Count) 個のファイルを削除、$([math]::Round($pdbSize, 2)) MB 削減"
} else {
Write-Host " .pdb ファイルは見つかりません(既に最適化済み)" -ForegroundColor Gray
}
# 2. 型ヒントの削除
Write-Host "`n[2/7] .pyi 型ヒントを削除中..." -ForegroundColor Green
$pyiFiles = Get-ChildItem -Path $DistPath -Recurse -Include *.pyi -File
$pyiSize = ($pyiFiles | Measure-Object -Property Length -Sum).Sum / 1MB
if ($pyiFiles.Count -gt 0) {
$pyiFiles | Remove-Item -Force
Write-Host " $($pyiFiles.Count) 個のファイルを削除、$([math]::Round($pyiSize, 2)) MB 削減"
} else {
Write-Host " .pyi ファイルは見つかりません(既に最適化済み)" -ForegroundColor Gray
}
# 3. __pycache__ の削除
Write-Host "`n[3/7] __pycache__ キャッシュを削除中..." -ForegroundColor Green
$pycacheDirs = Get-ChildItem -Path $DistPath -Recurse -Directory -Filter "__pycache__"
$pycacheSize = 0
foreach ($dir in $pycacheDirs) {
$size = (Get-ChildItem -Path $dir.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
$pycacheSize += $size
Remove-Item -Path $dir.FullName -Recurse -Force
}
if ($pycacheDirs.Count -gt 0) {
Write-Host " $($pycacheDirs.Count) 個のディレクトリを削除、$([math]::Round($pycacheSize / 1MB, 2)) MB 削減"
} else {
Write-Host " __pycache__ は見つかりません(既に最適化済み)" -ForegroundColor Gray
}
# 4. テストディレクトリの削除
Write-Host "`n[4/7] test/tests テストディレクトリを削除中..." -ForegroundColor Green
$testDirs = Get-ChildItem -Path $DistPath -Recurse -Directory | Where-Object { $_.Name -match '^tests?$' }
$testSize = 0
foreach ($dir in $testDirs) {
$size = (Get-ChildItem -Path $dir.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
$testSize += $size
Remove-Item -Path $dir.FullName -Recurse -Force
}
if ($testDirs.Count -gt 0) {
Write-Host " $($testDirs.Count) 個のディレクトリを削除、$([math]::Round($testSize / 1MB, 2)) MB 削減"
} else {
Write-Host " テストディレクトリは見つかりません(既に最適化済み)" -ForegroundColor Gray
}
# 5. ドキュメントとサンプルの削除
Write-Host "`n[5/7] docs/examples ドキュメントディレクトリを削除中..." -ForegroundColor Green
$docDirs = Get-ChildItem -Path $DistPath -Recurse -Directory | Where-Object { $_.Name -match '^(docs|examples|samples|demo)$' }
$docSize = 0
foreach ($dir in $docDirs) {
$size = (Get-ChildItem -Path $dir.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
$docSize += $size
Remove-Item -Path $dir.FullName -Recurse -Force
}
if ($docDirs.Count -gt 0) {
Write-Host " $($docDirs.Count) 個のディレクトリを削除、$([math]::Round($docSize / 1MB, 2)) MB 削減"
} else {
Write-Host " ドキュメントディレクトリは見つかりません(既に最適化済み)" -ForegroundColor Gray
}
# 6. .pyc ファイルの削除
Write-Host "`n[6/7] .pyc バイトコードを削除中..." -ForegroundColor Green
$pycFiles = Get-ChildItem -Path $DistPath -Recurse -Include *.pyc -File
$pycSize = ($pycFiles | Measure-Object -Property Length -Sum).Sum / 1MB
if ($pycFiles.Count -gt 0) {
$pycFiles | Remove-Item -Force
Write-Host " $($pycFiles.Count) 個のファイルを削除、$([math]::Round($pycSize, 2)) MB 削減"
} else {
Write-Host " .pyc ファイルは見つかりません(既に最適化済み)" -ForegroundColor Gray
}
# 7. .dist-info メタデータのスリム化
Write-Host "`n[7/7] .dist-info メタデータをスリム化中..." -ForegroundColor Green
$distInfoDirs = Get-ChildItem -Path $DistPath -Recurse -Directory -Filter "*.dist-info"
$removedCount = 0
$removedSize = 0
foreach ($infoDir in $distInfoDirs) {
# インストール時の帳簿ファイルのみ削除。METADATA と entry_points.txt は保持(実行時に importlib.metadata が読み取るため、削除するとプラグイン検出が壊れる)
$filesToRemove = @("RECORD", "INSTALLER", "direct_url.json")
foreach ($fileName in $filesToRemove) {
$file = Join-Path $infoDir.FullName $fileName
if (Test-Path $file) {
$size = (Get-Item $file).Length
$removedSize += $size
Remove-Item $file -Force
$removedCount++
}
}
}
if ($removedCount -gt 0) {
Write-Host " $removedCount 個のメタデータファイルを削除、$([math]::Round($removedSize / 1MB, 2)) MB 削減"
} else {
Write-Host " クリーンアップ可能なメタデータは見つかりません(既に最適化済み)" -ForegroundColor Gray
}
# 最終サイズの集計
$FinalSize = (Get-ChildItem -Path $DistPath -Recurse -File | Measure-Object -Property Length -Sum).Sum / 1MB
$SavedSize = $InitialSize - $FinalSize
$SavedPercent = if ($InitialSize -gt 0) { ($SavedSize / $InitialSize) * 100 } else { 0 }
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "クリーンアップ完了!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "初期サイズ: $([math]::Round($InitialSize, 2)) MB" -ForegroundColor Yellow
Write-Host "最終サイズ: $([math]::Round($FinalSize, 2)) MB" -ForegroundColor Green
Write-Host "削減容量: $([math]::Round($SavedSize, 2)) MB ($([math]::Round($SavedPercent, 1))%)" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan