#!/usr/bin/env python3 """ Kiro Proxy Cross-platform Build Script Supports: Windows / macOS / Linux Usage: python build.py # Build for current platform python build.py --all # Show all platform instructions """ import os import sys import shutil import subprocess from pathlib import Path from kiro_proxy import __version__ as VERSION APP_NAME = "KiroProxy" MAIN_SCRIPT = "run.py" ICON_DIR = Path("assets") def get_platform(): if sys.platform == "win32": return "windows" elif sys.platform == "darwin": return "macos" else: return "linux" def ensure_pyinstaller(): try: import PyInstaller print(f"[OK] PyInstaller {PyInstaller.__version__} installed") except ImportError: print("[..] Installing PyInstaller...") subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"], check=True) def clean_build(): for d in ["build", "dist", f"{APP_NAME}.spec"]: if os.path.isdir(d): shutil.rmtree(d) elif os.path.isfile(d): os.remove(d) print("[OK] Cleaned build directories") def build_app(): platform = get_platform() print(f"\n{'='*50}") print(f" Building {APP_NAME} v{VERSION} - {platform}") print(f"{'='*50}\n") ensure_pyinstaller() clean_build() args = [ sys.executable, "-m", "PyInstaller", "--name", APP_NAME, "--onefile", "--clean", "--noconfirm", ] icon_file = None if platform == "windows" and (ICON_DIR / "icon.ico").exists(): icon_file = ICON_DIR / "icon.ico" elif platform == "macos" and (ICON_DIR / "icon.icns").exists(): icon_file = ICON_DIR / "icon.icns" elif (ICON_DIR / "icon.png").exists(): icon_file = ICON_DIR / "icon.png" if icon_file: args.extend(["--icon", str(icon_file)]) print(f"[OK] Using icon: {icon_file}") # 添加资源文件打包 if (ICON_DIR).exists(): if platform == "windows": args.extend(["--add-data", f"{ICON_DIR};assets"]) else: args.extend(["--add-data", f"{ICON_DIR}:assets"]) print(f"[OK] Adding assets directory") # 添加文档文件打包 docs_dir = Path("kiro_proxy/docs") if docs_dir.exists(): if platform == "windows": args.extend(["--add-data", f"{docs_dir};kiro_proxy/docs"]) else: args.extend(["--add-data", f"{docs_dir}:kiro_proxy/docs"]) print(f"[OK] Adding docs directory") hidden_imports = [ "uvicorn.logging", "uvicorn.protocols.http", "uvicorn.protocols.http.auto", "uvicorn.protocols.http.h11_impl", "uvicorn.protocols.websockets", "uvicorn.protocols.websockets.auto", "uvicorn.lifespan", "uvicorn.lifespan.on", "httpx", "httpx._transports", "httpx._transports.default", "anyio", "anyio._backends", "anyio._backends._asyncio", ] for imp in hidden_imports: args.extend(["--hidden-import", imp]) args.append(MAIN_SCRIPT) args = [a for a in args if a] print(f"[..] Running: {' '.join(args)}\n") result = subprocess.run(args) if result.returncode == 0: if platform == "windows": output = Path("dist") / f"{APP_NAME}.exe" else: output = Path("dist") / APP_NAME if output.exists(): size_mb = output.stat().st_size / (1024 * 1024) print(f"\n{'='*50}") print(f" [OK] Build successful!") print(f" Output: {output}") print(f" Size: {size_mb:.1f} MB") print(f"{'='*50}") create_release_package(platform, output) else: print("[FAIL] Build failed: output file not found") sys.exit(1) else: print("[FAIL] Build failed") sys.exit(1) def create_release_package(platform, binary_path): release_dir = Path("release") release_dir.mkdir(exist_ok=True) if platform == "windows": archive_name = f"{APP_NAME}-{VERSION}-Windows" shutil.copy(binary_path, release_dir / f"{APP_NAME}.exe") shutil.make_archive( str(release_dir / archive_name), "zip", release_dir, f"{APP_NAME}.exe" ) (release_dir / f"{APP_NAME}.exe").unlink() print(f" Release: release/{archive_name}.zip") elif platform == "macos": archive_name = f"{APP_NAME}-{VERSION}-macOS" shutil.copy(binary_path, release_dir / APP_NAME) os.chmod(release_dir / APP_NAME, 0o755) shutil.make_archive( str(release_dir / archive_name), "zip", release_dir, APP_NAME ) (release_dir / APP_NAME).unlink() print(f" Release: release/{archive_name}.zip") else: archive_name = f"{APP_NAME}-{VERSION}-Linux" shutil.copy(binary_path, release_dir / APP_NAME) os.chmod(release_dir / APP_NAME, 0o755) shutil.make_archive( str(release_dir / archive_name), "gztar", release_dir, APP_NAME ) (release_dir / APP_NAME).unlink() print(f" Release: release/{archive_name}.tar.gz") def show_all_platforms(): print(f""" {'='*60} Kiro Proxy Cross-platform Build Instructions {'='*60} This script must run on the target platform. [Windows] Run on Windows: python build.py Output: release/KiroProxy-{VERSION}-Windows.zip [macOS] Run on macOS: python build.py Output: release/KiroProxy-{VERSION}-macOS.zip [Linux] Run on Linux: python build.py Output: release/KiroProxy-{VERSION}-Linux.tar.gz [GitHub Actions] Push to GitHub and Actions will build all platforms. See .github/workflows/build.yml {'='*60} """) if __name__ == "__main__": if "--all" in sys.argv or "-a" in sys.argv: show_all_platforms() else: build_app()