ソースを参照

修改dataAnalysisBehavior、dataAnalysisBusiness、dataAnalysisService、dataContract、repositoryZN 模块的setup.py,基于nuitka包实现 python源代码文件编译为pyd文件,并打包至 whl文件中

zhouyang.xie 1 ヶ月 前
コミット
8d886184d4

+ 1 - 0
.gitignore

@@ -29,6 +29,7 @@ codegen/
 # Cache files
 *.slxc
 **/*.pyc
+**/*.pyd
 **/*.c
 
 # Cloud based storage dotfile

+ 1 - 1
dataAnalysisBehavior/__init__.py

@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+# # -*- coding: utf-8 -*-
 
 from . import *
 __all__=['behavior','common']

+ 1 - 1
dataAnalysisBehavior/behavior/analyst.py

@@ -1,4 +1,4 @@
-from .baseAnalyst import BaseAnalyst
+from behavior.baseAnalyst import BaseAnalyst
 import os
 import pandas as pd
 import numpy as np

+ 153 - 54
dataAnalysisBehavior/setup.py

@@ -1,66 +1,165 @@
-from setuptools import setup, find_packages
-from setuptools.command.build_py import build_py
-from setuptools.command.install import install
-import compileall
 import os
 import sys
+import re
 import shutil
-from pathlib import Path
+import subprocess
+from setuptools import setup, find_packages
+from setuptools.command.build_py import build_py
+from setuptools.command.bdist_wheel import bdist_wheel
+
+# ========== 配置 ==========
+PACKAGE_NAME = "dataAnalysisBehavior"      # 包名(仅用于元数据)
+COMPILE_DIRS = ["behavior", "common"]      # 需要编译的子目录(同时也是顶层包名)
+INSTALL_REQUIRES = []                      # 根据实际依赖填写
+CLEAN_COMPILED_AFTER_BUILD = True
+# =========================
+
+def log(msg):
+    print(f"[SETUP] {msg}")
+
+def compile_with_nuitka(py_files):
+    for py_file in py_files:
+        if os.path.basename(py_file) == "__init__.py":
+            continue
+        log(f"编译: {py_file}")
 
-compileDirs=['common','behavior']
+        output_dir = os.path.dirname(py_file)
+        cmd = [
+            sys.executable, "-m", "nuitka",
+            "--module",
+            "--no-pyi-file",
+            "--remove-output",
+            f"--output-dir={output_dir}",
+            py_file
+        ]
+        if sys.platform == "win32":
+            cmd.append("--mingw64")
+        result = subprocess.run(cmd, capture_output=True, text=True)
+        if result.returncode != 0:
+            log(f"编译失败: {py_file}\n{result.stderr}")
+            sys.exit(1)
+
+        base_name = os.path.splitext(os.path.basename(py_file))[0]
+        found = False
+        for ext in [".pyd", ".so"]:
+            pattern = re.escape(base_name) + r".*" + re.escape(ext)
+            for f in os.listdir(output_dir):
+                if re.match(pattern, f):
+                    src = os.path.join(output_dir, f)
+                    dst = os.path.join(output_dir, base_name + ext)
+                    shutil.move(src, dst)
+                    log(f"重命名: {src} -> {dst}")
+                    found = True
+                    break
+            if found:
+                break
+        if not found:
+            log(f"错误:未找到编译产物 {py_file}")
+            sys.exit(1)
+
+def clean_build_dir():
+    dirs_to_clean = ["build", f"{PACKAGE_NAME}.egg-info"]
+    for d in dirs_to_clean:
+        if os.path.exists(d):
+            shutil.rmtree(d)
+            log(f"已删除: {d}")
 
 class CustomBuildPy(build_py):
     def run(self):
-        # 获取当前 Python 版本信息
-        py_version = f".cpython-{sys.version_info.major}{sys.version_info.minor}"
+        src_root = os.getcwd()
+        log(f"源码根目录: {src_root}")
 
-        for dir in compileDirs:
-            for root,_,subFiles in os.walk(dir):
-                compileall.compile_dir(root, force=True)
+        # 1. 编译生成 .pyd/.so
+        for compile_dir in COMPILE_DIRS:
+            target_dir = os.path.join(src_root, compile_dir)
+            if not os.path.isdir(target_dir):
+                log(f"警告: 目录不存在 {target_dir}")
+                continue
+            all_py = []
+            for root, _, files in os.walk(target_dir):
+                for file in files:
+                    if file.endswith(".py"):
+                        all_py.append(os.path.join(root, file))
+            if all_py:
+                compile_with_nuitka(all_py)
+            else:
+                log(f"在 {target_dir} 中没有找到 .py 文件")
 
-                for file in subFiles:
-                    if file.endswith('.pyc'):
-                        pyc_path = os.path.join(root, file)
-                        new_File = file.replace(py_version, '')
-                        new_path=os.path.join(Path(root).parent,new_File)
-                        shutil.move(pyc_path, new_path)
+        # 2. 手动构建目录:直接复制到 build/lib/{compile_dir}(不创建顶层包目录)
+        build_lib = self.build_lib
+        if not build_lib:
+            build_lib = os.path.join(src_root, "build", "lib")
 
-        super().run()
+        for compile_dir in COMPILE_DIRS:
+            src_dir = os.path.join(src_root, compile_dir)
+            if not os.path.isdir(src_dir):
+                continue
+            dst_dir = os.path.join(build_lib, compile_dir)   # 直接作为顶级包
+            self.mkpath(dst_dir)
+            for root, _, files in os.walk(src_dir):
+                rel_path = os.path.relpath(root, src_dir)
+                target_subdir = os.path.join(dst_dir, rel_path) if rel_path != '.' else dst_dir
+                self.mkpath(target_subdir)
+                for file in files:
+                    src_file = os.path.join(root, file)
+                    if file == "__init__.py":
+                        # 复制 __init__.py
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
+                    elif file.endswith(".pyd") or file.endswith(".so"):
+                        # 复制编译好的二进制文件
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
+                    # 其他 .py 文件忽略不复制
 
-class InstallCommand(install):
+        # 3. 可选:清理源码目录中的 .pyd/.so
+        if CLEAN_COMPILED_AFTER_BUILD:
+            for compile_dir in COMPILE_DIRS:
+                target_dir = os.path.join(src_root, compile_dir)
+                if not os.path.isdir(target_dir):
+                    continue
+                for root, _, files in os.walk(target_dir):
+                    for file in files:
+                        if file.endswith(".pyd") or file.endswith(".so"):
+                            os.remove(os.path.join(root, file))
+                            log(f"清理源码编译产物: {os.path.join(root, file)}")
+
+class CustomBdistWheel(bdist_wheel):
     def run(self):
-        super().run()
-        # Remove all .py files from the installed package
-        # 若用于开发,可查看导入的自定义package 则注释本代码段
-        # 若用于部署交付,可应用本代码
-        # for root, _, files in os.walk(self.install_lib):
-        #     for file in files:
-        #         if file.endswith('.py'):
-        #             os.remove(os.path.join(root, file))
-
-setup(
-    name='dataAnalysisBehavior',
-    # version='1.2.202406281546',
-    version='1.2.202508190930',
-    description='Data Analysis Behavior Package', # 描述信息
-    author='Xie Zhou Yang', # 作者
-    packages=find_packages(),
-    cmdclass={
-        'build_py': CustomBuildPy,
-        'install': InstallCommand,
-    },
-    package_data={
-        'behavior': ['*.pyc'],
-        'common': ['*.pyc']
-    },
-    exclude_package_data={'': ['*.py']}
-)
-
-# 获取 setup.py 所在的绝对路径
-setup_dir = os.path.abspath(os.path.dirname(__file__))
-# 编译打包后,删除pyc后缀文件
-for root, _, files in os.walk(setup_dir):
-    for file in files:
-        if file.endswith('.pyc') or file.endswith('.c'):
-            print(os.path.join(root, file))
-            os.remove(os.path.join(root, file))
+        self.run_command("build_py")
+        self.plat_name = self.get_platform()
+        bdist_wheel.run(self)
+
+    def get_platform(self):
+        if sys.platform == "win32":
+            return "win_amd64"
+        elif sys.platform == "linux":
+            return "linux_x86_64"
+        else:
+            return super().get_platform()
+
+if __name__ == "__main__":
+    try:
+        subprocess.run([sys.executable, "-m", "nuitka", "--version"], capture_output=True, check=True)
+    except subprocess.CalledProcessError:
+        log("请先安装 Nuitka: pip install nuitka", "ERROR")
+        sys.exit(1)
+
+    clean_build_dir()
+
+    setup(
+        name=PACKAGE_NAME,
+        version="1.2.202508190930",
+        description="Data Analysis Behavior Package (compiled, no source in wheel)",
+        author="Xie Zhou Yang",
+        packages=["behavior", "common"],                # 直接指定顶层包
+        package_dir={"": "."},                          # 包目录在当前目录
+        cmdclass={
+            "build_py": CustomBuildPy,
+            "bdist_wheel": CustomBdistWheel,
+        },
+        install_requires=INSTALL_REQUIRES,
+        zip_safe=False,
+    )

+ 151 - 52
dataAnalysisBusiness/setup.py

@@ -1,64 +1,163 @@
-from setuptools import setup, find_packages
-from setuptools.command.build_py import build_py
-from setuptools.command.install import install
-import compileall
 import os
 import sys
+import re
 import shutil
-from pathlib import Path
+import subprocess
+from setuptools import setup, find_packages
+from setuptools.command.build_py import build_py
+from setuptools.command.bdist_wheel import bdist_wheel
+
+# ========== 配置 ==========
+PACKAGE_NAME = "dataAnalysisBusiness"
+COMPILE_DIRS = ["algorithm"]          # 需要编译的子目录(将作为顶层包)
+# INSTALL_REQUIRES = ["dataAnalysisBehavior"]   # 根据实际依赖填写
+INSTALL_REQUIRES = []
+CLEAN_COMPILED_AFTER_BUILD = True
+# =========================
+
+def log(msg):
+    print(f"[SETUP] {msg}")
+
+def compile_with_nuitka(py_files):
+    for py_file in py_files:
+        if os.path.basename(py_file) == "__init__.py":
+            continue
+        log(f"编译: {py_file}")
 
-compileDirs=['algorithm']
+        output_dir = os.path.dirname(py_file)
+        cmd = [
+            sys.executable, "-m", "nuitka",
+            "--module",
+            "--no-pyi-file",
+            "--remove-output",
+            f"--output-dir={output_dir}",
+            py_file
+        ]
+        if sys.platform == "win32":
+            cmd.append("--mingw64")
+        result = subprocess.run(cmd, capture_output=True, text=True)
+        if result.returncode != 0:
+            log(f"编译失败: {py_file}\n{result.stderr}")
+            sys.exit(1)
+
+        base_name = os.path.splitext(os.path.basename(py_file))[0]
+        found = False
+        for ext in [".pyd", ".so"]:
+            pattern = re.escape(base_name) + r".*" + re.escape(ext)
+            for f in os.listdir(output_dir):
+                if re.match(pattern, f):
+                    src = os.path.join(output_dir, f)
+                    dst = os.path.join(output_dir, base_name + ext)
+                    shutil.move(src, dst)
+                    log(f"重命名: {src} -> {dst}")
+                    found = True
+                    break
+            if found:
+                break
+        if not found:
+            log(f"错误:未找到编译产物 {py_file}")
+            sys.exit(1)
+
+def clean_build_dir():
+    dirs_to_clean = ["build", f"{PACKAGE_NAME}.egg-info"]
+    for d in dirs_to_clean:
+        if os.path.exists(d):
+            shutil.rmtree(d)
+            log(f"已删除: {d}")
 
 class CustomBuildPy(build_py):
     def run(self):
-        # 获取当前 Python 版本信息
-        py_version = f".cpython-{sys.version_info.major}{sys.version_info.minor}"
+        src_root = os.getcwd()
+        log(f"源码根目录: {src_root}")
 
-        for dir in compileDirs:
-            for root,_,subFiles in os.walk(dir):
-                compileall.compile_dir(root, force=True)
+        # 1. 编译生成 .pyd/.so
+        for compile_dir in COMPILE_DIRS:
+            target_dir = os.path.join(src_root, compile_dir)
+            if not os.path.isdir(target_dir):
+                log(f"警告: 目录不存在 {target_dir}")
+                continue
+            all_py = []
+            for root, _, files in os.walk(target_dir):
+                for file in files:
+                    if file.endswith(".py"):
+                        all_py.append(os.path.join(root, file))
+            if all_py:
+                compile_with_nuitka(all_py)
+            else:
+                log(f"在 {target_dir} 中没有找到 .py 文件")
 
-                for file in subFiles:
-                    if file.endswith('.pyc'):
-                        pyc_path = os.path.join(root, file)
-                        new_File = file.replace(py_version, '')
-                        new_path=os.path.join(Path(root).parent,new_File)
-                        shutil.move(pyc_path, new_path)
+        # 2. 手动构建:复制到 build/lib/{compile_dir}(直接作为顶级包)
+        build_lib = self.build_lib
+        if not build_lib:
+            build_lib = os.path.join(src_root, "build", "lib")
 
-        super().run()
+        for compile_dir in COMPILE_DIRS:
+            src_dir = os.path.join(src_root, compile_dir)
+            if not os.path.isdir(src_dir):
+                continue
+            dst_dir = os.path.join(build_lib, compile_dir)   # 顶级包
+            self.mkpath(dst_dir)
+            for root, _, files in os.walk(src_dir):
+                rel_path = os.path.relpath(root, src_dir)
+                target_subdir = os.path.join(dst_dir, rel_path) if rel_path != '.' else dst_dir
+                self.mkpath(target_subdir)
+                for file in files:
+                    src_file = os.path.join(root, file)
+                    if file == "__init__.py":
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
+                    elif file.endswith(".pyd") or file.endswith(".so"):
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
 
-class InstallCommand(install):
+        # 3. 清理源码目录中的 .pyd/.so
+        if CLEAN_COMPILED_AFTER_BUILD:
+            for compile_dir in COMPILE_DIRS:
+                target_dir = os.path.join(src_root, compile_dir)
+                if not os.path.isdir(target_dir):
+                    continue
+                for root, _, files in os.walk(target_dir):
+                    for file in files:
+                        if file.endswith(".pyd") or file.endswith(".so"):
+                            os.remove(os.path.join(root, file))
+                            log(f"清理源码编译产物: {os.path.join(root, file)}")
+
+class CustomBdistWheel(bdist_wheel):
     def run(self):
-        super().run()
-        # Remove all .py files from the installed package
-        # 若用于开发,可查看导入的自定义package 则注释本代码段
-        # 若用于部署交付,可应用本代码
-        # for root, _, files in os.walk(self.install_lib):
-        #     for file in files:
-        #         if file.endswith('.py'):
-        #             os.remove(os.path.join(root, file))
-
-setup(
-    name='dataAnalysisBusiness',
-    version='1.2.202508191130',
-    description='Data Analysis Business Package', # 描述信息
-    author='Xie Zhou Yang', # 作者
-    packages=find_packages(),
-    cmdclass={
-        'build_py': CustomBuildPy,
-        'install': InstallCommand,
-    },
-    package_data={
-        'algorithm': ['*.pyc']
-    },
-    exclude_package_data={'': ['*.py']}
-)
-
-# 获取 setup.py 所在的`绝对路径
-setup_dir = os.path.abspath(os.path.dirname(__file__))
-# 编译打包后,删除pyc后缀文件
-for root, _, files in os.walk(setup_dir):
-    for file in files:
-        if file.endswith('.pyc') or file.endswith('.c'):
-            print(os.path.join(root, file))
-            os.remove(os.path.join(root, file))
+        self.run_command("build_py")
+        self.plat_name = self.get_platform()
+        bdist_wheel.run(self)
+
+    def get_platform(self):
+        if sys.platform == "win32":
+            return "win_amd64"
+        elif sys.platform == "linux":
+            return "linux_x86_64"
+        else:
+            return super().get_platform()
+
+if __name__ == "__main__":
+    try:
+        subprocess.run([sys.executable, "-m", "nuitka", "--version"], capture_output=True, check=True)
+    except subprocess.CalledProcessError:
+        log("请先安装 Nuitka: pip install nuitka", "ERROR")
+        sys.exit(1)
+
+    clean_build_dir()
+
+    setup(
+        name=PACKAGE_NAME,
+        version="1.2.202508190930",
+        description="Data Analysis Business Package (compiled, flat structure)",
+        author="Xie Zhou Yang",
+        packages=["algorithm"],                # 顶层包
+        package_dir={"": "."},
+        cmdclass={
+            "build_py": CustomBuildPy,
+            "bdist_wheel": CustomBdistWheel,
+        },
+        install_requires=INSTALL_REQUIRES,
+        zip_safe=False,
+    )

+ 148 - 52
dataAnalysisService/setup.py

@@ -1,64 +1,160 @@
-from setuptools import setup, find_packages
-from setuptools.command.build_py import build_py
-from setuptools.command.install import install
-import compileall
 import os
 import sys
+import re
 import shutil
-from pathlib import Path
+import subprocess
+from setuptools import setup, find_packages
+from setuptools.command.build_py import build_py
+from setuptools.command.bdist_wheel import bdist_wheel
+
+# ========== 配置 ==========
+PACKAGE_NAME = "dataAnalysisService"
+COMPILE_DIRS = ["service"]                # 需要编译的子目录(作为顶层包)
+# INSTALL_REQUIRES = ["dataAnalysisBusiness"]
+INSTALL_REQUIRES = []
+CLEAN_COMPILED_AFTER_BUILD = True
+# =========================
+
+def log(msg):
+    print(f"[SETUP] {msg}")
+
+def compile_with_nuitka(py_files):
+    for py_file in py_files:
+        if os.path.basename(py_file) == "__init__.py":
+            continue
+        log(f"编译: {py_file}")
 
-compileDirs=['service']
+        output_dir = os.path.dirname(py_file)
+        cmd = [
+            sys.executable, "-m", "nuitka",
+            "--module",
+            "--no-pyi-file",
+            "--remove-output",
+            f"--output-dir={output_dir}",
+            py_file
+        ]
+        if sys.platform == "win32":
+            cmd.append("--mingw64")
+        result = subprocess.run(cmd, capture_output=True, text=True)
+        if result.returncode != 0:
+            log(f"编译失败: {py_file}\n{result.stderr}")
+            sys.exit(1)
+
+        base_name = os.path.splitext(os.path.basename(py_file))[0]
+        found = False
+        for ext in [".pyd", ".so"]:
+            pattern = re.escape(base_name) + r".*" + re.escape(ext)
+            for f in os.listdir(output_dir):
+                if re.match(pattern, f):
+                    src = os.path.join(output_dir, f)
+                    dst = os.path.join(output_dir, base_name + ext)
+                    shutil.move(src, dst)
+                    log(f"重命名: {src} -> {dst}")
+                    found = True
+                    break
+            if found:
+                break
+        if not found:
+            log(f"错误:未找到编译产物 {py_file}")
+            sys.exit(1)
+
+def clean_build_dir():
+    dirs_to_clean = ["build", f"{PACKAGE_NAME}.egg-info"]
+    for d in dirs_to_clean:
+        if os.path.exists(d):
+            shutil.rmtree(d)
+            log(f"已删除: {d}")
 
 class CustomBuildPy(build_py):
     def run(self):
-        # 获取当前 Python 版本信息
-        py_version = f".cpython-{sys.version_info.major}{sys.version_info.minor}"
+        src_root = os.getcwd()
+        log(f"源码根目录: {src_root}")
 
-        for dir in compileDirs:
-            for root,_,subFiles in os.walk(dir):
-                compileall.compile_dir(root, force=True)
+        for compile_dir in COMPILE_DIRS:
+            target_dir = os.path.join(src_root, compile_dir)
+            if not os.path.isdir(target_dir):
+                log(f"警告: 目录不存在 {target_dir}")
+                continue
+            all_py = []
+            for root, _, files in os.walk(target_dir):
+                for file in files:
+                    if file.endswith(".py"):
+                        all_py.append(os.path.join(root, file))
+            if all_py:
+                compile_with_nuitka(all_py)
+            else:
+                log(f"在 {target_dir} 中没有找到 .py 文件")
 
-                for file in subFiles:
-                    if file.endswith('.pyc'):
-                        pyc_path = os.path.join(root, file)
-                        new_File = file.replace(py_version, '')
-                        new_path=os.path.join(Path(root).parent,new_File)
-                        shutil.move(pyc_path, new_path)
+        build_lib = self.build_lib
+        if not build_lib:
+            build_lib = os.path.join(src_root, "build", "lib")
 
-        super().run()
+        for compile_dir in COMPILE_DIRS:
+            src_dir = os.path.join(src_root, compile_dir)
+            if not os.path.isdir(src_dir):
+                continue
+            dst_dir = os.path.join(build_lib, compile_dir)
+            self.mkpath(dst_dir)
+            for root, _, files in os.walk(src_dir):
+                rel_path = os.path.relpath(root, src_dir)
+                target_subdir = os.path.join(dst_dir, rel_path) if rel_path != '.' else dst_dir
+                self.mkpath(target_subdir)
+                for file in files:
+                    src_file = os.path.join(root, file)
+                    if file == "__init__.py":
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
+                    elif file.endswith(".pyd") or file.endswith(".so"):
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
 
-class InstallCommand(install):
+        if CLEAN_COMPILED_AFTER_BUILD:
+            for compile_dir in COMPILE_DIRS:
+                target_dir = os.path.join(src_root, compile_dir)
+                if not os.path.isdir(target_dir):
+                    continue
+                for root, _, files in os.walk(target_dir):
+                    for file in files:
+                        if file.endswith(".pyd") or file.endswith(".so"):
+                            os.remove(os.path.join(root, file))
+                            log(f"清理源码编译产物: {os.path.join(root, file)}")
+
+class CustomBdistWheel(bdist_wheel):
     def run(self):
-        super().run()
-        # Remove all .py files from the installed package
-        # 若用于开发,可查看导入的自定义package 则注释本代码段
-        # 若用于部署交付,可应用本代码
-        # for root, _, files in os.walk(self.install_lib):
-        #     for file in files:
-        #         if file.endswith('.py'):
-        #             os.remove(os.path.join(root, file))
-
-setup(
-    name='dataAnalysisService',
-    version='1.2.202505131440',
-    description='Data Analysis Service Package',  # 描述信息
-    author='Xie Zhou Yang',  # 作者
-    packages=find_packages(),
-    cmdclass={
-        'build_py': CustomBuildPy,
-        'install': InstallCommand,
-    },
-    package_data={
-        'service': ['*.pyc']
-    },
-    exclude_package_data={'': ['*.py']}
-)
-
-# 获取 setup.py 所在的绝对路径
-setup_dir = os.path.abspath(os.path.dirname(__file__))
-# 编译打包后,删除pyc后缀文件
-for root, _, files in os.walk(setup_dir):
-    for file in files:
-        if file.endswith('.pyc') or file.endswith('.c'):
-            print(os.path.join(root, file))
-            os.remove(os.path.join(root, file))
+        self.run_command("build_py")
+        self.plat_name = self.get_platform()
+        bdist_wheel.run(self)
+
+    def get_platform(self):
+        if sys.platform == "win32":
+            return "win_amd64"
+        elif sys.platform == "linux":
+            return "linux_x86_64"
+        else:
+            return super().get_platform()
+
+if __name__ == "__main__":
+    try:
+        subprocess.run([sys.executable, "-m", "nuitka", "--version"], capture_output=True, check=True)
+    except subprocess.CalledProcessError:
+        log("请先安装 Nuitka: pip install nuitka", "ERROR")
+        sys.exit(1)
+
+    clean_build_dir()
+
+    setup(
+        name=PACKAGE_NAME,
+        version="1.2.202508190930",
+        description="Data Analysis Service Package (compiled, flat structure)",
+        author="Xie Zhou Yang",
+        packages=["service"],
+        package_dir={"": "."},
+        cmdclass={
+            "build_py": CustomBuildPy,
+            "bdist_wheel": CustomBdistWheel,
+        },
+        install_requires=INSTALL_REQUIRES,
+        zip_safe=False,
+    )

+ 148 - 52
dataContract/setup.py

@@ -1,64 +1,160 @@
-from setuptools import setup, find_packages
-from setuptools.command.build_py import build_py
-from setuptools.command.install import install
-import compileall
 import os
 import sys
+import re
 import shutil
-from pathlib import Path
+import subprocess
+from setuptools import setup, find_packages
+from setuptools.command.build_py import build_py
+from setuptools.command.bdist_wheel import bdist_wheel
+
+# ========== 配置 ==========
+PACKAGE_NAME = "dataContract"
+COMPILE_DIRS = ["algorithmContract"]     # 需要编译的子目录(作为顶层包)
+# INSTALL_REQUIRES = ["repositoryZN"]
+INSTALL_REQUIRES = []
+CLEAN_COMPILED_AFTER_BUILD = True
+# =========================
+
+def log(msg):
+    print(f"[SETUP] {msg}")
+
+def compile_with_nuitka(py_files):
+    for py_file in py_files:
+        if os.path.basename(py_file) == "__init__.py":
+            continue
+        log(f"编译: {py_file}")
 
-compileDirs=['algorithmContract']
+        output_dir = os.path.dirname(py_file)
+        cmd = [
+            sys.executable, "-m", "nuitka",
+            "--module",
+            "--no-pyi-file",
+            "--remove-output",
+            f"--output-dir={output_dir}",
+            py_file
+        ]
+        if sys.platform == "win32":
+            cmd.append("--mingw64")
+        result = subprocess.run(cmd, capture_output=True, text=True)
+        if result.returncode != 0:
+            log(f"编译失败: {py_file}\n{result.stderr}")
+            sys.exit(1)
+
+        base_name = os.path.splitext(os.path.basename(py_file))[0]
+        found = False
+        for ext in [".pyd", ".so"]:
+            pattern = re.escape(base_name) + r".*" + re.escape(ext)
+            for f in os.listdir(output_dir):
+                if re.match(pattern, f):
+                    src = os.path.join(output_dir, f)
+                    dst = os.path.join(output_dir, base_name + ext)
+                    shutil.move(src, dst)
+                    log(f"重命名: {src} -> {dst}")
+                    found = True
+                    break
+            if found:
+                break
+        if not found:
+            log(f"错误:未找到编译产物 {py_file}")
+            sys.exit(1)
+
+def clean_build_dir():
+    dirs_to_clean = ["build", f"{PACKAGE_NAME}.egg-info"]
+    for d in dirs_to_clean:
+        if os.path.exists(d):
+            shutil.rmtree(d)
+            log(f"已删除: {d}")
 
 class CustomBuildPy(build_py):
     def run(self):
-        # 获取当前 Python 版本信息
-        py_version = f".cpython-{sys.version_info.major}{sys.version_info.minor}"
+        src_root = os.getcwd()
+        log(f"源码根目录: {src_root}")
 
-        for dir in compileDirs:
-            for root,_,subFiles in os.walk(dir):
-                compileall.compile_dir(root, force=True)
+        for compile_dir in COMPILE_DIRS:
+            target_dir = os.path.join(src_root, compile_dir)
+            if not os.path.isdir(target_dir):
+                log(f"警告: 目录不存在 {target_dir}")
+                continue
+            all_py = []
+            for root, _, files in os.walk(target_dir):
+                for file in files:
+                    if file.endswith(".py"):
+                        all_py.append(os.path.join(root, file))
+            if all_py:
+                compile_with_nuitka(all_py)
+            else:
+                log(f"在 {target_dir} 中没有找到 .py 文件")
 
-                for file in subFiles:
-                    if file.endswith('.pyc'):
-                        pyc_path = os.path.join(root, file)
-                        new_File = file.replace(py_version, '')
-                        new_path=os.path.join(Path(root).parent,new_File)
-                        shutil.move(pyc_path, new_path)
+        build_lib = self.build_lib
+        if not build_lib:
+            build_lib = os.path.join(src_root, "build", "lib")
 
-        super().run()
+        for compile_dir in COMPILE_DIRS:
+            src_dir = os.path.join(src_root, compile_dir)
+            if not os.path.isdir(src_dir):
+                continue
+            dst_dir = os.path.join(build_lib, compile_dir)
+            self.mkpath(dst_dir)
+            for root, _, files in os.walk(src_dir):
+                rel_path = os.path.relpath(root, src_dir)
+                target_subdir = os.path.join(dst_dir, rel_path) if rel_path != '.' else dst_dir
+                self.mkpath(target_subdir)
+                for file in files:
+                    src_file = os.path.join(root, file)
+                    if file == "__init__.py":
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
+                    elif file.endswith(".pyd") or file.endswith(".so"):
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
 
-class InstallCommand(install):
+        if CLEAN_COMPILED_AFTER_BUILD:
+            for compile_dir in COMPILE_DIRS:
+                target_dir = os.path.join(src_root, compile_dir)
+                if not os.path.isdir(target_dir):
+                    continue
+                for root, _, files in os.walk(target_dir):
+                    for file in files:
+                        if file.endswith(".pyd") or file.endswith(".so"):
+                            os.remove(os.path.join(root, file))
+                            log(f"清理源码编译产物: {os.path.join(root, file)}")
+
+class CustomBdistWheel(bdist_wheel):
     def run(self):
-        super().run()
-        # Remove all .py files from the installed package
-        # 若用于开发,可查看导入的自定义package 则注释本代码段
-        # 若用于部署交付,可应用本代码
-        # for root, _, files in os.walk(self.install_lib):
-        #     for file in files:
-        #         if file.endswith('.py'):
-        #             os.remove(os.path.join(root, file))
-
-setup(
-    name='dataContract',
-    version='1.2.202505131440',
-    description='Data Contract Package', # 描述信息
-    author='Xie Zhou Yang', # 作者
-    packages=find_packages(),
-    cmdclass={
-        'build_py': CustomBuildPy,
-        'install': InstallCommand,
-    },
-    package_data={
-        'algorithmContract': ['*.pyc']
-    },
-    exclude_package_data={'': ['*.py']}
-)
-
-# 获取 setup.py 所在的绝对路径
-setup_dir = os.path.abspath(os.path.dirname(__file__))
-# 编译打包后,删除pyc后缀文件
-for root, _, files in os.walk(setup_dir):
-    for file in files:
-        if file.endswith('.pyc') or file.endswith('.c'):
-            print(os.path.join(root, file))
-            os.remove(os.path.join(root, file))
+        self.run_command("build_py")
+        self.plat_name = self.get_platform()
+        bdist_wheel.run(self)
+
+    def get_platform(self):
+        if sys.platform == "win32":
+            return "win_amd64"
+        elif sys.platform == "linux":
+            return "linux_x86_64"
+        else:
+            return super().get_platform()
+
+if __name__ == "__main__":
+    try:
+        subprocess.run([sys.executable, "-m", "nuitka", "--version"], capture_output=True, check=True)
+    except subprocess.CalledProcessError:
+        log("请先安装 Nuitka: pip install nuitka", "ERROR")
+        sys.exit(1)
+
+    clean_build_dir()
+
+    setup(
+        name=PACKAGE_NAME,
+        version="1.0.202508190930",
+        description="Data Contract Package (compiled, flat structure)",
+        author="Xie Zhou Yang",
+        packages=["algorithmContract"],
+        package_dir={"": "."},
+        cmdclass={
+            "build_py": CustomBuildPy,
+            "bdist_wheel": CustomBdistWheel,
+        },
+        install_requires=INSTALL_REQUIRES,
+        zip_safe=False,
+    )

+ 180 - 54
repositoryZN/setup.py

@@ -1,66 +1,192 @@
-from setuptools import setup, find_packages
-from setuptools.command.build_py import build_py
-from setuptools.command.install import install
-import compileall
 import os
 import sys
+import re
 import shutil
-from pathlib import Path
+import subprocess
+from setuptools import setup, find_packages
+from setuptools.command.build_py import build_py
+from setuptools.command.bdist_wheel import bdist_wheel
+
+# ========== 配置 ==========
+PACKAGE_NAME = "repositoryZN"
+COMPILE_DIRS = ["utils"]           # 编译 utils 目录和根目录下的 .py
+INSTALL_REQUIRES = []                   # 无内部依赖
+CLEAN_COMPILED_AFTER_BUILD = True
+# =========================
+
+def log(msg):
+    print(f"[SETUP] {msg}")
+
+def compile_with_nuitka(py_files):
+    for py_file in py_files:
+        if os.path.basename(py_file) == "__init__.py":
+            continue
+        log(f"编译: {py_file}")
+
+        output_dir = os.path.dirname(py_file)
+        cmd = [
+            sys.executable, "-m", "nuitka",
+            "--module",
+            "--no-pyi-file",
+            "--remove-output",
+            f"--output-dir={output_dir}",
+            py_file
+        ]
+        if sys.platform == "win32":
+            cmd.append("--mingw64")
+        result = subprocess.run(cmd, capture_output=True, text=True)
+        if result.returncode != 0:
+            log(f"编译失败: {py_file}\n{result.stderr}")
+            sys.exit(1)
 
-compileDirs=['utils']
+        base_name = os.path.splitext(os.path.basename(py_file))[0]
+        found = False
+        for ext in [".pyd", ".so"]:
+            pattern = re.escape(base_name) + r".*" + re.escape(ext)
+            for f in os.listdir(output_dir):
+                if re.match(pattern, f):
+                    src = os.path.join(output_dir, f)
+                    dst = os.path.join(output_dir, base_name + ext)
+                    shutil.move(src, dst)
+                    log(f"重命名: {src} -> {dst}")
+                    found = True
+                    break
+            if found:
+                break
+        if not found:
+            log(f"错误:未找到编译产物 {py_file}")
+            sys.exit(1)
+
+def clean_build_dir():
+    dirs_to_clean = ["build", f"{PACKAGE_NAME}.egg-info"]
+    for d in dirs_to_clean:
+        if os.path.exists(d):
+            shutil.rmtree(d)
+            log(f"已删除: {d}")
 
 class CustomBuildPy(build_py):
     def run(self):
-        # 获取当前 Python 版本信息
-        py_version = f".cpython-{sys.version_info.major}{sys.version_info.minor}"
+        src_root = os.getcwd()
+        log(f"源码根目录: {src_root}")
+
+        # 1. 编译所有需要编译的目录(包括根目录 ".")
+        for compile_dir in COMPILE_DIRS:
+            if compile_dir == ".":
+                target_dir = src_root
+            else:
+                target_dir = os.path.join(src_root, compile_dir)
+            if not os.path.isdir(target_dir):
+                log(f"警告: 目录不存在 {target_dir}")
+                continue
+            all_py = []
+            for root, _, files in os.walk(target_dir):
+                for file in files:
+                    if file.endswith(".py"):
+                        all_py.append(os.path.join(root, file))
+            if all_py:
+                compile_with_nuitka(all_py)
+            else:
+                log(f"在 {target_dir} 中没有找到 .py 文件")
 
-        for dir in compileDirs:
-            for root,_,subFiles in os.walk(dir):
-                compileall.compile_dir(root, force=True)
+        # 2. 手动复制:对于 utils 目录作为顶级包,对于根目录下的文件直接复制到 build/lib 根目录
+        build_lib = self.build_lib
+        if not build_lib:
+            build_lib = os.path.join(src_root, "build", "lib")
 
-                for file in subFiles:
-                    if file.endswith('.pyc'):
-                        pyc_path = os.path.join(root, file)
-                        new_File = file.replace(py_version, '')
-                        new_path=os.path.join(Path(root).parent,new_File)
-                        shutil.move(pyc_path, new_path)
+        # 处理 utils 目录(作为包)
+        utils_src = os.path.join(src_root, "utils")
+        if os.path.isdir(utils_src):
+            utils_dst = os.path.join(build_lib, "utils")
+            self.mkpath(utils_dst)
+            for root, _, files in os.walk(utils_src):
+                rel_path = os.path.relpath(root, utils_src)
+                target_subdir = os.path.join(utils_dst, rel_path) if rel_path != '.' else utils_dst
+                self.mkpath(target_subdir)
+                for file in files:
+                    src_file = os.path.join(root, file)
+                    if file == "__init__.py":
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
+                    elif file.endswith(".pyd") or file.endswith(".so"):
+                        dst_file = os.path.join(target_subdir, file)
+                        self.copy_file(src_file, dst_file)
+                        log(f"复制 {src_file} -> {dst_file}")
 
-        super().run()
+        # 处理根目录下的文件(作为顶级模块,直接放在 build/lib 根目录)
+        for file in os.listdir(src_root):
+            if file.endswith(".pyd") or file.endswith(".so"):
+                src_file = os.path.join(src_root, file)
+                dst_file = os.path.join(build_lib, file)
+                self.copy_file(src_file, dst_file)
+                log(f"复制根模块 {src_file} -> {dst_file}")
+            elif file == "__init__.py":
+                # 如果根目录有 __init__.py,也复制(让根目录本身成为一个包)
+                src_file = os.path.join(src_root, file)
+                dst_file = os.path.join(build_lib, file)
+                self.copy_file(src_file, dst_file)
+                log(f"复制根 __init__.py {src_file} -> {dst_file}")
 
-class InstallCommand(install):
+        # 3. 清理源码目录中的 .pyd/.so
+        if CLEAN_COMPILED_AFTER_BUILD:
+            for compile_dir in COMPILE_DIRS:
+                if compile_dir == ".":
+                    target_dir = src_root
+                else:
+                    target_dir = os.path.join(src_root, compile_dir)
+                if not os.path.isdir(target_dir):
+                    continue
+                for root, _, files in os.walk(target_dir):
+                    for file in files:
+                        if file.endswith(".pyd") or file.endswith(".so"):
+                            os.remove(os.path.join(root, file))
+                            log(f"清理源码编译产物: {os.path.join(root, file)}")
+
+class CustomBdistWheel(bdist_wheel):
     def run(self):
-        super().run()
-        # Remove all .py files from the installed package
-        # 若用于开发,可查看导入的自定义package 则注释本代码段
-        # 若用于部署交付,可应用本代码
-        # for root, _, files in os.walk(self.install_lib):
-        #     for file in files:
-        #         if file.endswith('.py'):
-        #             os.remove(os.path.join(root, file))
-
-setup(
-    name='repositoryZN',
-    version='1.2.202505131440',
-    description='Repository Package', # 描述信息
-    author='Xie Zhou Yang', # 作者
-    packages=find_packages(),
-    cmdclass={
-        'build_py': CustomBuildPy,
-        'install': InstallCommand,
-    },
-    package_data={
-        'utils': ['*.pyc'],
-        'utils.minioUtil': ['*.pyc'],
-        'utils.rdbmsUtil': ['*.pyc']
-    },
-    exclude_package_data={'': ['*.py']}
-)
-
-# 获取 setup.py 所在的绝对路径
-setup_dir = os.path.abspath(os.path.dirname(__file__))
-# 编译打包后,删除pyc后缀文件
-for root, _, files in os.walk(setup_dir):
-    for file in files:
-        if file.endswith('.pyc') or file.endswith('.c'):
-            print(os.path.join(root, file))
-            os.remove(os.path.join(root, file))
+        self.run_command("build_py")
+        self.plat_name = self.get_platform()
+        bdist_wheel.run(self)
+
+    def get_platform(self):
+        if sys.platform == "win32":
+            return "win_amd64"
+        elif sys.platform == "linux":
+            return "linux_x86_64"
+        else:
+            return super().get_platform()
+
+if __name__ == "__main__":
+    try:
+        subprocess.run([sys.executable, "-m", "nuitka", "--version"], capture_output=True, check=True)
+    except subprocess.CalledProcessError:
+        log("请先安装 Nuitka: pip install nuitka", "ERROR")
+        sys.exit(1)
+
+    clean_build_dir()
+
+    # 动态确定要安装的包和模块
+    packages = []
+    if os.path.isdir("utils"):
+        packages.append("utils")
+    # 根目录下的顶级模块(如果存在这些 .py 文件,它们会被编译成 .pyd 并安装)
+    py_modules = []
+    for mod in ["jsonUtil", "logUtil", "csvFileUtil"]:
+        if os.path.exists(f"{mod}.py"):
+            py_modules.append(mod)
+
+    setup(
+        name=PACKAGE_NAME,
+        version="1.0.202508190930",
+        description="Repository ZN Package (compiled, flat structure)",
+        author="Xie Zhou Yang",
+        packages=packages,
+        py_modules=py_modules,
+        package_dir={"": "."},
+        cmdclass={
+            "build_py": CustomBuildPy,
+            "bdist_wheel": CustomBdistWheel,
+        },
+        install_requires=INSTALL_REQUIRES,
+        zip_safe=False,
+    )