main.spec 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # main.spec
  2. import sys
  3. import os
  4. import pkgutil
  5. import glob # 新增:匹配所有 module_* 目录
  6. from pathlib import Path
  7. # 1. 动态获取项目根目录(无需手动写死)
  8. PROJECT_ROOT = Path('/root/.hd/hd-crane-gs/backend').absolute() # spec文件所在目录 = 项目根目录
  9. # 修复:扫描所有 module_* 目录并构建 datas 配置
  10. def get_module_datas():
  11. """获取所有 module_* 目录的打包配置"""
  12. api_v1_dir = PROJECT_ROOT / "app" / "api" / "v1"
  13. module_datas = []
  14. if api_v1_dir.exists():
  15. # 匹配所有 module_* 目录(兼容所有系统)
  16. module_dirs = glob.glob(str(api_v1_dir / "module_*"))
  17. for mod_dir in module_dirs:
  18. mod_dir_path = Path(mod_dir)
  19. if mod_dir_path.is_dir():
  20. # 格式:(源目录, 目标目录) → 保持原结构 app/api/v1/module_xxx
  21. target_path = str(mod_dir_path.relative_to(PROJECT_ROOT))
  22. module_datas.append((str(mod_dir_path), target_path))
  23. return module_datas
  24. def get_all_needed_modules():
  25. all_modules = []
  26. # 扫描 app 目录下所有 .py 文件(排除 __init__.py)
  27. all_py_files = PROJECT_ROOT / "app"
  28. for file in all_py_files.rglob("*.py"):
  29. if file.name == "__init__.py":
  30. continue
  31. rel_path = file.relative_to(PROJECT_ROOT)
  32. mod_name = rel_path.with_suffix("").as_posix().replace("/", ".")
  33. all_modules.append(mod_name)
  34. all_modules = list(set(all_modules))
  35. print(f"✅ 扫描到 {len(all_modules)} 个模块")
  36. return all_modules
  37. # 执行扫描,获取所有模块
  38. hidden_imports = get_all_needed_modules()
  39. # 获取 module_* 目录的 datas 配置
  40. module_datas = get_module_datas()
  41. # 2. 导入PyInstaller核心模块
  42. from PyInstaller.utils.hooks import collect_all
  43. from PyInstaller.building.build_main import Analysis, PYZ, EXE, COLLECT
  44. # 基础配置
  45. block_cipher = None
  46. # 3. 收集第三方依赖(asyncmy)
  47. asyncmy_datas, asyncmy_binaries, asyncmy_hiddenimports = collect_all('asyncmy')
  48. # 4. 分析项目代码(核心修复:添加 module_datas)
  49. a = Analysis(
  50. ['main.py'], # 主文件(确保在 PROJECT_ROOT 下)
  51. pathex=[str(PROJECT_ROOT)], # 项目根目录
  52. binaries=asyncmy_binaries,
  53. datas=[
  54. *asyncmy_datas,
  55. *module_datas, # 关键:添加所有 module_* 目录的打包配置
  56. # 新增:打包所有 __init__.py(确保包目录被识别)
  57. (str(PROJECT_ROOT / "app"), "app"),
  58. (os.path.join(PROJECT_ROOT, "env"), "env"),
  59. (os.path.join(PROJECT_ROOT, "static"), "static"),
  60. (os.path.join(PROJECT_ROOT, "banner.txt"), "."),
  61. (os.path.join(PROJECT_ROOT, "app/api/v1/module_generator/gencode/templates"), "app/api/v1/module_generator/gencode/templates"),
  62. (os.path.join(PROJECT_ROOT, "app/scripts/data"), "app/scripts/data"),
  63. ],
  64. hiddenimports=[
  65. # 第三方依赖
  66. *asyncmy_hiddenimports,
  67. 'asyncmy.auth',
  68. 'asyncmy.connection',
  69. 'asyncmy.cursors',
  70. 'asyncmy.protocol',
  71. 'asyncmy.utils',
  72. 'passlib',
  73. 'passlib.context',
  74. 'passlib.handlers',
  75. 'passlib.handlers.bcrypt',
  76. 'passlib.handlers.md5_crypt',
  77. # 项目模块
  78. *hidden_imports,
  79. ],
  80. hookspath=[],
  81. hooksconfig={},
  82. runtime_hooks=[],
  83. excludes=[],
  84. noarchive=False, # 关键:不归档,保留目录结构
  85. )
  86. # 5. 打包为PYZ归档
  87. pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
  88. # 6. 生成可执行文件
  89. exe = EXE(
  90. pyz,
  91. a.scripts,
  92. a.binaries,
  93. a.zipfiles,
  94. a.datas,
  95. [],
  96. name='main',
  97. debug=False,
  98. bootloader_ignore_signals=False,
  99. strip=False,
  100. upx=False, # 欧拉系统禁用UPX
  101. runtime_tmpdir=None,
  102. console=True,
  103. disable_windowed_traceback=False,
  104. argv_emulation=False,
  105. target_arch=None,
  106. )