2025-12-13 2025-12-14

概要

PyInstaller や Nuitka、cxFreeze などPythonアプリをデスクトップアプリ化する手段はいくつがあるが、課題も多い。

  • 起動が遅すぎる
  • ライブラリの依存関係が変わる度にパラメータ調整が必要
  • そもそもビルドが通らない(DLL検出ミス地獄など)

そこで今回は、Inno Setup を使用してPythonアプリを配布する為の手順について記載する。
※配布対象のOSは Windows のみ

目次

Inno Setupとは

Inno Setupは Windowsアプリ用のインストーラーを簡単に作成できるオープンソースのフリーソフトウェア。

  • 商用・非商用問わず無料で利用可能
  • GUI(ウィザード形式)、CLIともサポート
  • Pascalで複雑な条件分岐やカスタマイズ可能

公式: https://jrsoftware.org/isinfo.php

Inno Setupを採用する事によるメリット/デメリット

メリット

  • 開発環境とほぼ同等の環境を作る事が出来る
  • Python処理自体をビルドしなくてよい
    • Nuitka や cxFreeze のようにビルドパラメータ調整が不要
    • そもそもビルドしないのでビルド失敗がない(DLL検出ミス地獄からの解放)
    • ビルド時間短縮
  • 起動が早い
    • PyInstallerやNuitkaの onefileでビルドした時のような一時ディレクトリへの展開がない為、起動が高速
  • 環境変数などの設定を柔軟に行う事が出来る
    • エントリポイントとしてバッチファイルを使用すれば、必要な設定をそこに書く事が出来る。
  • 組み合わせが可能
    • 部分的に CxFreeze で pyd(DDL)化したものを入れ込んだり
    • 他の言語(GoやRust)でビルドしたバイナリを内包したり
    • CodeMeterと組み合わせて使ったり

デメリット

  • サイズが少し大きくなる
  • インストーラ内での初期設定が少し面倒(pipなど)

インストール先のフォルダ構成

以下のようなフォルダ構成でインストールされるようにインストーラーを作成する。
※今回はアプリケーションフォルダ配下にPythonを内包する形とする。

アプリケーションフォルダ/
├─ python/             ... Python本体 及び ライブラリ
│  ├─ :
│  └─ :
├─ main.py
├─ run_app.bat
└─ その他リソース

開発用のリポジトリ構成

リポジトリ/
├─ app/                                       ... Pythonアプリケーション
│  ├─ main.py
│  ├─ requirements.txt
│  ├─ :
│  └─ run_app.bat                          ... アプリケーション起動用バッチ
├─ installer/
│  ├─ installer.iss                        ... Inno Setup定義
│  ├─ python-3.13.11-embed-amd64.zip       ... Embeddable Python (公式から取得)
│  ├─ get-pip.py                           ... pipの手動インストール用スクリプト
│  └─ wheels                               ... build_wheels.bat で作成
├─ tools/
│  └─ build_wheels.bat                     ... wheel をダウンロード/ビルドする為のバッチ
└─ .github/
   └─ workflows/
      └─ build-installer.yml            ... Github Action用のワークフロー

準備

Inno Setup のインストール

https://jrsoftware.org/isdl.php からダウンロードしてインストール
※今回は最新の 6.6.1 をインストール

Embeddable Python の取得

今回は完全オフラインに対応する為にインストーラー内にPython実行環境を内包する為、Embeddable Python(※) を事前にダウンロードしておく。
※解凍するだけで使える、ポータブルで軽量なPython実行環境
※python-3.13.11-embed-amd64.zip とか

https://www.python.org/downloads/windows/

get-pip.py の取得

pipを有効にする為に「pipを手動でインストールするためのスクリプト」を事前にダウンロードしておく(公式提供)
※Embeddable Python は そのままでは pip が使えない為

https://bootstrap.pypa.io/get-pip.py

wheelの準備

今回は完全オフラインで実行できるインストーラを作成する為、事前にPythonライブラリをダウンロードしておき、インストーラに内包する形を取る。
requirements.txt の内容から wheel/ 配下にライブラリをダウンロードするには以下の通り実行する。

python -m pip download --only-binary=:all: --platform win_amd64 --python-version 313 --implementation cp --abi cp313 -r requirements.txt -d wheels

※成功すると wheels 配下に各ライブラリの whl が作成される。( whl はそのライブラリの実行に必要なファイルを内包した zip ファイル )

ただし、wheel がないライブラリがあった場合、アプリケーションは動作しなくなる可能性がある為、ここでは wheel が無い場合にビルドして作成する為にバッチ化しておく。
※ビルド用の環境(MSVC Build Tools、Windows SDK、gcc など)がある事が前提

tools/build_wheels.bat

@echo off
cd /d "%~dp0"
setlocal enabledelayedexpansion

REM =====================================================
REM 設定
REM =====================================================
set PYTHON=python
set REQS=..\app\requirements.txt
set OUTDIR=..\installer\wheels

set PY_VER=313
set ABI=cp313
set PLATFORM=win_amd64
set IMPL=cp

REM =====================================================
REM 初期化
REM =====================================================
if not exist "%REQS%" (
  echo [ERROR] requirements.txt not found
  exit /b 1
)

if exist "%OUTDIR%" (
  echo [INFO] Cleaning existing wheels directory
  rmdir /s /q "%OUTDIR%"
)
mkdir "%OUTDIR%"

REM =====================================================
REM pip / setuptools / wheel のダウンロード
REM =====================================================
%PYTHON% -m pip download ^
  pip setuptools wheel ^
  -d "%OUTDIR%"


REM =====================================================
REM その他 wheelのダウンロード
REM =====================================================
%PYTHON% -m pip download ^
  --only-binary=:all: ^
  --platform %PLATFORM% ^
  --python-version %PY_VER% ^
  --implementation %IMPL% ^
  --abi %ABI% ^
  -r "%REQS%" ^
  -d "%OUTDIR%"

REM =====================================================
REM wheelがダウンロードできなかったライブラリを確認する
REM =====================================================
set MISSING=

for /f "usebackq delims==" %%A in ("%REQS%") do (
  set PKG=%%A
  set FOUND=0

  for %%W in ("%OUTDIR%\%%A-*.whl") do (
    set FOUND=1
  )

  if "!FOUND!"=="0" (
    echo [WARN] Wheel missing: %%A
    set MISSING=!MISSING! %%A
  )
)

REM =====================================================
REM wheelのビルド(wheelがなかったものだけ)
REM ※C++ビルド環境が必要
REM =====================================================
if not "%MISSING%"=="" (
  for %%P in (%MISSING%) do (
    echo [BUILD] %%P

    %PYTHON% -m pip wheel ^
      --no-deps ^
      %%P ^
      -w "%OUTDIR%"

    if errorlevel 1 (
      echo [ERROR] Failed to build wheel for %%P
      exit /b 1
    )
  )
) else (
  echo [INFO] All wheels were available prebuilt
)

REM =====================================================
REM 最終確認
REM =====================================================
if exist "%OUTDIR%\*.tar.gz" (
  echo [ERROR] sdist found in wheels directory
  dir "%OUTDIR%\*.tar.gz"
  exit /b 1
)

if exist "%OUTDIR%\*.zip" (
  echo [ERROR] unexpected zip found in wheels directory
  dir "%OUTDIR%\*.zip"
  exit /b 1
)

echo [SUCCESS] All dependencies are wheels only

REM =====================================================
REM 完了
REM =====================================================
endlocal
exit /b 0

wheelのダウンロードを実行(wheels配下にライブラリのwhlファイルがダウンロード または 作成される)

tools\build_wheels.bat

アプリケーション起動用バッチの作成

run_app.bat

@echo off
setlocal
cd /d "%~dp0"

REM アプリ専用 Python
set PYTHON=%CD%\python\pythonw.exe

if not exist "%PYTHON%" (
  echo Local Python not found.
  pause
  exit /b 1
)

"%PYTHON%" main.py

Inno Setup ファイル作成

Inno Setup の定義は iss というファイルにテキスト形式で記述する。
今回は以下の通り実行するインストーラーを作成する為の iss を作成する。

参考: https://jrsoftware.org/ishelp/index.php?topic=setupsection

  • [Files]
    • Embeddable Python の zip をコピー
    • get-pip.py のコピー
    • wheels のコピー
    • アプリケーションリソースのコピー
  • [Run]
    • Embeddable Python の zip を解凍
    • python313._pth を修正(site 有効化)
    • pip インストール(オフライン)
    • wheel から依存関係をインストール(オフライン)
    • インストール済みの wheelsを削除
    • インストール済みの Embeddable Python の zip を削除
    • アプリを起動
  • [Icons]
    • スタートメニューへのショートカット作成
    • デスクトップへのショートカット作成

installer.iss

; ==================================================
; オフラインで実行可能なインストーラー
; ==================================================

[Setup]
AppName=MyPythonApp
AppVersion=1.0.0
AppPublisher=MyCompany
DefaultDirName={localappdata}\MyPythonApp
DefaultGroupName=MyPythonApp
OutputBaseFilename=MyPythonAppInstaller
OutputDir=Output
Compression=lzma
SolidCompression=yes
PrivilegesRequired=lowest
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64

; ==================================================
; ファイルコピー
; ==================================================
[Files]
; Embeddable Python のコピー
Source: "python-3.13.11-embed-amd64.zip"; DestDir: "{app}";

; get-pipのコピー
Source: "get-pip.py"; DestDir: "{app}"

; wheel のコピー
Source: "wheels\*"; DestDir: "{app}\wheels"; Flags: recursesubdirs createallsubdirs

; アプリケーションのコピー
Source: "..\app\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs; Excludes: "__pycache__,*.pyc,.git\*,.venv\*"

; ==================================================
; ショートカット作成
; ==================================================
[Icons]
Name: "{group}\MyPythonApp"; Filename: "{cmd}"; Parameters: "/c start /min run_app.bat"; WorkingDir: "{app}"; IconFilename: "{app}\myapp.ico"
Name: "{userdesktop}\MyPythonApp"; Filename: "{cmd}"; Parameters: "/c start /min run_app.bat"; WorkingDir: "{app}"; IconFilename: "{app}\myapp.ico"


; ==================================================
; インストール
; ==================================================
[Run]
; 1) Python ZIP を展開
Filename: "powershell.exe"; \
Parameters: "-NoProfile -ExecutionPolicy Bypass -Command ""Expand-Archive -Force '{app}\python-3.13.11-embed-amd64.zip' '{app}\python'"""; \
StatusMsg: "Extracting Python runtime..."; \
Flags: waituntilterminated runhidden

; 2) python313._pth を修正(site 有効化)
Filename: "{cmd}"; \
Parameters: "/c echo import site>> ""{app}\python\python313._pth"""; \
StatusMsg: "Configuring Python..."; \
Flags: waituntilterminated runhidden

; 3) pip インストール(オフライン)
Filename: "{app}\python\python.exe"; \
Parameters: """{app}\get-pip.py"" --no-index --find-links ""{app}\wheels"""; \
StatusMsg: "Installing pip..."; \
Flags: waituntilterminated

; 4) wheel から依存関係をインストール(オフライン)
Filename: "{app}\python\python.exe"; \
Parameters: "-m pip install --no-index --find-links ""{app}\wheels"" -r ""{app}\requirements.txt"""; \
StatusMsg: "Installing dependencies (offline)..."; \
Flags: waituntilterminated

; 5) インストール済みのwheelsを削除
Filename: "{cmd}"; \
Parameters: "/c rmdir /s /q ""{app}\wheels"""; \
StatusMsg: "delete wheels..."; \
Flags: runhidden

; 6) インストール済みの embed python を削除
Filename: "{cmd}"; \
Parameters: "/c del /f /q ""{app}\python-3.13.11-embed-amd64.zip"""; \
StatusMsg: "delete embed python zip..."; \
Flags: runhidden

; 7) アプリを起動
Filename: "{cmd}"; \
Parameters: "/c start /min cmd /c ""{app}\run_app.bat"""; \
StatusMsg: "run my app..."; \
Description: "Run MyApp"; \
Flags: postinstall

; ==================================================
; アンインストール時の処理
; ==================================================
[UninstallRun]
Filename: "{cmd}"; \
Parameters: "/c rmdir /s /q ""{app}\python"""; \
RunOnceId: RemovePython

参考ページ

Flags の補足

セクションFlag意味使っている理由
Filesunzipzip 展開Embeddable Python
Filesdeleteafterinstall後で削除一時 exe
Filesrecursesubdirs再帰コピーwheels
Filescreateallsubdirs空ディレクトリ作成wheels/logs
Runwaituntilterminated完了待ちpip / venv
Runrunhidden非表示実行echo / quiet
Runrunasoriginaluser権限戻しlocalappdata
Runpostinstall完了後実行初回起動
Runskipifsilentsilent回避CI 対応

python313._pth について

このファイルは Embeddable Python にのみ含まれるファイルで、Python の起動時に使われるパス初期化ファイル。
これが存在する場合 Python は通常の挙動をほぼ全部やめてしまう。

Embeddable Python を解凍毎の初期状態では以下のようになっている。

python313._pth (デフォルト)

python313.zip
.
# Uncomment to run site.main() automatically
#import site

重要なのは最後の行で site をimportしないと pip が有効化されず、venv も使用できない状態になってしまう。

site がやっていること

  • Lib\site-packages を sys.path に追加
  • .pth ファイルの読み込み
  • pip, setuptools の有効化
  • venv の仕組みを成立させる

なので、pip 及び venv を有効化する為に、import site を有効化する必要がある。

python313._pth (変更後)

python313.zip
.
# Uncomment to run site.main() automatically
#import site
import site  # 有効化

インストーラの作成

CLIコマンドで作成
https://jrsoftware.org/ishelp/topic_compilercmdline.htm

iscc installer\installer.iss

補足(コンソールウィンドウを出したくない場合)

起動時にコンソールウィンドウを出したくない場合は、
以下のように小さなランチャーexeを用意してそこから起動する
※特にバッチで何もする事がないのであれば、直接 pythonw.exe 起動でも良いかも。

launcher.c

#include <windows.h>

int WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
) {
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;

    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    CreateProcess(
        NULL,
        "cmd /c \"run_app.bat\"",
        NULL, NULL,
        FALSE,
        CREATE_NO_WINDOW,
        NULL,
        NULL,
        &si,
        &pi
    );

    return 0;
}

ビルド( Build Tools for Visual Studio が必要)

cl launcher.c /O2

あとは installer.iss を以下の通り変更するだけ。

[Files]
Source: "launcher.exe"; DestDir: "{app}"

[Icons]
Name: "{group}\MyPythonApp"; Filename: "{app}\launcher.exe"
Name: "{userdesktop}\MyPythonApp"; Filename: "{app}\launcher.exe"

補足(Github Actions 用のワークフロー)

TODO: 動作確認

.github/workflows/build-installer.yml

name: Build Windows Installer (Offline Python, Wheels Included)

on:
  push:
    tags:
      - "v*"
  workflow_dispatch:

jobs:
  build:
    runs-on: windows-latest

    steps:
      # -------------------------------------------------
      # チェックアウト
      # -------------------------------------------------
      - name: Checkout repository
        uses: actions/checkout@v4

      # -------------------------------------------------
      # Pythonセットアップ
      # -------------------------------------------------
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.13"
          architecture: "x64"

      # -------------------------------------------------
      # pip & wheel アップグレード
      # -------------------------------------------------
      - name: Upgrade pip and wheel
        run: |
          python -m pip install --upgrade pip wheel

      # -------------------------------------------------
      # 依存ライブラリの wheel 取得
      # -------------------------------------------------
      - name: Build wheels (batch)
        run: |
          cd tools
          build_wheels.bat
          cd ..

      # -------------------------------------------------
      # Inno Setupのインストール
      # -------------------------------------------------
      - name: Install Inno Setup
        run: |
          choco install innosetup -y

      # -------------------------------------------------
      # インストーラーのビルド
      # -------------------------------------------------
      - name: Build installer
        run: |
          iscc installer\installer.iss

      # -------------------------------------------------
      # ビルド結果をアーティファクトとしてアップロード
      # -------------------------------------------------
      - name: Upload installer artifact
        uses: actions/upload-artifact@v4
        with:
          name: MyPythonAppInstaller
          path: installer\Output\*.exe

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2025-12-14 (日) 20:36:51 (41d)