feat: 添加可执行文件打包支持
- 新增CLI工具打包脚本build_exe.py,生成stp2glb.exe - 新增Web服务打包脚本build_web_exe.py,生成stp2glb-server.exe - 新增PyInstaller配置文件build.spec - 更新CLAUDE.md文档添加exe打包使用说明 - 支持完全独立部署,无需Python环境 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6f72a1b477
commit
ba45b855c6
49
CLAUDE.md
49
CLAUDE.md
@ -110,6 +110,55 @@ http://localhost:8000/docs
|
|||||||
python main.py test.stp test.glb
|
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功能
|
||||||
|
|
||||||
## 关键技术细节
|
## 关键技术细节
|
||||||
|
|
||||||
### 转换流程
|
### 转换流程
|
||||||
|
|||||||
58
build.spec
Normal file
58
build.spec
Normal file
@ -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,
|
||||||
|
)
|
||||||
89
build_exe.py
Normal file
89
build_exe.py
Normal file
@ -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()
|
||||||
86
build_web_exe.py
Normal file
86
build_web_exe.py
Normal file
@ -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()
|
||||||
Loading…
Reference in New Issue
Block a user