|
| 1 | +--- |
| 2 | +title: Create RID-specific, self-contained, and AOT .NET tools |
| 3 | +description: Learn how to create and package RID-specific, self-contained, and AOT .NET tools for platform-specific distribution. |
| 4 | +ms.topic: how-to |
| 5 | +ms.date: 11/12/2025 |
| 6 | +ai-usage: ai-assisted |
| 7 | +--- |
| 8 | + |
| 9 | +# Create RID-specific, self-contained, and AOT .NET tools |
| 10 | + |
| 11 | +**This article applies to:** ✔️ .NET SDK 10 and later versions |
| 12 | + |
| 13 | +Package .NET tools for specific platforms and architectures so you can distribute native, fast, and trimmed applications. This capability makes it easier to distribute native, fast, trimmed .NET applications for command-line tools like MCP servers or other platform-specific utilities. |
| 14 | + |
| 15 | +## Overview |
| 16 | + |
| 17 | +Starting with .NET SDK 10, you can create .NET tools that target specific Runtime Identifiers (RIDs). These tools can be: |
| 18 | + |
| 19 | +- **RID-specific**: Compiled for particular operating systems and architectures. |
| 20 | +- **Self-contained**: Include the .NET runtime and don't require a separate .NET installation. |
| 21 | +- **Native AOT**: Use Ahead-of-Time compilation for faster startup and smaller memory footprint. |
| 22 | + |
| 23 | +When users install a RID-specific tool, the .NET CLI automatically selects and installs the appropriate package for their platform. |
| 24 | + |
| 25 | +## Opt in to RID-specific packaging |
| 26 | + |
| 27 | +To create a RID-specific tool, configure your project with one of the following MSBuild properties: |
| 28 | + |
| 29 | +### RuntimeIdentifiers property |
| 30 | + |
| 31 | +Use `RuntimeIdentifiers` to specify the platforms your tool supports: |
| 32 | + |
| 33 | +```xml |
| 34 | +<Project Sdk="Microsoft.NET.Sdk"> |
| 35 | + <PropertyGroup> |
| 36 | + <OutputType>Exe</OutputType> |
| 37 | + <TargetFramework>net10.0</TargetFramework> |
| 38 | + <PackAsTool>true</PackAsTool> |
| 39 | + <ToolCommandName>mytool</ToolCommandName> |
| 40 | + <RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers> |
| 41 | + </PropertyGroup> |
| 42 | +</Project> |
| 43 | +``` |
| 44 | + |
| 45 | +### ToolPackageRuntimeIdentifiers property |
| 46 | + |
| 47 | +Alternatively, use `ToolPackageRuntimeIdentifiers` for tool-specific RID configuration: |
| 48 | + |
| 49 | +```xml |
| 50 | +<Project Sdk="Microsoft.NET.Sdk"> |
| 51 | + <PropertyGroup> |
| 52 | + <OutputType>Exe</OutputType> |
| 53 | + <TargetFramework>net10.0</TargetFramework> |
| 54 | + <PackAsTool>true</PackAsTool> |
| 55 | + <ToolCommandName>mytool</ToolCommandName> |
| 56 | + <ToolPackageRuntimeIdentifiers>win-x64;linux-x64;osx-arm64</ToolPackageRuntimeIdentifiers> |
| 57 | + </PropertyGroup> |
| 58 | +</Project> |
| 59 | +``` |
| 60 | + |
| 61 | +Use a semicolon-delimited list of RID values. For a list of Runtime Identifiers, see the [RID catalog](../rid-catalog.md). |
| 62 | + |
| 63 | +## Package your tool |
| 64 | + |
| 65 | +The packaging process differs depending on whether you're using AOT compilation. To build a NuGet package, or *.nupkg* file from the project, run the [dotnet pack](dotnet-pack.md) command. |
| 66 | + |
| 67 | +### RID-specific and self-contained tools |
| 68 | + |
| 69 | +For tools without AOT compilation, run `dotnet pack` once: |
| 70 | + |
| 71 | +```dotnetcli |
| 72 | +dotnet pack |
| 73 | +``` |
| 74 | + |
| 75 | +This command creates multiple NuGet packages: |
| 76 | + |
| 77 | +- One package for each RID: `<packageName>.<RID>.<packageVersion>.nupkg` |
| 78 | + - Example: `mytool.win-x64.1.0.0.nupkg` |
| 79 | + - Example: `mytool.linux-x64.1.0.0.nupkg` |
| 80 | + - Example: `mytool.osx-arm64.1.0.0.nupkg` |
| 81 | +- One RID-agnostic pointer package: `<packageName>.<packageVersion>.nupkg` |
| 82 | + - Example: `mytool.1.0.0.nupkg` |
| 83 | + |
| 84 | +### AOT tools |
| 85 | + |
| 86 | +For tools with AOT compilation (`<PublishAot>true</PublishAot>`), you must pack separately for each platform: |
| 87 | + |
| 88 | +- Pack the top-level package once (on any platform): |
| 89 | + |
| 90 | + ```dotnetcli |
| 91 | + dotnet pack |
| 92 | + ``` |
| 93 | + |
| 94 | +- Pack for each specific RID on the corresponding platform: |
| 95 | + |
| 96 | + ```dotnetcli |
| 97 | + dotnet pack -r win-x64 |
| 98 | + dotnet pack -r linux-x64 |
| 99 | + dotnet pack -r osx-arm64 |
| 100 | + ``` |
| 101 | + |
| 102 | + You must run each RID-specific pack command on the matching platform because AOT compilation produces native binaries. For more information about the prerequisites for Native AOT compilation, see [Native AOT deployment](../deploying/native-aot/index.md). |
| 103 | + |
| 104 | +## Package structure |
| 105 | + |
| 106 | +### Package types |
| 107 | + |
| 108 | +RID-specific tool packages use two package types: |
| 109 | + |
| 110 | +- **DotnetTool**: The top-level package that contains metadata. |
| 111 | +- **DotnetToolRidPackage**: The RID-specific packages that contain the actual tool binaries. |
| 112 | + |
| 113 | +### Package metadata |
| 114 | + |
| 115 | +The top-level package includes metadata that signals it's a RID-specific tool and lists the RID-specific packages. When you run `dotnet tool install`, the CLI reads this metadata to determine which RID-specific package to install for the current platform. |
| 116 | + |
| 117 | +## Publish your tool |
| 118 | + |
| 119 | +Publish all packages to NuGet.org or your package feed by using [dotnet nuget push](dotnet-nuget-push.md): |
| 120 | + |
| 121 | +```dotnetcli |
| 122 | +dotnet nuget push path/to/package/root/*.nupkg |
| 123 | +``` |
| 124 | + |
| 125 | +## Run a RID-specific tool |
| 126 | + |
| 127 | +Users run RID-specific tools the same way as platform-agnostic tools: |
| 128 | + |
| 129 | +```dotnetcli |
| 130 | +dnx mytool |
| 131 | +``` |
| 132 | + |
| 133 | +The CLI automatically: |
| 134 | + |
| 135 | +1. Downloads the top-level package. |
| 136 | +1. Reads the RID-specific metadata. |
| 137 | +1. Identifies the most appropriate package for the current platform. |
| 138 | +1. Downloads and runs the RID-specific package. |
| 139 | + |
| 140 | +## Example: Create an AOT tool |
| 141 | + |
| 142 | +Here's a complete example of creating an AOT-compiled RID-specific tool: |
| 143 | + |
| 144 | +1. Create a new console application: |
| 145 | + |
| 146 | + ```dotnetcli |
| 147 | + dotnet new console -n MyFastTool |
| 148 | + cd MyFastTool |
| 149 | + ``` |
| 150 | + |
| 151 | +1. Update the project file to enable AOT and RID-specific packaging: |
| 152 | + |
| 153 | + ```xml |
| 154 | + <Project Sdk="Microsoft.NET.Sdk"> |
| 155 | + <PropertyGroup> |
| 156 | + <OutputType>Exe</OutputType> |
| 157 | + <TargetFramework>net10.0</TargetFramework> |
| 158 | + <PackAsTool>true</PackAsTool> |
| 159 | + <ToolCommandName>myfasttool</ToolCommandName> |
| 160 | + <RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers> |
| 161 | + <PublishAot>true</PublishAot> |
| 162 | + <PackageId>MyFastTool</PackageId> |
| 163 | + <Version>1.0.0</Version> |
| 164 | + <Authors>Your Name</Authors> |
| 165 | + <Description>A fast AOT-compiled tool</Description> |
| 166 | + </PropertyGroup> |
| 167 | + </Project> |
| 168 | + ``` |
| 169 | + |
| 170 | +1. Add your application code in `Program.cs`: |
| 171 | + |
| 172 | + ```csharp |
| 173 | + Console.WriteLine("Hello from MyFastTool!"); |
| 174 | + Console.WriteLine($"Running on {Environment.OSVersion}"); |
| 175 | + ``` |
| 176 | + |
| 177 | +1. Pack the top-level package: |
| 178 | + |
| 179 | + ```dotnetcli |
| 180 | + dotnet pack |
| 181 | + ``` |
| 182 | + |
| 183 | +1. Pack for each specific RID (on the corresponding platform): |
| 184 | + |
| 185 | + On Windows: |
| 186 | + |
| 187 | + ```dotnetcli |
| 188 | + dotnet pack -r win-x64 |
| 189 | + ``` |
| 190 | + |
| 191 | + On Linux: |
| 192 | + |
| 193 | + ```dotnetcli |
| 194 | + dotnet pack -r linux-x64 |
| 195 | + ``` |
| 196 | + |
| 197 | + On macOS: |
| 198 | + |
| 199 | + ```dotnetcli |
| 200 | + dotnet pack -r osx-arm64 |
| 201 | + ``` |
| 202 | + |
| 203 | +1. Publish all packages to NuGet.org by using the [dotnet nuget push](dotnet-nuget-push.md) command. |
| 204 | + |
| 205 | +## See also |
| 206 | + |
| 207 | +- [Tutorial: Create a .NET tool](global-tools-how-to-create.md) |
| 208 | +- [.NET tools overview](global-tools.md) |
| 209 | +- [dotnet pack command](dotnet-pack.md) |
| 210 | +- [RID catalog](../rid-catalog.md) |
| 211 | +- [Native AOT deployment](../deploying/native-aot/index.md) |
0 commit comments