一键导入
dotnet-cli-distribution
Choosing CLI output format. AOT vs framework-dependent, RID matrix, single-file, dotnet tool.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Choosing CLI output format. AOT vs framework-dependent, RID matrix, single-file, dotnet tool.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Guidelines for writing TUnit tests in .NET, including setup, assertions, async testing, and best practices. Use when writing unit tests with TUnit framework, setting up TUnit in a .NET project, or migrating from other test frameworks to TUnit.
Hosted services, background jobs, outbox patterns, and graceful shutdown handling for ASP.NET Core applications. Includes patterns for reliable job processing, distributed systems, and lifecycle management. Use when implementing background processing in ASP.NET Core applications, handling outbox patterns for reliable message delivery, or managing graceful service shutdown.
Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, versioning, naming conventions, parameter ordering, and return types for NuGet packages and distributed systems. Use when designing public APIs for NuGet packages or libraries, making changes to existing public APIs, planning wire format changes for distributed systems, or reviewing pull requests for breaking changes.
Write modern, high-performance C# code using records, pattern matching, value objects, async/await, Span<T>/Memory<T>, and best-practice API design patterns. Emphasizes functional-style programming with C# 12+ features. Use when writing new C# code or refactoring existing code, designing public APIs for libraries or services, optimizing performance-critical code paths, or building async/await-heavy applications.
Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Covers both high-level abstractions and low-level synchronization primitives. Use when deciding how to handle concurrent operations in .NET, evaluating whether to use async/await, Channels, or Akka.NET, or managing state across multiple concurrent entities.
Design .NET types for performance. Covers struct vs class decision matrix, sealed by default, readonly structs, ref struct and Span/Memory selection, FrozenDictionary, ValueTask, and collection return types. Use when designing new types and APIs, reviewing code for performance issues, choosing between class, struct, and record, or working with collections and enumerables.
| name | dotnet-cli-distribution |
| description | Choosing CLI output format. AOT vs framework-dependent, RID matrix, single-file, dotnet tool. |
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.
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 |
dotnet tool (global/local) | ~200ms | < 1 MB (NuGet) | .NET SDK | Developer tools, .NET ecosystem users |
Native AOT single-file -- the gold standard for CLI distribution:
Framework-dependent deployment:
Self-contained (non-AOT):
dotnet tool packaging:
dotnet tool install -g mytoolTarget the four primary RIDs for broad coverage:
| RID | Platform | Notes |
|---|---|---|
linux-x64 | Linux x86_64 | Most Linux servers, CI runners, WSL |
linux-arm64 | Linux ARM64 | AWS Graviton, Raspberry Pi 4+, Apple Silicon VMs |
osx-arm64 | macOS Apple Silicon | M1/M2/M3+ Macs (primary macOS target) |
win-x64 | Windows x86_64 | Windows 10+, Windows Server |
| RID | When 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) |
<!-- 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:
# 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
Single-file publish bundles the application and its dependencies into one executable.
<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>
When combined with Native AOT, single-file is implicit -- AOT always produces a single native binary:
<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.
# 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)
Trimming removes unused code from the published output. For self-contained non-AOT builds:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<!-- Suppress known trim warnings for CLI scenarios -->
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>
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.
| 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 |
InvariantGlobalization=true)StripSymbols=true) -- keep separate symbol files for crash analysisOptimizationPreference=Size) -- minimal runtime performance impact for I/O-bound CLI toolsdotnet publish -c Release -r linux-x64 --self-contained false
Advantages:
Disadvantages:
dotnet publish -c Release -r linux-x64 --self-contained true
Advantages:
Disadvantages:
# 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/"
Always produce checksums for release artifacts:
# Generate SHA-256 checksums
cd artifacts
shasum -a 256 *.tar.gz *.zip > checksums-sha256.txt
See [skill:dotnet-cli-release-pipeline] for automating this in GitHub Actions.
-r <rid> at publish time instead.InvariantGlobalization=true.${SIGNING_KEY}) with a comment about CI secret storage for any signing or upload credentials.