#!/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 "临时文件已清理"
EG is a powerful 3D editor and game engine based on Panda3D.