dotnet-cli-distribution

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

dotnet-cli-distribution

dotnet-cli-distribution

CLI distribution strategy for .NET tools: choosing between Native AOT single-file publish, framework-dependent deployment, and
dotnet tool
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.
Version 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单文件发布、框架依赖部署、
dotnet tool
打包三种方案中做选择。包含跨平台目标的运行时标识符(RID)矩阵规划(linux-x64、osx-arm64、win-x64、linux-arm64)、单文件发布配置,以及CLI应用的二进制体积优化技术。
版本前提: 基于.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.
StrategyStartup TimeBinary SizeRuntime RequiredBest For
Native AOT single-file~10ms10-30 MBNonePerformance-critical CLI tools, broad distribution
Framework-dependent single-file~100ms1-5 MB.NET runtimeInternal tools where runtime is guaranteed
Self-contained single-file~100ms60-80 MBNoneSimple distribution without AOT complexity
dotnet tool
(global/local)
~200ms< 1 MB (NuGet).NET SDKDeveloper tools, .NET ecosystem users
根据目标受众和部署约束选择分发模式。
策略启动速度二进制体积需要预装运行时适用场景
Native AOT单文件~10ms10-30 MB性能敏感的CLI工具、大范围分发场景
框架依赖单文件~100ms1-5 MB.NET runtime运行时环境有保障的内部工具
自包含单文件~100ms60-80 MB无需处理AOT复杂度的简易分发场景
dotnet tool
(全局/本地)
~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
packaging:
  • 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:
RIDPlatformNotes
linux-x64
Linux x86_64Most Linux servers, CI runners, WSL
linux-arm64
Linux ARM64AWS Graviton, Raspberry Pi 4+, Apple Silicon VMs
osx-arm64
macOS Apple SiliconM1/M2/M3+ Macs (primary macOS target)
win-x64
Windows x86_64Windows 10+, Windows Server
覆盖四个主流RID即可实现大范围兼容:
RID平台说明
linux-x64
Linux x86_64绝大多数Linux服务器、CI运行器、WSL
linux-arm64
Linux ARM64AWS Graviton、树莓派4+、Apple Silicon虚拟机
osx-arm64
macOS Apple SiliconM1/M2/M3+ 系列Mac(macOS主流目标)
win-x64
Windows x86_64Windows 10+、Windows Server

Optional Extended Targets

可选扩展目标

RIDWhen to Include
osx-x64
Legacy Intel Mac support (declining market share)
linux-musl-x64
Alpine Linux / Docker scratch images
linux-musl-arm64
Alpine on ARM64
win-arm64
Windows on ARM (Surface Pro X, Snapdragon laptops)
RID新增场景
osx-x64
旧款Intel Mac支持(市场占比持续下降)
linux-musl-x64
Alpine Linux / Docker scratch镜像
linux-musl-arm64
ARM64架构的Alpine系统
win-arm64
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
undefined
xml
<!-- 发布时指定RID,不要写在csproj中(避免意外锁定RID) -->
<!-- 推荐使用 dotnet publish -r <rid> 命令指定 -->

<!-- 若必须为本地开发设置默认值 -->
<PropertyGroup Condition="'$(RuntimeIdentifier)' == ''">
  <RuntimeIdentifier>osx-arm64</RuntimeIdentifier>
</PropertyGroup>
通过命令行为每个RID发布:
bash
undefined

Publish 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
undefined
bash
undefined

Framework-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
StripSymbols
,
OptimizationPreference
,
InvariantGlobalization
, and
StackTraceSupport
.
Native AOT构建的体积由AOT专属的MSBuild属性控制,完整配置参见 [skill:dotnet-native-aot]。CLI场景相关的核心属性包括
StripSymbols
OptimizationPreference
InvariantGlobalization
StackTraceSupport

Size Comparison (Typical CLI Tool)

体积对比(典型CLI工具)

ConfigurationApproximate Size
Self-contained (no trim)60-80 MB
Self-contained + trimmed15-30 MB
Native AOT (default)15-25 MB
Native AOT + size optimized8-15 MB
Native AOT + invariant globalization + stripped5-10 MB
Framework-dependent1-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

实用体积缩减检查清单

  1. Enable invariant globalization if the tool does not need locale-specific formatting (
    InvariantGlobalization=true
    )
  2. Strip symbols on Linux/macOS (
    StripSymbols=true
    ) -- keep separate symbol files for crash analysis
  3. Optimize for size (
    OptimizationPreference=Size
    ) -- minimal runtime performance impact for I/O-bound CLI tools
  4. Disable reflection where possible -- use source generators for JSON serialization ([skill:dotnet-aot-architecture])
  5. Audit NuGet dependencies -- each dependency adds to the binary; remove unused packages

  1. 如果工具不需要本地化格式,启用固定全球化
    InvariantGlobalization=true
  2. Linux/macOS下剥离符号
    StripSymbols=true
    )—— 单独保留符号文件用于崩溃分析即可
  3. 优先优化体积
    OptimizationPreference=Size
    )—— 对IO密集型CLI工具的运行时性能影响极小
  4. 尽可能禁用反射—— JSON序列化使用源码生成方案([skill:dotnet-aot-architecture])
  5. 审计NuGet依赖—— 每个依赖都会增加二进制体积,移除未使用的包

Framework-Dependent vs Self-Contained Trade-offs

框架依赖 vs 自包含权衡

Framework-Dependent

框架依赖

bash
dotnet publish -c Release -r linux-x64 --self-contained false
Advantages:
  • 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 true
Advantages:
  • 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
undefined
bash
undefined

Quick 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
undefined

Producing Release Artifacts

生成发布产物

bash
#!/bin/bash
bash
#!/bin/bash

build-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
for rid in "${RIDS[@]}"; do echo "正在为 $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/"
undefined
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 "产物存放路径:$OUTPUT_DIR/"
undefined

Checksum Generation

校验和生成

Always produce checksums for release artifacts:
bash
undefined
始终为发布产物生成校验和:
bash
undefined

Generate 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

常见陷阱

  1. 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
    -r <rid>
    at publish time instead.
  2. Do not use PublishSingleFile with PublishAot. Native AOT output is inherently single-file. Setting both is redundant and may cause confusing build warnings.
  3. 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
    .
  4. 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.
  5. 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.
  6. Do not hardcode secrets in publish scripts. Use environment variable placeholders (
    ${SIGNING_KEY}
    ) with a comment about CI secret storage for any signing or upload credentials.

  1. 多平台CLI工具不要在.csproj中设置RuntimeIdentifier。项目文件中硬编码RID会导致无法构建其他平台版本,改为在发布时通过
    -r <rid>
    参数指定。
  2. 不要同时设置PublishSingleFile和PublishAot。Native AOT输出天然是单文件,同时设置两个属性属于冗余操作,可能引发奇怪的构建警告。
  3. 对体积敏感的CLI工具不要忘记启用InvariantGlobalization。全球化数据会让AOT二进制增加约25 MB体积,绝大多数不需要生成本地化日期/货币格式的CLI工具都应该开启
    InvariantGlobalization=true
  4. 不要分发未裁剪的自包含非AOT二进制。60-80 MB的CLI工具对终端用户很不友好,要么启用裁剪(PublishTrimmed)、使用AOT,要么改为框架依赖分发。
  5. 不要忘记为发布产物生成校验和。用户和包管理器需要SHA-256校验和验证下载完整性,自动校验和生成方法参见 [skill:dotnet-cli-release-pipeline]。
  6. 不要在发布脚本中硬编码密钥。任何签名或上传凭证都使用环境变量占位符(
    ${SIGNING_KEY}
    ),并备注CI密钥存储的相关说明。

References

参考资料