cythonize.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  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, encoding="utf-8"
  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 / "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. install_bat = """@echo off
  228. chcp 65001 >nul
  229. echo ========================================
  230. echo LongjoeAgent Windows Service Install
  231. echo ========================================
  232. echo.
  233. REM 检查是否以管理员权限运行
  234. net session >nul 2>&1
  235. if %errorlevel% neq 0 (
  236. echo Error: 请以管理员权限运行此脚本!
  237. echo 请右键点击此脚本 -> "以管理员身份运行"
  238. pause
  239. exit /b 1
  240. )
  241. REM 动态获取脚本所在目录
  242. set "PROJECT_DIR=%~dp0"
  243. set "PROJECT_DIR=%PROJECT_DIR:~0,-1%"
  244. echo 项目目录: %PROJECT_DIR%
  245. REM 检查必要文件是否存在
  246. if not exist "%PROJECT_DIR%\\nssm.exe" (
  247. echo 错误: nssm.exe 未在项目目录中找到!
  248. pause
  249. exit /b 1
  250. )
  251. if not exist "%PROJECT_DIR%\\app.py" (
  252. echo 错误: app.py 未在项目目录中找到!
  253. pause
  254. exit /b 1
  255. )
  256. REM 动态查找Python安装路径
  257. echo 搜索Python安装...
  258. for /f "delims=" %%i in ('where python') do (
  259. set "PYTHON_PATH=%%i"
  260. goto :found_python
  261. )
  262. :found_python
  263. if not defined PYTHON_PATH (
  264. echo 错误: 未找到Python!
  265. echo 请安装 Python 3.7+ 并确保其在 PATH 中
  266. pause
  267. exit /b 1
  268. )
  269. echo 找到Python: %PYTHON_PATH%
  270. REM 停止并移除现有服务
  271. echo Unstall Service
  272. net stop LongjoeAI >nul 2>&1
  273. timeout /t 2 /nobreak >nul
  274. "%PROJECT_DIR%\\nssm.exe" remove LongjoeAI confirm >nul 2>&1
  275. REM 关键修正:使用正确的引号格式(模仿您的成功命令)
  276. echo 安装 LongjoeAI 服务...
  277. "%PROJECT_DIR%\\nssm.exe" install LongjoeAI "cmd" "/c cd /d "%PROJECT_DIR%" && "%PYTHON_PATH%" app.py"
  278. if errorlevel 1 (
  279. echo 错误: 服务安装失败!
  280. echo.
  281. echo 调试信息:
  282. echo 项目目录: %PROJECT_DIR%
  283. echo Python 路径: %PYTHON_PATH%
  284. )
  285. REM 配置服务参数
  286. echo 配置服务参数...
  287. "%PROJECT_DIR%\\nssm.exe" set LongjoeAI DisplayName "Longjoe AI Agent Service"
  288. "%PROJECT_DIR%\\nssm.exe" set LongjoeAI Description "龙嘉软件AI助手服务"
  289. echo.
  290. echo LongjoeAI 服务安装完成!
  291. goto :start_service
  292. :start_service
  293. echo.
  294. echo Starting service...
  295. net start LongjoeAI
  296. if %errorlevel% equ 0 (
  297. echo SUCCESS: Service started successfully!
  298. echo.
  299. ) else (
  300. echo ERROR: Service failed to start
  301. echo.
  302. )
  303. echo.
  304. pause
  305. """
  306. # 服务卸载脚本
  307. uninstall_bat = """@echo off
  308. chcp 65001 >nul
  309. echo ========================================
  310. echo LongjoeAI Service Uninstall
  311. echo ========================================
  312. echo.
  313. REM 检查是否以管理员权限运行
  314. net session >nul 2>&1
  315. if %errorlevel% neq 0 (
  316. echo 错误: 请以管理员权限运行此脚本!
  317. echo 请右键点击此脚本 -> "以管理员身份运行"
  318. pause
  319. exit /b 1
  320. )
  321. REM 动态获取项目目录
  322. set "PROJECT_DIR=%~dp0"
  323. set "PROJECT_DIR=%PROJECT_DIR:~0,-1%"
  324. echo 停止服务...
  325. net stop LongjoeAI >nul 2>&1
  326. timeout /t 3 /nobreak >nul
  327. echo 卸载服务...
  328. "%PROJECT_DIR%\\nssm.exe" remove LongjoeAI confirm >nul 2>&1
  329. if errorlevel 1 (
  330. echo 尝试强制删除...
  331. sc delete LongjoeAI >nul 2>&1
  332. )
  333. echo LongjoeAI 服务卸载成功!
  334. echo.
  335. pause
  336. """
  337. # 服务管理脚本
  338. manage_bat = """@echo off
  339. chcp 65001 >nul
  340. echo ========================================
  341. echo LongjoeAI Service Management
  342. echo ========================================
  343. echo.
  344. set "PROJECT_DIR=%~dp0"
  345. set "PROJECT_DIR=%PROJECT_DIR:~0,-1%"
  346. echo 1. 启动服务
  347. echo 2. 停止服务
  348. echo 3. 重启服务
  349. echo 4. 服务状态
  350. echo 5. Exit
  351. echo.
  352. set /p choice=请选择操作 (1-5):
  353. if "%choice%"=="1" (
  354. net start LongjoeAI
  355. if %errorlevel% equ 0 echo Service started successfully.
  356. ) else if "%choice%"=="2" (
  357. net stop LongjoeAI
  358. if %errorlevel% equ 0 echo Service stopped successfully.
  359. ) else if "%choice%"=="3" (
  360. net stop LongjoeAI
  361. timeout /t 2 /nobreak >nul
  362. net start LongjoeAI
  363. if %errorlevel% equ 0 echo Service restarted successfully.
  364. ) else if "%choice%"=="4" (
  365. sc query LongjoeAI
  366. ) else if "%choice%"=="5" (
  367. exit
  368. ) else (
  369. echo 错误: 无效选择!
  370. )
  371. echo.
  372. pause
  373. """
  374. # 写入脚本文件
  375. (deploy_dir / "service_install.bat").write_text(install_bat, encoding="utf-8")
  376. (deploy_dir / "service_uninstall.bat").write_text(uninstall_bat, encoding="utf-8")
  377. (deploy_dir / "service_manage.bat").write_text(manage_bat, encoding="utf-8")
  378. print(
  379. " ✅ 创建服务脚本: service_install.bat, service_uninstall.bat, service_manage.bat"
  380. )
  381. def main():
  382. """主函数"""
  383. print_banner()
  384. # 1. 检查环境
  385. if not check_environment():
  386. sys.exit(1)
  387. # 2. 清理
  388. clean_build()
  389. # 3. 编译
  390. if not compile_project():
  391. sys.exit(1)
  392. # 4. 创建部署包
  393. create_deployment()
  394. # 5. 完成
  395. print("\n" + "=" * 60)
  396. print("🎉 编译完成!")
  397. print("=" * 60)
  398. print("\n📁 文件结构:")
  399. print(" • 源代码: 保持不变")
  400. print(" • 编译文件: build/compiled/")
  401. print(" • 部署包: build/deploy/")
  402. print("\n🚀 使用方法:")
  403. print(" 开发: python app.py")
  404. print(" 部署: 复制 build/deploy/ 到服务器")
  405. print(" 运行: 执行 service_install.bat")
  406. if __name__ == "__main__":
  407. main()