dotnet-cli-distribution
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesedotnet-cli-distribution
dotnet-cli-distribution
CLI distribution strategy for .NET tools: choosing between Native AOT single-file publish, framework-dependent deployment, and packaging. Runtime Identifier (RID) matrix planning for cross-platform targets (linux-x64, osx-arm64, win-x64, linux-arm64), single-file publish configuration, and binary size optimization techniques for CLI applications.
dotnet toolVersion assumptions: .NET 8.0+ baseline. Native AOT for console apps is fully supported since .NET 8. Single-file publish has been mature since .NET 6.
Out of scope: Native AOT MSBuild configuration (PublishAot, ILLink descriptors, EnableAotAnalyzer, trimming) -- see [skill:dotnet-native-aot]. AOT-first application design patterns (source gen over reflection, DI choices) -- see [skill:dotnet-aot-architecture]. Multi-platform packaging formats (Homebrew, apt/deb, winget, Scoop) -- see [skill:dotnet-cli-packaging]. Release CI/CD pipeline -- see [skill:dotnet-cli-release-pipeline]. Container-based distribution -- see [skill:dotnet-containers]. General CI/CD patterns -- see [skill:dotnet-gha-patterns] and [skill:dotnet-ado-patterns].
Cross-references: [skill:dotnet-native-aot] for AOT compilation pipeline, [skill:dotnet-aot-architecture] for AOT-safe design patterns, [skill:dotnet-cli-architecture] for CLI layered architecture, [skill:dotnet-cli-packaging] for platform-specific package formats, [skill:dotnet-cli-release-pipeline] for automated release workflows, [skill:dotnet-containers] for container-based distribution, [skill:dotnet-tool-management] for consumer-side tool installation and manifest management.
.NET工具的CLI分发策略:在Native AOT单文件发布、框架依赖部署、打包三种方案中做选择。包含跨平台目标的运行时标识符(RID)矩阵规划(linux-x64、osx-arm64、win-x64、linux-arm64)、单文件发布配置,以及CLI应用的二进制体积优化技术。
dotnet tool版本前提: 基于.NET 8.0+版本。控制台应用的Native AOT支持自.NET 8起完全成熟,单文件发布功能自.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]。
交叉引用:AOT编译流水线参见 [skill:dotnet-native-aot],AOT兼容设计模式参见 [skill:dotnet-aot-architecture],CLI分层架构参见 [skill:dotnet-cli-architecture],平台专属包格式参见 [skill:dotnet-cli-packaging],自动发布工作流参见 [skill:dotnet-cli-release-pipeline],容器化分发参见 [skill:dotnet-containers],用户端工具安装与清单管理参见 [skill:dotnet-tool-management]。
Distribution Strategy Decision Matrix
分发策略决策矩阵
Choose the distribution model based on target audience and deployment constraints.
| Strategy | Startup Time | Binary Size | Runtime Required | Best For |
|---|---|---|---|---|
| Native AOT single-file | ~10ms | 10-30 MB | None | Performance-critical CLI tools, broad distribution |
| Framework-dependent single-file | ~100ms | 1-5 MB | .NET runtime | Internal tools where runtime is guaranteed |
| Self-contained single-file | ~100ms | 60-80 MB | None | Simple distribution without AOT complexity |
| ~200ms | < 1 MB (NuGet) | .NET SDK | Developer tools, .NET ecosystem users |
根据目标受众和部署约束选择分发模式。
| 策略 | 启动速度 | 二进制体积 | 需要预装运行时 | 适用场景 |
|---|---|---|---|---|
| Native AOT单文件 | ~10ms | 10-30 MB | 无 | 性能敏感的CLI工具、大范围分发场景 |
| 框架依赖单文件 | ~100ms | 1-5 MB | .NET runtime | 运行时环境有保障的内部工具 |
| 自包含单文件 | ~100ms | 60-80 MB | 无 | 无需处理AOT复杂度的简易分发场景 |
| ~200ms | < 1 MB (NuGet) | .NET SDK | 开发者工具、.NET生态用户 |
When to Choose Each Strategy
各策略适用场景
Native AOT single-file -- the gold standard for CLI distribution:
- Zero dependencies on target machine (no .NET runtime needed)
- Fastest startup (~10ms vs ~100ms+ for JIT)
- Smallest binary when combined with trimming
- Trade-off: longer build times, no reflection unless preserved
- See [skill:dotnet-native-aot] for PublishAot MSBuild configuration
Framework-dependent deployment:
- Smallest artifact size (only app code, no runtime)
- Users must have .NET runtime installed
- Best for internal/enterprise tools where runtime is managed
- Can still use single-file publish for convenience
Self-contained (non-AOT):
- Includes .NET runtime in the artifact
- Larger binary than AOT but simpler build process
- Full reflection and dynamic code support
- Good compromise when AOT compat is difficult
dotnet tool- Distributed via NuGet -- simplest publishing workflow
- Users install with
dotnet tool install -g mytool - Requires .NET SDK on target (not just runtime)
- Best for developer-facing tools in the .NET ecosystem
- See [skill:dotnet-cli-packaging] for NuGet distribution details
Native AOT单文件 —— CLI分发的黄金标准:
- 目标机器零依赖(无需安装.NET runtime)
- 启动速度最快(
10ms,对比JIT模式的100ms+) - 配合裁剪可实现最小二进制体积
- 权衡:构建时间更长,除非显式保留否则反射功能不可用
- PublishAot MSBuild配置参见 [skill:dotnet-native-aot]
框架依赖部署:
- 产物体积最小(仅包含应用代码,无运行时)
- 用户必须预先安装对应版本.NET runtime
- 最适合运行时环境统一管控的内部/企业工具
- 仍可使用单文件发布提升使用便捷性
自包含(非AOT):
- 产物中包含.NET runtime
- 二进制体积比AOT大,但构建流程更简单
- 完整支持反射和动态代码
- 当AOT兼容改造成本较高时的折中方案
dotnet tool- 通过NuGet分发,发布流程最简单
- 用户通过即可安装
dotnet tool install -g mytool - 目标机器需要预装.NET SDK(不只是runtime)
- 最适合.NET生态面向开发者的工具
- NuGet分发细节参见 [skill:dotnet-cli-packaging]
Runtime Identifier (RID) Matrix
运行时标识符(RID)矩阵
Standard CLI RID Targets
标准CLI RID目标
Target the four primary RIDs for broad coverage:
| RID | Platform | Notes |
|---|---|---|
| Linux x86_64 | Most Linux servers, CI runners, WSL |
| Linux ARM64 | AWS Graviton, Raspberry Pi 4+, Apple Silicon VMs |
| macOS Apple Silicon | M1/M2/M3+ Macs (primary macOS target) |
| Windows x86_64 | Windows 10+, Windows Server |
覆盖四个主流RID即可实现大范围兼容:
| RID | 平台 | 说明 |
|---|---|---|
| Linux x86_64 | 绝大多数Linux服务器、CI运行器、WSL |
| Linux ARM64 | AWS Graviton、树莓派4+、Apple Silicon虚拟机 |
| macOS Apple Silicon | M1/M2/M3+ 系列Mac(macOS主流目标) |
| Windows x86_64 | Windows 10+、Windows Server |
Optional Extended Targets
可选扩展目标
| RID | When to Include |
|---|---|
| Legacy Intel Mac support (declining market share) |
| Alpine Linux / Docker scratch images |
| Alpine on ARM64 |
| Windows on ARM (Surface Pro X, Snapdragon laptops) |
| RID | 新增场景 |
|---|---|
| 旧款Intel Mac支持(市场占比持续下降) |
| Alpine Linux / Docker scratch镜像 |
| ARM64架构的Alpine系统 |
| ARM架构Windows设备(Surface Pro X、骁龙笔记本) |
RID Configuration in .csproj
.csproj中的RID配置
xml
<!-- Set per publish, not in csproj (avoids accidental RID lock-in) -->
<!-- Use dotnet publish -r <rid> instead -->
<!-- If you must set a default for local development -->
<PropertyGroup Condition="'$(RuntimeIdentifier)' == ''">
<RuntimeIdentifier>osx-arm64</RuntimeIdentifier>
</PropertyGroup>Publish per RID from the command line:
bash
undefinedxml
<!-- 发布时指定RID,不要写在csproj中(避免意外锁定RID) -->
<!-- 推荐使用 dotnet publish -r <rid> 命令指定 -->
<!-- 若必须为本地开发设置默认值 -->
<PropertyGroup Condition="'$(RuntimeIdentifier)' == ''">
<RuntimeIdentifier>osx-arm64</RuntimeIdentifier>
</PropertyGroup>通过命令行为每个RID发布:
bash
undefinedPublish for each target RID
为每个目标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
---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
---Single-File Publish
单文件发布
Single-file publish bundles the application and its dependencies into one executable.
单文件发布将应用及其依赖打包为单个可执行文件。
Configuration
配置
xml
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
<!-- Required for single-file -->
<SelfContained>true</SelfContained>
<!-- Embed PDB for stack traces (optional, adds ~2-5 MB) -->
<DebugType>embedded</DebugType>
<!-- Include native libraries in the single file -->
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>xml
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
<!-- 单文件发布必填 -->
<SelfContained>true</SelfContained>
<!-- 嵌入PDB保留栈追踪信息(可选,会增加~2-5 MB体积) -->
<DebugType>embedded</DebugType>
<!-- 将原生库嵌入单文件 -->
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>Single-File with Native AOT
Native AOT单文件
When combined with Native AOT, single-file is implicit -- AOT always produces a single native binary:
xml
<PropertyGroup>
<PublishAot>true</PublishAot>
<!-- PublishSingleFile is not needed -- AOT output is inherently single-file -->
<!-- SelfContained is implied by PublishAot -->
</PropertyGroup>See [skill:dotnet-native-aot] for the full AOT publish configuration including ILLink, type preservation, and analyzer setup.
与Native AOT结合使用时,单文件是默认特性——AOT始终会生成单个原生二进制文件:
xml
<PropertyGroup>
<PublishAot>true</PublishAot>
<!-- 无需设置PublishSingleFile,AOT输出天然是单文件 -->
<!-- PublishAot会默认启用SelfContained -->
</PropertyGroup>完整AOT发布配置(包含ILLink、类型保留、分析器设置)参见 [skill:dotnet-native-aot]。
Publish Command
发布命令
bash
undefinedbash
undefinedFramework-dependent single-file (requires .NET runtime on target)
框架依赖单文件(目标机器需要安装.NET runtime)
dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true --self-contained false
dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true --self-contained false
Self-contained single-file (includes runtime, no AOT)
自包含单文件(包含运行时,无AOT)
dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true --self-contained true
dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true --self-contained true
Native AOT (inherently single-file, smallest and fastest)
Native AOT(天然单文件,体积最小、速度最快)
dotnet publish -c Release -r linux-x64
dotnet publish -c Release -r linux-x64
(when PublishAot=true is in csproj)
前提是csproj中已设置PublishAot=true
---
---Size Optimization for CLI Binaries
CLI二进制体积优化
Trimming (Non-AOT)
裁剪(非AOT场景)
Trimming removes unused code from the published output. For self-contained non-AOT builds:
xml
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<!-- Suppress known trim warnings for CLI scenarios -->
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>裁剪会移除发布产物中未使用的代码,适用于自包含非AOT构建:
xml
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<!-- 禁用CLI场景下已知裁剪警告的屏蔽 -->
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>AOT Size Optimization
AOT体积优化
For Native AOT builds, size is controlled by AOT-specific MSBuild properties. See [skill:dotnet-native-aot] for the full configuration. Key CLI-relevant properties include , , , and .
StripSymbolsOptimizationPreferenceInvariantGlobalizationStackTraceSupportNative AOT构建的体积由AOT专属的MSBuild属性控制,完整配置参见 [skill:dotnet-native-aot]。CLI场景相关的核心属性包括、、和。
StripSymbolsOptimizationPreferenceInvariantGlobalizationStackTraceSupportSize Comparison (Typical CLI Tool)
体积对比(典型CLI工具)
| Configuration | Approximate Size |
|---|---|
| Self-contained (no trim) | 60-80 MB |
| Self-contained + trimmed | 15-30 MB |
| Native AOT (default) | 15-25 MB |
| Native AOT + size optimized | 8-15 MB |
| Native AOT + invariant globalization + stripped | 5-10 MB |
| Framework-dependent | 1-5 MB |
| 配置 | 近似体积 |
|---|---|
| 自包含(未裁剪) | 60-80 MB |
| 自包含+裁剪 | 15-30 MB |
| Native AOT(默认配置) | 15-25 MB |
| Native AOT+体积优化 | 8-15 MB |
| Native AOT+固定全球化+符号剥离 | 5-10 MB |
| 框架依赖 | 1-5 MB |
Practical Size Reduction Checklist
实用体积缩减检查清单
- Enable invariant globalization if the tool does not need locale-specific formatting ()
InvariantGlobalization=true - Strip symbols on Linux/macOS () -- keep separate symbol files for crash analysis
StripSymbols=true - Optimize for size () -- minimal runtime performance impact for I/O-bound CLI tools
OptimizationPreference=Size - Disable reflection where possible -- use source generators for JSON serialization ([skill:dotnet-aot-architecture])
- Audit NuGet dependencies -- each dependency adds to the binary; remove unused packages
- 如果工具不需要本地化格式,启用固定全球化()
InvariantGlobalization=true - Linux/macOS下剥离符号()—— 单独保留符号文件用于崩溃分析即可
StripSymbols=true - 优先优化体积()—— 对IO密集型CLI工具的运行时性能影响极小
OptimizationPreference=Size - 尽可能禁用反射—— JSON序列化使用源码生成方案([skill:dotnet-aot-architecture])
- 审计NuGet依赖—— 每个依赖都会增加二进制体积,移除未使用的包
Framework-Dependent vs Self-Contained Trade-offs
框架依赖 vs 自包含权衡
Framework-Dependent
框架依赖
bash
dotnet publish -c Release -r linux-x64 --self-contained falseAdvantages:
- Smallest artifact (1-5 MB)
- Serviced by runtime updates (security patches applied by runtime, not app rebuild)
- Faster publish times
Disadvantages:
- Requires matching .NET runtime on target
- Runtime version mismatch causes startup failures
- Users must manage runtime installation
bash
dotnet publish -c Release -r linux-x64 --self-contained false优势:
- 产物最小(1-5 MB)
- 可复用运行时更新(安全补丁由运行时更新覆盖,无需重建应用)
- 发布速度更快
劣势:
- 目标机器需要安装匹配版本的.NET runtime
- 运行时版本不匹配会导致启动失败
- 用户需要自行管理运行时安装
Self-Contained
自包含
bash
dotnet publish -c Release -r linux-x64 --self-contained trueAdvantages:
- No runtime dependency on target
- App controls exact runtime version
- Side-by-side deployment (multiple apps, different runtimes)
Disadvantages:
- Larger artifact (60-80 MB without trimming)
- Must rebuild and redistribute for runtime security patches
- One artifact per target RID
bash
dotnet publish -c Release -r linux-x64 --self-contained true优势:
- 目标机器无运行时依赖
- 应用可完全控制运行时版本
- 支持并行部署(多个应用可使用不同运行时版本)
劣势:
- 产物体积更大(未裁剪时60-80 MB)
- 运行时安全补丁需要重建并重新分发应用
- 每个目标RID对应单独的产物
Publishing Workflow
发布工作流
Local Development
本地开发
bash
undefinedbash
undefinedQuick local publish for testing
快速本地发布用于测试
dotnet publish -c Release -r osx-arm64
dotnet publish -c Release -r osx-arm64
Verify the binary
验证二进制文件
./bin/Release/net8.0/osx-arm64/publish/mytool --version
undefined./bin/Release/net8.0/osx-arm64/publish/mytool --version
undefinedProducing Release Artifacts
生成发布产物
bash
#!/bin/bashbash
#!/bin/bashbuild-all.sh -- Produce artifacts for all target RIDs
build-all.sh -- 为所有目标RID生成产物
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")
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
注意:x64架构下交叉编译ARM64的Native AOT需要对应平台工具链
See [skill:dotnet-cli-release-pipeline] for CI-based cross-compilation setup
基于CI的交叉编译配置参见 [skill:dotnet-cli-release-pipeline]
for rid in "${RIDS[@]}"; do
echo "Publishing for $rid..."
dotnet publish "$PROJECT"
-c Release
-r "$rid"
-o "$OUTPUT_DIR/$rid"
/p:Version="$VERSION" done
-c Release
-r "$rid"
-o "$OUTPUT_DIR/$rid"
/p:Version="$VERSION" done
for rid in "${RIDS[@]}"; do
echo "正在为 $rid 发布..."
dotnet publish "$PROJECT"
-c Release
-r "$rid"
-o "$OUTPUT_DIR/$rid"
/p:Version="$VERSION" done
-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/"
undefinedfor 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 "产物存放路径:$OUTPUT_DIR/"
undefinedChecksum Generation
校验和生成
Always produce checksums for release artifacts:
bash
undefined始终为发布产物生成校验和:
bash
undefinedGenerate SHA-256 checksums
生成SHA-256校验和
cd artifacts
shasum -a 256 *.tar.gz *.zip > checksums-sha256.txt
See [skill:dotnet-cli-release-pipeline] for automating this in GitHub Actions.
---cd artifacts
shasum -a 256 *.tar.gz *.zip > checksums-sha256.txt
GitHub Actions中自动实现该流程的方法参见 [skill:dotnet-cli-release-pipeline]。
---Agent Gotchas
常见陷阱
- Do not set RuntimeIdentifier in the .csproj for multi-platform CLI tools. Hardcoding a RID in the project file prevents building for other platforms. Pass at publish time instead.
-r <rid> - Do not use PublishSingleFile with PublishAot. Native AOT output is inherently single-file. Setting both is redundant and may cause confusing build warnings.
- Do not skip InvariantGlobalization for size-sensitive CLI tools. Globalization data adds ~25 MB to AOT binaries. Most CLI tools that do not format locale-specific dates/currencies should enable .
InvariantGlobalization=true - Do not distribute self-contained non-trimmed binaries. A 60-80 MB CLI tool is unacceptable for end users. Either trim (PublishTrimmed), use AOT, or distribute as framework-dependent.
- Do not forget to produce checksums for release artifacts. Users and package managers need SHA-256 checksums to verify download integrity. See [skill:dotnet-cli-release-pipeline] for automated checksum generation.
- Do not hardcode secrets in publish scripts. Use environment variable placeholders () with a comment about CI secret storage for any signing or upload credentials.
${SIGNING_KEY}
- 多平台CLI工具不要在.csproj中设置RuntimeIdentifier。项目文件中硬编码RID会导致无法构建其他平台版本,改为在发布时通过参数指定。
-r <rid> - 不要同时设置PublishSingleFile和PublishAot。Native AOT输出天然是单文件,同时设置两个属性属于冗余操作,可能引发奇怪的构建警告。
- 对体积敏感的CLI工具不要忘记启用InvariantGlobalization。全球化数据会让AOT二进制增加约25 MB体积,绝大多数不需要生成本地化日期/货币格式的CLI工具都应该开启。
InvariantGlobalization=true - 不要分发未裁剪的自包含非AOT二进制。60-80 MB的CLI工具对终端用户很不友好,要么启用裁剪(PublishTrimmed)、使用AOT,要么改为框架依赖分发。
- 不要忘记为发布产物生成校验和。用户和包管理器需要SHA-256校验和验证下载完整性,自动校验和生成方法参见 [skill:dotnet-cli-release-pipeline]。
- 不要在发布脚本中硬编码密钥。任何签名或上传凭证都使用环境变量占位符(),并备注CI密钥存储的相关说明。
${SIGNING_KEY}