setup.py 4.6 KB

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