cythonize.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. # cythonize.py
  2. import os
  3. import sys
  4. import shutil
  5. from pathlib import Path
  6. import subprocess
  7. import time
  8. def print_banner():
  9. """打印标题"""
  10. print("=" * 60)
  11. print(" LongjoeAgent - Cython 编译工具")
  12. print("=" * 60)
  13. def check_environment():
  14. """检查环境"""
  15. print("\n🔍 检查环境...")
  16. # 检查Python
  17. if sys.version_info < (3, 7):
  18. print(f"❌ 需要Python 3.7+,当前: {sys.version}")
  19. return False
  20. # 检查依赖
  21. try:
  22. import Cython
  23. print(f"✅ Cython {Cython.__version__}")
  24. except ImportError:
  25. print("❌ 未安装Cython: pip install cython")
  26. return False
  27. try:
  28. import setuptools
  29. print(f"✅ setuptools {setuptools.__version__}")
  30. except ImportError:
  31. print("❌ 未安装setuptools: pip install setuptools wheel")
  32. return False
  33. return True
  34. def clean_build():
  35. """清理构建目录"""
  36. print("\n🧹 清理之前构建...")
  37. dirs_to_remove = ["build", "dist", "cython_build", "LongjoeAgent.egg-info"]
  38. for dir_name in dirs_to_remove:
  39. dir_path = Path(dir_name)
  40. if dir_path.exists():
  41. try:
  42. shutil.rmtree(dir_path)
  43. print(f" 已删除: {dir_name}")
  44. except:
  45. pass
  46. # 清理.c文件
  47. for c_file in Path(".").rglob("*.c"):
  48. try:
  49. c_file.unlink()
  50. except:
  51. pass
  52. def compile_project():
  53. """编译项目"""
  54. print("\n⚡ 开始编译...")
  55. start_time = time.time()
  56. # 运行setup.py
  57. result = subprocess.run(
  58. [sys.executable, "setup.py"], capture_output=True, text=True
  59. )
  60. if result.returncode != 0:
  61. print("❌ 编译失败!")
  62. if result.stderr:
  63. # 显示关键错误信息
  64. lines = result.stderr.split("\n")
  65. for line in lines:
  66. if "error" in line.lower() or "Error" in line:
  67. print(f" {line}")
  68. return False
  69. end_time = time.time()
  70. print(f"✅ 编译完成 ({end_time - start_time:.1f}秒)")
  71. # 显示编译结果
  72. show_compile_results()
  73. return True
  74. def show_compile_results():
  75. """显示编译结果"""
  76. compiled_dir = Path("build") / "compiled"
  77. if not compiled_dir.exists():
  78. print("❌ 编译目录不存在")
  79. return
  80. # 统计编译文件
  81. pyd_files = list(compiled_dir.rglob("*.pyd"))
  82. so_files = list(compiled_dir.rglob("*.so"))
  83. if pyd_files:
  84. print(f"\n📦 生成 {len(pyd_files)} 个 .pyd 文件:")
  85. for f in pyd_files[:5]:
  86. print(f" • {f.relative_to(compiled_dir)}")
  87. if len(pyd_files) > 5:
  88. print(f" ... 和 {len(pyd_files) - 5} 个其他文件")
  89. elif so_files:
  90. print(f"\n📦 生成 {len(so_files)} 个 .so 文件:")
  91. for f in so_files[:5]:
  92. print(f" • {f.relative_to(compiled_dir)}")
  93. if len(so_files) > 5:
  94. print(f" ... 和 {len(so_files) - 5} 个其他文件")
  95. else:
  96. print("❌ 未找到编译文件")
  97. def create_deployment():
  98. """创建部署包"""
  99. print("\n📦 创建部署包...")
  100. compiled_dir = Path("build") / "compiled"
  101. deploy_dir = Path("build") / "deploy"
  102. if not compiled_dir.exists():
  103. print("❌ 编译目录不存在")
  104. return
  105. # 清理旧的部署包
  106. if deploy_dir.exists():
  107. shutil.rmtree(deploy_dir)
  108. # 复制编译文件
  109. deploy_dir.mkdir(parents=True, exist_ok=True)
  110. for src_file in compiled_dir.rglob("*"):
  111. if src_file.is_file():
  112. rel_path = src_file.relative_to(compiled_dir)
  113. dst_file = deploy_dir / rel_path
  114. dst_file.parent.mkdir(parents=True, exist_ok=True)
  115. shutil.copy2(src_file, dst_file)
  116. # 复制必要文件
  117. essential_files = [
  118. "app.py",
  119. "get_device_id.py",
  120. ".env.example",
  121. "requirements.txt",
  122. ".registration.example",
  123. ]
  124. for file_name in essential_files:
  125. src = Path(file_name)
  126. if src.exists():
  127. dst = deploy_dir / file_name
  128. shutil.copy2(src, dst)
  129. print(f" 复制: {file_name}")
  130. # 复制 config 目录下的 JSON 配置文件
  131. config_src = Path("config")
  132. if config_src.exists():
  133. config_dst = deploy_dir / "config"
  134. config_dst.mkdir(exist_ok=True)
  135. # 复制所有 JSON 文件
  136. json_files = list(config_src.rglob("*.json"))
  137. for json_file in json_files:
  138. if json_file.is_file():
  139. rel_path = json_file.relative_to(config_src)
  140. dst_file = config_dst / rel_path
  141. dst_file.parent.mkdir(parents=True, exist_ok=True)
  142. shutil.copy2(json_file, dst_file)
  143. print(f" 复制配置文件: config/{rel_path}")
  144. if json_files:
  145. print(f"✅ 已复制 {len(json_files)} 个配置文件")
  146. # 创建日志目录
  147. (deploy_dir / "chat_logs").mkdir(exist_ok=True)
  148. # 创建启动脚本
  149. create_start_scripts(deploy_dir)
  150. # 创建 Windows 服务脚本,获取当前设备ID脚本
  151. if sys.platform == "win32":
  152. create_windows_service_scripts(deploy_dir)
  153. create_get_device_id_script(deploy_dir)
  154. print(f"✅ 部署包: {deploy_dir}")
  155. def create_get_device_id_script(deploy_dir):
  156. """创建获取设备ID脚本"""
  157. print(" 创建获取设备ID脚本...")
  158. bat_content = """@echo off
  159. chcp 65001 >nul
  160. echo === LongjoeAgent ===
  161. echo.
  162. REM 检查Python
  163. python --version >nul 2>&1
  164. if errorlevel 1 (
  165. echo 错误: 未找到Python
  166. pause
  167. exit /b 1
  168. )
  169. REM 获取当前设备ID
  170. echo 获取当前设备ID...
  171. python get_device_id.py
  172. pause
  173. """
  174. bat_file = deploy_dir / "2_get_device_id.bat"
  175. bat_file.write_text(bat_content, encoding="utf-8")
  176. def create_start_scripts(deploy_dir):
  177. """创建启动脚本"""
  178. # Windows
  179. bat_content = """@echo off
  180. chcp 65001 >nul
  181. echo === LongjoeAgent ===
  182. echo.
  183. REM 检查Python
  184. python --version >nul 2>&1
  185. if errorlevel 1 (
  186. echo 错误: 未找到Python
  187. pause
  188. exit /b 1
  189. )
  190. REM 启动
  191. echo 启动服务...
  192. python app.py
  193. pause
  194. """
  195. bat_file = deploy_dir / "start.bat"
  196. bat_file.write_text(bat_content, encoding="utf-8")
  197. # Linux/Mac
  198. sh_content = """#!/bin/bash
  199. echo "=== LongjoeAgent ==="
  200. # 检查Python
  201. if ! command -v python3 &> /dev/null; then
  202. echo "错误: 未找到Python3"
  203. exit 1
  204. fi
  205. # 启动
  206. echo "启动服务..."
  207. python3 app.py
  208. """
  209. sh_file = deploy_dir / "start.sh"
  210. sh_file.write_text(sh_content, encoding="utf-8")
  211. if sys.platform != "win32":
  212. os.chmod(sh_file, 0o755)
  213. print(" 创建启动脚本: start.bat, start.sh")
  214. def create_windows_service_scripts(deploy_dir):
  215. """创建 Windows 服务管理脚本"""
  216. print(" 创建 Windows 服务脚本...")
  217. # 检查本地 nssm.exe
  218. nssm_exe = Path("nssm.exe")
  219. if not nssm_exe.exists():
  220. print(" ⚠️ 未找到 nssm.exe,跳过服务脚本创建")
  221. return
  222. # 复制 nssm.exe 到部署目录
  223. nssm_dst = deploy_dir / "nssm.exe"
  224. shutil.copy2(nssm_exe, nssm_dst)
  225. print(" ✅ 复制 nssm.exe")
  226. # 配置虚拟环境脚本
  227. venv_bat = """
  228. @echo off
  229. chcp 65001 >nul
  230. echo ========================================
  231. echo Python Virtual Environment Setup Script
  232. echo ========================================
  233. echo.
  234. REM 动态获取脚本所在目录
  235. set "PROJECT_DIR=%~dp0"
  236. set "PROJECT_DIR=%PROJECT_DIR:~0,-1%"
  237. echo 项目目录: %PROJECT_DIR%
  238. REM 1. Check if in project root
  239. if not exist "%PROJECT_DIR%\\requirements.txt" (
  240. echo [ERROR] Please run in project root directory!
  241. echo requirements.txt not found
  242. pause
  243. exit /b 1
  244. )
  245. REM 2. Check Python
  246. python --version >nul 2>&1
  247. if errorlevel 1 (
  248. echo [ERROR] Python not found. Please install Python or add to PATH
  249. pause
  250. exit /b 1
  251. )
  252. REM 3. Check existing venv
  253. if exist "venv\\" (
  254. echo [WARN] Virtual environment 'venv' already exists
  255. set /p choice=Delete and recreate? [Y/N]:
  256. if /i "%choice%"=="Y" (
  257. echo Removing old virtual environment...
  258. rmdir /s /q venv
  259. ) else (
  260. echo Skip creation, install dependencies directly...
  261. goto :install_deps
  262. )
  263. echo.
  264. )
  265. REM 4. Create virtual environment
  266. echo Step 1: Creating virtual environment...
  267. python -m venv venv
  268. if errorlevel 1 (
  269. echo [ERROR] Failed to create virtual environment
  270. pause
  271. exit /b 1
  272. )
  273. echo [OK] Virtual environment created
  274. echo.
  275. REM 5. Install dependencies
  276. :install_deps
  277. echo Step 2: Installing dependencies...
  278. call venv\\Scripts\\activate
  279. if errorlevel 1 (
  280. echo [ERROR] Failed to activate virtual environment
  281. pause
  282. exit /b 1
  283. )
  284. echo Installing packages from requirements.txt...
  285. pip install -r "%PROJECT_DIR%\\requirements.txt" -i https://mirrors.aliyun.com/pypi/simple/
  286. if errorlevel 1 (
  287. echo [ERROR] Failed to install dependencies
  288. pause
  289. exit /b 1
  290. )
  291. echo [OK] Dependencies installed
  292. echo.
  293. REM 6. Verification
  294. echo Step 3: Verification...
  295. echo Installed packages:
  296. venv\\Scripts\\python.exe -m pip list
  297. if errorlevel 1 (
  298. echo [WARN] Failed to list packages
  299. )
  300. echo.
  301. echo ========================================
  302. echo [SUCCESS] Virtual environment setup complete!
  303. pause
  304. """
  305. # 服务安装脚本
  306. install_bat = """@echo off
  307. chcp 65001 >nul
  308. echo ========================================
  309. echo LongjoeAgent Windows Service Install
  310. echo ========================================
  311. echo.
  312. REM 检查是否以管理员权限运行
  313. net session >nul 2>&1
  314. if %errorlevel% neq 0 (
  315. echo Error: 请以管理员身份运行此脚本!
  316. pause
  317. exit /b 1
  318. )
  319. REM 动态获取脚本所在目录
  320. set "PROJECT_DIR=%~dp0"
  321. set "PROJECT_DIR=%PROJECT_DIR:~0,-1%"
  322. echo 项目目录: %PROJECT_DIR%
  323. REM 检查必要文件是否存在
  324. if not exist "%PROJECT_DIR%\\nssm.exe" (
  325. echo 错误: nssm.exe 未在项目目录中找到!
  326. pause
  327. exit /b 1
  328. )
  329. if not exist "%PROJECT_DIR%\\app.py" (
  330. echo 错误: app.py 未在项目目录中找到!
  331. pause
  332. exit /b 1
  333. )
  334. REM 设置虚拟环境Python路径
  335. set "VENV_PYTHON_PATH=%PROJECT_DIR%\\venv\\Scripts\\python.exe"
  336. REM 检查虚拟环境Python是否存在
  337. echo 检查虚拟环境...
  338. if not exist "%VENV_PYTHON_PATH%" (
  339. echo 错误: 虚拟环境中未找到Python!
  340. echo 预期路径: %VENV_PYTHON_PATH%
  341. echo.
  342. echo 请确保:
  343. echo 1. 已创建虚拟环境: python -m venv venv
  344. echo 2. 已安装依赖: venv\\Scripts\\pip install -r requirements.txt
  345. pause
  346. exit /b 1
  347. )
  348. echo 找到虚拟环境Python: %VENV_PYTHON_PATH%
  349. REM 停止并移除现有服务
  350. echo Unstall Service
  351. net stop LongjoeAI >nul 2>&1
  352. timeout /t 2 /nobreak >nul
  353. "%PROJECT_DIR%\\nssm.exe" remove LongjoeAI confirm >nul 2>&1
  354. REM 关键修正:使用正确的引号格式(模仿您的成功命令)
  355. echo 安装 LongjoeAI 服务...
  356. "%PROJECT_DIR%\\nssm.exe" install LongjoeAI "cmd" "/c cd /d "%PROJECT_DIR%" && "%VENV_PYTHON_PATH%" app.py"
  357. if errorlevel 1 (
  358. echo 错误: 服务安装失败!
  359. echo.
  360. echo 调试信息:
  361. echo 项目目录: %PROJECT_DIR%
  362. echo Python 路径: %VENV_PYTHON_PATH%
  363. pause
  364. exit /b 1
  365. )
  366. REM 配置服务参数
  367. echo 配置服务参数...
  368. "%PROJECT_DIR%\\nssm.exe" set LongjoeAI DisplayName "Longjoe AI Agent Service"
  369. "%PROJECT_DIR%\\nssm.exe" set LongjoeAI Description "龙嘉软件AI助手服务"
  370. echo.
  371. echo LongjoeAI 服务安装完成!
  372. goto :start_service
  373. :start_service
  374. echo.
  375. echo Starting service...
  376. net start LongjoeAI
  377. if %errorlevel% equ 0 (
  378. echo SUCCESS: Service started successfully!
  379. echo.
  380. ) else (
  381. echo ERROR: Service failed to start
  382. echo.
  383. )
  384. echo.
  385. pause
  386. """
  387. # 服务卸载脚本
  388. uninstall_bat = """@echo off
  389. chcp 65001 >nul
  390. echo ========================================
  391. echo LongjoeAI Service Uninstall
  392. echo ========================================
  393. echo.
  394. REM 检查是否以管理员权限运行
  395. net session >nul 2>&1
  396. if %errorlevel% neq 0 (
  397. echo 错误: 请以管理员身份运行运行此脚本!
  398. pause
  399. exit /b 1
  400. )
  401. REM 动态获取项目目录
  402. set "PROJECT_DIR=%~dp0"
  403. set "PROJECT_DIR=%PROJECT_DIR:~0,-1%"
  404. echo 停止服务...
  405. net stop LongjoeAI >nul 2>&1
  406. timeout /t 3 /nobreak >nul
  407. echo 卸载服务...
  408. "%PROJECT_DIR%\\nssm.exe" remove LongjoeAI confirm >nul 2>&1
  409. if errorlevel 1 (
  410. echo 尝试强制删除...
  411. sc delete LongjoeAI >nul 2>&1
  412. )
  413. echo LongjoeAI 服务卸载成功!
  414. echo.
  415. pause
  416. """
  417. # 服务管理脚本
  418. manage_bat = """@echo off
  419. chcp 65001 >nul
  420. REM 检查是否以管理员权限运行
  421. net session >nul 2>&1
  422. if %errorlevel% neq 0 (
  423. echo 错误: 请以管理员身份运行运行此脚本!
  424. pause
  425. exit /b 1
  426. )
  427. :menu
  428. cls
  429. echo ========================================
  430. echo LongjoeAI Service Management
  431. echo ========================================
  432. echo.
  433. set "PROJECT_DIR=%~dp0"
  434. set "PROJECT_DIR=%PROJECT_DIR:~0,-1%"
  435. echo 1. 启动服务
  436. echo 2. 停止服务
  437. echo 3. 重启服务
  438. echo 4. 服务状态
  439. echo 5. Exit
  440. echo.
  441. set /p choice=请选择操作 (1-5):
  442. if "%choice%"=="1" (
  443. net start LongjoeAI
  444. if %errorlevel% equ 0 echo Service started successfully.
  445. ) else if "%choice%"=="2" (
  446. net stop LongjoeAI
  447. if %errorlevel% equ 0 echo Service stopped successfully.
  448. ) else if "%choice%"=="3" (
  449. net stop LongjoeAI
  450. timeout /t 2 /nobreak >nul
  451. net start LongjoeAI
  452. if %errorlevel% equ 0 echo Service restarted successfully.
  453. ) else if "%choice%"=="4" (
  454. sc query LongjoeAI
  455. ) else if "%choice%"=="5" (
  456. exit
  457. ) else (
  458. echo 错误: 无效选择!
  459. )
  460. echo.
  461. pause
  462. goto menu
  463. """
  464. # 写入脚本文件
  465. (deploy_dir / "1_venv_install.bat").write_text(venv_bat, encoding="utf-8")
  466. (deploy_dir / "3_service_install.bat").write_text(install_bat, encoding="utf-8")
  467. (deploy_dir / "service_uninstall.bat").write_text(uninstall_bat, encoding="utf-8")
  468. (deploy_dir / "service_manage.bat").write_text(manage_bat, encoding="utf-8")
  469. print(
  470. " ✅ 创建服务脚本: service_install.bat, service_uninstall.bat, service_manage.bat"
  471. )
  472. def main():
  473. """主函数"""
  474. print_banner()
  475. # 1. 检查环境
  476. if not check_environment():
  477. sys.exit(1)
  478. # 2. 清理
  479. clean_build()
  480. # 3. 编译
  481. if not compile_project():
  482. sys.exit(1)
  483. # 4. 创建部署包
  484. create_deployment()
  485. # 5. 完成
  486. print("\n" + "=" * 60)
  487. print("🎉 编译完成!")
  488. print("=" * 60)
  489. print("\n📁 文件结构:")
  490. print(" • 源代码: 保持不变")
  491. print(" • 编译文件: build/compiled/")
  492. print(" • 部署包: build/deploy/")
  493. print("\n🚀 使用方法:")
  494. print(" 开发: python app.py")
  495. print(" 部署: 复制 build/deploy/ 到服务器")
  496. print(" 运行: 执行 service_install.bat")
  497. if __name__ == "__main__":
  498. main()