# -*- coding: utf-8 -*- r"""编译 Inno Setup 安装包。 做三件事: 1. 把三份文档合并成 license-combined.txt(Inno 的 LicenseFile 只认一个文件) 2. 把 .iss 里的版本号同步成当前版本 3. 调 ISCC.exe 编译 """ from __future__ import annotations import re import subprocess import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") INSTALLER = ROOT / "installer" ISS = INSTALLER / "ykxmz.iss" COMBINED = INSTALLER / "license-combined.txt" ISCC = Path(r"C:\Program Files (x86)\Inno Setup 6\ISCC.exe") DOCS = ("LICENSE.txt", "BEFORE-INSTALL.txt", "AFTER-INSTALL.txt") def current_version() -> str: text = (ROOT / "_build" / "package_remote.py").read_text(encoding="utf-8") match = re.search(r'VERSION\s*=\s*"([^"]+)"', text) return match.group(1) if match else "0.0.0" def build_license() -> None: """三份文档拼成一份,给 Inno 的许可页用。 每份之间加分隔线,免得读起来像一份文档。 ⚠ 这里**顺手把版本号注入进去**,不让它依赖源文档里写的是什么。 为什么:源文档里的版本号是硬编码的,实测 docs/LICENSE.txt 一直 停在 1.7.1(升级脚本只替换当时那个版本号,比如 1.8.2 -> 1.9.0,而 LICENSE.txt 里写的是 1.7.1,就永远匹配不上)。 结果安装时许可页上显示的是 1.7.1。 改成在这里统一注入之后,源文档写什么都不影响: 版本:X.Y.Z -> 版本:<当前版本> 软件:ykxmz X.Y.Z -> 软件:ykxmz <当前版本> ⚠ 只改**头部和落款**这两个格式,不改"八、版本记录"里的历史条目 —— 那是变更日志,本来就是一堆旧版本号。 """ version = current_version() parts = [] for index, name in enumerate(DOCS): path = ROOT / "docs" / name if not path.is_file(): print(f" [警告] 缺少 {name}") continue text = path.read_text(encoding="utf-8-sig").strip() # 把"版本:x.y.z"和"软件:ykxmz x.y.z"换成当前版本, # 但跳过"版本记录"那一段(历史日志)。 out_lines = [] in_history = False for line in text.splitlines(): stripped = line.strip() if "版本记录" in stripped: in_history = True if not in_history: if re.match(r"^版本:\d+\.\d+\.\d+$", stripped): line = line.replace(stripped, f"版本:{version}") elif re.match(r"^软件:ykxmz \d+\.\d+\.\d+$", stripped): line = line.replace(stripped, f"软件:ykxmz {version}") out_lines.append(line) text = "\n".join(out_lines) if index: parts.append("") parts.append("=" * 70) parts.append("") parts.append(text) body = "\n".join(parts) + "\n" # 装许可页的正文要 UTF-8 **带 BOM**,否则 Inno 在中文系统上会显示乱码 COMBINED.write_bytes(b"\xef\xbb\xbf" + body.encode("utf-8")) print(f" 合并三份文档 -> {COMBINED.name}" f"({len(body)} 字符,版本注入为 {version})") def sync_version(version: str) -> None: text = ISS.read_text(encoding="utf-8") new = re.sub(r'#define MyAppVersion "[^"]*"', f'#define MyAppVersion "{version}"', text) if new != text: ISS.write_text(new, encoding="utf-8", newline="\r\n") print(f" .iss 版本号 -> {version}") else: print(f" .iss 版本号已经是 {version}") def main() -> int: if not ISCC.is_file(): print(f" [失败] 找不到 Inno Setup: {ISCC}") return 1 version = current_version() print(f" 编译 ykxmz 安装包 v{version}") print() build_license() sync_version(version) agent = ROOT / "dist_exe" / "ykxmz" / "ykxmz.exe" if not agent.is_file(): print(f" [失败] 被控端还没构建:{agent}") print(" 先跑 windows\\build_exe.ps1") return 1 print() print(" 调 ISCC 编译…") proc = subprocess.run([str(ISCC), str(ISS)], capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=1800) out = (proc.stdout or "") + (proc.stderr or "") for line in out.splitlines(): line = line.strip() if line and ("Error" in line or "error" in line or "Successful" in line or "compil" in line.lower() or "warning" in line.lower()): print(f" {line}") if proc.returncode != 0: print(f" [失败] ISCC 退出码 {proc.returncode}") print(out[-1500:]) return 1 exe = ROOT / "dist" / f"ykxmz-setup-{version}.exe" if not exe.is_file(): print(f" [失败] 没生成 {exe}") return 1 size = exe.stat().st_size / 1024 / 1024 print() print(f" ✓ 安装包: {exe.name} {size:.2f} MB") return 0 if __name__ == "__main__": sys.exit(main())