关于 dotnet-cli-distribution
为 .NET CLI 开发者提供分发策略指南,帮助选择 Native AOT、依赖框架部署或 dotnet tool,并规划跨平台 RID 与单文件发布。还介绍二进制体积优化和发布产物校验。
主要功能
- 01比较 CLI 分发方式
- 02规划跨平台 RID 目标
- 03配置单文件发布
- 04优化发布二进制体积
- 05生成发布产物校验和
适用场景
- 01为 CLI 工具选择部署方式
- 02确定跨平台构建目标
- 03缩减自包含发布的体积
- 04准备可校验的发布文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-cli-distribution安装后在 Claude Code 中输入/dotnet-cli-distribution确认已加载。
详细介绍
dotnet-cli-distribution
.NET 工具的 CLI 分发策略:在 Native AOT 单文件发布、依赖框架的部署和 dotnet tool 打包之间做出选择。针对跨平台目标(linux-x64、osx-arm64、win-x64、linux-arm64)规划运行时标识符(RID)矩阵、配置单文件发布,以及优化 CLI 应用二进制文件大小的技巧。
**版本假设:**以 .NET 8.0+ 为基线。自 .NET 8 起,控制台应用已全面支持 Native AOT。自 .NET 6 起,单文件发布已相当成熟。
**范围之外:**Native AOT MSBuild 配置(PublishAot、ILLink 描述符、EnableAotAnalyzer、裁剪)——请参阅 [skill:dotnet-native-aot]。以 AOT 为先的应用设计模式(优先使用源生成而非反射、DI 选择)——请参阅 [skill:dotnet-aot-architecture]。多平台打包格式(Homebrew、apt/deb、winget、Scoop)——请参阅 [skill:dotnet-cli-packaging]。发布 CI/CD 流水线——请参阅 [skill:dotnet-cli-release-pipeline]。基于容器的分发——请参阅 [skill:dotnet-containers]。通用 CI/CD 模式——请参阅 [skill:dotnet-gha-patterns] 和 [skill:dotnet-ado-patterns]。
交叉引用:[skill:dotnet-native-aot] 介绍 AOT 编译流水线,[skill:dotnet-aot-architecture] 介绍 AOT 安全的设计模式,[skill:dotnet-cli-architecture] 介绍 CLI 分层架构,[skill:dotnet-cli-packaging] 介绍特定平台的打包格式,[skill:dotnet-cli-release-pipeline] 介绍自动化发布工作流,[skill:dotnet-containers] 介绍基于容器的分发,[skill:dotnet-tool-management] 介绍面向使用者的工具安装和清单管理。
分发策略决策矩阵
根据目标受众和部署限制选择分发模型。
| 策略 | 启动时间 | 二进制文件大小 | 是否需要运行时 | 最适用于 |
|---|---|---|---|---|
| Native AOT 单文件 | ~10ms | 10-30 MB | 无 | 对性能要求高的 CLI 工具、广泛分发 |
| 依赖框架的单文件 | ~100ms | 1-5 MB | .NET 运行时 | 可确保有运行时的内部工具 |
| 独立单文件 | ~100ms | 60-80 MB | 无 | 无需 AOT 复杂性的简单分发 |
dotnet tool(全局/本地) | ~200ms | < 1 MB(NuGet) | .NET SDK | 开发者工具、.NET 生态系统用户 |
如何选择策略
Native AOT 单文件——CLI 分发的黄金标准:
- 目标计算机上零依赖(无需 .NET 运行时)
- 启动最快(~10ms,相比 JIT 的 ~100ms+)
- 与裁剪结合时,二进制文件最小
- 权衡:构建时间更长,除非保留反射功能,否则无法使用反射
- PublishAot MSBuild 配置请参阅 [skill:dotnet-native-aot]
依赖框架的部署:
- 产物大小最小(仅包含应用代码,不含运行时)
- 用户必须安装 .NET 运行时
- 最适用于运行时由内部/企业统一管理的工具
- 仍可使用单文件发布,提升便利性
独立部署(非 AOT):
- 产物中包含 .NET 运行时
- 二进制文件比 AOT 更大,但构建流程更简单
- 完整支持反射和动态代码
- AOT 兼容性难以实现时,是不错的折中方案
dotnet tool 打包:
- 通过 NuGet 分发——发布工作流最简单
- 用户使用
dotnet tool install -g mytool安装 - 目标设备需要 .NET SDK(不只是运行时)
- 最适用于 .NET 生态系统中面向开发者的工具
- NuGet 分发详情请参阅 [skill:dotnet-cli-packaging]
运行时标识符(RID)矩阵
CLI 的标准 RID 目标
针对四个主要 RID,以实现广泛覆盖:
| RID | 平台 | 备注 |
|---|---|---|
linux-x64 | Linux x86_64 | 大多数 Linux 服务器、CI 运行器、WSL |
linux-arm64 | Linux ARM64 | AWS Graviton、Raspberry Pi 4+、Apple Silicon 虚拟机 |
osx-arm64 | macOS Apple Silicon | M1/M2/M3+ Mac(主要 macOS 目标) |
win-x64 | Windows x86_64 | Windows 10+、Windows Server |
可选的扩展目标
| RID | 包含时机 |
|---|---|
osx-x64 | 支持旧款 Intel Mac(市场份额持续下降) |
linux-musl-x64 | Alpine Linux / Docker scratch 镜像 |
linux-musl-arm64 | ARM64 上的 Alpine |
win-arm64 | Windows on ARM(Surface Pro X、Snapdragon 笔记本电脑) |
在 .csproj 中配置 RID
<PropertyGroup Condition="'$(RuntimeIdentifier)' == ''">
<RuntimeIdentifier>osx-arm64</RuntimeIdentifier>
</PropertyGroup>
从命令行按 RID 发布:
# Publish for each target RID
dotnet publish -c Release -r linux-x64
dotnet publish -c Release -r linux-arm64
dotnet publish -c Release -r osx-arm64
dotnet publish -c Release -r win-x64
单文件发布
单文件发布会将应用及其依赖项打包到一个可执行文件中。
配置
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<DebugType>embedded</DebugType>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
配合 Native AOT 使用单文件发布
与 Native AOT 结合时,单文件发布是隐式的——AOT 始终会生成一个原生二进制文件:
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
请参阅 [skill:dotnet-native-aot],了解完整的 AOT 发布配置,包括 ILLink、类型保留和分析器设置。
发布命令
# Framework-dependent single-file (requires .NET runtime on target)
dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true --self-contained false
# Self-contained single-file (includes runtime, no AOT)
dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true --self-contained true
# Native AOT (inherently single-file, smallest and fastest)
dotnet publish -c Release -r linux-x64
# (when PublishAot=true is in csproj)
优化 CLI 二进制文件的大小
裁剪(非 AOT)
裁剪会从发布输出中移除未使用的代码。对于自包含的非 AOT 构建:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>
AOT 大小优化
对于 Native AOT 构建,可通过 AOT 专用 MSBuild 属性控制大小。完整配置请参阅 [skill:dotnet-native-aot]。与 CLI 相关的关键属性包括 StripSymbols、OptimizationPreference、InvariantGlobalization 和 StackTraceSupport。
大小对比(典型 CLI 工具)
| 配置 | 大致大小 |
|---|---|
| 自包含(不裁剪) | 60-80 MB |
| 自包含 + 裁剪 | 15-30 MB |
| Native AOT(默认) | 15-25 MB |
| Native AOT + 大小优化 | 8-15 MB |
| Native AOT + 不变全球化 + 剥离符号 | 5-10 MB |
| 依赖框架 | 1-5 MB |
实用的大小缩减清单
- 启用不变全球化,前提是工具不需要特定区域设置的格式化(
InvariantGlobalization=true) - 在 Linux/macOS 上剥离符号(
StripSymbols=true)——保留单独的符号文件用于崩溃分析 - 针对大小进行优化(
OptimizationPreference=Size)——对 I/O 密集型 CLI 工具的运行时性能影响极小 - 尽可能禁用反射——使用源生成器进行 JSON 序列化([skill:dotnet-aot-architecture])
- 审查 NuGet 依赖项——每个依赖项都会增大二进制文件;移除未使用的包
依赖框架与自包含的权衡
依赖框架
dotnet publish -c Release -r linux-x64 --self-contained false
优点:
- 产物最小(1-5 MB)
- 由运行时更新提供维护(安全补丁由运行时应用,无需重新构建应用)
- 发布速度更快
缺点:
- 目标环境必须安装匹配的 .NET 运行时
- 运行时版本不匹配会导致启动失败
- 用户必须自行管理运行时安装
自包含
dotnet publish -c Release -r linux-x64 --self-contained true
优点:
- 目标环境无需依赖运行时
- 应用可控制确切的运行时版本
- 支持并行部署(多个应用使用不同运行时)
缺点:
- 产物较大(不裁剪时为 60-80 MB)
- 必须为运行时安全补丁重新构建并分发
- 每个目标 RID 都需要一个产物
发布工作流
本地开发
# Quick local publish for testing
dotnet publish -c Release -r osx-arm64
# Verify the binary
./bin/Release/net8.0/osx-arm64/publish/mytool --version
生成发布产物
#!/bin/bash
# build-all.sh -- Produce artifacts for all target RIDs
set -euo pipefail
VERSION="${1:?Usage: build-all.sh <version>}"
PROJECT="src/MyCli/MyCli.csproj"
OUTPUT_DIR="artifacts"
RIDS=("linux-x64" "linux-arm64" "osx-arm64" "win-x64")
# Note: Native AOT cross-compilation for ARM64 on x64 requires platform toolchain
# See [skill:dotnet-cli-release-pipeline] for CI-based cross-compilation setup
for rid in "${RIDS[@]}"; do
echo "Publishing for $rid..."
dotnet publish "$PROJECT" \
-c Release \
-r "$rid" \
-o "$OUTPUT_DIR/$rid" \
/p:Version="$VERSION"
done
# Create archives
for rid in "${RIDS[@]}"; do
if [[ "$rid" == win-* ]]; then
(cd "$OUTPUT_DIR/$rid" && zip -q "../mytool-$VERSION-$rid.zip" *)
else
tar -czf "$OUTPUT_DIR/mytool-$VERSION-$rid.tar.gz" -C "$OUTPUT_DIR/$rid" .
fi
done
echo "Artifacts in $OUTPUT_DIR/"
生成校验和
始终为发布产物生成校验和:
# Generate SHA-256 checksums
cd artifacts
shasum -a 256 *.tar.gz *.zip > checksums-sha256.txt
请参阅 [skill:dotnet-cli-release-pipeline],了解如何在 GitHub Actions 中自动执行此操作。
Agent 注意事项
- **不要在多平台 CLI 工具的 .csproj 中设置 RuntimeIdentifier。**在项目文件中硬编码 RID 会导致无法为其他平台构建。应在发布时传入
-r <rid>。 - **不要同时使用 PublishSingleFile 和 PublishAot。**Native AOT 输出本身就是单文件。两者同时设置是多余的,还可能导致令人困惑的构建警告。
- **对于对体积敏感的 CLI 工具,不要忽略 InvariantGlobalization。**全球化数据会使 AOT 二进制文件增加约 25 MB。大多数不格式化特定区域设置的日期或货币的 CLI 工具都应启用
InvariantGlobalization=true。 - **不要分发未修剪的独立部署二进制文件。**对于终端用户来说,60-80 MB 的 CLI 工具无法接受。应进行修剪(PublishTrimmed)、使用 AOT,或采用依赖框架的方式分发。
- **不要忘记为发布产物生成校验和。**用户和包管理器需要 SHA-256 校验和来验证下载完整性。请参阅 [skill:dotnet-cli-release-pipeline],了解如何自动生成校验和。
- **不要在发布脚本中硬编码密钥。**对于任何签名或上传凭据,都应使用环境变量占位符(
${SIGNING_KEY}),并注释说明 CI 密钥的存储位置。