diff --git a/Start_Run.py b/Start_Run.py index 3e90d01d..761abc00 100644 --- a/Start_Run.py +++ b/Start_Run.py @@ -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() \ No newline at end of file + app = MyWorld() + app.run() + except Exception: + _write_startup_log("Unhandled exception during startup:") + _write_startup_log(traceback.format_exc()) + raise diff --git a/build_scripts/BUILD_GUIDE.md b/build_scripts/BUILD_GUIDE.md new file mode 100644 index 00000000..9da27171 --- /dev/null +++ b/build_scripts/BUILD_GUIDE.md @@ -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 Shell(Visual 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年 diff --git a/build_scripts/PACKAGING_RISK_AUDIT.md b/build_scripts/PACKAGING_RISK_AUDIT.md new file mode 100644 index 00000000..d682c70b --- /dev/null +++ b/build_scripts/PACKAGING_RISK_AUDIT.md @@ -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 diff --git a/build_scripts/README.md b/build_scripts/README.md new file mode 100644 index 00000000..e993d6d4 --- /dev/null +++ b/build_scripts/README.md @@ -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年 diff --git a/build_scripts/build_linux.sh b/build_scripts/build_linux.sh new file mode 100644 index 00000000..3bd76752 --- /dev/null +++ b/build_scripts/build_linux.sh @@ -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 + + + com.yourcompany.EG + CC0-1.0 + Proprietary + EG + 3D Editor and Game Engine + + EG is a powerful 3D editor and game engine based on Panda3D. + + EG.desktop + https://your-website.com + + EG + + +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 +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 "临时文件已清理" diff --git a/build_scripts/build_windows.ps1 b/build_scripts/build_windows.ps1 new file mode 100644 index 00000000..0404aaa2 --- /dev/null +++ b/build_scripts/build_windows.ps1 @@ -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 diff --git a/core/world.py b/core/world.py index 2daf6cfc..c11acf33 100644 --- a/core/world.py +++ b/core/world.py @@ -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播放必需) diff --git a/main.py b/main.py index d656757d..2de2095e 100644 --- a/main.py +++ b/main.py @@ -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()
EG is a powerful 3D editor and game engine based on Panda3D.