You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
9.0 KiB
235 lines
9.0 KiB
#!/usr/bin/env python |
|
# -*- coding: utf-8 -*- |
|
""" |
|
打包脚本:将 factory_sliceing 项目打包成 exe 文件 |
|
使用方法: python build_exe.py |
|
""" |
|
|
|
import os |
|
import sys |
|
import subprocess |
|
import shutil |
|
|
|
def build_exe(): |
|
"""使用 PyInstaller 打包项目""" |
|
|
|
# 获取当前脚本所在目录(factory_sliceing 目录) |
|
current_dir = os.path.dirname(os.path.abspath(__file__)) |
|
project_root = os.path.dirname(current_dir) # make2 目录 |
|
|
|
# 切换到 factory_sliceing 目录 |
|
os.chdir(current_dir) |
|
|
|
# PyInstaller 命令参数 |
|
main_script = os.path.join(current_dir, 'main.py') |
|
|
|
# 构建 PyInstaller 命令 |
|
cmd = [ |
|
'pyinstaller', |
|
'--name=factory_sliceing', # 生成的 exe 名称 |
|
'--onefile', # 打包成单个 exe 文件 |
|
'--console', # 显示控制台窗口(因为需要命令行参数) |
|
'--clean', # 清理临时文件 |
|
'--noconfirm', # 不询问确认 |
|
|
|
# 添加隐藏导入(如果 PyInstaller 无法自动检测) |
|
'--hidden-import=auto_sliceing_operate', |
|
'--hidden-import=auto_sliceing_operate.main_begin_sliceing', |
|
'--hidden-import=auto_sliceing_operate.main_download_zip', |
|
'--hidden-import=download_batch_data', |
|
'--hidden-import=download_batch_data.main_download_batch_data_and_trans', |
|
'--hidden-import=auto_sliceing_operate.utils', |
|
'--hidden-import=download_batch_data.utils', |
|
'--hidden-import=utils.config', |
|
|
|
# 配置文件解析模块 |
|
'--hidden-import=toml', |
|
|
|
# UI 自动化相关模块 |
|
'--hidden-import=uiautomation', |
|
|
|
# Windows API 相关模块(pywin32) |
|
# 注意:pywin32 需要特殊处理,使用 collect-all 确保包含所有 DLL 和模块 |
|
'--hidden-import=win32gui', |
|
'--hidden-import=win32con', |
|
'--hidden-import=win32api', |
|
'--hidden-import=win32process', |
|
'--hidden-import=win32timezone', |
|
'--hidden-import=win32clipboard', |
|
'--hidden-import=pywintypes', |
|
'--collect-all=pywin32', # 收集 pywin32 的所有模块和 DLL 文件 |
|
|
|
# 其他可能需要的模块 |
|
'--hidden-import=psutil', # 进程管理(可选,但有 try-except) |
|
|
|
# MinIO 客户端相关模块 |
|
'--hidden-import=minio', |
|
'--hidden-import=minio.error', |
|
'--hidden-import=minio.api', |
|
'--hidden-import=minio.commonconfig', |
|
'--hidden-import=minio.credentials', |
|
'--hidden-import=minio.deleteobjects', |
|
'--hidden-import=minio.helpers', |
|
'--hidden-import=minio.select', |
|
'--hidden-import=minio.signer', |
|
'--hidden-import=minio.time', |
|
'--collect-all=minio', # 收集 minio 的所有子模块和依赖 |
|
|
|
# 收集 uiautomation 的所有子模块(确保完整打包) |
|
'--collect-all=uiautomation', |
|
|
|
# 添加数据文件 |
|
# small_machine_transform.py 需要作为数据文件包含,因为它会被 Blender 作为外部脚本调用 |
|
# 注意:PyInstaller 会将数据文件放在 _MEIPASS 目录下 |
|
] |
|
|
|
# 添加 small_machine_transform.py 数据文件 |
|
small_transform_file = os.path.join(current_dir, "download_batch_data", "utils", "small_machine_transform.py") |
|
if os.path.exists(small_transform_file): |
|
# Windows 使用分号,Linux/Mac 使用冒号 |
|
path_sep = ';' if sys.platform == 'win32' else ':' |
|
# 添加到 utils/ 目录(get_resource_path('utils/small_machine_transform.py') 会查找这里) |
|
cmd.append(f'--add-data={small_transform_file}{path_sep}utils') |
|
# 也添加到 download_batch_data/utils/ 目录(备用路径) |
|
cmd.append(f'--add-data={small_transform_file}{path_sep}download_batch_data/utils') |
|
|
|
# 添加工作目录设置和主脚本 |
|
cmd.extend([ |
|
f'--workpath={os.path.join(current_dir, "build")}', |
|
f'--distpath={os.path.join(current_dir, "dist")}', |
|
f'--specpath={os.path.join(current_dir, "build")}', |
|
main_script |
|
]) |
|
|
|
print("=" * 60) |
|
print("开始打包 factory_sliceing 项目...") |
|
print("=" * 60) |
|
print(f"工作目录: {current_dir}") |
|
print(f"主脚本: {main_script}") |
|
print() |
|
|
|
try: |
|
# 检查 PyInstaller 是否安装 |
|
result = subprocess.run(['pyinstaller', '--version'], |
|
capture_output=True, text=True) |
|
if result.returncode != 0: |
|
print("错误: PyInstaller 未安装") |
|
print("请运行: pip install pyinstaller") |
|
return False |
|
|
|
print(f"PyInstaller 版本: {result.stdout.strip()}") |
|
print() |
|
|
|
# 执行打包命令 |
|
print("执行打包命令...") |
|
print(" ".join(cmd)) |
|
print() |
|
|
|
result = subprocess.run(cmd, check=True) |
|
|
|
if result.returncode == 0: |
|
print() |
|
print("=" * 60) |
|
print("打包成功!") |
|
print("=" * 60) |
|
exe_path = os.path.join(current_dir, 'dist', 'factory_sliceing.exe') |
|
if os.path.exists(exe_path): |
|
file_size = os.path.getsize(exe_path) / (1024 * 1024) # MB |
|
print(f"生成的 exe 文件: {exe_path}") |
|
print(f"文件大小: {file_size:.2f} MB") |
|
print() |
|
|
|
# 复制配置文件到 dist 目录(如果存在) |
|
config_file = os.path.join(current_dir, 'config.toml') |
|
dist_config = os.path.join(current_dir, 'dist', 'config.toml') |
|
if os.path.exists(config_file): |
|
try: |
|
shutil.copy2(config_file, dist_config) |
|
print(f"配置文件已复制到: {dist_config}") |
|
except Exception as e: |
|
print(f"警告: 复制配置文件失败: {e}") |
|
print(f"请手动将 config.toml 复制到 exe 同级目录: {os.path.dirname(exe_path)}") |
|
else: |
|
# 如果 config.toml 不存在,尝试复制示例文件 |
|
example_config = os.path.join(current_dir, 'config.example.toml') |
|
if os.path.exists(example_config): |
|
try: |
|
shutil.copy2(example_config, dist_config) |
|
print(f"配置文件示例已复制到: {dist_config}") |
|
print("请根据实际情况修改配置文件") |
|
except Exception as e: |
|
print(f"警告: 复制配置文件示例失败: {e}") |
|
else: |
|
print("提示: 请将 config.toml 放在 exe 同级目录") |
|
|
|
print() |
|
print("使用方法:") |
|
print(" factory_sliceing.exe <command> [work_dir]") |
|
print(" 可用命令: batch_download | begin_sliceing | download_zip") |
|
print() |
|
print("注意: 配置文件 config.toml 需要放在 exe 同级目录") |
|
return True |
|
else: |
|
print("打包失败!") |
|
return False |
|
|
|
except subprocess.CalledProcessError as e: |
|
print(f"打包过程中出现错误: {e}") |
|
return False |
|
except FileNotFoundError: |
|
print("错误: 找不到 PyInstaller") |
|
print("请先安装: pip install pyinstaller") |
|
return False |
|
except Exception as e: |
|
print(f"发生未知错误: {e}") |
|
return False |
|
|
|
def clean_build_files(): |
|
"""清理构建产生的临时文件""" |
|
current_dir = os.path.dirname(os.path.abspath(__file__)) |
|
|
|
dirs_to_remove = [ |
|
os.path.join(current_dir, 'build'), |
|
os.path.join(current_dir, '__pycache__'), |
|
] |
|
|
|
files_to_remove = [ |
|
os.path.join(current_dir, 'factory_sliceing.spec'), |
|
] |
|
|
|
print("清理临时文件...") |
|
for dir_path in dirs_to_remove: |
|
if os.path.exists(dir_path): |
|
try: |
|
shutil.rmtree(dir_path) |
|
print(f"已删除: {dir_path}") |
|
except Exception as e: |
|
print(f"删除 {dir_path} 失败: {e}") |
|
|
|
for file_path in files_to_remove: |
|
if os.path.exists(file_path): |
|
try: |
|
os.remove(file_path) |
|
print(f"已删除: {file_path}") |
|
except Exception as e: |
|
print(f"删除 {file_path} 失败: {e}") |
|
|
|
if __name__ == '__main__': |
|
import argparse |
|
|
|
parser = argparse.ArgumentParser(description='打包 factory_sliceing 项目为 exe') |
|
parser.add_argument('--clean', action='store_true', |
|
help='打包后清理临时文件') |
|
parser.add_argument('--clean-only', action='store_true', |
|
help='仅清理临时文件,不打包') |
|
|
|
args = parser.parse_args() |
|
|
|
if args.clean_only: |
|
clean_build_files() |
|
else: |
|
success = build_exe() |
|
if success and args.clean: |
|
print() |
|
clean_build_files() |
|
|
|
|