diff --git a/CLAUDE.md b/CLAUDE.md index 5367c67..fba3620 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -110,6 +110,55 @@ http://localhost:8000/docs python main.py test.stp test.glb ``` +## 可执行文件打包 + +项目支持打包成独立的exe文件,可在无Python环境的机器上直接运行。 + +### 打包CLI工具 +```bash +# 安装PyInstaller +pip install pyinstaller + +# 打包CLI工具 +python build_exe.py +``` + +### 打包Web服务 +```bash +# 打包Web服务 +python build_web_exe.py +``` + +### 打包结果 +打包完成后将在`dist/`目录生成两个独立的exe文件: + +- **`dist/stp2glb.exe`** - CLI工具(284MB) + ```bash + # 基本转换 + stp2glb.exe input.stp output.glb + + # 高质量转换 + stp2glb.exe input.stp output.glb --quality high + + # 查看帮助 + stp2glb.exe --help + ``` + +- **`dist/stp2glb-server.exe`** - Web服务(约300MB) + ```bash + # 启动Web服务 + stp2glb-server.exe + + # 访问API文档: http://localhost:8000/docs + # 健康检查: http://localhost:8000/health + ``` + +### 部署特性 +- **完全独立**: 无需Python环境和依赖库 +- **绿色软件**: 单文件部署,无需安装 +- **内网兼容**: 可在离线环境直接运行 +- **功能完整**: 保留所有CLI和Web API功能 + ## 关键技术细节 ### 转换流程 diff --git a/build.spec b/build.spec new file mode 100644 index 0000000..7224e30 --- /dev/null +++ b/build.spec @@ -0,0 +1,58 @@ +# -*- mode: python ; coding: utf-8 -*- + +a = Analysis( + ['main.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[ + 'OCC', + 'OCC.Core', + 'OCC.Core.STEPCAFControl_Reader', + 'OCC.Core.RWGltf_CafWriter', + 'OCC.Core.TDocStd_Document', + 'OCC.Core.XCAFDoc_DocumentTool', + 'OCC.Core.XCAFApp_Application', + 'OCC.Core.BRepMesh_IncrementalMesh', + 'OCC.Core.BRep_Builder', + 'OCC.Core.TopoDS_Compound', + 'trimesh', + 'trimesh.exchange', + 'trimesh.exchange.gltf', + 'fastapi', + 'uvicorn', + 'pydantic', + ], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=None, + noarchive=False, +) + +pyz = PYZ(a.pure, a.zipped_data, cipher=None) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='stp2glb', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) \ No newline at end of file diff --git a/build_exe.py b/build_exe.py new file mode 100644 index 0000000..280688d --- /dev/null +++ b/build_exe.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +""" +构建脚本 - 将项目打包成可执行文件 +""" + +import subprocess +import sys +import os +from pathlib import Path + +def install_pyinstaller(): + """安装PyInstaller""" + print("安装PyInstaller...") + try: + subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"], + check=True, capture_output=True) + print("✅ PyInstaller安装完成") + except subprocess.CalledProcessError as e: + print(f"❌ PyInstaller安装失败: {e}") + return False + return True + +def build_exe(): + """构建可执行文件""" + print("\n开始构建可执行文件...") + + # 构建命令 + cmd = [ + sys.executable, "-m", "PyInstaller", + "--onefile", + "--name", "stp2glb", + "--console", + "--add-data", "core;core", + "--add-data", "utils;utils", + "--hidden-import", "OCC", + "--hidden-import", "OCC.Core", + "--hidden-import", "trimesh", + "--hidden-import", "fastapi", + "--hidden-import", "uvicorn", + "--hidden-import", "pydantic", + "main.py" + ] + + try: + result = subprocess.run(cmd, check=True, capture_output=True, text=True) + print("✅ 构建成功!") + + # 检查输出文件 + exe_path = Path("dist/stp2glb.exe") + if exe_path.exists(): + size_mb = exe_path.stat().st_size / (1024 * 1024) + print(f"📁 可执行文件: {exe_path.absolute()}") + print(f"📏 文件大小: {size_mb:.1f} MB") + + return True + + except subprocess.CalledProcessError as e: + print(f"❌ 构建失败: {e}") + if e.stdout: + print("标准输出:", e.stdout) + if e.stderr: + print("错误输出:", e.stderr) + return False + +def main(): + """主函数""" + print("🔨 STP2GLB 可执行文件构建工具") + print("=" * 40) + + # 检查是否已安装PyInstaller + try: + import PyInstaller + print("✅ 找到PyInstaller") + except ImportError: + if not install_pyinstaller(): + sys.exit(1) + + # 构建可执行文件 + if build_exe(): + print("\n🎉 构建完成!") + print("使用方法:") + print(" dist/stp2glb.exe input.stp output.glb") + print(" dist/stp2glb.exe --help") + else: + print("\n💥 构建失败") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/build_web_exe.py b/build_web_exe.py new file mode 100644 index 0000000..cfbd362 --- /dev/null +++ b/build_web_exe.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +""" +Web服务打包脚本 - 将FastAPI服务打包成可执行文件 +""" + +import subprocess +import sys +import os +from pathlib import Path + +def build_web_exe(): + """构建Web服务可执行文件""" + print("\n开始构建Web服务可执行文件...") + + # 构建命令 + cmd = [ + sys.executable, "-m", "PyInstaller", + "--onefile", + "--name", "stp2glb-server", + "--console", + "--add-data", "core;core", + "--add-data", "utils;utils", + "--add-data", "api;api", + "--add-data", "services;services", + "--hidden-import", "OCC", + "--hidden-import", "OCC.Core", + "--hidden-import", "trimesh", + "--hidden-import", "fastapi", + "--hidden-import", "uvicorn", + "--hidden-import", "pydantic", + "--hidden-import", "uvicorn.protocols.http.auto", + "--hidden-import", "uvicorn.protocols.websockets.auto", + "--hidden-import", "uvicorn.lifespan.on", + "app.py" + ] + + try: + result = subprocess.run(cmd, check=True, capture_output=True, text=True) + print("✅ Web服务构建成功!") + + # 检查输出文件 + exe_path = Path("dist/stp2glb-server.exe") + if exe_path.exists(): + size_mb = exe_path.stat().st_size / (1024 * 1024) + print(f"📁 可执行文件: {exe_path.absolute()}") + print(f"📏 文件大小: {size_mb:.1f} MB") + + return True + + except subprocess.CalledProcessError as e: + print(f"❌ Web服务构建失败: {e}") + if e.stdout: + print("标准输出:", e.stdout) + if e.stderr: + print("错误输出:", e.stderr) + return False + +def main(): + """主函数""" + print("🌐 STP2GLB Web服务打包工具") + print("=" * 40) + + # 检查是否已安装PyInstaller + try: + import PyInstaller + print("✅ 找到PyInstaller") + except ImportError: + print("❌ 未找到PyInstaller,请先运行 pip install pyinstaller") + sys.exit(1) + + # 构建Web服务可执行文件 + if build_web_exe(): + print("\n🎉 Web服务构建完成!") + print("使用方法:") + print(" dist/stp2glb-server.exe") + print(" 访问 http://localhost:8000/docs 查看API文档") + print(" 访问 http://localhost:8000/health 进行健康检查") + print("\n📝 现在你有两个exe文件:") + print(" dist/stp2glb.exe - CLI工具") + print(" dist/stp2glb-server.exe - Web服务") + else: + print("\n💥 Web服务构建失败") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file