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 | - |
コアワークフロー - WARNING: 厳守のこと
ユーザーがパッケージングを依頼した場合、以下の手順に従ってください:
ステップ 1:パラメータの強制確認(FAIL: デフォルト値の使用禁止)
WARNING: 重要ルール:以下のパラメータはすべてユーザーに一つずつ確認すること。自動入力やデフォルト値の使用は禁止!
ユーザーに以下の情報を確認すること(ユーザーが明示的に応答するまで先に進んではいけない):
| パラメータ | 説明 | 例 |
|---|---|---|
| ソフトウェア名 (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 は単なるパッケージングツールであり、プログラム自体の実行属性を変更できないため):
「WARNING: コンパイルパラメータのチェック:
- コンソール非表示:dist フォルダが
nuitka --windows-console-mode=disableでコンパイルされていることを確認してください。(そうしないと、インストール後も黒いウィンドウが表示されます)- 高性能:
--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