setup.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. # setup.py
  2. from setuptools import setup, Extension
  3. from Cython.Build import cythonize
  4. from Cython.Distutils import build_ext
  5. import os
  6. import sys
  7. from pathlib import Path
  8. import re
  9. # 获取当前目录
  10. current_dir = Path(__file__).parent
  11. COMPILED_DIR = current_dir / "build" / "compiled"
  12. # 定义要排除的文件
  13. EXCLUDE_FILES = {
  14. "app.py", # 不编译主应用文件
  15. "get_device_id.py", # 不编译设备ID获取文件
  16. "setup.py", # 不编译本文件
  17. "cythonize.py", # 不编译辅助脚本
  18. "check_deploy.py", # 不编译部署检查
  19. "terminal_demo.py", # 不编译终端演示文件
  20. "test_polling_performance.py", # 不编译测试文件
  21. }
  22. # 定义要排除的目录
  23. EXCLUDE_DIRS = {
  24. "build",
  25. "dist",
  26. "__pycache__",
  27. ".git",
  28. ".vscode",
  29. ".idea",
  30. "venv",
  31. "env",
  32. "node_modules",
  33. "chat_logs",
  34. "logs",
  35. "tests",
  36. "test",
  37. "cython_build", # Cython临时目录
  38. }
  39. def should_exclude_dir(dir_name):
  40. """判断是否应该排除目录"""
  41. return dir_name in EXCLUDE_DIRS or dir_name.startswith(".")
  42. def find_py_files(base_dir):
  43. """查找所有需要编译的Python文件"""
  44. py_files = []
  45. for root, dirs, files in os.walk(base_dir):
  46. # 排除不需要的目录
  47. dirs[:] = [d for d in dirs if not should_exclude_dir(d)]
  48. for file in files:
  49. if file.endswith(".py") and file not in EXCLUDE_FILES:
  50. full_path = os.path.join(root, file)
  51. # 排除build目录下的文件
  52. if "build" in full_path.split(os.sep):
  53. continue
  54. py_files.append(full_path)
  55. return py_files
  56. def create_extension_modules():
  57. """创建Cython扩展模块列表"""
  58. extensions = []
  59. py_files = find_py_files(current_dir)
  60. for py_file in py_files:
  61. # 转换为相对路径
  62. rel_path = os.path.relpath(py_file, current_dir)
  63. # 生成模块名(去掉.py)
  64. module_name = rel_path[:-3].replace(os.sep, ".")
  65. # 创建Extension
  66. extension = Extension(
  67. module_name,
  68. [py_file],
  69. extra_compile_args=[
  70. "/O2" if sys.platform == "win32" else "-O3",
  71. "/std:c++17" if sys.platform == "win32" else "-std=c++17",
  72. ],
  73. language="c",
  74. )
  75. extensions.append(extension)
  76. return extensions
  77. def main():
  78. """主编译函数"""
  79. print(f"编译目标目录: {COMPILED_DIR}")
  80. # 确保编译目录存在
  81. COMPILED_DIR.mkdir(parents=True, exist_ok=True)
  82. # 创建扩展模块
  83. extensions = create_extension_modules()
  84. if not extensions:
  85. print("❌ 未找到需要编译的Python文件")
  86. return
  87. print(f"找到 {len(extensions)} 个Python文件需要编译:")
  88. for i, ext in enumerate(extensions[:10], 1): # 只显示前10个
  89. print(f" {i:2}. {ext.name}")
  90. if len(extensions) > 10:
  91. print(f" ... 和 {len(extensions) - 10} 个其他文件")
  92. # 配置编译选项
  93. compiler_directives = {
  94. "language_level": 3,
  95. "boundscheck": False, # 关闭边界检查
  96. "wraparound": False, # 关闭环绕检查
  97. "initializedcheck": False, # 关闭初始化检查
  98. "nonecheck": False, # 关闭None检查
  99. "overflowcheck": False, # 关闭溢出检查
  100. "cdivision": True, # 使用C除法
  101. "infer_types": True, # 类型推断
  102. "optimize.use_switch": True, # 优化switch语句
  103. }
  104. # 编译
  105. cythonized = cythonize(
  106. extensions,
  107. compiler_directives=compiler_directives,
  108. nthreads=0, # 0表示自动使用所有核心
  109. build_dir="./cython_build", # 临时构建目录
  110. )
  111. # 修复:使用自定义BuildExt类
  112. class CustomBuildExt(build_ext):
  113. def initialize_options(self):
  114. super().initialize_options()
  115. # 设置输出目录
  116. self.build_lib = str(COMPILED_DIR)
  117. def get_ext_fullpath(self, ext_name):
  118. """获取扩展的完整路径,确保输出到指定目录"""
  119. # 首先调用父类方法
  120. filename = self.get_ext_filename(ext_name)
  121. # 确保输出到编译目录
  122. return os.path.join(self.build_lib, filename)
  123. # 修复:使用setuptools的setup,但通过cmdclass和options控制
  124. setup(
  125. name="LongjoeAgent",
  126. ext_modules=cythonized,
  127. cmdclass={"build_ext": CustomBuildExt},
  128. options={
  129. "build_ext": {
  130. "build_lib": str(COMPILED_DIR),
  131. "inplace": False, # 不在原地构建
  132. }
  133. },
  134. script_args=["build_ext"], # 只构建扩展
  135. )
  136. if __name__ == "__main__":
  137. main()