File size: 6,116 Bytes
d3cadd5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | #!/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()
|