forked from HaujetZhao/CapsWriter-Offline
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathzip_release.py
More file actions
301 lines (238 loc) · 9.16 KB
/
Copy pathzip_release.py
File metadata and controls
301 lines (238 loc) · 9.16 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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
打包脚本 - 使用 7zip 压缩 dist 目录中的构建产物
功能:
1. 打包 CapsWriter-Offline(服务端+客户端)
2. 打包 CapsWriter-Offline-Client(仅客户端)
3. 智能排除模型文件(.onnx, .dll, .json 等),但保留说明文档
"""
import os
import subprocess
from pathlib import Path
from datetime import datetime
def find_7zip():
"""查找 7zip 可执行文件"""
possible_paths = [
r"C:\Program Files\7-Zip\7z.exe",
r"C:\Program Files (x86)\7-Zip\7z.exe",
]
# 从 PATH 环境变量查找
for path in os.environ.get("PATH", "").split(os.pathsep):
possible_paths.append(os.path.join(path, "7z.exe"))
for path in possible_paths:
if os.path.exists(path):
return path
return None
def should_include_file(file_path, is_client_only=False):
"""
判断文件是否应该被打包
打包规则:
- 所有文件,除了 models/模型名/子目录/... 的内容
- models/模型名/文件 会被打包(层级深度 == 2)
- models/模型名/子目录/文件 不会被打包(层级深度 >= 3)
- 如果是【仅客户端】打包:
- 排除 core 目录下的所有 .dll 文件(客户端不需要本地识别引擎)
"""
path = Path(file_path)
parts = path.parts
# 1. 客户端特殊排除逻辑
if is_client_only:
# 排除 core 中的 dll 文件
if 'core' in parts and path.suffix.lower() == '.dll':
return False
# 2. 检查是否在 models 目录下
if 'models' not in parts:
return True # 非 models 目录,全部打包
# 找到 models 在路径中的位置
try:
models_index = parts.index('models')
except ValueError:
return True
# 排除 models 目录下的所有 .zip 文件(原始压缩包不打包)
if 'models' in parts and path.suffix.lower() == '.zip' or path.suffix.lower() == '.cfg':
return False
# models/模型名/子目录/... 的深度 >= 3 不打包
depth = len(parts) - models_index
if depth >= 4: # models/模型名/子目录/文件 或更深
return False
else: # models/模型名/文件 或更浅
return True
def create_file_list(dist_folder, output_file='file_list.txt', is_client_only=False):
"""
创建要打包的文件列表
7zip 使用 @参数从文件读取列表
每行一个文件路径(相对于 dist 父目录)
"""
files = []
# 遍历 dist 目录,收集所有要打包的文件
dist_path = Path(dist_folder)
if not dist_path.exists():
return files, None
for root, dirs, filenames in os.walk(dist_path):
# 排除不需要打包的文件夹
dirs[:] = [d for d in dirs if d not in ('__pycache__', '.vscode', '.git')]
for filename in filenames:
file_path = os.path.join(root, filename)
if should_include_file(file_path, is_client_only):
# 计算相对于 dist 父目录的路径
rel_path = os.path.relpath(file_path, dist_path.parent)
files.append(rel_path)
if not files:
return files, None
# 写入文件列表
list_file = Path(output_file)
list_file.write_text('\n'.join(files), encoding='utf-8')
return files, list_file
def package_with_7zip(source_dir, output_zip, file_list_file):
"""使用 7zip 打包目录"""
seven_zip = find_7zip()
if not seven_zip:
raise FileNotFoundError(
"找不到 7zip。请确认已安装 7-Zip。\n"
"下载地址: https://www.7-zip.org/"
)
source_path = Path(source_dir)
if not source_path.exists():
raise FileNotFoundError(f"源目录不存在: {source_dir}")
# 确保输出目录存在
output_path = Path(output_zip)
output_path.parent.mkdir(parents=True, exist_ok=True)
# 文件列表处理
# 文件列表在当前工作目录,需要从 dist 目录访问
dist_dir = source_path.parent
list_file_abs = Path(file_list_file).absolute()
list_file_rel_to_dist = os.path.relpath(list_file_abs, dist_dir)
# 构建 7zip 命令
# 使用 -tzip 创建 ZIP 格式(兼容性好)
# 使用 -mx9 最大压缩
# 使用 @file_list.txt 从文件读取要打包的文件列表
cmd = [
seven_zip,
'a', # 添加到压缩包
'-tzip', # ZIP 格式
'-mx9', # 最大压缩级别
str(output_path.absolute()), # 输出文件(绝对路径)
f'@{list_file_rel_to_dist}', # 从文件读取列表(相对于 dist 目录)
]
# 读取文件列表统计信息
with open(file_list_file, 'r', encoding='utf-8') as f:
files_count = len(f.readlines())
print(f"\n正在打包: {source_path.name}")
print(f"输出文件: {output_zip}")
print(f"打包文件数: {files_count}")
print(f"工作目录: {dist_dir.absolute()}")
# 执行压缩(从 dist 目录运行)
result = subprocess.run(
cmd,
cwd=str(dist_dir), # 从 dist 目录运行
capture_output=True,
text=True,
encoding='utf-8',
errors='ignore'
)
if result.returncode != 0:
print(f"\n错误: 7zip 执行失败")
print(f"STDOUT: {result.stdout}")
print(f"STDERR: {result.stderr}")
raise subprocess.CalledProcessError(result.returncode, cmd)
print("\n✅ 打包成功!")
# 显示压缩包信息
info_result = subprocess.run(
[seven_zip, 'l', str(output_path.absolute())],
capture_output=True,
text=True,
encoding='utf-8',
errors='ignore'
)
if info_result.returncode == 0:
# 解析文件数量和大小
lines = info_result.stdout.split('\n')
for line in lines:
if 'files' in line.lower() or '文件夹' in line or '文件' in line:
print(f"\n压缩包信息: {line.strip()}")
break
def main():
"""主函数"""
dist_dir = Path('dist')
# 检查 dist 目录
if not dist_dir.exists():
print(f"错误: dist 目录不存在")
print(f"请先运行 PyInstaller 构建: pyinstaller build.spec")
return
print("=" * 60)
print("CapsWriter-Offline 打包脚本")
print("=" * 60)
# 构建输出目录
release_dir = Path('release')
release_dir.mkdir(exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d")
# 打包配置列表
packages = []
# 检查 CapsWriter-Offline(服务端+客户端)
server_dist = dist_dir / 'CapsWriter-Offline'
if server_dist.exists():
packages.append({
'source': server_dist,
'output': release_dir / f'CapsWriter-Offline-{timestamp}.zip',
'name': '服务端+客户端'
})
# 检查 CapsWriter-Offline-Client(仅客户端)
client_dist = dist_dir / 'CapsWriter-Offline-Client'
if client_dist.exists():
packages.append({
'source': client_dist,
'output': release_dir / f'CapsWriter-Offline-Client-{timestamp}.zip',
'name': '仅客户端'
})
if not packages:
print(f"\n错误: dist 目录中没有找到构建产物")
print(f"请先运行 PyInstaller 构建:")
print(f" pyinstaller build.spec")
print(f" pyinstaller build-client.spec")
return
print(f"\n找到 {len(packages)} 个待打包的构建产物")
# 逐个打包
success_count = 0
for idx, pkg in enumerate(packages):
try:
print(f"\n{'=' * 60}")
print(f"打包: {pkg['name']}")
print(f"{'=' * 60}")
# 生成唯一的文件列表名(避免冲突)
list_file_name = f'file_list_{idx}.txt'
# 生成文件列表
is_client_only = pkg['source'].name == 'CapsWriter-Offline-Client'
files, list_file = create_file_list(pkg['source'], list_file_name, is_client_only)
if not files:
print(f"\n警告: 没有找到要打包的文件")
continue
print(f"文件列表: {list_file}")
# 打包
package_with_7zip(
pkg['source'],
pkg['output'],
list_file
)
success_count += 1
# 删除临时文件列表
try:
list_file.unlink()
print(f"已删除临时文件列表: {list_file}")
except Exception as cleanup_error:
print(f"警告: 无法删除临时文件列表 {list_file}: {cleanup_error}")
except Exception as e:
print(f"\n打包失败: {e}")
# 总结
print(f"\n{'=' * 60}")
print(f"打包完成: {success_count}/{len(packages)} 成功")
print(f"{'=' * 60}")
print(f"\n输出目录: {release_dir.absolute()}")
# 列出生成的文件
if success_count > 0:
print(f"\n生成的文件:")
for file in sorted(release_dir.glob('*.zip')):
size_mb = file.stat().st_size / (1024 * 1024)
print(f" {file.name} ({size_mb:.1f} MB)")
if __name__ == '__main__':
main()