Harden Windows and Linux packaging

This commit is contained in:
tian 2026-03-15 01:20:20 +08:00
parent cfe92a9a80
commit 3d994dd556
8 changed files with 1557 additions and 30 deletions

View File

@ -1,8 +1,11 @@
import os
import sys
import traceback
from datetime import datetime
# 添加项目根目录到 Python 路径
project_root = os.path.dirname(os.path.abspath(__file__))
# 添加项目根目录到 Python 路径。打包后可通过环境变量覆盖,
# 以适配 Windows dist / Linux AppDir 等不同目录布局。
project_root = os.environ.get("EG_PROJECT_ROOT") or os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, project_root)
# 设置工作目录为项目根目录
@ -20,30 +23,34 @@ sys.path.insert(0, render_pipeline_file_path)
icons_path = os.path.join(project_root, "icons")
sys.path.insert(0, icons_path)
def _write_startup_log(message: str) -> None:
"""Write startup diagnostics next to the executable/source root."""
log_path = os.path.join(project_root, "eg_startup.log")
with open(log_path, "a", encoding="utf-8") as handle:
handle.write(f"\n[{datetime.now().isoformat(timespec='seconds')}] {message}\n")
# 现在可以导入并运行主程序
if __name__ == "__main__":
args = sys.argv[1:]
# args = "/home/tiger/桌面/Test1"
# args = "C:/Users/29381/Desktop/1"
print(f'Path is {args}')
# 将整个列表转换为字符串(包括方括号)
args_str = ''.join(args)
try:
args = sys.argv[1:]
_write_startup_log(f"Launch args: {args}")
_write_startup_log(f"Working directory: {os.getcwd()}")
from main import MyWorld
if args:
# print(f"[DEBUG] 启动时传入项目路径: {args_str}")
# 创建应用实例
app = MyWorld()
# 如果传入了项目路径,尝试打开项目
if hasattr(app, 'project_manager'):
# print(f"[DEBUG] 尝试打开项目: {args_str}")
success = app.project_manager.openProject(args_str)
# print(f"[DEBUG] 项目打开结果: {success}")
# 将整个列表转换为字符串(包括方括号)
args_str = ''.join(args)
from main import MyWorld
if args:
app = MyWorld()
if hasattr(app, 'project_manager'):
app.project_manager.openProject(args_str)
app.run()
else:
# print(f"[DEBUG] 项目管理器未初始化")
pass
app.run()
else:
# print(f"[DEBUG] 无项目路径,正常启动")
app = MyWorld()
app.run()
app = MyWorld()
app.run()
except Exception:
_write_startup_log("Unhandled exception during startup:")
_write_startup_log(traceback.format_exc())
raise

View File

@ -0,0 +1,395 @@
# 元泰 EG 构建完整指南
本文档提供从零开始构建元泰 EG 安装程序的完整步骤。
---
## 📋 环境要求
### 必须条件
| 组件 | 版本 | 说明 |
|------|------|------|
| **Python** | **3.11.x** | 必须使用 3.11,其他版本不保证兼容 |
| Windows | 10/11 | 64位系统 |
| 磁盘空间 | 5GB+ | 构建需要大量临时文件 |
| 内存 | 4GB+ | 建议 8GB |
### 检查 Python 版本
```powershell
# 检查版本
python --version
# 必须显示: Python 3.11.x
# 使用项目提供的检查脚本
python build_scripts/check_python_version.py
```
如果版本不对,请参考 `build_scripts/PYTHON_VERSION_REQUIREMENT.md` 安装正确版本。
---
## 🚀 快速开始5分钟
### 步骤 1: 安装依赖
```powershell
# 确保是 Python 3.11
python --version
# 安装最小依赖(推荐)
python -m pip install -r requirements/requirements-minimal.txt
```
### 步骤 2: 验证安装
```powershell
# 测试程序能否运行
python Start_Run.py
```
### 步骤 3: 构建安装程序
```powershell
# 进入 VS Dev Shell必须有 C++ 编译器)
# 然后执行构建脚本
.\build_scripts\build_windows.ps1 -Version "1.0.0"
```
---
## 📌 打包原则
从 2026-03 的修复开始,项目采用“保守打包优先”的策略:
- 优先保证发行版可运行,不先追求安装包最小化
- 对 `RenderPipeline`、动态插件、文件路径动态导入使用显式白名单
- 将 `RenderPipelineFile`、`Resources`、`config`、`demo` 等目录视为运行时资源,整体复制
- 保留启动日志 `eg_startup.log`,用于定位打包后启动异常
详细背景和源码整改建议请参考:
- `build_scripts/PACKAGING_RISK_AUDIT.md`
---
## 📦 依赖说明
### 最小依赖requirements-minimal.txt
只包含程序运行必需的依赖:
```
Panda3D==1.10.16 # 3D 引擎核心
panda3d-frame # Panda3D 扩展
panda3d-imgui==1.1.0 # ImGui 集成
imgui-bundle==1.92.4 # ImGui 绑定
openvr==2.2.0 # VR 支持
Pillow==11.3.0 # 图像处理
PyYAML==5.4.1 # 配置解析
psutil==5.9.0 # 系统工具
```
### 已移除的依赖
以下依赖已从项目中移除,节省约 **170MB**
| 依赖 | 节省空间 | 原因 |
|------|---------|------|
| PyQt5 | ~50 MB | 未使用 |
| PySide6 | ~100 MB | 未使用 |
| PyQt5-Qt5, PyQt5_sip 等 | ~20 MB | 配套库 |
### 清理旧依赖
如果之前安装过旧依赖,建议清理:
```powershell
# 卸载未使用的 Qt 库
python -m pip uninstall -y PyQt5 PyQt5-Qt5 PyQt5_sip PySide6 PySide6_Addons PySide6_Essentials shiboken6
# 清理 pip 缓存
python -m pip cache purge
```
---
## 🔧 详细构建步骤
### 第一步:环境准备
1. **安装 Python 3.11**
- 下载https://www.python.org/downloads/release/python-3119/
- 安装时勾选 "Add Python to PATH"
2. **安装 Visual Studio Build Tools**Windows 必需)
- 下载https://visualstudio.microsoft.com/visual-cpp-build-tools/
- 选择 "使用 C++ 的桌面开发"
- 或安装完整 Visual Studio 2022
3. **验证环境**
```powershell
# 检查 Python
python --version # Python 3.11.x
# 检查编译器(在 VS Dev Shell 中)
cl # 应显示版本信息
```
### 第二步:安装项目依赖
```powershell
# 进入项目目录
cd C:\path\to\EG
# 安装依赖
python -m pip install -r requirements/requirements-minimal.txt
# 验证安装
python -c "import panda3d.core; import imgui_bundle; print('✓ 依赖安装成功')"
```
### 第三步:运行测试
```powershell
# 测试程序能否正常启动
python Start_Run.py
```
### 第四步:打开 VS Dev Shell
**什么是 VS Dev Shell**
VS Dev ShellVisual Studio 开发人员命令提示符)是一个包含 C++ 编译器cl.exe的特殊命令行窗口Nuitka 需要它来编译 Python 代码为机器码。
**如何打开 VS Dev Shell**
#### 方法 1: 通过开始菜单(推荐)
1. 按 `Win` 键打开开始菜单
2. 搜索:`Developer Command Prompt`
- 或中文:`开发人员命令提示符`
3. 点击 **"Developer Command Prompt for VS 2022"**
4. 建议右键选择 **"以管理员身份运行"**
#### 方法 2: 通过 Visual Studio
1. 打开 **Visual Studio 2022**
2. 点击菜单栏的 **"工具"** → **"命令行"** → **"开发者命令提示符"**
#### 方法 3: 使用 PowerShell 命令
```powershell
# 如果知道 VS 安装路径
& "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64
```
**验证 VS Dev Shell 已正确打开:**
```powershell
# 在 VS Dev Shell 中执行
cl
# 应该显示类似:
# 用于 x64 的 Microsoft (R) C/C++ 优化编译器 19.xx.xxxxx 版
```
### 第五步:构建安装程序
**VS Dev Shell** 中执行:
#### 方法 A使用构建脚本推荐
```powershell
# 1. 进入项目目录
cd C:\Users\Tellme\apps\EG
# 2. 切换到 PowerShell如果当前是 cmd
powershell
# 3. 执行构建
.\build_scripts\build_windows.ps1 -Version "1.0.0"
```
该脚本已包含以下关键修复:
- 修正 `RenderPipeline` 在打包后可能找错 `config/pipeline.yaml` 的问题
- 显式包含 `rpcore`、`rplibs`、`rpplugins`
- 自动探测并尽量纳入 `plugins`、`gltf`、`playwright`、`PyQt5` 等可选包
- 自动复制运行时资源目录
#### 方法 B手动构建
```powershell
# 在 VS Dev Shell 中
cd C:\path\to\EG
python -m nuitka `
--standalone `
--include-package=panda3d `
--include-package=direct `
--include-package=imgui_bundle `
--include-package=p3dimgui `
--include-package=openvr `
--include-package=RenderPipelineFile `
--include-package=rpcore `
--include-package=rplibs `
--include-package=rpplugins `
--follow-import-to=rpcore `
--follow-import-to=rpplugins `
--include-data-files="$env:APPDATA\Python\Python311\site-packages\imgui_bundle\glfw3.dll=imgui_bundle\glfw3.dll" `
--windows-icon-from-ico=icons/app.ico `
--windows-disable-console `
--output-dir=build/nuitka `
--jobs=4 `
Start_Run.py
```
说明:
- 当前更推荐 `standalone + 资源目录复制` 的方式,而不是 `onefile`
- `RenderPipeline`、动态插件和外部资源目录在 `onefile` 下更容易出现路径问题
### 第五步:打包资源
构建完成后,手动复制资源文件:
```powershell
# 创建输出目录
$Version = "1.0.0"
New-Item -ItemType Directory -Force -Path "dist\EG_$Version" | Out-Null
# 复制可执行文件
Copy-Item "build\nuitka\Start_Run.exe" "dist\EG_$Version\元泰.exe" -Force
# 复制资源目录
$dirs = @("config", "icons", "Resources", "tex", "templates",
"core", "gui", "project", "scene", "scripts",
"ssbo_component", "tools", "TransformGizmo", "ui",
"RenderPipelineFile", "vr_actions", "new")
foreach ($dir in $dirs) {
Copy-Item $dir "dist\EG_$Version\$dir" -Recurse -Force
}
Write-Host "✓ 打包完成: dist\EG_$Version"
```
如果使用 `build_windows.ps1`,通常不需要再手动执行这一段,因为脚本已经自动复制资源。
### Linux 构建说明
Linux 构建脚本:
```bash
./build_scripts/build_linux.sh "1.0.0"
```
Linux 脚本已按与 Windows 相同的思路补强:
- 使用保守白名单显式包含核心包
- 自动探测并尽量纳入可选依赖
- 整体复制运行时资源目录
- 在 AppRun 中设置 `EG_PROJECT_ROOT`
- 让 AppImage 内的程序从真实资源根目录启动
---
## 📁 构建输出
构建完成后,输出目录结构:
```
dist/EG_1.0.0/
├── 元泰.exe # 主程序 (~120MB)
├── config/ # 配置文件
├── icons/ # 图标资源
├── Resources/ # 模型、材质等资源
├── RenderPipelineFile/ # 渲染管线
├── ... # 其他数据目录
└── (无 .py 源代码) # 已编译为机器码
```
---
## 🐛 常见问题
### Q1: Python 版本错误
**错误**`必须使用 Python 3.11,当前版本是 3.13.5`
**解决**
```powershell
# 安装 Python 3.11
# 参考 build_scripts/PYTHON_VERSION_REQUIREMENT.md
```
### Q2: 找不到 C++ 编译器
**错误**`未找到 Visual C++ 编译器`
**解决**
1. 安装 Visual Studio Build Tools
2. 在 **VS Dev Shell** 中运行构建
### Q3: 缺少 DLL
**错误**`Cannot find glfw3.dll`
**解决**:确保 `--include-data-files` 参数正确指向 DLL 路径:
```powershell
# 注意 Python 版本路径
# Python 3.11: ...\Python311\...
# Python 3.13: ...\Python313\...
```
### Q4: ModuleNotFoundError: rpcore
**错误**`No module named 'rpcore'`
**解决**:确保使用 `--follow-import-to=rpcore`
### Q5: 找不到 pipeline.yaml / rpplugins.*
**错误示例**
- `Failed to load YAML file: File not found`
- `No such file or directory: /$$rpconfig/pipeline.yaml`
- `ModuleNotFoundError: No module named 'rpplugins.ao'`
**原因**
- `RenderPipeline` 在打包后可能把基路径识别错
- 插件通过配置文件动态加载,打包器不会稳定自动发现
**解决**
- 使用仓库中的最新 `build_windows.ps1` / `build_linux.sh`
- 确保 `RenderPipelineFile` 被整体复制
- 确保 `rpcore`、`rplibs`、`rpplugins` 被显式纳入打包
### Q6: 双击后闪退,没有控制台输出
**解决**
- 查看发行目录中的 `eg_startup.log`
- 该文件会记录启动阶段 traceback
- 遇到打包问题时,优先保留这个日志进行排查
---
## 📚 相关文档
| 文档 | 用途 |
|------|------|
| `build_scripts/BUILD_README.md` | 构建说明 |
| `build_scripts/INSTALLER_GUIDE.md` | 完整指南 |
| `build_scripts/QUICK_REFERENCE.md` | 快速参考 |
| `build_scripts/ICON_GUIDE.md` | 图标准备 |
| `build_scripts/PACKAGING_RISK_AUDIT.md` | 打包风险与整改建议 |
| `build_scripts/PYTHON_VERSION_REQUIREMENT.md` | Python 版本要求 |
| `build_scripts/PACKAGING_CHECKLIST.md` | 打包检查清单 |
---
**最后更新**: 2024年

View File

@ -0,0 +1,246 @@
# 元泰 EG 打包风险与源码整改清单
本文档用于记录当前项目在 Windows/Linux 打包时容易导致运行失败、功能失效或行为不一致的风险点,并给出建议的整改方向。
目标原则:
- 打包优先保证可运行,不追求安装包最小化
- 对动态导入、配置驱动加载、原生扩展依赖保持保守策略
- 对已声明移除的依赖,源码中不应再保留残留引用
---
## 一、已确认的打包风险
### 1. RenderPipeline 配置与插件属于运行时动态加载
相关位置:
- [RenderPipelineFile/config/plugins.yaml](/C:/Users/Tellme/apps/EG/RenderPipelineFile/config/plugins.yaml)
- [core/world.py](/C:/Users/Tellme/apps/EG/core/world.py)
- [build_scripts/build_windows.ps1](/C:/Users/Tellme/apps/EG/build_scripts/build_windows.ps1)
问题说明:
- `RenderPipeline` 会根据配置文件在运行时动态加载 `rpplugins.*`
- 这类插件不是普通静态 import打包工具无法稳定自动发现
- 已实际出现 `ModuleNotFoundError: No module named 'rpplugins.ao'`
建议:
- 打包脚本显式包含 `rpcore`、`rplibs`、`rpplugins`
- 保留整个 `RenderPipelineFile` 目录
- 显式设置 `RenderPipeline``base_path``config_dir`
- 后续新增插件时,同步检查打包白名单
### 2. 文件路径驱动的动态模块加载
相关位置:
- [main.py](/C:/Users/Tellme/apps/EG/main.py)
- [core/script_system.py](/C:/Users/Tellme/apps/EG/core/script_system.py)
问题说明:
- 使用 `importlib.util.spec_from_file_location()` 按文件路径加载模块
- 例如 `demo/video_integration.py`
- 这类模块不会被打包器通过静态分析自动发现
建议:
- 继续将对应目录视为运行时资源并整体复制
- 更理想的方案是逐步改为标准包导入
- 所有此类动态加载点都应登记到打包白名单
### 3. 运行时外部工具依赖目录结构
相关位置:
- [core/world.py](/C:/Users/Tellme/apps/EG/core/world.py)
- [core/tool_manager.py](/C:/Users/Tellme/apps/EG/core/tool_manager.py)
问题说明:
- 程序会在运行时启动 `toolkit/day_time_editor/main.py`
- 还会启动 `toolkit/material_editor/main.py`
- 还会启动 `toolkit/plugin_configurator/main.py`
- 如果这些目录未被完整复制,主程序可能能启动,但局部功能会失效
建议:
- 将整个 `RenderPipelineFile` 视为正式运行时资源
- 子工具路径不应依赖“开发目录结构恰好存在”
- 后续可考虑增加启动前存在性检测和更友好的错误提示
---
## 二、建议项目团队整改的源码问题
### 1. 残留的 PyQt5 引用需要清理
相关位置:
- [ui/icon_manager.py](/C:/Users/Tellme/apps/EG/ui/icon_manager.py)
- [core/selection.py](/C:/Users/Tellme/apps/EG/core/selection.py)
- [scene/scene_manager_convert_tiles_mixin.py](/C:/Users/Tellme/apps/EG/scene/scene_manager_convert_tiles_mixin.py)
- [core/world.py](/C:/Users/Tellme/apps/EG/core/world.py)
问题说明:
- 项目文档中已经说明移除了 `PyQt5/PySide6`
- 但源码仍保留多处 `PyQt5` 引用
- 这会导致打包后功能行为不一致,或在特定分支下运行时报错
建议:
- 用当前实际 UI 栈替代残留 Qt 逻辑
- 删除仅为兼容历史方案保留的无效 Qt 代码
- 对必须保留的兼容逻辑,至少统一做降级处理和提示
### 2. Playwright 浏览器依赖不应处于“隐式依赖”状态
相关位置:
- [core/imgui_webview.py](/C:/Users/Tellme/apps/EG/core/imgui_webview.py)
问题说明:
- 该模块依赖 `playwright.async_api`
- 同时还依赖 Chromium 浏览器安装
- 如果发行版没有附带这些条件,对应功能会直接不可用
建议:
- 明确决定是否正式支持此功能
- 如果正式支持,则纳入依赖与打包流程
- 如果不正式支持,则在发行版中禁用并提示,而不是运行时才报错
### 3. glTF 专用加载依赖缺少明确归档
相关位置:
- [ui/panels/animation_tools.py](/C:/Users/Tellme/apps/EG/ui/panels/animation_tools.py)
问题说明:
- 这里存在运行时 `import gltf`
- 这不是启动必经路径,因此问题可能只在用户点击特定功能时暴露
建议:
- 如果该功能是正式能力,则将 `gltf` 明确加入依赖清单和打包清单
- 如果只是补充路径,应在缺模块时给出明确降级提示
### 4. 平台特定依赖应显式治理
相关位置:
- [core/tool_manager.py](/C:/Users/Tellme/apps/EG/core/tool_manager.py)
问题说明:
- 这里使用 `win32gui`、`win32con`
- 当前代码虽然做了异常兜底,但功能可能 silently fail
建议:
- 明确是否正式依赖 pywin32
- 如果依赖,则加入打包环境
- 如果不依赖,则删除窗口激活逻辑或改为可观测提示
### 5. 硬编码开发机路径需要移除
相关位置:
- [core/world.py](/C:/Users/Tellme/apps/EG/core/world.py)
问题说明:
- 存在 `/home/tiger/...` 这类硬编码本地路径
- 即使当前分支未使用,也不应保留在仓库中
建议:
- 清理所有开发机私有绝对路径
- 改为项目资源路径、配置路径或用户可配置路径
---
## 三、建议长期执行的工程规则
### 1. 建立“动态导入登记表”
所有以下模式都必须登记并同步给打包脚本:
- `importlib.import_module`
- `spec_from_file_location`
- `__import__`
- 配置文件驱动插件加载
- 子进程启动外部 Python 工具
### 2. 建立“运行时资源目录白名单”
以下内容不应依赖打包器自动分析:
- 配置文件
- shader
- 模型
- 字体
- 图标
- demo 脚本
- 插件目录
- 工具目录
### 3. 已移除依赖必须同步清理源码
如果项目文档声明:
- 已移除 `PyQt5`
- 已移除 `PySide6`
则源码中不应继续保留这些引用,避免“文档说没有,代码里还在偷偷依赖”的情况。
### 4. 可选功能必须有明确产品策略
对以下类型的依赖,需要明确归类:
- 浏览器内核
- glTF 专用加载器
- 平台专用 API
- 外部命令行工具
只能二选一:
- 正式支持并纳入安装/打包流程
- 默认禁用并提供明确提示
不应继续保持“开发环境可用、发行版碰运气”的状态。
---
## 四、当前打包策略建议
当前推荐策略:
- 尽量显式包含相关包,不追求安装包最小化
- 尽量整体复制运行时资源目录
- 对启动阶段异常保留日志文件,便于定位下一层问题
建议构建策略:
- 显式包含 `rpcore`、`rplibs`、`rpplugins`
- 尽量显式包含 `core`、`scene`、`project`、`ui`、`gui`、`scripts`、`ssbo_component`、`tools`、`TransformGizmo`
- 自动探测并尽量包含 `plugins`、`gltf`、`playwright` 等可选包
- 整体复制 `RenderPipelineFile`、`Resources`、`config`、`demo` 等目录
---
## 五、建议团队后续执行顺序
1. 先确保打包脚本采用保守策略,优先恢复发行版可运行性
2. 再逐步清理源码中的动态依赖、残留 Qt 引用、硬编码路径
3. 最后再考虑做安装包体积优化,而不是反过来
---
最后更新2026-03-14

143
build_scripts/README.md Normal file
View File

@ -0,0 +1,143 @@
# build_scripts 目录说明
本目录包含元泰 EG 项目的所有构建相关脚本和文档。
---
## 📁 目录结构
```
build_scripts/
├── 🔧 构建脚本
│ ├── build_windows.ps1 # Windows 主构建脚本 ⭐
│ ├── build_linux.sh # Linux 构建脚本
│ ├── build_optimized.ps1 # 优化构建脚本(可选)
│ ├── quick_build.bat # Windows 快速入口
│ ├── quick_build.sh # Linux 快速入口
│ └── setup.py # 构建配置(可导入)
├── 🔍 检查脚本
│ ├── check_env.ps1 # Windows 环境检查
│ ├── check_env.sh # Linux 环境检查
│ ├── check_python_version.py # Python 版本检查 ⭐
│ └── analyze_packaging.py # 打包分析工具
└── 📖 文档
├── BUILD_GUIDE.md # 完整构建指南 ⭐⭐⭐
├── BUILD_README.md # 构建说明
├── INSTALLER_GUIDE.md # 详细使用指南
├── QUICK_REFERENCE.md # 命令速查表
├── ICON_GUIDE.md # 图标准备
├── PACKAGING_RISK_AUDIT.md # 打包风险与源码整改清单 ⭐
├── PYTHON_VERSION_REQUIREMENT.md # Python 3.11 要求 ⭐
└── PACKAGING_CHECKLIST.md # 打包检查清单
```
---
## 🚀 快速开始
### 第一次构建?
请按顺序阅读:
1. `BUILD_GUIDE.md` - 完整构建步骤
2. `PYTHON_VERSION_REQUIREMENT.md` - Python 3.11 安装
3. `ICON_GUIDE.md` - 准备应用图标(可选)
4. `PACKAGING_RISK_AUDIT.md` - 打包风险与源码整改清单
### 已经配置好环境?
直接使用:
```powershell
# Windows
.\build_scripts\build_windows.ps1 -Version "1.0.0"
# Linux
./build_scripts/build_linux.sh "1.0.0"
```
---
## 📋 关键检查点
构建前请确认:
- [ ] Python 3.11.x 已安装
- [ ] `python build_scripts/check_python_version.py` 通过
- [ ] `pip install -r requirements/requirements-minimal.txt` 完成
- [ ] Visual Studio Build Tools 已安装Windows
- [ ] 在 VS Dev Shell 中运行构建
---
## 🔧 核心脚本说明
### build_windows.ps1
Windows 主构建脚本,功能:
- 检查 Python 3.11
- 检查 C++ 编译器
- 执行保守白名单 Nuitka 编译
- 自动探测并尽量纳入可选依赖
- 复制运行时资源文件
- 生成最终输出
关键策略:
- 优先保证发行版可运行,不追求最小体积
- 显式包含 `rpcore`、`rplibs`、`rpplugins`
- 整体复制 `RenderPipelineFile`、`Resources`、`config`、`demo` 等目录
### build_linux.sh
Linux 主构建脚本,功能:
- 执行保守白名单 Nuitka 编译
- 构建 AppDir / AppImage
- 显式设置 `EG_PROJECT_ROOT`,保证 AppImage 中资源路径正确
- 复制运行时资源文件
用法:
```powershell
./build_scripts/build_linux.sh "1.0.0" --clean
```
### check_python_version.py
Python 版本检查工具:
```powershell
python build_scripts/check_python_version.py
```
### analyze_packaging.py
打包内容分析工具:
```powershell
python build_scripts/analyze_packaging.py
```
---
## 📝 重要变更
### 2024年更新
- ✅ 指定 Python 3.11 为必须版本
- ✅ 移除 PyQt5/PySide6 依赖(节省 ~170MB
- ✅ 简化依赖配置
- ✅ 重新组织文档结构
---
## ❓ 常见问题
**Q: 构建失败怎么办?**
A: 查看 `BUILD_GUIDE.md` 的"常见问题"章节
**Q: Python 版本不对?**
A: 阅读 `PYTHON_VERSION_REQUIREMENT.md`
**Q: 缺少图标?**
A: 参考 `ICON_GUIDE.md` 创建或下载占位图标
---
**最后更新**: 2024年

View File

@ -0,0 +1,525 @@
#!/bin/bash
# EG Linux 构建脚本 - 使用 Nuitka 编译 + AppImage 打包(保守白名单版)
# 用法: ./build_linux.sh [版本号] [--clean] [--skip-compile] [--deb]
set -e
# 配置
VERSION=${1:-"1.0.0"}
CLEAN=false
SKIP_COMPILE=false
BUILD_DEB=false
# 解析参数
for arg in "$@"; do
case $arg in
--clean)
CLEAN=true
shift
;;
--skip-compile)
SKIP_COMPILE=true
shift
;;
--deb)
BUILD_DEB=true
shift
;;
esac
done
# 路径配置
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
DIST_DIR="$PROJECT_ROOT/dist"
BUILD_DIR="$PROJECT_ROOT/build"
NUITKA_DIR="$BUILD_DIR/nuitka"
APPDIR="$BUILD_DIR/EG_$VERSION.AppDir"
OUTPUT_NAME="EG_$VERSION"
# 应用信息
APP_NAME="EG"
COMPANY_NAME="EG Team"
APP_DESCRIPTION="EG 3D Editor and Game Engine"
# 需要包含的数据目录
DATA_DIRS=(
# 配置和资源
"config"
"icons"
"Resources"
"tex"
"templates"
# 核心模块
"core"
"gui"
"project" # 项目管理模块
"scene"
"scripts"
"ssbo_component"
"tools"
"TransformGizmo"
"ui"
# 引擎和扩展
"RenderPipelineFile"
"vr_actions"
# 项目模板
"new" # 默认空项目模板
"demo"
)
# 需要包含的单个文件
DATA_FILES=(
"imgui.ini"
)
REQUIRED_PACKAGES=(
"panda3d"
"direct"
"imgui_bundle"
"p3dimgui"
"openvr"
"RenderPipelineFile"
"rpcore"
"rplibs"
"rpplugins"
"core"
"scene"
"project"
"ui"
"gui"
"scripts"
"ssbo_component"
"tools"
"TransformGizmo"
"vr_actions"
)
OPTIONAL_PACKAGES=(
"plugins"
"gltf"
"playwright"
"PyQt5"
)
OPTIONAL_MODULES=(
"playwright.async_api"
"win32gui"
"win32con"
"PyQt5.QtCore"
"PyQt5.QtGui"
"PyQt5.QtWidgets"
)
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 辅助函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[OK]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
local step=$1
local total=$2
local msg=$3
echo ""
echo -e "${YELLOW}[$step/$total]${NC} $msg"
}
python_importable() {
local module_name="$1"
python3 -c "import importlib.util, sys; sys.exit(0 if importlib.util.find_spec('$module_name') else 1)" >/dev/null 2>&1
}
# ==================== 主流程 ====================
echo "========================================"
echo " EG Linux Builder v$VERSION"
echo "========================================"
echo "项目路径: $PROJECT_ROOT"
# 步骤 1: 清理
log_step 1 6 "清理旧构建..."
if [ "$CLEAN" = true ]; then
rm -rf "$BUILD_DIR"
rm -rf "$DIST_DIR"
log_success "已清理所有构建文件"
fi
mkdir -p "$BUILD_DIR"
mkdir -p "$DIST_DIR"
# 步骤 2: 环境检查
log_step 2 6 "检查构建环境..."
# 检查必要的命令
for cmd in python3 pip3 gcc g++; do
if ! command -v $cmd &> /dev/null; then
log_error "$cmd 未安装"
exit 1
fi
done
PYTHON_VERSION=$(python3 --version 2>&1)
log_success "Python: $PYTHON_VERSION"
# 检查并安装构建依赖
log_info "检查构建依赖..."
pip3 install -q nuitka pyinstaller 2>/dev/null || true
log_success "构建依赖已就绪"
# 安装项目依赖
log_info "检查项目依赖..."
pip3 install -q -r "$PROJECT_ROOT/requirements/requirements-minimal.txt" 2>/dev/null || log_warning "部分依赖可能安装失败"
log_success "项目依赖已就绪"
# 步骤 3: Nuitka 编译
if [ "$SKIP_COMPILE" = false ]; then
log_step 3 6 "使用 Nuitka 编译主程序..."
cd "$PROJECT_ROOT"
REQUIRED_PACKAGE_ARGS=()
for package in "${REQUIRED_PACKAGES[@]}"; do
REQUIRED_PACKAGE_ARGS+=("--include-package=$package")
done
OPTIONAL_PACKAGE_ARGS=()
for package in "${OPTIONAL_PACKAGES[@]}"; do
if python_importable "$package"; then
OPTIONAL_PACKAGE_ARGS+=("--include-package=$package")
log_info "检测到可选包并纳入打包: $package"
fi
done
OPTIONAL_MODULE_ARGS=()
for module in "${OPTIONAL_MODULES[@]}"; do
if python_importable "$module"; then
OPTIONAL_MODULE_ARGS+=("--include-module=$module")
log_info "检测到可选模块并纳入打包: $module"
fi
done
# 构建 Nuitka 选项
NUITKA_OPTS=(
"--standalone"
"--follow-import-to=rpcore"
"--follow-import-to=rpplugins"
"--follow-import-to=core"
"--follow-import-to=scene"
"--follow-import-to=project"
"--follow-import-to=ui"
"--nofollow-import-to=tkinter,test,unittest,setuptools,pip"
"--linux-onefile-icon=icons/app.png"
"--lto=no"
"--jobs=$(nproc)"
"--output-dir=$NUITKA_DIR"
"--remove-output"
)
NUITKA_OPTS+=("${REQUIRED_PACKAGE_ARGS[@]}")
NUITKA_OPTS+=("${OPTIONAL_PACKAGE_ARGS[@]}")
NUITKA_OPTS+=("${OPTIONAL_MODULE_ARGS[@]}")
log_info "开始编译 (可能需要几分钟)..."
python3 -m nuitka "${NUITKA_OPTS[@]}" Start_Run.py
if [ $? -ne 0 ]; then
log_error "Nuitka 编译失败"
exit 1
fi
log_success "编译成功"
else
log_step 3 6 "跳过编译 (--skip-compile)"
fi
# 步骤 4: 准备 AppDir
log_step 4 6 "准备 AppDir..."
rm -rf "$APPDIR"
mkdir -p "$APPDIR/usr/bin"
mkdir -p "$APPDIR/usr/lib/EG"
mkdir -p "$APPDIR/usr/share/applications"
mkdir -p "$APPDIR/usr/share/icons/hicolor/256x256/apps"
mkdir -p "$APPDIR/usr/share/icons/hicolor/128x128/apps"
mkdir -p "$APPDIR/usr/share/icons/hicolor/64x64/apps"
mkdir -p "$APPDIR/usr/share/metainfo"
# 复制 Nuitka standalone 目录
DIST_SOURCE="$NUITKA_DIR/Start_Run.dist"
if [ ! -d "$DIST_SOURCE" ]; then
log_error "找不到编译后的 standalone 目录: $DIST_SOURCE"
exit 1
fi
cp -r "$DIST_SOURCE/"* "$APPDIR/usr/lib/EG/"
if [ -f "$APPDIR/usr/lib/EG/Start_Run.bin" ]; then
cp "$APPDIR/usr/lib/EG/Start_Run.bin" "$APPDIR/usr/bin/EG"
elif [ -f "$APPDIR/usr/lib/EG/Start_Run" ]; then
cp "$APPDIR/usr/lib/EG/Start_Run" "$APPDIR/usr/bin/EG"
else
EXE_SOURCE=$(find "$APPDIR/usr/lib/EG" -maxdepth 1 -type f -executable | head -1)
if [ -z "$EXE_SOURCE" ]; then
log_error "找不到可执行文件"
exit 1
fi
cp "$EXE_SOURCE" "$APPDIR/usr/bin/EG"
fi
chmod +x "$APPDIR/usr/bin/EG"
log_success "复制可执行文件和 standalone 运行时"
# 复制数据目录
COPIED=0
for dir in "${DATA_DIRS[@]}"; do
if [ -d "$PROJECT_ROOT/$dir" ]; then
cp -r "$PROJECT_ROOT/$dir" "$APPDIR/usr/lib/EG/"
# 清理 Python 缓存
find "$APPDIR/usr/lib/EG/$dir" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
find "$APPDIR/usr/lib/EG/$dir" -name "*.pyc" -delete 2>/dev/null || true
log_success "复制目录: $dir"
((COPIED++))
else
log_warning "目录不存在: $dir"
fi
done
log_success "共复制 $COPIED 个数据目录"
# 复制单个文件
for file in "${DATA_FILES[@]}"; do
if [ -f "$PROJECT_ROOT/$file" ]; then
cp "$PROJECT_ROOT/$file" "$APPDIR/usr/lib/EG/"
log_success "复制文件: $file"
fi
done
# 创建 .desktop 文件
cat > "$APPDIR/usr/share/applications/EG.desktop" << EOF
[Desktop Entry]
Name=EG
Name[zh_CN]=EG 编辑器
Comment=$APP_DESCRIPTION
Comment[zh_CN]=EG 3D 编辑器和游戏引擎
Exec=EG
Icon=EG
Type=Application
Categories=Graphics;3DGraphics;Development;Game;
MimeType=application/x-eg-project;
Terminal=false
StartupNotify=true
StartupWMClass=EG
Keywords=3D;Editor;Game;Engine;Panda3D;
EOF
# 创建 AppStream metadata (用于软件中心)
cat > "$APPDIR/usr/share/metainfo/EG.appdata.xml" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>com.yourcompany.EG</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>Proprietary</project_license>
<name>EG</name>
<summary>3D Editor and Game Engine</summary>
<description>
<p>EG is a powerful 3D editor and game engine based on Panda3D.</p>
</description>
<launchable type="desktop-id">EG.desktop</launchable>
<url type="homepage">https://your-website.com</url>
<provides>
<binary>EG</binary>
</provides>
</component>
EOF
# 复制图标
for size in 256 128 64; do
ICON_SRC="$PROJECT_ROOT/icons/app_${size}.png"
if [ -f "$ICON_SRC" ]; then
cp "$ICON_SRC" "$APPDIR/usr/share/icons/hicolor/${size}x${size}/apps/EG.png"
elif [ -f "$PROJECT_ROOT/icons/app.png" ]; then
# 如果只有一张图,复制到所有尺寸
cp "$PROJECT_ROOT/icons/app.png" "$APPDIR/usr/share/icons/hicolor/${size}x${size}/apps/EG.png"
break
fi
done
# 创建 AppRun
cat > "$APPDIR/AppRun" << 'EOF'
#!/bin/bash
# AppRun for EG
SELF=$(readlink -f "$0")
HERE=${SELF%/*}
# 设置环境变量
export PATH="${HERE}/usr/bin:${PATH}"
export EG_PROJECT_ROOT="${HERE}/usr/lib/EG"
export LD_LIBRARY_PATH="${HERE}/usr/lib/EG:${HERE}/usr/lib:${LD_LIBRARY_PATH}"
export PYTHONPATH="${HERE}/usr/lib/EG:${HERE}/usr/lib/EG/RenderPipelineFile:${PYTHONPATH}"
# 设置 XDG 目录
export XDG_DATA_DIRS="${HERE}/usr/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
# 切换到项目根目录,确保相对资源路径与 Windows 打包版保持一致
cd "${EG_PROJECT_ROOT}" || exit 1
# 执行主程序
exec "${HERE}/usr/bin/EG" "$@"
EOF
chmod +x "$APPDIR/AppRun"
# 复制 .desktop 和图标到根目录
cp "$APPDIR/usr/share/applications/EG.desktop" "$APPDIR/"
if [ -f "$APPDIR/usr/share/icons/hicolor/256x256/apps/EG.png" ]; then
cp "$APPDIR/usr/share/icons/hicolor/256x256/apps/EG.png" "$APPDIR/EG.png"
elif [ -f "$APPDIR/usr/share/icons/hicolor/128x128/apps/EG.png" ]; then
cp "$APPDIR/usr/share/icons/hicolor/128x128/apps/EG.png" "$APPDIR/EG.png"
elif [ -f "$APPDIR/usr/share/icons/hicolor/64x64/apps/EG.png" ]; then
cp "$APPDIR/usr/share/icons/hicolor/64x64/apps/EG.png" "$APPDIR/EG.png"
fi
log_success "AppDir 准备完成"
# 步骤 5: 创建 AppImage
log_step 5 6 "创建 AppImage..."
cd "$DIST_DIR"
# 下载 appimagetool
APPIMAGETOOL="appimagetool-x86_64.AppImage"
if [ ! -f "$APPIMAGETOOL" ]; then
log_info "下载 appimagetool..."
wget -q "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" -O "$APPIMAGETOOL" || {
log_error "下载 appimagetool 失败"
exit 1
}
chmod +x "$APPIMAGETOOL"
log_success "下载完成"
fi
# 创建 AppImage
APPIMAGE_NAME="EG-${VERSION}-x86_64.AppImage"
log_info "构建 AppImage..."
# 使用 --appimage-extract-and-run 避免 fuse 依赖
ARCH=x86_64 ./"$APPIMAGETOOL" --appimage-extract-and-run "$APPDIR" "$APPIMAGE_NAME" 2>&1 | while read line; do
echo " $line"
done
if [ -f "$APPIMAGE_NAME" ]; then
chmod +x "$APPIMAGE_NAME"
SIZE=$(du -h "$APPIMAGE_NAME" | cut -f1)
log_success "AppImage 创建成功: $APPIMAGE_NAME ($SIZE)"
else
log_error "AppImage 创建失败"
exit 1
fi
# 可选: 创建 DEB 包
if [ "$BUILD_DEB" = true ]; then
log_step 5 6 "创建 DEB 包..."
DEB_DIR="$BUILD_DIR/eg_${VERSION}_amd64"
mkdir -p "$DEB_DIR/DEBIAN"
mkdir -p "$DEB_DIR/opt/EG"
mkdir -p "$DEB_DIR/usr/share/applications"
mkdir -p "$DEB_DIR/usr/share/icons/hicolor/256x256/apps"
mkdir -p "$DEB_DIR/usr/bin"
# 控制文件
cat > "$DEB_DIR/DEBIAN/control" << EOF
Package: eg
Version: $VERSION
Section: graphics
Priority: optional
Architecture: amd64
Depends: libgl1, libopenal1, libglu1-mesa, python3 (>= 3.8)
Maintainer: $COMPANY_NAME <support@your-website.com>
Description: EG 3D Editor and Game Engine
EG is a powerful 3D editor and game engine
based on Panda3D with advanced rendering.
EOF
# 复制文件
cp -r "$APPDIR/usr/"* "$DEB_DIR/opt/EG/"
# 创建启动器
cat > "$DEB_DIR/usr/bin/EG" << EOF
#!/bin/bash
cd /opt/EG
./bin/EG "\$@"
EOF
chmod +x "$DEB_DIR/usr/bin/EG"
# 复制 desktop 文件和图标
cp "$APPDIR/usr/share/applications/EG.desktop" "$DEB_DIR/usr/share/applications/"
if [ -f "$APPDIR/usr/share/icons/hicolor/256x256/apps/EG.png" ]; then
cp "$APPDIR/usr/share/icons/hicolor/256x256/apps/EG.png" "$DEB_DIR/usr/share/icons/hicolor/256x256/apps/"
fi
# 构建 deb
dpkg-deb --build "$DEB_DIR" "$DIST_DIR/eg_${VERSION}_amd64.deb"
if [ -f "$DIST_DIR/eg_${VERSION}_amd64.deb" ]; then
SIZE=$(du -h "$DIST_DIR/eg_${VERSION}_amd64.deb" | cut -f1)
log_success "DEB 包创建成功: eg_${VERSION}_amd64.deb ($SIZE)"
fi
fi
# 步骤 6: 创建 tarball
log_step 6 6 "创建 tarball..."
cd "$BUILD_DIR"
# 创建普通 tarball
TARBALL="$DIST_DIR/EG_${VERSION}_Linux_Portable.tar.gz"
tar -czf "$TARBALL" -C "$APPDIR/usr/lib/EG" .
SIZE=$(du -h "$TARBALL" | cut -f1)
log_success "Tarball 创建成功: EG_${VERSION}_Linux_Portable.tar.gz ($SIZE)"
# ==================== 完成 ====================
echo ""
echo "========================================"
echo -e "${GREEN} 构建完成!${NC}"
echo "========================================"
echo ""
echo -e "${BLUE}输出文件:${NC}"
for file in "$DIST_DIR"/EG-*; do
if [ -f "$file" ]; then
name=$(basename "$file")
size=$(du -h "$file" | cut -f1)
echo -e " ${GREEN}${NC} $name (${size})"
fi
done
echo ""
echo -e "${BLUE}输出目录:${NC} $DIST_DIR"
echo ""
# 清理
rm -rf "$APPDIR"
log_info "临时文件已清理"

View File

@ -0,0 +1,201 @@
# 元泰 EG Windows 构建脚本 - Nuitka
param([string]$Version = "1.0.0")
$ErrorActionPreference = "Stop"
$ProjectRoot = "C:\Users\Tellme\apps\EG"
$BuildDir = "$ProjectRoot\build"
$DistDir = "$ProjectRoot\dist\EG_$Version"
$DataDirs = @(
"config",
"icons",
"Resources",
"tex",
"templates",
"core",
"gui",
"project",
"scene",
"scripts",
"ssbo_component",
"tools",
"TransformGizmo",
"ui",
"RenderPipelineFile",
"vr_actions",
"new",
"demo"
)
$DataFiles = @(
"imgui.ini"
)
$RequiredPackages = @(
"panda3d",
"direct",
"imgui_bundle",
"p3dimgui",
"openvr",
"RenderPipelineFile",
"rpcore",
"rplibs",
"rpplugins",
"core",
"scene",
"project",
"ui",
"gui",
"scripts",
"ssbo_component",
"tools",
"TransformGizmo",
"vr_actions"
)
$OptionalPackages = @(
"plugins",
"gltf",
"playwright",
"PyQt5"
)
$OptionalModules = @(
"playwright.async_api",
"win32gui",
"win32con",
"PyQt5.QtCore",
"PyQt5.QtGui",
"PyQt5.QtWidgets"
)
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " 元泰 EG 构建 (Nuitka)" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
# 必须在 VS Dev Shell 中运行
if (!(Get-Command cl -ErrorAction SilentlyContinue)) {
Write-Host "错误: 请在 VS Dev Shell 中运行此脚本" -ForegroundColor Red
exit 1
}
# 清理
Write-Host "`n[1/4] 清理..." -ForegroundColor Yellow
Remove-Item $BuildDir -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item $DistDir -Recurse -Force -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $DistDir | Out-Null
Write-Host " 完成" -ForegroundColor Green
# 设置环境
Write-Host "`n[2/4] 设置环境..." -ForegroundColor Yellow
$env:PYTHONPATH = "$ProjectRoot;$ProjectRoot\RenderPipelineFile"
$env:PYTHONDONTWRITEBYTECODE = "1"
$imguiPath = (py -3.11 -c "import imgui_bundle, os; print(os.path.dirname(imgui_bundle.__file__))").Replace('\', '/')
$glfwDll = "$imguiPath/glfw3.dll"
Write-Host " glfw3.dll: $glfwDll" -ForegroundColor Gray
function Test-PythonImportable {
param([string]$ModuleName)
& py -3.11 -c "import importlib.util, sys; sys.exit(0 if importlib.util.find_spec('$ModuleName') else 1)" | Out-Null
return ($LASTEXITCODE -eq 0)
}
$requiredPackageArgs = @()
foreach ($package in $RequiredPackages) {
$requiredPackageArgs += "--include-package=$package"
}
$detectedOptionalPackageArgs = @()
foreach ($package in $OptionalPackages) {
if (Test-PythonImportable $package) {
$detectedOptionalPackageArgs += "--include-package=$package"
Write-Host " 检测到可选包并纳入打包: $package" -ForegroundColor Gray
}
}
$detectedOptionalModuleArgs = @()
foreach ($module in $OptionalModules) {
if (Test-PythonImportable $module) {
$detectedOptionalModuleArgs += "--include-module=$module"
Write-Host " 检测到可选模块并纳入打包: $module" -ForegroundColor Gray
}
}
# Nuitka 编译
Write-Host "`n[3/4] Nuitka 编译 (预计 20-40 分钟)..." -ForegroundColor Yellow
Write-Host " 关键修复: 使用正斜杠作为目标路径" -ForegroundColor Gray
Write-Host ""
$nuitkaArgs = @(
"-m", "nuitka"
"--standalone"
"--follow-import-to=rpcore"
"--follow-import-to=rpplugins"
"--follow-import-to=core"
"--follow-import-to=scene"
"--follow-import-to=project"
"--follow-import-to=ui"
"--nofollow-import-to=tkinter,test,unittest,setuptools,pip"
# 关键: 正斜杠作为目标路径
"--include-data-files=$glfwDll=imgui_bundle/glfw3.dll"
"--windows-icon-from-ico=icons/app.ico"
"--windows-console-mode=disable"
"--windows-company-name=元泰"
"--windows-product-name=元泰 EG"
"--windows-file-version=$Version"
"--windows-product-version=$Version"
"--output-dir=$BuildDir"
"--jobs=8"
"--lto=no"
"Start_Run.py"
)
$nuitkaArgs += $requiredPackageArgs
$nuitkaArgs += $detectedOptionalPackageArgs
$nuitkaArgs += $detectedOptionalModuleArgs
& py -3.11 @nuitkaArgs
if ($LASTEXITCODE -ne 0) {
Write-Host "`n[错误] 编译失败!" -ForegroundColor Red
exit 1
}
Write-Host "`n 编译成功" -ForegroundColor Green
# 复制到输出目录
Write-Host "`n[4/4] 整理输出..." -ForegroundColor Yellow
$sourceDir = "$BuildDir\Start_Run.dist"
Copy-Item "$sourceDir\*" $DistDir -Recurse -Force
# 复制项目运行时资源。Nuitka 会收集 Python 模块,但这些目录里的配置、
# shader、模型、图标等仍然需要以真实文件形式存在于 dist 中。
foreach ($dir in $DataDirs) {
$sourcePath = Join-Path $ProjectRoot $dir
if (Test-Path $sourcePath) {
Copy-Item $sourcePath (Join-Path $DistDir $dir) -Recurse -Force
Write-Host " 已复制目录: $dir" -ForegroundColor Gray
}
}
foreach ($file in $DataFiles) {
$sourcePath = Join-Path $ProjectRoot $file
if (Test-Path $sourcePath) {
Copy-Item $sourcePath $DistDir -Force
Write-Host " 已复制文件: $file" -ForegroundColor Gray
}
}
# 重命名 exe
if (Test-Path "$DistDir\Start_Run.exe") {
Rename-Item "$DistDir\Start_Run.exe" "元泰.exe"
}
$exeSize = [math]::Round((Get-Item "$DistDir\元泰.exe").Length / 1MB, 2)
Write-Host " 元泰.exe ($exeSize MB)" -ForegroundColor Green
# 完成
Write-Host "`n========================================" -ForegroundColor Green
Write-Host " 构建完成!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host "输出: $DistDir\元泰.exe" -ForegroundColor White
Write-Host ""
Write-Host "测试运行:" -ForegroundColor Yellow
Write-Host " $DistDir\元泰.exe" -ForegroundColor Gray

View File

@ -14,8 +14,9 @@ from direct.showbase.ShowBase import ShowBase
from direct.showbase.ShowBaseGlobal import globalClock
from scene.scene_manager import SceneManager
# 设置 RenderPipelineFile 路径
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 设置 RenderPipelineFile 路径。打包后可通过环境变量显式指定项目根目录,
# 避免 AppImage / 独立发行目录的布局差异导致资源定位错误。
project_root = os.environ.get("EG_PROJECT_ROOT") or os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
render_pipeline_path = os.path.join(project_root, "RenderPipelineFile")
sys.path.insert(0, render_pipeline_path)
@ -65,8 +66,16 @@ class CoreWorld(ShowBase):
if is_fullscreen:
loadPrcFileData("", "fullscreen #t")
# 创建渲染管线
# 创建渲染管线。打包后 Nuitka 会把 rpcore/rplibs 平铺到发行目录,
# 自动探测到的 base_path 可能变成 dist 根目录,导致找不到
# RenderPipelineFile/config/pipeline.yaml这里显式指回真实管线目录。
self.render_pipeline = RenderPipeline()
rp_base_dir = os.path.join(project_root, "RenderPipelineFile")
rp_config_dir = os.path.join(rp_base_dir, "config")
if os.path.isdir(rp_base_dir):
self.render_pipeline.mount_mgr.base_path = rp_base_dir
if os.path.isdir(rp_config_dir):
self.render_pipeline.mount_mgr.config_dir = rp_config_dir
self.render_pipeline.pre_showbase_init()
# 强制开启多线程支持 (Video播放必需)

View File

@ -817,5 +817,6 @@ class MyWorld(PanelDelegates, CoreWorld):
except Exception as e:
print(f"绑定 TransformGizmo 灯光同步事件失败: {e}")
demo = MyWorld()
demo.run()
if __name__ == "__main__":
demo = MyWorld()
demo.run()