win系统适配完成
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
GEO Tool — 跨平台打包构建脚本
|
||||
|
||||
用法:
|
||||
python build_scripts/build.py # 打包 Python 可执行文件
|
||||
python build_scripts/build.py --clean # 先清理旧构建再打包
|
||||
python build_scripts/build.py tauri # 打包 Python 可执行文件 + 构建 Tauri 桌面应用
|
||||
|
||||
输出:
|
||||
dist_python/geo_tool_app (macOS / Linux)
|
||||
dist_python/geo_tool_app.exe (Windows)
|
||||
src-tauri/target/release/GEO工具 (Tauri 桌面应用)
|
||||
|
||||
依赖:
|
||||
pip install pyinstaller
|
||||
cargo tauri build (for tauri 命令)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# ── 项目根目录 ────────────────────────────────────────────────
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
SPEC_FILE = PROJECT_ROOT / "geo_tool_app.spec"
|
||||
DIST_DIR = PROJECT_ROOT / "dist_python"
|
||||
BUILD_DIR = PROJECT_ROOT / "build_python"
|
||||
TAURI_DIR = PROJECT_ROOT / "src-tauri"
|
||||
|
||||
|
||||
def check_prerequisites():
|
||||
"""检查必要工具是否已安装"""
|
||||
# 检查 PyInstaller
|
||||
try:
|
||||
import PyInstaller # noqa: F401
|
||||
except ImportError:
|
||||
print("正在安装 PyInstaller ...")
|
||||
subprocess.check_call(
|
||||
[sys.executable, "-m", "pip", "install", "pyinstaller"],
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
|
||||
# 检查项目依赖是否已安装
|
||||
try:
|
||||
import streamlit # noqa: F401
|
||||
except ImportError:
|
||||
print("正在安装项目依赖 ...")
|
||||
# 优先用 uv,否则 pip install pyproject.toml
|
||||
for installer in ["uv", "pip"]:
|
||||
try:
|
||||
subprocess.check_call(
|
||||
[installer, "sync"] if installer == "uv" else [installer, "install", "."],
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
break
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
continue
|
||||
|
||||
|
||||
def clean_build():
|
||||
"""清理之前的构建产物"""
|
||||
for d in [DIST_DIR, BUILD_DIR]:
|
||||
if d.exists():
|
||||
print(f" 清理 {d} ...")
|
||||
shutil.rmtree(d)
|
||||
|
||||
# 清理 spec 生成的临时目录
|
||||
temp_spec_dir = PROJECT_ROOT / "__pycache__"
|
||||
if temp_spec_dir.exists():
|
||||
shutil.rmtree(temp_spec_dir)
|
||||
|
||||
|
||||
def build_tauri():
|
||||
"""先打包 Python 可执行文件,然后构建 Tauri 桌面应用"""
|
||||
build()
|
||||
print("\n正在构建 Tauri 桌面应用 ...\n")
|
||||
result = subprocess.run(
|
||||
["cargo", "tauri", "build"],
|
||||
cwd=str(TAURI_DIR),
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print(f"\n[错误] Tauri 构建失败 (返回码: {result.returncode})")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(" Tauri 构建完成!")
|
||||
release_dir = TAURI_DIR / "target" / "release"
|
||||
if sys.platform == "darwin":
|
||||
bundles = list(release_dir.glob("*.dmg")) + list((release_dir / "bundle" / "dmg").glob("*.dmg")) + list((release_dir / "bundle" / "macos").glob("*.app"))
|
||||
elif sys.platform == "win32":
|
||||
bundles = list(release_dir.glob("*.msi")) + list((release_dir / "bundle" / "msi").glob("*.msi"))
|
||||
else:
|
||||
bundles = list(release_dir.glob("GEO工具")) + list(release_dir.glob("*.AppImage"))
|
||||
for b in bundles:
|
||||
size_mb = b.stat().st_size / (1024 * 1024) if b.is_file() else sum(f.stat().st_size for f in b.rglob("*")) / (1024 * 1024)
|
||||
print(f" 输出: {b} ({size_mb:.1f} MB)")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
def build():
|
||||
"""执行 PyInstaller 打包"""
|
||||
# 确保 run_bundle.py 存在
|
||||
entry_point = PROJECT_ROOT / "run_bundle.py"
|
||||
if not entry_point.exists():
|
||||
print(f"[错误] 未找到入口文件: {entry_point}")
|
||||
sys.exit(1)
|
||||
|
||||
# 检查关键资源是否存在(给出警告而非中断)
|
||||
resources = [
|
||||
("geo_tool.py", "file"),
|
||||
("config.json", "file"),
|
||||
("geo_data.db", "file"),
|
||||
(".streamlit", "dir"),
|
||||
("modules", "dir"),
|
||||
]
|
||||
for path, _kind in resources:
|
||||
p = PROJECT_ROOT / path
|
||||
if not p.exists():
|
||||
print(f"[警告] 资源不存在: {p}")
|
||||
|
||||
# 构建命令
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"PyInstaller",
|
||||
str(SPEC_FILE),
|
||||
"--distpath",
|
||||
str(DIST_DIR),
|
||||
"--workpath",
|
||||
str(BUILD_DIR),
|
||||
"--clean",
|
||||
"--noconfirm",
|
||||
]
|
||||
|
||||
print("=" * 60)
|
||||
print(" GEO Tool — 打包构建")
|
||||
print("=" * 60)
|
||||
print(f" 平台: {sys.platform}")
|
||||
print(f" Python: {sys.version}")
|
||||
print(f" 入口: {entry_point}")
|
||||
print(f" Spec: {SPEC_FILE}")
|
||||
print(f" 输出目录: {DIST_DIR}")
|
||||
print("=" * 60)
|
||||
|
||||
# 执行打包
|
||||
result = subprocess.run(cmd, cwd=str(PROJECT_ROOT))
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"\n[错误] 打包失败 (返回码: {result.returncode})")
|
||||
print(f" 请检查 {BUILD_DIR}/ 下的日志文件获取详细错误信息。")
|
||||
sys.exit(1)
|
||||
|
||||
# 输出结果
|
||||
print("\n" + "=" * 60)
|
||||
print(" 打包完成!")
|
||||
print("=" * 60)
|
||||
|
||||
# 查找产物
|
||||
if sys.platform == "win32":
|
||||
output = DIST_DIR / "geo_tool_app.exe"
|
||||
elif sys.platform == "darwin":
|
||||
output = DIST_DIR / "geo_tool_app"
|
||||
else:
|
||||
output = DIST_DIR / "geo_tool_app"
|
||||
|
||||
if output.exists():
|
||||
size_mb = output.stat().st_size / (1024 * 1024)
|
||||
print(f" 输出文件: {output}")
|
||||
print(f" 文件大小: {size_mb:.1f} MB")
|
||||
else:
|
||||
# macOS 下可能生成 .app
|
||||
app_bundle = DIST_DIR / "geo_tool_app.app"
|
||||
if app_bundle.exists():
|
||||
size_mb = sum(f.stat().st_size for f in app_bundle.rglob("*")) / (1024 * 1024)
|
||||
print(f" 输出文件: {app_bundle}")
|
||||
print(f" 文件大小: {size_mb:.1f} MB")
|
||||
|
||||
print(f" 运行方式: 双击 {output.name} 或终端执行 {output}")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="GEO Tool 跨平台打包脚本",
|
||||
)
|
||||
parser.add_argument(
|
||||
"mode",
|
||||
nargs="?",
|
||||
default="python",
|
||||
choices=["python", "tauri"],
|
||||
help="打包模式: python (默认) 或 tauri (Python + 桌面壳)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--clean",
|
||||
action="store_true",
|
||||
help="构建前清理旧的 dist 和 build 目录",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip-deps",
|
||||
action="store_true",
|
||||
help="跳过依赖检查(仅安装 PyInstaller)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# 切换到项目根目录
|
||||
os.chdir(str(PROJECT_ROOT))
|
||||
|
||||
# 清理
|
||||
if args.clean:
|
||||
print("清理旧的构建产物 ...")
|
||||
clean_build()
|
||||
|
||||
# 检查环境
|
||||
if not args.skip_deps:
|
||||
check_prerequisites()
|
||||
|
||||
# 打包
|
||||
if args.mode == "tauri":
|
||||
build_tauri()
|
||||
else:
|
||||
build()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user