Skip to content
text
# Related Code
build.py             - PyInstaller 构建脚本
dmg_settings.py      - DMG 配置
Makefile             - 构建命令
pyproject.toml       - 依赖配置

构建系统

构建工具链

工具用途
PyInstaller打包 Python 为独立可执行文件
dmgbuild创建 macOS DMG 安装包
Makefile构建命令封装

PyInstaller 配置

build.py 主要逻辑

python
# build.py
def build_app():
    PyInstaller.__main__.run([
        'main.py',
        '--onedir',           # 目录模式(非单文件)
        '--windowed',         # GUI 应用,无控制台
        '--name', f'HF Model Downloader-{arch}',
        '--icon', icon_path,
        '--add-data', 'assets:assets',
    ])

为什么用 onedir 而非 onefile

模式优点缺点
onefile单文件分发启动慢(解压临时目录)
onedir启动快多文件

对于 GUI 应用,启动速度更重要。

资源打包

python
# --add-data 格式:源路径:目标路径
'--add-data', 'assets:assets'

运行时通过 resource_utils.py 获取正确路径:

python
# src/resource_utils.py
def get_resource_path(relative_path):
    if getattr(sys, 'frozen', False):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.dirname(__file__), '..', relative_path)

架构检测

python
# build.py
def get_architecture():
    arch = platform.machine().lower()
    if arch in ['arm64', 'aarch64']:
        return 'arm64'
    return 'x86_64'

产物命名包含架构:

  • HF Model Downloader-arm64.app
  • HF Model Downloader-x86_64.app

DMG 创建

dmg_settings.py

python
# dmg_settings.py
application = defines.get('app', 'HF Model Downloader.app')
appname = os.path.basename(application)

format = 'UDBZ'  # 压缩格式

files = [application]
symlinks = {'Applications': '/Applications'}

icon_locations = {
    appname: (140, 120),
    'Applications': (500, 120)
}

DMG 布局

┌────────────────────────────────────┐
│                                    │
│   [App Icon]     →    [Apps]       │
│                                    │
│      拖拽安装到 Applications        │
│                                    │
└────────────────────────────────────┘

Makefile 构建流程

make build

makefile
build:
    @$(PYTHON) build.py

执行 PyInstaller 打包。

make dmg

makefile
dmg: build
    @cd $(DIST_DIR) && \
    mv "*.app" "HF Model Downloader.app" && \
    dmgbuild -s settings.py "HF Model Downloader" "$(APP_NAME)-$(ARCH_NAME).dmg"
  1. 依赖 build 目标
  2. 重命名 .app(去掉架构后缀)
  3. 调用 dmgbuild 创建 DMG

完整构建流程

平台特定处理

macOS

python
# build.py
if sys.platform == 'darwin':
    icon_path = 'assets/icon.icns'

Windows

python
# build.py
if sys.platform == 'win32':
    icon_path = 'assets/icon.ico'
    # 额外处理 UTF-8 编码

图标格式

平台格式文件
macOS.icnsassets/icon.icns
Windows.icoassets/icon.ico
Linux.pngassets/icon.png

依赖处理

隐式导入

PyInstaller 可能遗漏动态导入的模块:

python
# build.py
hiddenimports = [
    'huggingface_hub',
    'modelscope',
    'tqdm',
]

排除不需要的模块

python
excludes = [
    'matplotlib',
    'scipy',
    'tkinter',
]

减少打包体积。

构建验证

bash
# 验证构建产物
make test-build

# 检查产物大小
ls -lh dist/

# 测试运行
open dist/HF\ Model\ Downloader-arm64.app

常见问题

Q: 构建后应用无法启动

检查点:

  1. 多进程初始化是否正确 (freeze_support())
  2. 资源路径是否使用 get_resource_path()
  3. 隐式导入是否完整

Q: 打包体积过大

优化方向:

  1. 排除未使用的模块
  2. 使用 UPX 压缩(可选)
  3. 检查数据文件大小

Q: DMG 创建失败

确保:

  1. .app 存在于 dist 目录
  2. dmgbuild 已安装
  3. macOS 系统(DMG 仅限 macOS)