# 【pyinstaller】將Python程式打包成執行檔
> Lee Tsung Tang
>
###### tags: `python` `pyinstaller`
[TOC]
{%hackmd @88u1wNUtQpyVz9FsQYeBRg/r1vSYkogS %}
因為要開發一個桌面應用給別人使用,因此研究了一下打包python的方式。`pyinstaller`應該是其中最方便的工具。
# 重點
- How PyInstaller can simplify application distribution
- How to use PyInstaller on your own projects
# Introduction
一般來說如果要把程式給別人使用時,需要先安裝python、安裝套件、虛擬環境....之後才能開始使用,但`pyinstaller` 可以簡單的將python程式打包成執行檔,方便任何人在不同環境中使用。
- `pyInstaller` 會編譯python code並將所有依賴的套件打包,使用者不需要額外處理環境問題。
- `pyInstaller`可以包裝成 Windows(.exe), Linux(regular executable), or macOS(.app)的執行檔
# Using PyInstaller
install PyInstaller from `PyPI`.
```shell=
pip install pyinstaller
```
> 如果是純python dependencies的程式編譯成執行檔的機會更高,但多數時候`C`/`C++` extensions 也能編譯成功。
> 例如常見的`NumPy`, `PyQt5`, and `Matplotlib`等套件,`pyinstaller`都能支援。詳細的套件支援狀況可以參見[pyinstaller documentation](https://github.com/pyinstaller/pyinstaller/wiki/Supported-Packages)。
>即使不在支援列表上的套件,多數打包時不會有任何問題。
## 執行步驟
To try creating an executable with ==all the defaults==, simply give PyInstaller the name of your main entry-point script.
1. cd 到對應的資料夾
2. `$ pyinstaller your.py`
# Digging Into PyInstaller Artifacts
PyInstaller 編譯完會在指定路徑生成一大堆 output。對使用者來說大部分的output都不重要,只需要知道找到真正的執行檔就好,但為了對`pyinstaller`有更全面的了解,以下會簡單說明。
> 預設狀態下,`pyinstaller`會生成3個文件/檔案
- A *.spec file
- A build/ folder
- A dist/ folder
## Spec File
> spec file 預設以編譯的py檔檔名命名,例如`main.py` >> `main.spec`
>
:warning: 以下的例子都是編譯`main.py`的結果
This file can be ==modified and re-used to create executables later==. You can make future builds a bit faster by providing this spec file instead of the entry-point script to the pyinstaller command.
There are a few specific use-cases for PyInstaller spec files. However, for simple projects, *you won’t need to worry about those details unless you want to heavily customize how your project is built*.
## Build Folder
build folder的資料夾結構如下
```
build/
|
└── main/
├── Analysis-00.toc
├── base_library.zip
├── COLLECT-00.toc
├── EXE-00.toc
├── PKG-00.pkg
├── PKG-00.toc
├── PYZ-00.pyz
├── PYZ-00.toc
├── warn-main.txt
└── xref-main.html
```
> build folder主要用來儲存建構執行檔的metadata以及各種內部紀錄文件
>
>The build folder can be useful for **debugging**, but unless you have problems, this folder ==can largely be ignored==.
## Dist Folder
資料夾架構
```
dist/
|
└── main/
└── main
```
dict folder內會有一個以主程式為名的資料夾(此處主程式為`main.py`)。main folder內包執行檔(用以執行app)以及其他相依的檔案。
> The executable to run is ==dist/main/main or dist/main/main.exe if you’re on Windows==.
>
> 要執行app需要在發布時包含完整的dist/main folder
# Customizing Your Builds
簡單介紹幾個實用的參數
- -h, --help 查看可用的參數
```shell=
$ pyinstaller -h
```
<br/>
- -n, --name 將所有原本以主程式命名的檔案更名
```shell=
$ pyinstaller main.py --name APP
```
<br/>
- -F, --onefile 打包成單一檔案(只有一個執行檔)
```shell=
$ pyinstaller cli.py --onefile
```
With the above command, your dist/ folder will only contain a single executable instead of a folder with all the dependencies in separate files.
<br/>
- -w 使用視窗,無控制台
```shell=
$ pyinstaller main.py -w
```
<br/>
- -c, –console 使用控制台,無視窗
<br/>
- -i, –icon 可執行檔案的圖示
# RecursionError: maximum recursion depth exceeded
打包時很常遇到的error `RecursionError: maximum recursion depth exceeded`
解決方法是在spec檔最前面加上
```python=
import sys
sys.setrecursionlimit(5000)
```
限制遞迴的次數上限
接著以spec檔打包
```shell=
pyinstaller myapp.spec
```
<br/>
<br/>
<br/>
:::info
其他debug或無法編譯的問題可以參考下面的資料
:::
# 參考資料
[PyInstaller Manual](https://pyinstaller.readthedocs.io/en/stable/index.html)
[Using PyInstaller to Easily Distribute Python Applications](https://realpython.com/pyinstaller-python/#pyinstaller)
[【Python】將Python打包成exe檔](https://medium.com/pyladies-taiwan/python-%E5%B0%87python%E6%89%93%E5%8C%85%E6%88%90exe%E6%AA%94-32a4bacbe351)