245 lines
8.8 KiB
Python
245 lines
8.8 KiB
Python
import sys
|
|
import subprocess
|
|
import venv
|
|
import shutil
|
|
import argparse
|
|
from pathlib import Path
|
|
import platform
|
|
|
|
# 配置常量
|
|
VENV_DIR = Path("venv")
|
|
system = platform.system().lower()
|
|
if system == "windows":
|
|
SHARE_PATH = Path(r"\\lfs.tpshos\dpi-sync\autool_config")
|
|
elif system == "darwin":
|
|
SHARE_PATH = Path(r"/Volumes/share/autool_config")
|
|
else:
|
|
SHARE_PATH = None # 非 Windows/macOS 系统不使用网络共享
|
|
|
|
def run_command(cmd):
|
|
"""封装子进程调用"""
|
|
print(f"Running: {' '.join(map(str, cmd))}")
|
|
try:
|
|
subprocess.check_call(cmd)
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"Error executing command: {e}")
|
|
sys.exit(1)
|
|
|
|
def download_file(src_path, dst_path):
|
|
"""封装文件下载/拷贝逻辑"""
|
|
if src_path.exists():
|
|
dst_path.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy(src_path, dst_path)
|
|
print(f"Downloaded: {src_path.name} to {dst_path}")
|
|
else:
|
|
print(f"Warning: Source file not found at {src_path}")
|
|
|
|
def get_venv_python():
|
|
"""获取虚拟环境中的 Python 解释器路径 (兼容 Windows/macOS/Linux)"""
|
|
if sys.platform == "win32":
|
|
return VENV_DIR / "Scripts" / "python.exe"
|
|
return VENV_DIR / "bin" / "python"
|
|
|
|
def parse_args():
|
|
"""解析命令行参数"""
|
|
parser = argparse.ArgumentParser(
|
|
description="Setup development environment for autool",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
python setup.py # Install Android dependencies (default)
|
|
python setup.py --offline # Install without network share / LFS
|
|
python setup.py --android # Install Android dependencies explicitly
|
|
python setup.py --ios # Install iOS dependencies
|
|
python setup.py --all # Install all platform dependencies
|
|
"""
|
|
)
|
|
platform_group = parser.add_mutually_exclusive_group()
|
|
platform_group.add_argument('--android', action='store_true',
|
|
help='Install Android dependencies (default)')
|
|
platform_group.add_argument('--ios', action='store_true',
|
|
help='Install iOS dependencies')
|
|
platform_group.add_argument('--web', action='store_true',
|
|
help='Install Web dependencies')
|
|
platform_group.add_argument('--all', action='store_true',
|
|
help='Install all platform dependencies')
|
|
parser.add_argument('--offline', action='store_true',
|
|
help='Offline mode: skip network share and LFS pull operations')
|
|
return parser.parse_args()
|
|
|
|
def get_requirements_files(args):
|
|
"""根据命令行参数返回需要安装的依赖文件列表"""
|
|
base_dir = Path("doc")
|
|
|
|
# 总是安装公共依赖(包含 DroidBot 核心依赖)
|
|
files = [base_dir / "requirements-common.txt"]
|
|
|
|
if args.all:
|
|
# 安装所有平台依赖
|
|
files.extend([
|
|
base_dir / "requirements-android.txt",
|
|
base_dir / "requirements-ios.txt",
|
|
base_dir / "requirements-web.txt"
|
|
])
|
|
platform_name = "All platforms"
|
|
elif args.ios:
|
|
# 仅安装 iOS 依赖
|
|
files.append(base_dir / "requirements-ios.txt")
|
|
platform_name = "iOS"
|
|
elif args.web:
|
|
# 仅安装 Web 依赖
|
|
files.append(base_dir / "requirements-web.txt")
|
|
platform_name = "Web"
|
|
else:
|
|
# 默认安装 Android 依赖
|
|
files.append(base_dir / "requirements-android.txt")
|
|
platform_name = "Android"
|
|
|
|
return files, platform_name
|
|
|
|
def install_requirements(python_bin, req_files, local_source=None):
|
|
"""安装依赖包,带有超时和无限重试机制"""
|
|
for req_file in req_files:
|
|
if not req_file.exists():
|
|
print(f"Warning: {req_file.name} not found, skipping.")
|
|
continue
|
|
|
|
print(f"\nInstalling requirements from {req_file.name}...")
|
|
retry_count = 0
|
|
|
|
while True:
|
|
retry_count += 1
|
|
print(f"\n[Attempt {retry_count}] Running pip install...")
|
|
|
|
cmd = [
|
|
python_bin, "-m", "pip", "install",
|
|
"--timeout", "30",
|
|
"--retries", "5",
|
|
]
|
|
|
|
if local_source:
|
|
cmd.extend([
|
|
"--find-links", str(local_source),
|
|
"--only-binary", ":all:"
|
|
])
|
|
|
|
cmd.extend(["-r", req_file])
|
|
|
|
print(f"Running: {' '.join(map(str, cmd))}")
|
|
|
|
try:
|
|
subprocess.check_call(cmd)
|
|
print(f"[SUCCESS] Successfully installed requirements from {req_file.name}")
|
|
break
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"[ERROR] pip install failed: {e}")
|
|
print(f"[INFO] Retrying in 3 seconds... (press Ctrl+C to stop)")
|
|
import time
|
|
time.sleep(3)
|
|
|
|
def main():
|
|
# 解析命令行参数
|
|
args = parse_args()
|
|
|
|
print(f"System: {platform.system()}")
|
|
print(f"System Python: {sys.executable}")
|
|
print(f"Python Version: {sys.version}")
|
|
|
|
# 1. 创建虚拟环境
|
|
if not VENV_DIR.exists():
|
|
print(f"\nCreating virtual environment in {VENV_DIR}...")
|
|
venv.EnvBuilder(with_pip=True).create(VENV_DIR)
|
|
else:
|
|
print("\nVirtual environment already exists.")
|
|
|
|
python_bin = get_venv_python()
|
|
if not python_bin.exists():
|
|
print(f"Error: Virtual environment python not found at {python_bin}")
|
|
sys.exit(1)
|
|
|
|
# 2. 升级 pip (带无限重试)
|
|
print("\nUpgrading pip...")
|
|
retry_count = 0
|
|
while True:
|
|
retry_count += 1
|
|
print(f"\n[Attempt {retry_count}] Upgrading pip...")
|
|
cmd = [python_bin, "-m", "pip", "install", "--upgrade", "pip"]
|
|
print(f"Running: {' '.join(map(str, cmd))}")
|
|
try:
|
|
subprocess.check_call(cmd)
|
|
print("[SUCCESS] Successfully upgraded pip")
|
|
break
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"[ERROR] pip upgrade failed: {e}")
|
|
print(f"[INFO] Retrying in 3 seconds... (press Ctrl+C to stop)")
|
|
import time
|
|
time.sleep(3)
|
|
|
|
# 3. 根据平台选择安装对应依赖
|
|
req_files, platform_name = get_requirements_files(args)
|
|
print(f"\n{'='*60}")
|
|
print(f"Installing dependencies for: {platform_name}")
|
|
print(f"{'='*60}")
|
|
|
|
local_source = None
|
|
# if platform_name == "Android":
|
|
# local_source = SHARE_PATH / "android_packages"
|
|
# print(f"Using local package source: {local_source}")
|
|
|
|
install_requirements(python_bin, req_files, local_source=local_source)
|
|
|
|
# 4. 从共享文件夹下载配置文件(失败时从示例文件复制)
|
|
print(f"\n{'='*60}")
|
|
print("Setting up configuration files...")
|
|
print(f"{'='*60}")
|
|
|
|
# 处理 .env 文件
|
|
env_file = Path(".env")
|
|
env_example = Path(".env.example")
|
|
|
|
if not env_file.exists():
|
|
# 尝试从共享路径下载(非离线模式下使用网络共享)
|
|
if not args.offline and SHARE_PATH is not None:
|
|
share_env = SHARE_PATH / ".env"
|
|
if share_env.exists():
|
|
download_file(share_env, env_file)
|
|
elif env_example.exists():
|
|
print(f"Copying {env_example} to {env_file}...")
|
|
shutil.copy(env_example, env_file)
|
|
print(f"⚠️ IMPORTANT: Please edit {env_file} and fill in your actual API keys and tokens!")
|
|
else:
|
|
print(f"⚠️ WARNING: {env_example} not found. Please create {env_file} manually.")
|
|
elif env_example.exists():
|
|
# 离线模式或无可用的共享路径:从示例文件复制
|
|
print(f"Copying {env_example} to {env_file}...")
|
|
shutil.copy(env_example, env_file)
|
|
print(f"⚠️ IMPORTANT: Please edit {env_file} and fill in your actual API keys and tokens!")
|
|
else:
|
|
print(f"⚠️ WARNING: {env_example} not found. Please create {env_file} manually.")
|
|
else:
|
|
print(f"✓ {env_file} already exists")
|
|
|
|
print(f"\n{'='*60}")
|
|
print(f"Environment initialization completed for {platform_name}!")
|
|
print(f"{'='*60}")
|
|
print(f"\nTo activate the virtual environment:")
|
|
if sys.platform == "win32":
|
|
print(f" {VENV_DIR}\\Scripts\\activate")
|
|
else:
|
|
print(f" source {VENV_DIR}/bin/activate")
|
|
|
|
# 5. 下载lfs文件
|
|
if args.offline:
|
|
print(f"\n{'='*60}")
|
|
print("Offline mode: skipping LFS pull")
|
|
print(f"{'='*60}")
|
|
else:
|
|
print(f"\n{'='*60}")
|
|
print("Downloading lfs files from share...")
|
|
# 取消本地的 fetchexclude
|
|
run_command(["git", "config", "lfs.fetchexclude", ""])
|
|
run_command(["git", "lfs", "pull"])
|
|
|
|
if __name__ == "__main__":
|
|
main()
|