本指南详细介绍如何将 Avalonia 应用程序打包为 macOS 原生应用(.app)、DMG 安装包和 PKG 安装包。
本指南支持三种 macOS 应用打包格式:
| 格式 | 适用场景 | 特点 |
|---|---|---|
| .app 包 | 开发调试 | 原生应用格式,可直接运行 |
| DMG 安装包 | 用户分发 | 磁盘镜像,拖拽安装体验 |
| PKG 安装包 | 企业分发 | 标准安装包,一键安装到 Applications |
- macOS 10.15 或更高版本
- Xcode Command Line Tools
- .NET 9.0 SDK 或更高版本
# 安装 Xcode Command Line Tools
xcode-select --install
# 验证 .NET 安装
dotnet --version
# 验证 codesign 可用性
codesign --version典型的 Avalonia 项目结构:
YourAvaloniaApp/
├── YourAvaloniaApp/ # 核心库项目
├── YourAvaloniaApp.Desktop/ # 桌面平台项目
├── YourAvaloniaApp.sln
└── Directory.Packages.props
确保您的 Desktop 项目包含以下配置:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia.Desktop"/>
<PackageReference Include="Avalonia.Diagnostics">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\YourAvaloniaApp\YourAvaloniaApp.csproj"/>
</ItemGroup>
</Project># 恢复 NuGet 包
dotnet restore
# 发布 macOS ARM64 版本(推荐使用 AOT 编译)
dotnet publish YourAvaloniaApp.Desktop/YourAvaloniaApp.Desktop.csproj \
-r osx-arm64 \
-c Release \
-p:PublishAot=true注意:
- 使用
osx-arm64适用于 Apple Silicon (M1/M2/M3) Mac - 使用
osx-x64适用于 Intel Mac - AOT 编译可以显著减少应用大小和提高启动速度
# 清理旧的构建文件
rm -rf YourApp.app
# 创建 .app 包目录结构
mkdir -p "YourApp.app/Contents/MacOS"
mkdir -p "YourApp.app/Contents/Resources"对于精简版本,只需要复制这四个核心文件:
# 复制主执行文件
cp YourAvaloniaApp.Desktop/bin/Release/net9.0/osx-arm64/publish/YourAvaloniaApp.Desktop \
YourApp.app/Contents/MacOS/
# 复制必要的动态库
cp YourAvaloniaApp.Desktop/bin/Release/net9.0/osx-arm64/publish/libAvaloniaNative.dylib \
YourApp.app/Contents/MacOS/
cp YourAvaloniaApp.Desktop/bin/Release/net9.0/osx-arm64/publish/libHarfBuzzSharp.dylib \
YourApp.app/Contents/MacOS/
cp YourAvaloniaApp.Desktop/bin/Release/net9.0/osx-arm64/publish/libSkiaSharp.dylib \
YourApp.app/Contents/MacOS/cat > YourApp.app/Contents/Info.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>YourAvaloniaApp.Desktop</string>
<key>CFBundleIconFile</key>
<string>icon</string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.yourapp</string>
<key>CFBundleName</key>
<string>YourApp</string>
<key>CFBundleDisplayName</key>
<string>Your App Display Name</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2024 Your Company. All rights reserved.</string>
</dict>
</plist>
EOF# 设置可执行权限
chmod +x YourApp.app/Contents/MacOS/YourAvaloniaApp.Desktop
# 使用临时签名(适用于本地开发)
codesign --force --deep --sign - YourApp.app
# 验证签名
codesign --verify --verbose YourApp.app# 创建 DMG 内容目录
mkdir -p dmg_contents
cp -R YourApp.app dmg_contents/
# 创建 Applications 文件夹的符号链接
ln -s /Applications dmg_contents/Applications
# 创建 DMG 文件
hdiutil create -volname "Your App Name" \
-srcfolder dmg_contents \
-ov -format UDZO \
YourApp.dmg
# 清理临时文件
rm -rf dmg_contents创建一个自动化构建脚本 build-macos.sh:
#!/bin/bash
# 配置变量
APP_NAME="YourApp"
PROJECT_NAME="YourAvaloniaApp"
DESKTOP_PROJECT="${PROJECT_NAME}.Desktop"
BUNDLE_ID="com.yourcompany.yourapp"
VERSION="1.0.0"
echo "开始构建 macOS 应用..."
# 清理旧文件
echo "清理旧文件..."
rm -rf "${APP_NAME}.app" dmg_contents "${APP_NAME}.dmg"
# 恢复依赖
echo "恢复依赖..."
dotnet restore
# 发布应用
echo "发布应用..."
dotnet publish "${DESKTOP_PROJECT}/${DESKTOP_PROJECT}.csproj" \
-r osx-arm64 \
-c Release \
-p:PublishAot=true
# 创建 .app 包结构
echo "创建 .app 包..."
mkdir -p "${APP_NAME}.app/Contents/MacOS"
mkdir -p "${APP_NAME}.app/Contents/Resources"
# 复制文件
echo "复制必要文件..."
PUBLISH_DIR="${DESKTOP_PROJECT}/bin/Release/net9.0/osx-arm64/publish"
cp "${PUBLISH_DIR}/${DESKTOP_PROJECT}" "${APP_NAME}.app/Contents/MacOS/"
cp "${PUBLISH_DIR}/libAvaloniaNative.dylib" "${APP_NAME}.app/Contents/MacOS/"
cp "${PUBLISH_DIR}/libHarfBuzzSharp.dylib" "${APP_NAME}.app/Contents/MacOS/"
cp "${PUBLISH_DIR}/libSkiaSharp.dylib" "${APP_NAME}.app/Contents/MacOS/"
# 创建 Info.plist
echo "创建 Info.plist..."
cat > "${APP_NAME}.app/Contents/Info.plist" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>${DESKTOP_PROJECT}</string>
<key>CFBundleIconFile</key>
<string>icon</string>
<key>CFBundleIdentifier</key>
<string>${BUNDLE_ID}</string>
<key>CFBundleName</key>
<string>${APP_NAME}</string>
<key>CFBundleDisplayName</key>
<string>${APP_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>${VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${VERSION}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2024. All rights reserved.</string>
</dict>
</plist>
EOF
# 设置权限和签名
echo "设置权限和签名..."
chmod +x "${APP_NAME}.app/Contents/MacOS/${DESKTOP_PROJECT}"
codesign --force --deep --sign - "${APP_NAME}.app"
codesign --verify --verbose "${APP_NAME}.app"
# 创建 DMG
echo "创建 DMG..."
mkdir -p dmg_contents
cp -R "${APP_NAME}.app" dmg_contents/
ln -s /Applications dmg_contents/Applications
hdiutil create -volname "${APP_NAME}" -srcfolder dmg_contents -ov -format UDZO "${APP_NAME}.dmg"
rm -rf dmg_contents
echo "构建完成!"
echo "生成的文件:"
echo " - ${APP_NAME}.app (应用包)"
echo " - ${APP_NAME}.dmg (安装包)"使用脚本:
# 设置执行权限
chmod +x build-macos.sh
# 运行构建
./build-macos.sh除了 DMG 格式,我们还提供了 PKG 打包方案,这是 macOS 的标准安装包格式,适合企业分发和自动化部署。
- ✅ 一键安装: 双击即可自动安装到
/Applications目录 - ✅ MAUI 体验: 提供类似 MAUI 的一键打包流程
- ✅ 企业友好: 支持命令行安装和卸载
- ✅ 标准格式: macOS 原生支持的安装包格式
-
下载 PKG 打包脚本:
curl -O https://raw.githubusercontent.com/interface95/avalonia-macos-build-guide/main/build-pkg.sh chmod +x build-pkg.sh
-
配置项目(在 Desktop 项目的
.csproj中添加):<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64' OR '$(RuntimeIdentifier)' == 'osx-x64'"> <CreatePackage>true</CreatePackage> <PackageId>com.yourcompany.yourapp</PackageId> <Title>YourAppName</Title> <PackageVersion>1.0.0</PackageVersion> </PropertyGroup>
-
一键打包:
./build-pkg.sh
-
安装测试:
sudo installer -pkg YourApp.pkg -target /
完整的 PKG 打包配置和高级功能请参考:
该文档包含:
- 详细的步骤说明
- 项目配置示例
- GitHub Actions 自动化
- 故障排除和调试技巧
- 生产环境代码签名
- 多架构支持
创建 .github/workflows/build-macos.yml 文件实现自动化构建:
name: Build macOS DMG
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
include-prerelease: true
- name: Restore dependencies
run: dotnet restore
- name: Publish for macOS ARM64 with AOT
run: dotnet publish YourAvaloniaApp.Desktop/YourAvaloniaApp.Desktop.csproj -r osx-arm64 -c Release -p:PublishAot=true
- name: Create .app bundle
run: |
# Extract version from tag (remove 'v' prefix)
VERSION=${GITHUB_REF#refs/tags/v}
echo "Building version: $VERSION"
# Create app bundle structure
mkdir -p "YourApp.app/Contents/MacOS"
mkdir -p "YourApp.app/Contents/Resources"
# Copy published files to app bundle
cp YourAvaloniaApp.Desktop/bin/Release/net9.0/osx-arm64/publish/YourAvaloniaApp.Desktop YourApp.app/Contents/MacOS/
cp YourAvaloniaApp.Desktop/bin/Release/net9.0/osx-arm64/publish/libAvaloniaNative.dylib YourApp.app/Contents/MacOS/
cp YourAvaloniaApp.Desktop/bin/Release/net9.0/osx-arm64/publish/libHarfBuzzSharp.dylib YourApp.app/Contents/MacOS/
cp YourAvaloniaApp.Desktop/bin/Release/net9.0/osx-arm64/publish/libSkiaSharp.dylib YourApp.app/Contents/MacOS/
# Create Info.plist with dynamic version
cat > YourApp.app/Contents/Info.plist << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>YourAvaloniaApp.Desktop</string>
<key>CFBundleIconFile</key>
<string>icon</string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.yourapp</string>
<key>CFBundleName</key>
<string>YourApp</string>
<key>CFBundleDisplayName</key>
<string>YourApp</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>$VERSION</string>
<key>CFBundleShortVersionString</key>
<string>$VERSION</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2024. All rights reserved.</string>
</dict>
</plist>
EOF
# Make executable
chmod +x YourApp.app/Contents/MacOS/YourAvaloniaApp.Desktop
# Sign with ad-hoc signature for distribution
codesign --force --deep --sign - YourApp.app
# Verify signing
codesign --verify --verbose YourApp.app
- name: Create DMG
run: |
# Create DMG contents directory
mkdir -p dmg_contents
cp -R YourApp.app dmg_contents/
# Create symbolic link to Applications
ln -s /Applications dmg_contents/Applications
# Create DMG
hdiutil create -volname "YourApp" -srcfolder dmg_contents -ov -format UDZO YourApp.dmg
- name: Upload DMG artifact
uses: actions/upload-artifact@v4
with:
name: YourApp-macOS-ARM64
path: YourApp.dmg
retention-days: 30
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: YourApp.dmg
name: Release ${{ github.ref_name }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}触发构建:
# 创建并推送版本标签
git tag v1.0.0
git push origin v1.0.0-
权限被拒绝错误
# 确保设置了正确的执行权限 chmod +x YourApp.app/Contents/MacOS/YourExecutable -
签名失败
# 清除现有签名并重新签名 codesign --remove-signature YourApp.app codesign --force --deep --sign - YourApp.app -
应用无法启动
- 检查 Info.plist 中的 CFBundleExecutable 是否与实际可执行文件名匹配
- 确保所有必要的 .dylib 文件都已复制
-
NuGet 权限警告
# 修复 NuGet 目录权限 sudo chown -R $(whoami) ~/.local/share/NuGet
-
验证 .app 包结构
find YourApp.app -type f -exec ls -la {} \; -
检查依赖关系
otool -L YourApp.app/Contents/MacOS/YourExecutable
-
测试应用启动
# 从命令行启动以查看错误信息 ./YourApp.app/Contents/MacOS/YourExecutable
-
创建 .icns 图标文件:
# 使用 sips 工具转换 PNG 到 ICNS sips -s format icns icon.png --out icon.icns -
复制到应用包:
cp icon.icns YourApp.app/Contents/Resources/
对于 App Store 或公开分发,您需要使用有效的开发者证书:
# 使用开发者证书签名
codesign --force --deep --sign "Developer ID Application: Your Name" YourApp.app
# 公证(macOS 10.15+)
xcrun notarytool submit YourApp.dmg \
--apple-id your-apple-id@example.com \
--password your-app-specific-password \
--team-id YOUR_TEAM_ID \
--wait为了支持 Intel 和 Apple Silicon Mac:
# 发布 Intel 版本
dotnet publish -r osx-x64 -c Release -p:PublishAot=true
# 发布 ARM64 版本
dotnet publish -r osx-arm64 -c Release -p:PublishAot=true
# 使用 lipo 合并二进制文件
lipo -create \
path/to/osx-x64/YourApp \
path/to/osx-arm64/YourApp \
-output YourApp.app/Contents/MacOS/YourApp本文档采用 MIT 许可证。
欢迎提交 Issue 和 Pull Request 来改进本指南。
注意: 本指南基于 .NET 9.0 和 Avalonia 11.x 版本。对于不同版本,可能需要调整相应的配置。