setup.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import os
  2. import sys
  3. import re
  4. import shutil
  5. import subprocess
  6. from setuptools import setup, find_packages
  7. from setuptools.command.build_py import build_py
  8. from setuptools.command.bdist_wheel import bdist_wheel
  9. # ========== 配置 ==========
  10. PACKAGE_NAME = "dataContract"
  11. COMPILE_DIRS = ["algorithmContract"] # 需要编译的子目录(作为顶层包)
  12. # INSTALL_REQUIRES = ["repositoryZN"]
  13. INSTALL_REQUIRES = []
  14. CLEAN_COMPILED_AFTER_BUILD = True
  15. # =========================
  16. def log(msg):
  17. print(f"[SETUP] {msg}")
  18. def compile_with_nuitka(py_files):
  19. for py_file in py_files:
  20. if os.path.basename(py_file) == "__init__.py":
  21. continue
  22. log(f"编译: {py_file}")
  23. output_dir = os.path.dirname(py_file)
  24. cmd = [
  25. sys.executable, "-m", "nuitka",
  26. "--module",
  27. "--no-pyi-file",
  28. "--remove-output",
  29. f"--output-dir={output_dir}",
  30. py_file
  31. ]
  32. if sys.platform == "win32":
  33. cmd.append("--mingw64")
  34. result = subprocess.run(cmd, capture_output=True, text=True)
  35. if result.returncode != 0:
  36. log(f"编译失败: {py_file}\n{result.stderr}")
  37. sys.exit(1)
  38. base_name = os.path.splitext(os.path.basename(py_file))[0]
  39. found = False
  40. for ext in [".pyd", ".so"]:
  41. pattern = re.escape(base_name) + r".*" + re.escape(ext)
  42. for f in os.listdir(output_dir):
  43. if re.match(pattern, f):
  44. src = os.path.join(output_dir, f)
  45. dst = os.path.join(output_dir, base_name + ext)
  46. shutil.move(src, dst)
  47. log(f"重命名: {src} -> {dst}")
  48. found = True
  49. break
  50. if found:
  51. break
  52. if not found:
  53. log(f"错误:未找到编译产物 {py_file}")
  54. sys.exit(1)
  55. def clean_build_dir():
  56. dirs_to_clean = ["build", f"{PACKAGE_NAME}.egg-info"]
  57. for d in dirs_to_clean:
  58. if os.path.exists(d):
  59. shutil.rmtree(d)
  60. log(f"已删除: {d}")
  61. class CustomBuildPy(build_py):
  62. def run(self):
  63. src_root = os.getcwd()
  64. log(f"源码根目录: {src_root}")
  65. for compile_dir in COMPILE_DIRS:
  66. target_dir = os.path.join(src_root, compile_dir)
  67. if not os.path.isdir(target_dir):
  68. log(f"警告: 目录不存在 {target_dir}")
  69. continue
  70. all_py = []
  71. for root, _, files in os.walk(target_dir):
  72. for file in files:
  73. if file.endswith(".py"):
  74. all_py.append(os.path.join(root, file))
  75. if all_py:
  76. compile_with_nuitka(all_py)
  77. else:
  78. log(f"在 {target_dir} 中没有找到 .py 文件")
  79. build_lib = self.build_lib
  80. if not build_lib:
  81. build_lib = os.path.join(src_root, "build", "lib")
  82. for compile_dir in COMPILE_DIRS:
  83. src_dir = os.path.join(src_root, compile_dir)
  84. if not os.path.isdir(src_dir):
  85. continue
  86. dst_dir = os.path.join(build_lib, compile_dir)
  87. self.mkpath(dst_dir)
  88. for root, _, files in os.walk(src_dir):
  89. rel_path = os.path.relpath(root, src_dir)
  90. target_subdir = os.path.join(dst_dir, rel_path) if rel_path != '.' else dst_dir
  91. self.mkpath(target_subdir)
  92. for file in files:
  93. src_file = os.path.join(root, file)
  94. if file == "__init__.py":
  95. dst_file = os.path.join(target_subdir, file)
  96. self.copy_file(src_file, dst_file)
  97. log(f"复制 {src_file} -> {dst_file}")
  98. elif file.endswith(".pyd") or file.endswith(".so"):
  99. dst_file = os.path.join(target_subdir, file)
  100. self.copy_file(src_file, dst_file)
  101. log(f"复制 {src_file} -> {dst_file}")
  102. if CLEAN_COMPILED_AFTER_BUILD:
  103. for compile_dir in COMPILE_DIRS:
  104. target_dir = os.path.join(src_root, compile_dir)
  105. if not os.path.isdir(target_dir):
  106. continue
  107. for root, _, files in os.walk(target_dir):
  108. for file in files:
  109. if file.endswith(".pyd") or file.endswith(".so"):
  110. os.remove(os.path.join(root, file))
  111. log(f"清理源码编译产物: {os.path.join(root, file)}")
  112. class CustomBdistWheel(bdist_wheel):
  113. def run(self):
  114. self.run_command("build_py")
  115. self.plat_name = self.get_platform()
  116. bdist_wheel.run(self)
  117. def get_platform(self):
  118. if sys.platform == "win32":
  119. return "win_amd64"
  120. elif sys.platform == "linux":
  121. return "linux_x86_64"
  122. else:
  123. return super().get_platform()
  124. if __name__ == "__main__":
  125. try:
  126. subprocess.run([sys.executable, "-m", "nuitka", "--version"], capture_output=True, check=True)
  127. except subprocess.CalledProcessError:
  128. log("请先安装 Nuitka: pip install nuitka", "ERROR")
  129. sys.exit(1)
  130. clean_build_dir()
  131. setup(
  132. name=PACKAGE_NAME,
  133. version="1.0.202508190930",
  134. description="Data Contract Package (compiled, flat structure)",
  135. author="Xie Zhou Yang",
  136. packages=["algorithmContract"],
  137. package_dir={"": "."},
  138. cmdclass={
  139. "build_py": CustomBuildPy,
  140. "bdist_wheel": CustomBdistWheel,
  141. },
  142. install_requires=INSTALL_REQUIRES,
  143. zip_safe=False,
  144. )