diff --git a/launcher.py b/launcher.py index 630b55b..b33c63b 100644 --- a/launcher.py +++ b/launcher.py @@ -5,16 +5,63 @@ from __future__ import annotations import argparse import multiprocessing import os +import shutil import sys from pathlib import Path +def _copy_missing_tree(src: Path, dst: Path) -> None: + if not src.exists(): + return + + for item in src.rglob("*"): + rel = item.relative_to(src) + target = dst / rel + + if item.is_dir(): + target.mkdir(parents=True, exist_ok=True) + continue + + if target.exists(): + continue + + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(item, target) + + +def _materialize_packaged_resources(base_dir: Path) -> None: + if not getattr(sys, "frozen", False): + return + + meipass = Path(getattr(sys, "_MEIPASS", "")) + if not meipass: + return + + packaged_configs = meipass / "configs" + target_configs = base_dir / "configs" + _copy_missing_tree(packaged_configs, target_configs) + + packaged_env_example = meipass / ".env.example" + target_env_example = base_dir / ".env.example" + if packaged_env_example.exists() and not target_env_example.exists(): + shutil.copy2(packaged_env_example, target_env_example) + + (base_dir / "logs" / "operation_logs").mkdir(parents=True, exist_ok=True) + + def prepare_environment() -> Path: - if getattr(sys, "frozen", False): + raw_base_dir = os.environ.get("CADHUB_BASE_DIR") + if raw_base_dir: + base_dir = Path(raw_base_dir).resolve() + elif getattr(sys, "frozen", False): base_dir = Path(sys.executable).resolve().parent else: base_dir = Path(__file__).resolve().parent + base_dir.mkdir(parents=True, exist_ok=True) + + _materialize_packaged_resources(base_dir) + os.environ.setdefault("CADHUB_BASE_DIR", str(base_dir)) os.chdir(base_dir) return base_dir @@ -50,8 +97,12 @@ def main() -> None: print("Reload mode requires workers=1, adjusting automatically.") args.workers = 1 + uvicorn_app = app + if args.reload or args.workers != 1: + uvicorn_app = "app.main:app" + uvicorn.run( - app, + uvicorn_app, host=args.host, port=args.port, reload=args.reload, diff --git a/scripts/CadHubManage.spec b/scripts/CadHubManage.spec index edaf5b3..5993fd5 100644 --- a/scripts/CadHubManage.spec +++ b/scripts/CadHubManage.spec @@ -5,7 +5,7 @@ a = Analysis( ['D:\\App\\python\\CadHubManage\\launcher.py'], pathex=[], binaries=[], - datas=[], + datas=[('D:\\App\\python\\CadHubManage\\configs', 'configs'), ('D:\\App\\python\\CadHubManage\\.env.example', '.')], hiddenimports=[], hookspath=[], hooksconfig={}, diff --git a/scripts/build_exe.py b/scripts/build_exe.py index 524bfb0..64d0548 100644 --- a/scripts/build_exe.py +++ b/scripts/build_exe.py @@ -24,22 +24,24 @@ def build_executable(*, clean: bool) -> Path: dist_dir = project_root / "dist" build_dir = project_root / "build" temp_dist = dist_dir / "tmp_pyinstaller" - bundle_dir = dist_dir / "CadHubManage" exe_name = "CadHubManage.exe" + output_exe = dist_dir / exe_name if clean: if build_dir.exists(): shutil.rmtree(build_dir) if temp_dist.exists(): shutil.rmtree(temp_dist) - if bundle_dir.exists(): - shutil.rmtree(bundle_dir) + if output_exe.exists(): + output_exe.unlink() ensure_pyinstaller() dist_dir.mkdir(parents=True, exist_ok=True) temp_dist.mkdir(parents=True, exist_ok=True) + entry_script = project_root / "launcher.py" + command = [ sys.executable, "-m", @@ -55,33 +57,31 @@ def build_executable(*, clean: bool) -> Path: str(build_dir), "--specpath", str(project_root / "scripts"), - str(project_root / "launcher.py"), ] - subprocess.check_call(command) + configs_src = project_root / "configs" + if configs_src.exists(): + command += ["--add-data", f"{configs_src}{os.pathsep}configs"] - bundle_dir.mkdir(parents=True, exist_ok=True) + env_example = project_root / ".env.example" + if env_example.exists(): + command += ["--add-data", f"{env_example}{os.pathsep}."] + + command.append(str(entry_script)) + + subprocess.check_call(command) built_exe = temp_dist / exe_name if not built_exe.exists(): raise FileNotFoundError(f"未找到 PyInstaller 生成的可执行文件: {built_exe}") - shutil.move(str(built_exe), str(bundle_dir / exe_name)) - - configs_src = project_root / "configs" - if configs_src.exists(): - shutil.copytree(configs_src, bundle_dir / "configs", dirs_exist_ok=True) - - env_example = project_root / ".env.example" - if env_example.exists(): - shutil.copy2(env_example, bundle_dir / ".env.example") - - log_dir = bundle_dir / "logs" - (log_dir / "operation_logs").mkdir(parents=True, exist_ok=True) + if output_exe.exists(): + output_exe.unlink() + shutil.move(str(built_exe), str(output_exe)) shutil.rmtree(temp_dist) - return bundle_dir / exe_name + return output_exe def parse_args() -> argparse.Namespace: @@ -94,7 +94,7 @@ def main() -> None: args = parse_args() exe_path = build_executable(clean=args.clean) print(f"构建完成: {exe_path}") - print("请将 dist/CadHubManage 目录整体分发到目标机器运行 CadHubManage.exe") + print("将 dist/CadHubManage.exe 单文件分发到目标机器即可运行") if __name__ == "__main__":