当你执行 PyInstaller 时,PyInstaller 做的第一件事就是构建一个.spec文件;该文件存储在--specpath目录中,默认情况下就是当前目录。spec文件实际上是可执行的 Python 代码,PyInstaller通过执行spec文件来构建应用程序。

以该命令为例:

pyinstaller -F myscript.py

PyInstaller 会首先构建一个myscript.spec文件,它对你传递给pyinstaller命令的大部分选项进行编码;生成的myscript.spec文件内容:

# -*- mode: python ; coding: utf-8 -*-

a = Analysis(
    ['myscript.py'],
    pathex=[],
    binaries=[],
    datas=[],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
    optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.datas,
    [],
    name='myscript',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

一般情况下,你无需检查或修改 spec 文件中的内容;

pyi-makespec 用法

你可以使用pyi-makespec命令来创建 spec 文件;

该命令的用法与pyinstaller命令相同;但它只会生成 spec 文件,不会继续构建可执行文件;

参考:

pyi-makespac [options] name.py [other scripts ...]

创建 spec 文件并根据需求修改后,可以通过将 spec 文件传递给pyinstaller命令来构建程序;

pyinstaller [options] name.spec

创建 spec 文件时,大多数命令选项都会编码在 spec 文件中,如果在执行pyinstaller时指定了这些选项,将会被忽略;

从 spec 文件构建程序时,只有以下选项才有效:

  • --upx-dir
  • --distpath
  • --workpath
  • --noconfirm
  • --clean
  • --log-level

SPEC 文件详解

spec 文件主要使用了 4 个类:AnalysisPYZEXECOLLECT

Analysis:分析你的 Python 脚本依赖,识别需要打包的文件。

a = Analysis(
    ['myscript.py'],        # 主脚本,等同于 `pyinstaller myscript.py`
    pathex=[],              # 相当于 `--paths=DIR`,设置导入模块时搜索的额外路径
    binaries=[],            # 额外的二进制文件(如 DLL、.so),无对应命令行选项
    datas=[],               # 额外的数据文件,等同于 `--add-data`
    hiddenimports=[],       # 隐式导入模块,等同于 `--hidden-import`
    hookspath=[],           # 自定义 hook 路径,等同于 `--additional-hooks-dir`
    hooksconfig={},         # hook 行为配置项,暂无命令行参数对应
    runtime_hooks=[],       # 程序启动前执行的钩子脚本,等同于 `--runtime-hook`
    excludes=[],            # 要排除的模块,等同于 `--exclude-module`
    noarchive=False,        # 等同于 `--noarchive`,是否禁用 .pyz 打包
    optimize=0,             # Python 优化等级:0=默认,1=`-O`,2=`-OO`,等同于 `--optimize`
)

PYZ:创建一个基于 zlib 的 PYZ 档案,其中包含字节编译的纯 Python 模块;也就是将依赖的.pyc字节码文件打包为.pyz文件。

通常不需要手动更改;如果noarchive=True,将不会生成.pyz文件。

pyz = PYZ(a.pure)

EXE:生成最终的可执行文件

exe = EXE(
    pyz,                        # PYZ 包
    a.scripts,                  # 主脚本封装器
    [],                         # 其他 zip 文件(通常为空)
    exclude_binaries=True,      # 等同于 --onedir,推迟二进制文件打包
    name='myscript',            # 生成的可执行文件名,等同于 `--name`
    debug=False,                # 启用调试信息,等同于 `--debug`
    bootloader_ignore_signals=False, # 忽略信号(用于容器化环境)
    strip=False,                # 是否用 strip 命令减小体积,等同于 `--strip`
    upx=True,                   # 是否用 UPX 压缩,等同于 `--upx-dir` 自动启用
    console=True,               # 是否显示控制台,等同于 `--console`(CLI)或 `--windowed`(GUI)
    disable_windowed_traceback=False, # 是否关闭 GUI 出错弹窗,等同于 `--disable-windowed-traceback`
    argv_emulation=False,       # 仅用于 macOS,等同于 `--argv-emulation`
    target_arch=None,           # 架构目标,如 'x86_64',等同于 `--target-arch`
    codesign_identity=None,     # macOS 用于代码签名
    entitlements_file=None,     # macOS 安全许可配置文件
)

COLLECT:在单目录模式下才会使用,将可执行文件和依赖项打包到输出目录

coll = COLLECT(
    exe,
    a.binaries,
    a.datas,
    strip=False,           # 是否再对资源文件 strip(减小体积)
    upx=True,              # 是否对资源使用 UPX
    upx_exclude=[],        # 不压缩的文件(支持通配符)
    name='myscript',       # 输出目录名称,等同于 `--name`
)