#!/usr/bin/env python3 """ 跨平台 Python 模块编译脚本 将核心 Python 模块编译为机器码(.pyd 或 .so)以提升性能。 支持的平台: - Windows: 生成 .pyd 文件 - Linux: 生成 .so 文件 使用方法: python compile_machine_code.py [options] 选项: --compile, -c 编译指定的模块(默认) --list, -l 列出已编译的模块 --clean, -k 清理编译生成的文件 --help, -h 显示帮助信息 注意: 1. 需要安装 C 编译器 (Windows 上需要 Visual Studio Build Tools, Linux 上需要 GCC) 2. 需要安装 mypyc: pip install mypyc 3. 编译后的文件是平台相关的,不能跨平台复制 4. 建议在部署的目标环境上运行此脚本 """ import os import sys import glob import subprocess import shutil import argparse # 检测当前平台和 Python 版本 PLATFORM = sys.platform PYTHON_VERSION = f"{sys.version_info.major}{sys.version_info.minor}" # 例如 "314" if PLATFORM.startswith('win'): EXTENSION = '.pyd' BUILD_PREFIX = f'cp{PYTHON_VERSION}-win_amd64' BUILD_PATH = os.path.join('build', f'lib.win-amd64-cpython-{PYTHON_VERSION}') elif PLATFORM.startswith('linux'): EXTENSION = '.so' BUILD_PREFIX = f'cp{PYTHON_VERSION}-x86_64-linux-gnu' BUILD_PATH = os.path.join('build', f'lib.linux-x86_64-cpython-{PYTHON_VERSION}') else: print(f"不支持的平台: {PLATFORM}") sys.exit(1) # 要编译的模块列表 # 注意:Mypyc 对动态特性支持有限,只选择计算密集或类型明确的模块 MODULES = [ # 工具模块 'core/utils/json_utils.py', # JSON 处理 'core/utils/executor.py', # 代码执行引擎 'core/utils/singleton.py', # 单例模式基类 'core/utils/exceptions.py', # 自定义异常 'core/utils/logger.py', # 日志模块 # 核心管理模块 'core/managers/command_manager.py', # 指令匹配和分发 'core/managers/admin_manager.py', # 管理员管理 'core/managers/permission_manager.py', # 权限管理 'core/managers/plugin_manager.py', # 插件管理器 'core/managers/redis_manager.py', # Redis 管理器 'core/managers/image_manager.py', # 图片管理器 # 核心基础模块 'core/ws.py', # WebSocket 核心 'core/bot.py', # Bot 核心抽象 'core/config_loader.py', # 配置加载 'core/config_models.py', # 配置模型 'core/permission.py', # 权限枚举 # API 模块 - 注意:这些类会被 Bot 类多继承使用 # 因此不适合编译,否则会导致 "multiple bases have instance lay-out conflict" 错误 # 'core/api/base.py', # API 基础类 # 'core/api/account.py', # 账号相关 API # 'core/api/friend.py', # 好友相关 API # 'core/api/group.py', # 群组相关 API # 'core/api/media.py', # 媒体相关 API # 'core/api/message.py', # 消息相关 API # 数据模型(适合编译的高频使用数据类) 'models/message.py', # 消息段模型 'models/sender.py', # 发送者模型 'models/objects.py', # API 响应数据模型 # 事件处理相关 'core/handlers/event_handler.py', # 事件处理器 # 注意:以下文件不适合编译 # - 主程序文件(main.py) # - 测试文件(tests/目录) # - 插件文件(plugins/目录) # - 编译脚本(compile_machine_code.py等) # - 临时文件(scratch_files/目录) # - 抽象基类(models/events/base.py) # - 事件工厂(models/events/factory.py) # - 包含复杂动态特性的文件 ] def list_compiled_modules(): """列出已编译的模块""" print(f"\n已编译的 {PLATFORM} 模块:") print("=" * 50) # 查找所有编译后的文件 compiled_files = [] for ext in [EXTENSION, f'__mypyc{EXTENSION}']: compiled_files.extend(glob.glob(f'**/*{ext}', recursive=True)) # 过滤掉虚拟环境中的文件 compiled_files = [f for f in compiled_files if 'venv' not in f] if compiled_files: for f in sorted(compiled_files): size = os.path.getsize(f) // 1024 # KB print(f"{f} ({size} KB)") else: print(f"未找到已编译的 {EXTENSION} 文件") print(f"\n总计: {len(compiled_files)} 个文件") def clean_compiled_files(): """清理编译生成的文件""" print(f"\n清理编译生成的 {EXTENSION} 文件...") # 查找所有编译后的文件 compiled_files = [] for ext in [EXTENSION, f'__mypyc{EXTENSION}']: compiled_files.extend(glob.glob(f'**/*{ext}', recursive=True)) # 过滤掉虚拟环境中的文件 compiled_files = [f for f in compiled_files if 'venv' not in f] if compiled_files: for f in sorted(compiled_files): try: os.remove(f) print(f"已删除: {f}") except Exception as e: print(f"删除失败 {f}: {e}") # 清理 build 目录 if os.path.exists('build'): try: shutil.rmtree('build') print("已删除 build 目录") except Exception as e: print(f"删除 build 目录失败: {e}") else: print(f"没有可清理的 {EXTENSION} 文件") def get_platform_specific_module_name(module_path): """获取平台特定的模块文件名""" module_name = module_path.replace('.py', '') return f"{module_name}.{BUILD_PREFIX}{EXTENSION}" def compile_module(module_path): """编译单个模块""" print(f"\n编译: {module_path}") try: # 直接调用 mypyc 命令行工具 result = subprocess.run( [sys.executable, '-m', 'mypyc', module_path], capture_output=True, text=True, check=True ) # 获取平台特定的模块名 platform_module = get_platform_specific_module_name(module_path) mypyc_platform_module = platform_module.replace(EXTENSION, f'__mypyc{EXTENSION}') # 检查编译产物是否在当前目录 if os.path.exists(platform_module): print(f" ✓ 编译成功: {platform_module}") return True else: # 检查 build 目录中是否有编译产物 build_module_path = os.path.join(BUILD_PATH, platform_module) build_mypyc_path = os.path.join(BUILD_PATH, mypyc_platform_module) if os.path.exists(build_module_path): # 如果在 build 目录中,复制到正确位置 os.makedirs(os.path.dirname(platform_module), exist_ok=True) shutil.copy2(build_module_path, platform_module) shutil.copy2(build_mypyc_path, mypyc_platform_module) print(f" ✓ 编译成功(已从 build 目录复制): {platform_module}") return True else: print(f" ✗ 编译失败:找不到编译产物") if result.stdout: print(f" 编译输出:{result.stdout[:500]}...") if result.stderr: print(f" 错误信息:{result.stderr[:500]}...") return False except subprocess.CalledProcessError as e: print(f" ✗ 编译失败,退出码: {e.returncode}") if e.stdout: print(f" 编译输出:{e.stdout[:500]}...") if e.stderr: print(f" 错误信息:{e.stderr[:500]}...") return False except Exception as e: print(f" ✗ 编译失败,意外错误: {e}") return False def should_skip_module(module_path): """检查模块是否应该被跳过编译""" try: with open(module_path, 'r', encoding='utf-8') as f: content = f.read() # 检查是否包含抽象基类相关代码 if 'from abc import ABC' in content or 'from abc import abstractmethod' in content: return True, "包含抽象基类,不适合编译" # 检查是否包含动态特性 if 'eval(' in content or 'exec(' in content or 'getattr(' in content or 'setattr(' in content: return True, "包含动态特性,不适合编译" return False, "" except Exception as e: return True, f"读取文件时出错: {e}" def compile_all_modules(): """编译所有指定的模块""" print(f"\n开始编译 {len(MODULES)} 个模块 (平台: {PLATFORM})") print("=" * 60) # 验证模块文件是否存在并检查是否适合编译 valid_modules = [] for module_path in MODULES: if os.path.exists(module_path): should_skip, reason = should_skip_module(module_path) if should_skip: print(f"跳过: {module_path} ({reason})") else: valid_modules.append(module_path) else: print(f"警告: 模块 {module_path} 不存在,将被跳过") if not valid_modules: print("错误: 没有有效的模块可编译") return False # 编译模块 success_count = 0 for module_path in valid_modules: if compile_module(module_path): success_count += 1 print(f"\n" + "=" * 60) print(f"编译完成: {success_count}/{len(valid_modules)} 个模块成功") if success_count == len(valid_modules): print("✓ 所有模块编译成功") return True else: print("✗ 部分模块编译失败") return False def main(): """主函数""" # 检查 Python 版本 if not (sys.version_info.major == 3 and sys.version_info.minor == 14): print("警告: 推荐使用 Python 3.14 以获得最佳性能") print(f"当前版本: {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}") print("继续编译可能导致兼容性问题") print() parser = argparse.ArgumentParser(description='跨平台 Python 模块编译脚本') group = parser.add_mutually_exclusive_group() group.add_argument('--compile', '-c', action='store_true', default=True, help='编译指定的模块 (默认)') group.add_argument('--list', '-l', action='store_true', help='列出已编译的模块') group.add_argument('--clean', '-k', action='store_true', help='清理编译生成的文件') args = parser.parse_args() # 检查是否安装了 mypyc try: import mypyc except ImportError: print("错误: 未安装 mypyc,请先安装: pip install mypyc") sys.exit(1) if args.list: list_compiled_modules() elif args.clean: clean_compiled_files() else: compile_all_modules() print("\n使用 --list 选项查看已编译的模块") if __name__ == '__main__': main()