#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ GPCT-standalone 自动打包脚本 支持: Windows (PyInstaller) + NSIS 安装包生成 """ import os import sys import json import shutil import subprocess from datetime import datetime from pathlib import Path # 配置 PROJECT_DIR = Path("C:/PPRO/TG-PlatformPlus") VENV_DIR = PROJECT_DIR / "venv" DIST_DIR = PROJECT_DIR / "dist" BUILD_DIR = PROJECT_DIR / "build" SPEC_DIR = PROJECT_DIR / "spec" NSIS_SCRIPT = PROJECT_DIR / "GPCT-standalone.nsi" VERSION_FILE = PROJECT_DIR / "version.json" def run_command(cmd, cwd=None, check=True): """执行命令并返回结果""" print(f"执行: {cmd}") result = subprocess.run( cmd, shell=True, cwd=cwd or PROJECT_DIR, capture_output=True, text=True, encoding='utf-8' ) if result.returncode != 0 and check: print(f"[错误] 命令执行失败:") print(f"stdout: {result.stdout}") print(f"stderr: {result.stderr}") sys.exit(1) return result def activate_venv(): """激活虚拟环境""" venv_python = VENV_DIR / "Scripts" / "python.exe" if not venv_python.exists(): print(f"[错误] 未找到虚拟环境 Python: {venv_python}") sys.exit(1) return venv_python def clean_build(): """清理旧的构建文件""" print("\n[1/6] 清理旧的构建文件...") dirs_to_clean = [DIST_DIR, BUILD_DIR] for dir_path in dirs_to_clean: if dir_path.exists(): shutil.rmtree(dir_path) print(f" - 已删除: {dir_path}") def update_version(): """更新版本信息""" print("\n[2/6] 更新版本信息...") if VERSION_FILE.exists(): with open(VERSION_FILE, 'r', encoding='utf-8') as f: version_data = json.load(f) # 更新发布时间 version_data['releaseTime'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") with open(VERSION_FILE, 'w', encoding='utf-8') as f: json.dump(version_data, f, ensure_ascii=False, indent=4) print(f" - 版本: {version_data.get('version', 'N/A')}") print(f" - 发布时间: {version_data['releaseTime']}") else: print(f" [警告] 未找到版本文件: {VERSION_FILE}") def build_with_pyinstaller(): """使用 PyInstaller 打包""" print("\n[3/6] 使用 PyInstaller 打包...") python_exe = activate_venv() # 查找 spec 文件 spec_files = list(SPEC_DIR.glob("*.spec")) if not spec_files: print(f"[错误] 未找到 spec 文件在: {SPEC_DIR}") sys.exit(1) spec_file = spec_files[0] print(f" - 使用 spec 文件: {spec_file}") # 执行 PyInstaller cmd = f'"{python_exe}" -m PyInstaller "{spec_file}" --clean' result = run_command(cmd) if "completed successfully" in result.stdout or (DIST_DIR / "GPCT-standalone").exists(): print(" - PyInstaller 打包成功") else: print(f" [警告] 请检查输出: {result.stdout}") def copy_additional_files(): """复制额外文件到输出目录""" print("\n[4/6] 复制额外文件...") output_dir = DIST_DIR / "GPCT-standalone" / "_internal" # 创建 dataFile 目录 datafile_dir = output_dir / "dataFile" datafile_dir.mkdir(parents=True, exist_ok=True) # 复制 dataFile 文件 src_datafile = PROJECT_DIR / "dataFile" if src_datafile.exists(): for file in ["config.json", "data.db"]: src = src_datafile / file if src.exists(): shutil.copy2(src, datafile_dir / file) print(f" - 已复制: {file}") # 复制加密狗 DLL dog_dll = PROJECT_DIR / "Syunew3D_x64.dll" if dog_dll.exists(): shutil.copy2(dog_dll, output_dir / "Syunew3D_x64.dll") print(f" - 已复制: Syunew3D_x64.dll") # 复制 influxd.exe influxd = PROJECT_DIR / "influxd.exe" if influxd.exists(): shutil.copy2(influxd, output_dir / "influxd.exe") print(f" - 已复制: influxd.exe") def build_nsis_installer(): """构建 NSIS 安装包""" print("\n[5/6] 构建 NSIS 安装包...") if not NSIS_SCRIPT.exists(): print(f" [跳过] 未找到 NSIS 脚本: {NSIS_SCRIPT}") return # 查找 makensis makensis_paths = [ r"C:\Program Files (x86)\NSIS\makensis.exe", r"C:\Program Files\NSIS\makensis.exe", ] makensis = None for path in makensis_paths: if os.path.exists(path): makensis = path break if not makensis: # 尝试从环境变量查找 result = subprocess.run("where makensis", shell=True, capture_output=True, text=True) if result.returncode == 0: makensis = result.stdout.strip().split('\n')[0] if not makensis: print(" [跳过] 未找到 NSIS (makensis.exe),请安装 NSIS") return print(f" - 使用 NSIS: {makensis}") # 执行 NSIS 编译 cmd = f'"{makensis}" "{NSIS_SCRIPT}"' result = run_command(cmd, check=False) if result.returncode == 0: print(" - NSIS 安装包构建成功") # 查找生成的安装包 installer = PROJECT_DIR / "GPCT-standalone-Setup.exe" if installer.exists(): print(f" - 安装包位置: {installer}") else: print(f" [警告] NSIS 构建可能失败: {result.stderr}") def verify_build(): """验证构建结果""" print("\n[6/6] 验证构建结果...") exe_path = DIST_DIR / "GPCT-standalone" / "GPCT-standalone.exe" if exe_path.exists(): size = exe_path.stat().st_size / (1024 * 1024) # MB print(f" - 可执行文件: {exe_path}") print(f" - 文件大小: {size:.2f} MB") # 检查关键文件 internal_dir = DIST_DIR / "GPCT-standalone" / "_internal" critical_files = [ "config.json", "data.db", "Syunew3D_x64.dll" ] print(" - 关键文件检查:") for file in critical_files: file_path = internal_dir / file if file_path.exists(): print(f" [OK] {file}") else: print(f" [缺失] {file}") return True else: print(f" [错误] 未找到可执行文件: {exe_path}") return False def main(): """主函数""" print("=" * 50) print(" GPCT-standalone 自动打包脚本") print("=" * 50) # 检查项目目录 if not PROJECT_DIR.exists(): print(f"[错误] 项目目录不存在: {PROJECT_DIR}") sys.exit(1) # 执行构建步骤 clean_build() update_version() build_with_pyinstaller() copy_additional_files() build_nsis_installer() success = verify_build() print("\n" + "=" * 50) if success: print(" 打包完成!") print(f" 输出目录: {DIST_DIR / 'GPCT-standalone'}") else: print(" 打包过程中出现错误") print("=" * 50) return 0 if success else 1 if __name__ == "__main__": sys.exit(main())