Python3.11(venv)+PySide6の環境でNuitkaを使用して実行ファイルを生成する

NuitkaについてPySide6を使用したツールを開発していた当時(今年の1月くらい)はPython3.11には対応してなかったのですが、久しぶりに見に行ったらPython3.11に実験的に対応との記述を見つけたため試してみました。

## Nuitkaインストール
`pip install Nuitka`

## 環境情報
```
> python --version
Python 3.11.0

> pip --version
pip 22.3 from [ワークスペースのパス]\venv\Lib\site-packages\pip (python 3.11)

> nuitka --version
1.5.8
Commercial: None
Python: 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)]
Flavor: CPython Official
Executable: [ワークスペースのパス]\venv\Scripts\python.exe
OS: Windows
Arch: x86_64
WindowsRelease: 10
Version C compiler: C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Tools\MSVC\14.16.27023\bin\HostX64\x64\cl.exe (cl 14.1).
```

## とりあえずビルド
参考にしたページや`--help`の情報より以下のようなコマンドを実行してみました
```
> nuitka --enable-plugin=pyside6 --standalone --onefile --follow-imports app.py
Nuitka-Options:INFO: Used command line options: --enable-plugin=pyside6 --standalone --onefile --follow-imports app.py
Nuitka:WARNING: The Python version '3.11' is only experimentally supported by Nuitka '1.5.8', but an upcoming release will change that. In the mean time use Python version '3.10' instead or newer Nuitka.
Nuitka-Options:INFO: Following all imports is the default for onefile mode and need not be specified.
Nuitka-Plugins:WARNING: pyside6: Make sure to use PySide 6.5.0 or higher, otherwise Qt slots won't work in all cases.
Nuitka:INFO: Starting Python compilation with Nuitka '1.5.8' on Python '3.11' commercial grade 'not installed'.
Nuitka-Plugins:INFO: pyside6: Injecting pre-module load code for module 'PySide6':
Nuitka-Plugins:INFO: pyside6:     Adding binary folder to runtime 'PATH' environment variable for proper Qt loading.
Nuitka-Plugins:INFO: implicit-imports: Injecting post-module load code for module 'PySide6.QtCore':
Nuitka-Plugins:INFO: implicit-imports:     According to Yaml configuration.
Nuitka-Plugins:INFO: pyside6: Injecting post-module load code for module 'PySide6.QtCore':
Nuitka-Plugins:INFO: pyside6:     Setting Qt library path to distribution folder. We need to avoid loading target
Nuitka-Plugins:INFO: pyside6:     system Qt plugins, which may be from another Qt version.
Nuitka-Plugins:INFO: options-nanny: Note, when using 'PySide6', consider using '--disable-console' option. Otherwise a terminal window will open. However for debugging, terminal output is the easiest way to see informative traceback information.
Nuitka-Plugins:INFO: multiprocessing: Injecting pre-module load code for module 'multiprocessing':
Nuitka-Plugins:INFO: multiprocessing:     Monkey patching "multiprocessing" load environment.
Nuitka-Plugins:INFO: multiprocessing: Injecting post-module load code for module 'multiprocessing':
Nuitka-Plugins:INFO: multiprocessing:     Monkey patching "multiprocessing" for compiled methods.
Nuitka-Plugins:WARNING: pyside6: Unwanted import of 'tkinter' that is redundant with 'PySide6' encountered. Use '--nofollow-import-to=tkinter' or uninstall it for best compatibility with pure Python execution.
Nuitka:INFO: Completed Python level compilation and optimization.
Nuitka:INFO: Generating source code for C backend compiler.
Nuitka:INFO: Running data composer tool for optimal constant value handling.                                 
Nuitka:INFO: Running C compilation via Scons.
Nuitka-Scons:INFO: Backend C compiler: cl (cl 14.1).
Nuitka-Scons:INFO: Backend linking program with 406 files (no progress information available).
Nuitka-Scons:INFO: Compiled 406 C files using clcache with 400 cache hits and 6 cache misses.
Nuitka-Plugins:INFO: data-files: Included data file 'certifi\cacert.pem' due to package data for 'certifi'.
Nuitka-Plugins:INFO: pyside6: Including Qt plugins 'iconengines,imageformats,platforms,styles,tls' below 'PySide6\qt-plugins'.
Nuitka-Plugins:INFO: dll-files: Found 1 file DLLs from cv2 installation.
Nuitka-Plugins:INFO: dll-files: Found 1 file DLLs from numpy installation.
Nuitka-Postprocessing:INFO: Creating single file from dist folder, this may take a while.
Nuitka-Onefile:INFO: Running bootstrap binary compilation via Scons.
Nuitka-Scons:INFO: Onefile C compiler: cl (cl 14.1).
Nuitka-Scons:INFO: Onefile linking program with 1 files (no progress information available).
Nuitka-Scons:INFO: Compiled 1 C files using clcache with 0 cache hits and 1 cache misses.
Nuitka-Onefile:INFO: Using compression for onefile payload.
Nuitka-Onefile:INFO: Onefile payload compression ratio (25.24%) size 226839475 to 57256271.
Nuitka-Onefile:INFO: Keeping onefile build directory 'app.onefile-build'.
Nuitka:INFO: Keeping dist folder 'app.dist' for inspection, no need to use it.
Nuitka:INFO: Keeping build directory 'app.build'.
Nuitka:INFO: Successfully created 'app.exe'.
```

ビルド自体は無事に終わり実行ファイルが生成できていました！
![nuitka-build01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384986/f44fdcfc-c72f-2eb6-533a-7013162c8e8b.png)
起動もできているようです。
ただし、上記のログの中にいくつかWarningが含まれていることを確認。

## Warning等に対処
```
Nuitka-Plugins:WARNING: pyside6: Make sure to use PySide 6.5.0 or higher, otherwise Qt slots won't work in all cases.
```
これは開発を行っていた当時のPySide6のバージョンが6.4.1だったことが原因でした。
PySide6を最新(6.5.0)にアップデートすることで解決できました。

```
Nuitka-Plugins:WARNING: pyside6: Unwanted import of 'tkinter' that is redundant with 'PySide6' encountered. Use '--nofollow-import-to=tkinter' or uninstall it for best compatibility with pure Python execution.
```
不要なインポートが行われているとのこと。
`--nofollow-import-to=tkinter`オプションを追加することでWarningが消え、生成される実行ファイルのサイズが少しだけ小さくなりました。

```
Nuitka-Plugins:INFO: options-nanny: Note, when using 'PySide6', consider using '--disable-console' option. Otherwise a terminal window will open. However for debugging, terminal output is the easiest way to see informative traceback information.
```
`--disable-console`オプションを付けることで実行時にターミナルが別途起動することを防ぐことができるようです。
ログは[logzero](https://pypi.org/project/logzero/)というライブラリを使用してファイルにも出力しているためこのオプションを使用しました。

## 最終的なコマンド
上記のWarningへの対処を経て最終的に以下のようなコマンドとなりました
```
> nuitka --enable-plugin=pyside6 --standalone --onefile --follow-imports --nofollow-import-to=tkinter --disable-console app.py
Nuitka-Options:INFO: Used command line options: --enable-plugin=pyside6 --standalone --onefile --follow-imports --nofollow-import-to=tkinter --disable-console app.py
Nuitka:WARNING: The Python version '3.11' is only experimentally supported by Nuitka '1.5.8', but an upcoming release will change that. In the mean time use Python version '3.10' instead or newer Nuitka.
Nuitka-Options:INFO: Following all imports is the default for onefile mode and need not be specified.
Nuitka:INFO: Starting Python compilation with Nuitka '1.5.8' on Python '3.11' commercial grade 'not installed'.
Nuitka-Plugins:INFO: pyside6: Injecting pre-module load code for module 'PySide6':
Nuitka-Plugins:INFO: pyside6:     Adding binary folder to runtime 'PATH' environment variable for proper Qt loading.
Nuitka-Plugins:INFO: implicit-imports: Injecting post-module load code for module 'PySide6.QtCore':
Nuitka-Plugins:INFO: implicit-imports:     According to Yaml configuration.
Nuitka-Plugins:INFO: pyside6: Injecting post-module load code for module 'PySide6.QtCore':
Nuitka-Plugins:INFO: pyside6:     Setting Qt library path to distribution folder. We need to avoid loading target
Nuitka-Plugins:INFO: pyside6:     system Qt plugins, which may be from another Qt version.
Nuitka-Plugins:INFO: multiprocessing: Injecting pre-module load code for module 'multiprocessing':
Nuitka-Plugins:INFO: multiprocessing:     Monkey patching "multiprocessing" load environment.
Nuitka-Plugins:INFO: multiprocessing: Injecting post-module load code for module 'multiprocessing':
Nuitka-Plugins:INFO: multiprocessing:     Monkey patching "multiprocessing" for compiled methods.
Nuitka:INFO: Completed Python level compilation and optimization.                                  
Nuitka:INFO: Generating source code for C backend compiler.
Nuitka:INFO: Running data composer tool for optimal constant value handling.                                 
Nuitka:INFO: Running C compilation via Scons.
Nuitka-Scons:INFO: Backend C compiler: cl (cl 14.1).
Nuitka-Scons:INFO: Backend linking program with 406 files (no progress information available).
Nuitka-Scons:INFO: Compiled 406 C files using clcache with 405 cache hits and 1 cache misses.
Nuitka-Plugins:INFO: data-files: Included data file 'certifi\cacert.pem' due to package data for 'certifi'.
Nuitka-Plugins:INFO: pyside6: Including Qt plugins 'iconengines,imageformats,platforms,styles,tls' below 'PySide6\qt-plugins'.
Nuitka-Plugins:INFO: dll-files: Found 1 file DLLs from cv2 installation.
Nuitka-Plugins:INFO: dll-files: Found 1 file DLLs from numpy installation.
Nuitka-Postprocessing:INFO: Creating single file from dist folder, this may take a while.
Nuitka-Onefile:INFO: Running bootstrap binary compilation via Scons.
Nuitka-Scons:INFO: Onefile C compiler: cl (cl 14.1).
Nuitka-Scons:INFO: Onefile linking program with 1 files (no progress information available).
Nuitka-Scons:INFO: Compiled 1 C files using clcache with 0 cache hits and 1 cache misses.
Nuitka-Onefile:INFO: Using compression for onefile payload.
Nuitka-Onefile:INFO: Onefile payload compression ratio (25.05%) size 223104715 to 55882630.
Nuitka-Onefile:INFO: Keeping onefile build directory 'app.onefile-build'.
Nuitka:INFO: Keeping dist folder 'app.dist' for inspection, no need to use it.
Nuitka:INFO: Keeping build directory 'app.build'.
Nuitka:INFO: Successfully created 'app.exe'.
```
先ほどのビルドで表示されていたWarningについて出力されなくなりました。
また、生成された`app.exe`を実行することで作成したツールが正常に動作することも確認できました。

![nuitka-build04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384986/edb0057a-94ed-ddf4-5f97-a2eb50bc01d9.png)
↑起動直後の初期設定画面

![nuitka-build05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384986/b89420e9-531d-3c86-15f3-642fab8c0bc3.png)
↑その後の切り抜きツール画面

## その他 - 除外設定
appという名前のpythonファイルを実行ファイル化する場合、`--standalone`や`--onefile`のオプションを付けることでカレントに
- `app.build`
- `app.dist`
- `app.onefile-build`
などのディレクトリと
- `app.exe`
が出来上がります。

プロジェクトの管理にGitを使っているため以下のような除外設定を行いました。
```.gitignore
# Nuitka
/app.build
/app.dist
/app.onefile-build
app.exe
```

## その他 - pyinstallerとの比較
これまで実行ファイルを作成するのに[pyinstaller](https://pypi.org/project/pyinstaller/)というライブラリを使ってきたので、そちらとの比較を行ってみることにしました。

以下のコマンドでインストール＆ビルド
```
> pip install pyinstaller
> pyinstaller app.py --onefile --noconsole
```

ビルドの際、Nuitkaとは異なるディレクトリにファイルを生成するのでその辺の除外設定を追加
```:.gitignore
# pyinstaller
/build
/dist
```

ビルドに掛かる時間の計測については以下のようなプログラムを作成してみました。
```python
> python
import subprocess
import time

start_time = time.time()
subprocess.run("pyinstaller app.py --onefile --noconsole", shell=True)
end_time = time.time()

print("実行時間:", end_time - start_time, "秒")

start_time = time.time()
subprocess.run("nuitka --enable-plugin=pyside6 --standalone --onefile --follow-imports --nofollow-import-to=tkinter --disable-console app.py", shell=True)
end_time = time.time()

print("実行時間:", end_time - start_time, "秒")

exit();
```

なお、生成した実行ファイルの起動時間の計測については良い案が思いつかず、[こちらの起動時間が計測できるというフリーソフト](http://www.vector.co.jp/soft/winnt/personal/se481044.html)を使用し画面中央に出てくるウィンドウを即閉じるという計測を行いました:cry:

### 計測結果
||Nuitka 1.5.8|pyinstaller 5.11.0|
|---|---|---|
|ビルドに掛かった時間| 347.59秒 | 71.06 秒 |
|app.exeの起動時間(おおよそ)| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384986/7299264a-0b39-8719-f01b-8ef35d5f00d3.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384986/4e1697fb-dcdc-7c0b-d028-172e846514f2.png) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384986/4a71dcde-9731-d989-fbff-37a37d4497a7.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384986/104ae69f-2124-cf67-edbd-c5c94ea8571e.png) |
|app.exeのファイルサイズ|53.4 MB|91.6 MB|

ビルドの時間とファイルサイズは結構な違いとなって出ました。
ビルドに掛かる時間について計測を行っている際にコンソール眺めていた感じでは、Nuitkaは主にonefile化するところで時間が掛かっているような印象を受けました。

## 参考にしたページ
- [Nuitka the Python Compiler &#8212; Nuitka the Python Compiler  documentation](https://nuitka.net/)
    - Nuitkaの公式サイト
- [Python 3.11 and Nuitka experimental support &#8212; Nuitka the Python Compiler  documentation](https://nuitka.net/posts/python311-experimental-support.html)
    - 上記公式のPython3.11の実験的サポートについての記事
- [Nuitka/Nuitka: Nuitka is a Python compiler written in Python.](https://github.com/Nuitka/Nuitka)
    - Nuitkaのリポジトリ
- [NuitkaでPythonプログラムを配布してみよう | インフォメーション・ディベロプメント](https://www.idnet.co.jp/column/page_245.html)
    - Nuitkaについて日本語で紹介しているページ
    - numpyに関するプラグインとかは2023/05現在は設定不要となったみたい？