Merge pull request #174 from Snapmaker/dev_2.3.0_alves

fix linux build failed bug
This commit is contained in:
Alves
2026-03-05 11:06:11 +08:00
committed by GitHub
3 changed files with 672 additions and 24 deletions

204
installer.nsi Normal file
View File

@@ -0,0 +1,204 @@
; [1] PACK_SOURCE_DIR = compile-time only (e.g. .\build\Snapmaker_Orca). [2] INSTALL_DIR_RUNTIME = runtime install dir (default .\ = $EXEDIR).
!include "MUI2.nsh"
!include "FileFunc.nsh"
!include "LogicLib.nsh"
!define PRODUCT_NAME "Snapmaker Orca"
!define PRODUCT_PUBLISHER "Snapmaker"
!define PRODUCT_WEB_SITE "https://github.com/Snapmaker/OrcaSlicer"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
!define PRODUCT_INSTALL_KEY "Software\${PRODUCT_PUBLISHER}\${PRODUCT_NAME}"
!ifndef VERSION
!define VERSION "2.2.3"
!endif
!ifndef SOURCE_DIR
!define SOURCE_DIR ".\build\Snapmaker_Orca"
!endif
!define PACK_SOURCE_DIR "${SOURCE_DIR}"
; 64-bit app: use PROGRAMFILES64 so default path is C:\Program Files\Snapmaker_Orca, not (x86)
!define INSTALL_DIR_RUNTIME "$PROGRAMFILES64\Snapmaker_Orca"
InstallDir "${INSTALL_DIR_RUNTIME}"
!ifndef OUTPUT_FILE
!define OUTPUT_FILE "Snapmaker_Orca_Windows_Installer_V${VERSION}.exe"
!endif
; License page: show LICENSE.txt from repo root (same dir as this .nsi)
!ifndef LICENSE_FILE
!define LICENSE_FILE ".\LICENSE.txt"
!endif
RequestExecutionLevel admin
; No /SOLID to avoid "Internal compiler error #12345: error mmapping datablock"
SetCompressor lzma
VIProductVersion "${VERSION}.0"
VIAddVersionKey "ProductName" "${PRODUCT_NAME}"
VIAddVersionKey "Comments" "Snapmaker Orca is an open source slicer for FDM printers"
VIAddVersionKey "CompanyName" "${PRODUCT_PUBLISHER}"
VIAddVersionKey "LegalCopyright" "Copyright (C) ${PRODUCT_PUBLISHER}"
VIAddVersionKey "FileDescription" "${PRODUCT_NAME} ${VERSION} Installer"
VIAddVersionKey "FileVersion" "${VERSION}"
VIAddVersionKey "ProductVersion" "${VERSION}"
VIAddVersionKey "InternalName" "${PRODUCT_NAME}"
VIAddVersionKey "LegalTrademarks" ""
VIAddVersionKey "OriginalFilename" "${OUTPUT_FILE}"
; Installer and uninstaller icon: set by build_and_pack.bat via /DICON_FILE=path (e.g. Snapmaker_Orca.ico or snapmaker.ico)
!ifdef ICON_FILE
!define MUI_ICON "${ICON_FILE}"
!define MUI_UNICON "${ICON_FILE}"
!else
!define MUI_ICON ".\resources\images\Snapmaker_Orca.ico"
!define MUI_UNICON ".\resources\images\Snapmaker_Orca.ico"
!endif
!define MUI_WELCOMEPAGE_TITLE "Welcome to ${PRODUCT_NAME} Setup"
!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of ${PRODUCT_NAME} ${VERSION}.$\r$\n$\r$\nClick Next to continue."
!insertmacro MUI_PAGE_WELCOME
!ifdef LICENSE_FILE
!define MUI_LICENSEPAGE_CHECKBOX
!insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}"
!endif
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_DIRECTORYPAGE_TEXT_TOP "Choose the folder in which to install ${PRODUCT_NAME}."
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_TEXT "Run ${PRODUCT_NAME}"
!define MUI_FINISHPAGE_RUN_FUNCTION "LaunchApp"
!define MUI_FINISHPAGE_LINK "Visit ${PRODUCT_NAME} website"
!define MUI_FINISHPAGE_LINK_LOCATION "${PRODUCT_WEB_SITE}"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "SimpChinese"
!insertmacro MUI_LANGUAGE "English"
Name "${PRODUCT_NAME} ${VERSION}"
OutFile "${OUTPUT_FILE}"
Section "Main program" SecMain
SectionIn RO
SetOutPath "$INSTDIR"
DetailPrint "Installing ${PRODUCT_NAME}..."
DetailPrint "Target dir: $INSTDIR"
DetailPrint "Copying files..."
; PACK_SOURCE_DIR = compile time only. At runtime this File extracts from embedded payload to $INSTDIR. Exclude include and lib dirs.
File /r /x "*.pdb" /x "*.ilk" /x "*.exp" /x "*.lib" /x "*.obj" /x "*.idb" /x "*.tlog" /x "*.h" /x "*.hpp" /x "*.c" /x "*.cpp" /x "*.cxx" /x "*.cc" /x "*.vcxproj" /x "*.vcxproj.filters" /x "*.sln" /x "*.cmake" /x "*.py" /x "*.md" /x "*.vcxproj.user" /x "CMakeFiles" /x "RelWithDebInfo" /x "Debug" /x "MinSizeRel" /x ".vs" /x "vcpkg_installed" /x "*.dir" /x "include\*" /x "lib\*" "${PACK_SOURCE_DIR}\*.*"
IfFileExists "$INSTDIR\snapmaker-orca.exe" 0 extract_error
DetailPrint "Creating uninstaller..."
WriteUninstaller "$INSTDIR\Uninstall.exe"
DetailPrint "Writing registry..."
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\Uninstall.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "InstallLocation" "$INSTDIR"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\snapmaker-orca.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "HelpLink" "${PRODUCT_WEB_SITE}"
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "NoModify" 1
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "NoRepair" 1
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_INSTALL_KEY}" "Version" "${VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_INSTALL_KEY}" "InstallPath" "$INSTDIR"
DetailPrint "Installation complete!"
Goto end_section
extract_error:
MessageBox MB_OK|MB_ICONSTOP "Installation failed: snapmaker-orca.exe was not found in the package. The installer may be corrupted."
Abort
end_section:
SectionEnd
Section "Desktop shortcut" SecDesktop
DetailPrint "Creating desktop shortcut..."
CreateShortcut "$DESKTOP\Snapmaker Orca.lnk" "$INSTDIR\snapmaker-orca.exe" "" "$INSTDIR\snapmaker-orca.exe" 0
SectionEnd
Section "Start menu shortcut" SecStartMenu
DetailPrint "Creating start menu shortcut..."
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortcut "$SMPROGRAMS\${PRODUCT_NAME}\Snapmaker Orca.lnk" "$INSTDIR\snapmaker-orca.exe" "" "$INSTDIR\snapmaker-orca.exe" 0
CreateShortcut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0
SectionEnd
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SecMain} "Install ${PRODUCT_NAME} and all required files."
!insertmacro MUI_DESCRIPTION_TEXT ${SecDesktop} "Create a desktop shortcut for ${PRODUCT_NAME}."
!insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} "Create a start menu shortcut for ${PRODUCT_NAME}."
!insertmacro MUI_FUNCTION_DESCRIPTION_END
Section "Uninstall"
DetailPrint "Uninstalling ${PRODUCT_NAME}..."
DetailPrint "Checking for running processes..."
nsExec::ExecToLog 'taskkill /F /IM snapmaker-orca.exe /T'
Sleep 500
DetailPrint "Removing desktop shortcut..."
Delete "$DESKTOP\Snapmaker Orca.lnk"
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
DetailPrint "Removing start menu shortcut..."
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"
DetailPrint "Removing install directory..."
RMDir /r /REBOOTOK "$INSTDIR"
RMDir "$INSTDIR"
DetailPrint "Removing registry entries..."
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_INSTALL_KEY}"
DeleteRegKey HKCU "${PRODUCT_INSTALL_KEY}"
DetailPrint "Uninstall complete!"
SectionEnd
Function LaunchApp
ExecShell "open" "$INSTDIR\snapmaker-orca.exe"
FunctionEnd
Function .onInit
ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString"
StrCmp $R0 "" done
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
"${PRODUCT_NAME} is already installed.$\n$\nClick OK to uninstall the old version, or Cancel to abort." \
IDOK uninst
Abort
uninst:
ClearErrors
ExecWait '$R0 _?=$INSTDIR'
IfErrors no_remove_uninstaller done
no_remove_uninstaller:
done:
FunctionEnd

466
scripts/sign_and_package.sh Normal file
View File

@@ -0,0 +1,466 @@
#!/bin/bash
# macOS 应用签名、打包、公证完整流程脚本
# 用法: ./scripts/sign_and_package.sh [arm64|x86_64] [app_path]
set -e
# 检测架构参数
ARCH="${1:-$(uname -m)}"
# 标准化架构名称
case "$ARCH" in
arm64|aarch64)
ARCH="arm64"
;;
x86_64|x86-64|amd64)
ARCH="x86_64"
;;
*)
echo "错误: 不支持的架构 $ARCH"
echo "用法: $0 [arm64|x86_64] [app_path]"
exit 1
;;
esac
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
BUILD_DIR="$PROJECT_DIR/build/$ARCH"
APP_NAME="Snapmaker_Orca"
APP_NAME_EX="Snapmaker Orca"
DMG_NAME="Snapmaker_Orca_${ARCH}.dmg"
# 证书配置
CERTIFICATE_ID="Developer ID Application: Shenzhen Snapmaker Technologies Co., Ltd. (5NGD3B3V37)"
ENTITLEMENTS="$PROJECT_DIR/scripts/disable_validation.entitlements"
# ============================================
# 公证凭据配置(已设置)
# ============================================
NOTARY_APPLE_ID="snapmaker-app@snapmaker.com"
NOTARY_TEAM_ID="5NGD3B3V37"
#NOTARY_KEYCHAIN_PROFILE="snapmaker"
NOTARY_PASSWORD="guhi-nuxy-mgnh-cbqs"
echo "=========================================="
echo "macOS 应用签名、打包、公证完整流程"
echo "=========================================="
echo "架构: $ARCH"
echo "证书: $CERTIFICATE_ID"
echo "TEAM_ID: 5NGD3B3V37"
echo "项目目录: $PROJECT_DIR"
echo
# ============================================
# 查找应用
# ============================================
# 如果提供了 app 路径
if [ -n "$2" ]; then
SOURCE_APP="$2"
if [ ! -d "$SOURCE_APP" ]; then
echo "错误: 找不到应用: $SOURCE_APP"
exit 1
fi
echo "使用指定应用: $SOURCE_APP"
else
# 自动查找编译好的 app
for possible_path in \
"$BUILD_DIR/src/Release/$APP_NAME.app" \
"$BUILD_DIR/src/RelWithDebInfo/$APP_NAME.app" \
"$BUILD_DIR/src/$APP_NAME.app" \
"$BUILD_DIR/src/Debug/$APP_NAME.app"
do
if [ -d "$possible_path" ]; then
SOURCE_APP="$possible_path"
echo "找到应用: $SOURCE_APP"
break
fi
done
# 检查空格版本名称
if [ -z "$SOURCE_APP" ]; then
for possible_path in \
"$BUILD_DIR/src/Release/Snapmaker Orca.app" \
"$BUILD_DIR/Snapmaker_Orca/Snapmaker Orca.app"
do
if [ -d "$possible_path" ]; then
SOURCE_APP="$possible_path"
echo "找到应用: $SOURCE_APP"
break
fi
done
fi
if [ -z "$SOURCE_APP" ]; then
echo "错误: 在 $BUILD_DIR 中找不到编译好的 $APP_NAME.app"
echo "请先编译 $ARCH 版本: ./build_release_macos.sh -s -a $ARCH"
exit 1
fi
fi
# 创建临时工作目录
WORK_DIR="$BUILD_DIR/sign_package"
STAGING_DIR="$WORK_DIR/staging"
rm -rf "$WORK_DIR"
mkdir -p "$STAGING_DIR"
# 清理所有可能的残留挂载点(在开始工作前)
echo "清理可能的残留挂载点..."
for mount_point in /Volumes/Snapmaker* /Volumes/Snapmaker*; do
if [ -d "$mount_point" ]; then
echo " 卸载: $mount_point"
hdiutil detach "$mount_point" -force 2>/dev/null || true
fi
done
sleep 1
# 复制应用到工作目录
echo
echo "=========================================="
echo "步骤 1/6: 复制应用"
echo "=========================================="
echo "复制应用到工作目录..."
cp -R "$SOURCE_APP" "$STAGING_DIR/$APP_NAME.app"
FINAL_APP="$STAGING_DIR/$APP_NAME.app"
# 删除 .DS_Store 文件
find "$FINAL_APP" -name '.DS_Store' -delete
# 删除 PkgInfo 文件(冗余文件)
rm -f "$FINAL_APP/Contents/PkgInfo" 2>/dev/null || true
# 清理所有扩展属性(包括 com.apple.quarantine避免 Gatekeeper 问题
echo "清理扩展属性..."
xattr -cr "$FINAL_APP"
# ============================================
# 打包外部依赖库
# ============================================
APP_MACOS_DIR="$FINAL_APP/Contents/MacOS"
APP_FRAMEWORKS_DIR="$FINAL_APP/Contents/Frameworks"
EXECUTABLE="$APP_MACOS_DIR/$APP_NAME"
# 确保 Frameworks 目录存在
mkdir -p "$APP_FRAMEWORKS_DIR"
echo
echo "检查并打包外部依赖库..."
# 查找所有外部依赖(非系统库)
EXTERNAL_LIBS=$(otool -L "$EXECUTABLE" | grep -E "opt/homebrew|usr/local|opt/local" | awk '{print $1}')
if [ -n "$EXTERNAL_LIBS" ]; then
echo "发现外部依赖:"
echo "$EXTERNAL_LIBS"
echo
for LIB_PATH in $EXTERNAL_LIBS; do
if [ -f "$LIB_PATH" ]; then
LIB_NAME=$(basename "$LIB_PATH")
echo "处理: $LIB_NAME"
# 获取实际的库文件路径(处理符号链接)- macOS 兼容方式
if command -v realpath &> /dev/null; then
REAL_LIB=$(realpath "$LIB_PATH" 2>/dev/null || echo "$LIB_PATH")
else
# macOS 不支持 realpath/readlink -f使用 perl
REAL_LIB=$(perl -MCwd=abs_path -e 'print abs_path(shift)' "$LIB_PATH" 2>/dev/null || echo "$LIB_PATH")
fi
REAL_NAME=$(basename "$REAL_LIB")
# 复制实际的库文件
if [ ! -f "$APP_FRAMEWORKS_DIR/$REAL_NAME" ]; then
cp "$REAL_LIB" "$APP_FRAMEWORKS_DIR/$REAL_NAME"
# 修改库的 ID 为文件名(不带路径)
install_name_tool -id "$REAL_NAME" "$APP_FRAMEWORKS_DIR/$REAL_NAME"
# 删除库中的 rpath避免问题
install_name_tool -delete_rpath "@loader_path/../lib" "$APP_FRAMEWORKS_DIR/$REAL_NAME" 2>/dev/null || true
install_name_tool -delete_rpath "@loader_path/lib" "$APP_FRAMEWORKS_DIR/$REAL_NAME" 2>/dev/null || true
fi
# 注释掉:不创建中间符号链接,避免冗余
# if [ "$LIB_NAME" != "$REAL_NAME" ]; then
# (cd "$APP_FRAMEWORKS_DIR" && ln -sf "$REAL_NAME" "$LIB_NAME")
# fi
# 更新可执行文件中的依赖引用
install_name_tool -change "$LIB_PATH" "@executable_path/../Frameworks/$REAL_NAME" "$EXECUTABLE" 2>/dev/null || true
install_name_tool -change "$REAL_LIB" "@executable_path/../Frameworks/$REAL_NAME" "$EXECUTABLE" 2>/dev/null || true
fi
done
echo
echo "已打包的依赖库:"
ls -la "$APP_FRAMEWORKS_DIR/"
else
echo "没有外部依赖需要处理"
fi
# 移除不需要的 rpath
echo
echo "清理 rpath..."
install_name_tool -delete_rpath "/opt/homebrew/lib" "$EXECUTABLE" 2>/dev/null || true
install_name_tool -delete_rpath "/usr/local/lib" "$EXECUTABLE" 2>/dev/null || true
install_name_tool -delete_rpath "/opt/local/lib" "$EXECUTABLE" 2>/dev/null || true
# 修复 Resources 符号链接 (如果是符号链接)
RESOURCES_LINK="$FINAL_APP/Contents/Resources"
if [ -L "$RESOURCES_LINK" ]; then
echo "修复 Resources 符号链接..."
RESOURCES_TARGET=$(readlink "$RESOURCES_LINK")
rm "$RESOURCES_LINK"
cp -R "$RESOURCES_TARGET" "$RESOURCES_LINK"
fi
# 验证依赖
echo
echo "验证最终依赖:"
otool -L "$EXECUTABLE" | grep -E "@executable|libzstd|libsentry" || echo "无特殊依赖"
# ============================================
# 步骤 2/6: 签名应用
# ============================================
echo
echo "=========================================="
echo "步骤 2/6: 签名应用"
echo "=========================================="
APP_FRAMEWORKS_DIR="$FINAL_APP/Contents/Frameworks"
APP_MACOS_DIR="$FINAL_APP/Contents/MacOS"
EXECUTABLE="$APP_MACOS_DIR/$APP_NAME"
# 2.1 移除现有签名
echo "2.1 移除现有签名..."
codesign --remove-signature "$FINAL_APP" 2>/dev/null || true
# 2.2 签名 Frameworks 和动态库(使用 runtime 选项)
echo "2.2 签名 Frameworks 和动态库(使用 runtime 选项)..."
if [ -d "$APP_FRAMEWORKS_DIR" ]; then
# 签名所有 .framework
for framework in "$APP_FRAMEWORKS_DIR"/*.framework; do
if [ -d "$framework" ]; then
echo " - 签名: $(basename "$framework")"
codesign --force --verbose --options runtime --timestamp --sign "$CERTIFICATE_ID" "$framework" 2>/dev/null || true
fi
done
# 签名所有 .dylib
for dylib in "$APP_FRAMEWORKS_DIR"/*.dylib; do
if [ -f "$dylib" ]; then
echo " - 签名: $(basename "$dylib")"
codesign --force --verbose --options runtime --timestamp --sign "$CERTIFICATE_ID" "$dylib"
fi
done
# 签名其他可能存在的库文件(如 .so
for lib in "$APP_FRAMEWORKS_DIR"/*.*; do
if [ -f "$lib" ]; then
case "$lib" in
*.dylib) ;; # 已处理,跳过
*)
echo " - 签名: $(basename "$lib")"
codesign --force --verbose --options runtime --timestamp --sign "$CERTIFICATE_ID" "$lib"
;;
esac
fi
done
fi
# 2.3 签名辅助工具
echo "2.3 签名辅助工具(使用 runtime 选项)..."
if [ -f "$APP_MACOS_DIR/crashpad_handler" ]; then
echo " - 签名: crashpad_handler"
codesign --force --verbose --options runtime --timestamp --sign "$CERTIFICATE_ID" "$APP_MACOS_DIR/crashpad_handler"
fi
# 2.4 签名整个 app bundle应用 entitlements
echo "2.4 签名整个 app bundle应用 entitlements..."
echo " 这会签名所有组件并将 entitlements 应用到主可执行文件"
codesign --force --verbose --options runtime --timestamp \
--entitlements "$ENTITLEMENTS" \
--sign "$CERTIFICATE_ID" \
"$FINAL_APP"
# 2.5 验证签名和 entitlements
echo "2.5 验证签名和 entitlements..."
echo " 检查签名..."
codesign -vvv "$FINAL_APP" 2>&1 | grep -E "valid on disk|Authority|TeamIdentifier" | head -5
echo ""
echo " 检查 entitlements..."
if codesign -d --entitlements - "$FINAL_APP" 2>&1 | grep -q "com.apple.security.cs.disable-library-validation"; then
echo " ✓ Entitlements 正确嵌入!"
else
echo "警告: 预期的 entitlements 未找到"
fi
# ============================================
# 步骤 3/6: 创建并签名 DMG
# 流程与 GitHub Actions 完全一致:准备内容 -> 一步 create UDZO不挂载-> 签名 DMG
# 不挂载可避免本地「操作不被允许」;打开 DMG 后为系统默认图标布局
# ============================================
echo
echo "=========================================="
echo "步骤 3/6: 创建并签名 DMG"
echo "=========================================="
DMG_CONTENT_DIR="$WORK_DIR/dmg_content"
rm -rf "$DMG_CONTENT_DIR"
mkdir -p "$DMG_CONTENT_DIR"
rm -rf "$DMG_CONTENT_DIR/.fseventsd" 2>/dev/null || true
# 复制应用(显示名 Snapmaker Orca.app并创建 Applications 符号链接(与 CI 一致)
echo "准备 DMG 内容..."
cp -R "$FINAL_APP" "$DMG_CONTENT_DIR/$APP_NAME_EX.app"
# 清理 DMG 内容中的扩展属性(重要!避免 Gatekeeper 问题)
xattr -cr "$DMG_CONTENT_DIR/$APP_NAME_EX.app"
ln -sfn /Applications "$DMG_CONTENT_DIR/Applications"
# 卷名不使用下划线,避免 macOS 安全机制阻止
DMG_VOLNAME="Snapmaker_Orca"
FINAL_DMG_PATH="$BUILD_DIR/$DMG_NAME"
rm -f "$FINAL_DMG_PATH"
# 再次清理可能残留的挂载点
if [ -d "/Volumes/$DMG_VOLNAME" ]; then
echo "检测到残留挂载点 /Volumes/$DMG_VOLNAME,正在强制卸载..."
hdiutil detach "/Volumes/$DMG_VOLNAME" -force 2>/dev/null || true
sleep 2
fi
# 检查是否有同名 DMG 已挂载
MOUNTED_DMG=$(hdiutil info | grep "/Volumes/$DMG_VOLNAME" || true)
if [ -n "$MOUNTED_DMG" ]; then
echo "警告: 发现已挂载的同名卷,尝试卸载..."
hdiutil info | grep "/Volumes/$DMG_VOLNAME" | grep -o '/dev/disk[0-9]*' | while read -r disk; do
hdiutil detach "$disk" -force 2>/dev/null || true
done
sleep 2
fi
echo "创建 DMG: $FINAL_DMG_PATH (卷名: $DMG_VOLNAME)"
if ! hdiutil create \
-volname "$DMG_VOLNAME" \
-srcfolder "$DMG_CONTENT_DIR" \
-ov \
-format UDZO \
-imagekey zlib-level=9 \
-o "$FINAL_DMG_PATH"; then
echo ""
echo "错误: hdiutil create 失败"
echo "尝试使用替代方法创建 DMG..."
# 备用方案:使用 mktemp 创建临时卷名
TEMP_VOLNAME="Snapmaker_Orca_$$"
if hdiutil create \
-volname "$TEMP_VOLNAME" \
-srcfolder "$DMG_CONTENT_DIR" \
-ov \
-format UDZO \
-imagekey zlib-level=9 \
-o "$FINAL_DMG_PATH"; then
echo "使用临时卷名创建成功"
else
echo "错误: DMG 创建失败,请手动检查 /Volumes 目录"
echo "运行 'ls -la /Volumes/' 查看挂载点"
echo "运行 'hdiutil info' 查看所有挂载的磁盘镜像"
exit 1
fi
fi
[ ! -f "$FINAL_DMG_PATH" ] && echo "错误: 未生成 DMG" && exit 1
# 签名 DMG
echo "签名 DMG..."
codesign --force --timestamp --sign "$CERTIFICATE_ID" "$FINAL_DMG_PATH"
echo "验证 DMG 签名..."
codesign -vvv "$FINAL_DMG_PATH" 2>&1 | head -3
rm -rf "$DMG_CONTENT_DIR"
echo ""
echo "=========================================="
echo "DMG 创建和签名完成!"
echo "=========================================="
echo "DMG: $FINAL_DMG_PATH"
echo "大小: $(du -h "$FINAL_DMG_PATH" | cut -f1)"
# ============================================
# 步骤 4/6: 公证 DMG
# ============================================
echo ""
echo "=========================================="
echo "步骤 4/6: 公证 DMG"
echo "=========================================="
# 判断是否可公证:检查密码是否已设置
echo "检查公证凭据..."
echo " Apple ID: $NOTARY_APPLE_ID"
echo " Team ID: $NOTARY_TEAM_ID"
if [ -z "$NOTARY_PASSWORD" ] || [ "$NOTARY_PASSWORD" = "__PLEASE_ENTER_PASSWORD__" ]; then
echo ""
echo "密码未设置!"
echo ""
echo "请在脚本中设置密码:"
echo " NOTARY_PASSWORD=\"your-app-specific-password\""
echo ""
echo "或者通过环境变量设置:"
echo " export NOTARY_PASSWORD=\"your-app-specific-password\""
echo ""
echo "跳过公证步骤..."
else
echo "✓ 密码已配置"
echo ""
echo "=========================================="
echo "步骤 5/6: 提交公证"
echo "=========================================="
echo "提交 DMG 到 Apple 公证服务..."
xcrun notarytool submit "$FINAL_DMG_PATH" \
--apple-id "$NOTARY_APPLE_ID" \
--team-id "$NOTARY_TEAM_ID" \
--password "$NOTARY_PASSWORD" \
--wait \
--progress
echo ""
echo "=========================================="
echo "步骤 6/6: 装订公证票据"
echo "=========================================="
echo "装订公证票据到 DMG..."
xcrun stapler staple "$FINAL_DMG_PATH"
# 验证公证结果
echo ""
echo "验证公证结果..."
xcrun stapler validate -v "$FINAL_DMG_PATH"
echo ""
echo "=========================================="
echo "公证完成!"
echo "=========================================="
echo "此 DMG 已签名并公证,可以在任何 Mac 上无缝运行"
fi
echo ""
echo "=========================================="
echo "完成!"
echo "=========================================="
echo "架构: $ARCH"
echo "应用: $FINAL_APP"
echo "DMG: $FINAL_DMG_PATH"
echo "证书: $CERTIFICATE_ID"
echo "TEAM_ID: 5NGD3B3V37"
echo ""
echo "使用方法:"
echo " 1. 打开 DMG: open $FINAL_DMG_PATH"
echo " 2. 将 $APP_NAME_EX.app 拖拽到 Applications 文件夹"
echo " 3. 从 Applications 运行应用"
echo "=========================================="

View File

@@ -30,7 +30,6 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
m_physical_extruder_count = print_config.nozzle_diameter.values.size();
if (m_physical_extruder_count == 0) {
m_physical_extruder_count = 1; // 防止除零默认为1
BOOST_LOG_TRIVIAL(warning) << "GCodeWriter::apply_print_config: nozzle_diameter is empty, using default physical_extruder_count=1";
}
bool use_mach_limits = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware ||
print_config.gcode_flavor.value == gcfKlipper || print_config.gcode_flavor.value == gcfRepRapFirmware;
@@ -50,7 +49,6 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
{
BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: START - Creating Extruder objects for " << extruder_ids.size() << " extruders";
std::sort(extruder_ids.begin(), extruder_ids.end());
m_extruder = nullptr; // this points to object inside `m_extruders`, so should be cleared too
@@ -58,16 +56,11 @@ void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
m_extruders.reserve(extruder_ids.size());
for (unsigned int extruder_id : extruder_ids) {
int physical_extruder_id = get_physical_extruder(extruder_id);
BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: Creating Extruder - filament_id=" << extruder_id
<< " -> physical_extruder_id=" << physical_extruder_id;
m_extruders.emplace_back(Extruder(extruder_id, physical_extruder_id, &this->config, config.single_extruder_multi_material.value));
}
/* we enable support for multiple extruder if any extruder greater than 0 is used
(even if prints only uses that one) since we need to output Tx commands
first extruder has index 0 */
this->multiple_extruders = (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0;
BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: END - multiple_extruders=" << this->multiple_extruders;
this->multiple_extruders = (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0;
}
std::string GCodeWriter::preamble()
@@ -504,13 +497,6 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s
std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
{
if (std::isnan(point(0)) || std::isinf(point(0)) || std::isnan(point(1)) || std::isinf(point(1))) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: travel_to_xy received NaN/inf point"
<< " extruder=" << (m_extruder ? m_extruder->id() : -1)
<< " point=(" << point(0) << ", " << point(1) << ")"
<< " comment=" << comment;
}
m_pos(0) = point(0);
m_pos(1) = point(1);
@@ -731,14 +717,6 @@ bool GCodeWriter::will_move_z(double z) const
std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment, bool force_no_extrusion)
{
if (std::isnan(point(0)) || std::isinf(point(0)) || std::isnan(point(1)) || std::isinf(point(1))) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: extrude_to_xy received NaN/inf point"
<< " extruder=" << (m_extruder ? m_extruder->id() : -1)
<< " point=(" << point(0) << ", " << point(1) << ")"
<< " dE=" << dE
<< " comment=" << comment;
}
m_pos(0) = point(0);
m_pos(1) = point(1);
if(std::abs(dE) <= std::numeric_limits<double>::epsilon())