新增 _env.py 共享工具与 dev/run/tauri_dev/build_server/build_tauri/build_docker/clean 脚本,
删除旧 build.{sh,ps1,bat}、根 run.bat/debug.bat/start.sh/dev.py、supervisord.conf、packaging/Dockerfile,
Docker 改以根 Dockerfile 为准并重写 compose(/data 卷)。
219 lines
7.3 KiB
Python
219 lines
7.3 KiB
Python
"""Tauri 桌面版编译脚本。
|
||
|
||
用法:
|
||
python scripts/build_tauri.py # 编译当前系统的桌面版
|
||
python scripts/build_tauri.py --platform windows # 指定平台(windows/linux/macos)
|
||
|
||
特性:
|
||
- Windows 安装包内置 WebView2 离线安装器(tauri.conf.json 已配置 offlineInstaller),
|
||
适合无外网、未预装 WebView2 Runtime 的机器。
|
||
- Windows NSIS 安装器默认安装到 D:\\Program Files\\CapacityReport(无 D 盘时回落系统盘)。
|
||
- 跨平台限制:Tauri 无法可靠跨系统交叉编译,--platform 必须与当前系统一致,否则会提示需在目标系统本机构建。
|
||
|
||
产物:dist/desktop/ 下的安装包(Windows: .msi/.exe;Linux: .deb/.AppImage;macOS: .dmg)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import json
|
||
import shutil
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||
import _env # noqa: E402
|
||
|
||
TAURI_CONF = _env.SRC_TAURI_DIR / "tauri.conf.json"
|
||
NSIS_HOOK = _env.SRC_TAURI_DIR / "windows" / "nsis-hooks.nsh"
|
||
|
||
INSTALLER_EXTS = {
|
||
"windows": (".msi", ".exe"),
|
||
"linux": (".deb", ".AppImage", ".rpm"),
|
||
"macos": (".dmg", ".app.tar.gz"),
|
||
}
|
||
|
||
|
||
# --------------------------------------------------------------------------- NSIS 定制(Windows 默认装 D 盘)
|
||
|
||
_RESTORE_LINE = " Call RestorePreviousInstallLocation"
|
||
|
||
_INSTALL_DIR_OVERRIDE = """ Call RestorePreviousInstallLocation
|
||
|
||
!if "${INSTALLMODE}" == "perMachine"
|
||
${If} ${FileExists} "D:\\*.*"
|
||
StrCpy $INSTDIR "D:\\Program Files\\${PRODUCTNAME}"
|
||
${Else}
|
||
${If} ${RunningX64}
|
||
StrCpy $INSTDIR "$PROGRAMFILES64\\${PRODUCTNAME}"
|
||
${Else}
|
||
StrCpy $INSTDIR "$PROGRAMFILES\\${PRODUCTNAME}"
|
||
${EndIf}
|
||
${EndIf}
|
||
!endif
|
||
"""
|
||
|
||
_ON_INIT_TAIL = """ !if "${INSTALLMODE}" == "both"
|
||
!insertmacro MULTIUSER_INIT
|
||
!endif
|
||
FunctionEnd"""
|
||
|
||
_ON_INIT_OVERRIDE = """ !if "${INSTALLMODE}" == "both"
|
||
!insertmacro MULTIUSER_INIT
|
||
!endif
|
||
|
||
!if "${INSTALLMODE}" == "perMachine"
|
||
${If} ${FileExists} "D:\\*.*"
|
||
StrCpy $INSTDIR "D:\\Program Files\\${PRODUCTNAME}"
|
||
${Else}
|
||
${If} ${RunningX64}
|
||
StrCpy $INSTDIR "$PROGRAMFILES64\\${PRODUCTNAME}"
|
||
${Else}
|
||
StrCpy $INSTDIR "$PROGRAMFILES\\${PRODUCTNAME}"
|
||
${EndIf}
|
||
${EndIf}
|
||
!endif
|
||
FunctionEnd"""
|
||
|
||
_RESTORE_FUNCTION = """Function RestorePreviousInstallLocation
|
||
ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" ""
|
||
StrCmp $4 "" +2 0
|
||
StrCpy $INSTDIR $4
|
||
FunctionEnd"""
|
||
|
||
_RESTORE_OVERRIDE = """Function RestorePreviousInstallLocation
|
||
!if "${INSTALLMODE}" == "perMachine"
|
||
${If} ${FileExists} "D:\\*.*"
|
||
StrCpy $INSTDIR "D:\\Program Files\\${PRODUCTNAME}"
|
||
Return
|
||
${EndIf}
|
||
!endif
|
||
|
||
ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" ""
|
||
StrCmp $4 "" +2 0
|
||
StrCpy $INSTDIR $4
|
||
FunctionEnd"""
|
||
|
||
|
||
def find_nsis_template() -> Path:
|
||
registry = Path.home() / ".cargo" / "registry" / "src"
|
||
if not registry.exists():
|
||
_env.die(f"未找到 Cargo registry:{registry}", "先运行一次 cargo tauri build 拉取 tauri-bundler。")
|
||
candidates = [
|
||
path
|
||
for path in registry.rglob("installer.nsi")
|
||
if "tauri-bundler-" in str(path) and path.parts[-4:] == ("bundle", "windows", "nsis", "installer.nsi")
|
||
]
|
||
if not candidates:
|
||
_env.die("未找到 Tauri NSIS 模板(installer.nsi)", "先运行一次 cargo tauri build 拉取 tauri-bundler。")
|
||
return sorted(candidates, reverse=True)[0]
|
||
|
||
|
||
def make_nsis_template(tmp_dir: Path) -> Path:
|
||
source = find_nsis_template()
|
||
content = source.read_text(encoding="utf-8")
|
||
|
||
import re
|
||
|
||
hook_path = str(NSIS_HOOK.resolve())
|
||
content, n = re.subn(
|
||
r'\{\{#if installer_hooks\}\}\s*!include "\{\{installer_hooks\}\}"\s*\{\{/if\}\}',
|
||
f'!include "{hook_path}"',
|
||
content,
|
||
)
|
||
if n == 0:
|
||
_env.die("Tauri NSIS 模板已变化:installer_hooks 标记未找到")
|
||
|
||
for marker, replacement, label in (
|
||
(_RESTORE_LINE, _INSTALL_DIR_OVERRIDE, "RestorePreviousInstallLocation 调用点"),
|
||
(_ON_INIT_TAIL, _ON_INIT_OVERRIDE, ".onInit 结尾"),
|
||
(_RESTORE_FUNCTION, _RESTORE_OVERRIDE, "RestorePreviousInstallLocation 函数"),
|
||
):
|
||
if marker not in content:
|
||
_env.die(f"Tauri NSIS 模板已变化:{label}标记未找到")
|
||
content = content.replace(marker, replacement)
|
||
|
||
target = tmp_dir / "tauri-nsis-installer.nsi"
|
||
target.parent.mkdir(parents=True, exist_ok=True)
|
||
target.write_text(content.replace("\r\n", "\n"), encoding="utf-8", newline="\n")
|
||
return target
|
||
|
||
|
||
def build_with_windows_nsis(tmp_dir: Path) -> None:
|
||
template = make_nsis_template(tmp_dir)
|
||
original = TAURI_CONF.read_text(encoding="utf-8")
|
||
try:
|
||
config = json.loads(original)
|
||
config["bundle"]["windows"]["nsis"]["template"] = str(template)
|
||
TAURI_CONF.write_text(json.dumps(config, ensure_ascii=False, indent=2), encoding="utf-8")
|
||
_env.remove_path(_env.SRC_TAURI_DIR / "target" / "release" / "bundle")
|
||
_env.step("编译 Tauri 桌面版(cargo tauri build)")
|
||
_env.run(["cargo", "tauri", "build"], cwd=_env.SRC_TAURI_DIR)
|
||
finally:
|
||
TAURI_CONF.write_text(original, encoding="utf-8")
|
||
|
||
|
||
def collect_installers(platform: str) -> None:
|
||
bundle_dir = _env.SRC_TAURI_DIR / "target" / "release" / "bundle"
|
||
out_dir = _env.DIST_DIR / "desktop"
|
||
_env.remove_path(out_dir)
|
||
out_dir.mkdir(parents=True, exist_ok=True)
|
||
exts = INSTALLER_EXTS[platform]
|
||
found = 0
|
||
for path in bundle_dir.rglob("*"):
|
||
if path.is_file() and path.name.endswith(exts):
|
||
shutil.copy2(path, out_dir / path.name)
|
||
found += 1
|
||
_env.info(f"安装包:{path.name}")
|
||
if found == 0:
|
||
_env.warn(f"未在 {bundle_dir} 找到安装包产物")
|
||
_env.step(f"桌面版完成:{out_dir}")
|
||
|
||
|
||
def main() -> int:
|
||
_env.setup_console()
|
||
parser = argparse.ArgumentParser(description="编译 Tauri 桌面版")
|
||
parser.add_argument(
|
||
"--platform",
|
||
choices=["current", "windows", "linux", "macos"],
|
||
default="current",
|
||
help="目标平台(默认当前系统)",
|
||
)
|
||
args = parser.parse_args()
|
||
|
||
host = _env.host_platform()
|
||
platform = host if args.platform == "current" else args.platform
|
||
if platform != host:
|
||
_env.die(
|
||
f"无法在 {host} 上交叉编译 {platform} 桌面版。",
|
||
f"Tauri 需要在目标系统本机构建,请在 {platform} 机器上运行本脚本。",
|
||
)
|
||
|
||
_env.ensure_rust()
|
||
python = _env.ensure_venv(extra_packages=["pyinstaller"])
|
||
_env.build_frontend(api_base=_env.DESKTOP_API_BASE)
|
||
|
||
tmp = _env.DIST_DIR / ".tmp"
|
||
_env.build_tauri_sidecar(python, tmp / "pyinstaller" / "work", tmp / "pyinstaller" / "dist")
|
||
_env.ensure_tauri_cli()
|
||
|
||
if platform == "windows":
|
||
build_with_windows_nsis(tmp)
|
||
else:
|
||
_env.remove_path(_env.SRC_TAURI_DIR / "target" / "release" / "bundle")
|
||
_env.step("编译 Tauri 桌面版(cargo tauri build)")
|
||
_env.run(["cargo", "tauri", "build"], cwd=_env.SRC_TAURI_DIR)
|
||
|
||
collect_installers(platform)
|
||
|
||
_env.step("清理中间产物")
|
||
_env.remove_path(tmp)
|
||
_env.remove_path(_env.FRONTEND_DIR / "dist")
|
||
_env.remove_path(_env.SRC_TAURI_DIR / "binaries")
|
||
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|