setup.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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 = "repositoryZN"
  11. COMPILE_DIRS = ["utils"] # 编译 utils 目录和根目录下的 .py
  12. INSTALL_REQUIRES = [] # 无内部依赖
  13. CLEAN_COMPILED_AFTER_BUILD = True
  14. # =========================
  15. def log(msg):
  16. print(f"[SETUP] {msg}")
  17. def compile_with_nuitka(py_files):
  18. for py_file in py_files:
  19. if os.path.basename(py_file) == "__init__.py":
  20. continue
  21. log(f"编译: {py_file}")
  22. output_dir = os.path.dirname(py_file)
  23. cmd = [
  24. sys.executable, "-m", "nuitka",
  25. "--module",
  26. "--no-pyi-file",
  27. "--remove-output",
  28. f"--output-dir={output_dir}",
  29. py_file
  30. ]
  31. if sys.platform == "win32":
  32. cmd.append("--mingw64")
  33. result = subprocess.run(cmd, capture_output=True, text=True)
  34. if result.returncode != 0:
  35. log(f"编译失败: {py_file}\n{result.stderr}")
  36. sys.exit(1)
  37. base_name = os.path.splitext(os.path.basename(py_file))[0]
  38. found = False
  39. for ext in [".pyd", ".so"]:
  40. pattern = re.escape(base_name) + r".*" + re.escape(ext)
  41. for f in os.listdir(output_dir):
  42. if re.match(pattern, f):
  43. src = os.path.join(output_dir, f)
  44. dst = os.path.join(output_dir, base_name + ext)
  45. shutil.move(src, dst)
  46. log(f"重命名: {src} -> {dst}")
  47. found = True
  48. break
  49. if found:
  50. break
  51. if not found:
  52. log(f"错误:未找到编译产物 {py_file}")
  53. sys.exit(1)
  54. def clean_build_dir():
  55. dirs_to_clean = ["build", f"{PACKAGE_NAME}.egg-info"]
  56. for d in dirs_to_clean:
  57. if os.path.exists(d):
  58. shutil.rmtree(d)
  59. log(f"已删除: {d}")
  60. class CustomBuildPy(build_py):
  61. def run(self):
  62. src_root = os.getcwd()
  63. log(f"源码根目录: {src_root}")
  64. # 1. 编译所有需要编译的目录(包括根目录 ".")
  65. for compile_dir in COMPILE_DIRS:
  66. if compile_dir == ".":
  67. target_dir = src_root
  68. else:
  69. target_dir = os.path.join(src_root, compile_dir)
  70. if not os.path.isdir(target_dir):
  71. log(f"警告: 目录不存在 {target_dir}")
  72. continue
  73. all_py = []
  74. for root, _, files in os.walk(target_dir):
  75. for file in files:
  76. if file.endswith(".py"):
  77. all_py.append(os.path.join(root, file))
  78. if all_py:
  79. compile_with_nuitka(all_py)
  80. else:
  81. log(f"在 {target_dir} 中没有找到 .py 文件")
  82. # 2. 手动复制:对于 utils 目录作为顶级包,对于根目录下的文件直接复制到 build/lib 根目录
  83. build_lib = self.build_lib
  84. if not build_lib:
  85. build_lib = os.path.join(src_root, "build", "lib")
  86. # 处理 utils 目录(作为包)
  87. utils_src = os.path.join(src_root, "utils")
  88. if os.path.isdir(utils_src):
  89. utils_dst = os.path.join(build_lib, "utils")
  90. self.mkpath(utils_dst)
  91. for root, _, files in os.walk(utils_src):
  92. rel_path = os.path.relpath(root, utils_src)
  93. target_subdir = os.path.join(utils_dst, rel_path) if rel_path != '.' else utils_dst
  94. self.mkpath(target_subdir)
  95. for file in files:
  96. src_file = os.path.join(root, file)
  97. if file == "__init__.py":
  98. dst_file = os.path.join(target_subdir, file)
  99. self.copy_file(src_file, dst_file)
  100. log(f"复制 {src_file} -> {dst_file}")
  101. elif file.endswith(".pyd") or file.endswith(".so"):
  102. dst_file = os.path.join(target_subdir, file)
  103. self.copy_file(src_file, dst_file)
  104. log(f"复制 {src_file} -> {dst_file}")
  105. # 处理根目录下的文件(作为顶级模块,直接放在 build/lib 根目录)
  106. for file in os.listdir(src_root):
  107. if file.endswith(".pyd") or file.endswith(".so"):
  108. src_file = os.path.join(src_root, file)
  109. dst_file = os.path.join(build_lib, file)
  110. self.copy_file(src_file, dst_file)
  111. log(f"复制根模块 {src_file} -> {dst_file}")
  112. elif file == "__init__.py":
  113. # 如果根目录有 __init__.py,也复制(让根目录本身成为一个包)
  114. src_file = os.path.join(src_root, file)
  115. dst_file = os.path.join(build_lib, file)
  116. self.copy_file(src_file, dst_file)
  117. log(f"复制根 __init__.py {src_file} -> {dst_file}")
  118. # 3. 清理源码目录中的 .pyd/.so
  119. if CLEAN_COMPILED_AFTER_BUILD:
  120. for compile_dir in COMPILE_DIRS:
  121. if compile_dir == ".":
  122. target_dir = src_root
  123. else:
  124. target_dir = os.path.join(src_root, compile_dir)
  125. if not os.path.isdir(target_dir):
  126. continue
  127. for root, _, files in os.walk(target_dir):
  128. for file in files:
  129. if file.endswith(".pyd") or file.endswith(".so"):
  130. os.remove(os.path.join(root, file))
  131. log(f"清理源码编译产物: {os.path.join(root, file)}")
  132. class CustomBdistWheel(bdist_wheel):
  133. def run(self):
  134. self.run_command("build_py")
  135. self.plat_name = self.get_platform()
  136. bdist_wheel.run(self)
  137. def get_platform(self):
  138. if sys.platform == "win32":
  139. return "win_amd64"
  140. elif sys.platform == "linux":
  141. return "linux_x86_64"
  142. else:
  143. return super().get_platform()
  144. if __name__ == "__main__":
  145. try:
  146. subprocess.run([sys.executable, "-m", "nuitka", "--version"], capture_output=True, check=True)
  147. except subprocess.CalledProcessError:
  148. log("请先安装 Nuitka: pip install nuitka", "ERROR")
  149. sys.exit(1)
  150. clean_build_dir()
  151. # 动态确定要安装的包和模块
  152. packages = []
  153. if os.path.isdir("utils"):
  154. packages.append("utils")
  155. # 根目录下的顶级模块(如果存在这些 .py 文件,它们会被编译成 .pyd 并安装)
  156. py_modules = []
  157. for mod in ["jsonUtil", "logUtil", "csvFileUtil"]:
  158. if os.path.exists(f"{mod}.py"):
  159. py_modules.append(mod)
  160. setup(
  161. name=PACKAGE_NAME,
  162. version="1.0.202508190930",
  163. description="Repository ZN Package (compiled, flat structure)",
  164. author="Xie Zhou Yang",
  165. packages=packages,
  166. py_modules=py_modules,
  167. package_dir={"": "."},
  168. cmdclass={
  169. "build_py": CustomBuildPy,
  170. "bdist_wheel": CustomBdistWheel,
  171. },
  172. install_requires=INSTALL_REQUIRES,
  173. zip_safe=False,
  174. )