ZB100ImageGenerator.spec
3.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# -*- mode: python ; coding: utf-8 -*-
"""
PyInstaller spec for ZB100ImageGenerator.
跨平台构建配置的唯一真相源 (macOS + Windows)。
build_mac_universal.sh / build_windows.bat 只负责环境准备,
实际构建调用 `pyinstaller ZB100ImageGenerator.spec`。
修复点:
- macOS 上 PIL/_imaging.so 依赖 @rpath/libtiff.6.dylib 等原生库,
PyInstaller 把 @rpath 改写成 @loader_path/.. (= Contents/Frameworks/),
因此 .dylibs/*.dylib 必须平铺到 bundle 根目录。
策略 (三重保险):
1. PyInstaller 官方工具 collect_dynamic_libs(destdir='.') —— 推荐做法
2. 显式枚举 PIL/.dylibs/ 和 PIL/.libs/ —— 兜底
3. 详细打印每一步收集结果 —— 下次出错直接看日志定位
"""
import sys
from pathlib import Path
from PyInstaller.utils.hooks import collect_dynamic_libs
IS_MAC = sys.platform == 'darwin'
IS_WIN = sys.platform == 'win32'
print('=' * 60)
print(f'[spec] 平台: {sys.platform}, Python: {sys.version.split()[0]}')
# ----- 图标 -----
if IS_MAC:
ICON = 'zb100_mac.icns'
elif IS_WIN:
ICON = 'zb100_windows.ico'
else:
ICON = None
# ----- 数据文件 -----
datas = [('config.json', '.')]
if IS_WIN:
datas.append(('zb100_windows.ico', '.'))
# ===== Pillow 原生库收集 =====
# 策略 1: PyInstaller 官方推荐
try:
libs_std = collect_dynamic_libs('PIL', destdir='.')
print(f'[spec] collect_dynamic_libs("PIL", destdir=".") 返回 {len(libs_std)} 项:')
for src, dst in libs_std:
print(f'[spec] STD {Path(src).name} -> {dst}')
except Exception as e:
libs_std = []
print(f'[spec] collect_dynamic_libs 异常: {e}')
# 策略 2: 显式枚举 PIL 包里的原生库目录
libs_extra = []
pil_dir = None
try:
import PIL
pil_dir = Path(PIL.__file__).parent
print(f'[spec] PIL 安装位置: {pil_dir}')
for sub in ('.dylibs', '.libs'):
d = pil_dir / sub
if d.is_dir():
print(f'[spec] 发现 {sub}/ 目录, 内容:')
for lib in sorted(d.iterdir()):
print(f'[spec] {lib.name} ({lib.stat().st_size} bytes)')
if lib.suffix.lower() in ('.dylib', '.so', '.dll'):
libs_extra.append((str(lib), '.'))
else:
print(f'[spec] {sub}/ 不存在')
except Exception as e:
print(f'[spec] 枚举 PIL 失败: {e}')
# 合并去重 (按源路径)
seen = set()
pil_native_libs = []
for src, dst in libs_std + libs_extra:
if src not in seen:
seen.add(src)
pil_native_libs.append((src, dst))
print(f'[spec] 最终 Pillow 原生库数量: {len(pil_native_libs)}')
for src, dst in pil_native_libs:
print(f'[spec] FINAL {Path(src).name} -> {dst}')
if IS_MAC and len(pil_native_libs) == 0:
print('[spec] !!! 警告: macOS 构建但未发现 Pillow 原生库, 打包产物必然启动失败 !!!')
print('=' * 60)
a = Analysis(
['image_generator.py'],
pathex=[],
binaries=pil_native_libs,
datas=datas,
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='ZB100ImageGenerator',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=[ICON] if ICON else None,
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='ZB100ImageGenerator',
)
if IS_MAC:
app = BUNDLE(
coll,
name='ZB100ImageGenerator.app',
icon=ICON,
bundle_identifier=None,
)