RootCommand.ExecutableName truncates dotted app names on Linux/macOS when targeting .NET 11
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 76/100
Línea de trabajo
Start in src/command-line-api/src/System.CommandLine/RootCommand.cs, focusing on ExecutableName and ExecutablePath, then reproduce the published Contoso.Tool example on Linux or macOS. Compare net10.0 and net11.0 help output; done means dotted apphost names remain intact in RootCommand.ExecutableName and the Usage line.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
Starting with .NET 11, Environment.GetCommandLineArgs()[0] returns the name the apphost was invoked with instead of the managed .dll path (dotnet/runtime#131671). RootCommand.ExecutableName runs Path.GetFileNameWithoutExtension on that value. On Linux/macOS the apphost has no extension, so any app whose name contains a dot has its last segment treated as an "extension" and dropped. For example, Contoso.Tool becomes Contoso.
This affects the root command's Name, so it changes the Usage: line in help output. It also affects everything else derived from ExecutableName. Apps get this just by retargeting from net10.0 to net11.0, with no code changes.
Background: the .NET 11 runtime change
dotnet/runtime#131671, "Fix Environment.GetCommandLineArgs()[0] to report the host invocation name", merged 2026-08-10 and fixes dotnet/runtime#101837. It adds an ARGV0 host runtime property: hostpolicy captures the apphost's native argv[0], and CoreCLR uses it for element zero of Environment.GetCommandLineArgs(). From that PR:
| Command line | Environment.GetCommandLineArgs() |
|---|---|
dotnet.exe foo.dll 1 2 3 |
foo.dll, 1, 2, 3 (unchanged) |
foo 1 2 3 |
foo, 1, 2, 3 (previously the managed …/foo.dll path) |
../bar/special_foo.exe 1 2 3 (symlink to foo.exe) |
../bar/special_foo.exe, 1, 2, 3 |
Before this change, args[0] for an apphost launch always ended in .dll, so GetFileNameWithoutExtension happened to strip a real extension on every OS. Now it's the apphost name:
- Windows: the apphost name ends in
.exe, so stripping the extension is still correct. - Linux/macOS: the apphost has no extension, so a dotted name loses its last segment.
The System.CommandLine code involved
2.0.x servicing, e.g. 2.0.12 (dotnet/dotnet release/10.0.1xx, RootCommand.cs#L48-L54):
public static string ExecutableName
=> _executableName ??= Path.GetFileNameWithoutExtension(ExecutablePath).Replace(" ", "");
public static string ExecutablePath => _executablePath ??= Environment.GetCommandLineArgs()[0];
main (RootCommand.cs#L104-L137) has the same core logic: Path.GetFileNameWithoutExtension(path).Replace(" ", "") on L128, where path is Environment.GetCommandLineArgs()[0].
Repro
Contoso.Tool.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0;net11.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.12" />
</ItemGroup>
</Project>
Program.cs:
using System.CommandLine;
Console.WriteLine($"GetCommandLineArgs()[0] = {Environment.GetCommandLineArgs()[0]}");
Console.WriteLine($"RootCommand.ExecutableName = {RootCommand.ExecutableName}");
return new RootCommand("Sample tool").Parse(args).Invoke();
Publish for linux-x64 (I used SDK 11.0.100-rc.1.26425.128), then run ./Contoso.Tool --help on Linux.
net10.0
GetCommandLineArgs()[0] = /…/pub/net10.0/Contoso.Tool.dll
RootCommand.ExecutableName = Contoso.Tool
Description:
Sample tool
Usage:
Contoso.Tool [options]
net11.0
GetCommandLineArgs()[0] = ./Contoso.Tool
RootCommand.ExecutableName = Contoso
Description:
Sample tool
Usage:
Contoso [options]
On Windows the same net11.0 app still reports Contoso.Tool, because args[0] is Contoso.Tool.exe. The result is the same app showing different names per OS.
Who is affected
Any System.CommandLine app that:
- targets .NET 11 and is launched through its apphost rather than
dotnet app.dll, and - runs on Linux or macOS, and
- has a dot in its executable name. That's common, because the default
AssemblyNameis the project name, e.g.Contoso.Tool,MyCompany.Cli, or test projects likeFoo.Tests.
Impacts:
- Help output:
Usage:shows the wrong command name. - Other
ExecutableNameusers: code that usesRootCommand.ExecutableNamedirectly, and features that depend on it, such as completion/dotnet-suggestregistration, get the truncated name. A similar issue was fixed earlier forRegisterWithDotnetSuggestin #2316. - Tests: help-output snapshot tests become OS-dependent. We hit this in Aspire when retargeting our CLI tests to net11.0: help snapshots started failing only on Linux/macOS, where
Aspire.Cli.Testswas rendered asAspire.Cli(microsoft/aspire#20436). Our shippedaspirebinary isn't affected only because its name has no dot.
cc dotnet/runtime#131671, where I also asked whether the runtime change should be tagged as a breaking change: https://github.com/dotnet/runtime/pull/131671#issuecomment-5822378513
- Lenguaje dominante
- C#
- Estrellas
- 3.7k
- Forks
- 434
- Merge medio
- 6 h 27 min
- PR fusionados (30 d)
- 2
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de dotnet/command-line-api
-
German localization is incompletePosiblemente ocupada @b-v-d-e-v la tomó hace 15 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
dotnet/command-line-api#2852 ·
-
Incomplete French (fr) translation: RequiredOptionWasNotProvided not translatedPosiblemente ocupada @JPBlanc la tomó hace 112 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
dotnet/command-line-api#2822 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
dotnet/command-line-api#2792 · 2 comentarios · 16 reacciones ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
dotnet/command-line-api#2704 ·
-
GetCompletions should check exit code of invoked applicationPosiblemente ocupada @baradgur la tomó hace 1284 días. AbiertoArea-Completions bug help wanted
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
dotnet/command-line-api#2137 · 1 comentario · 3 reacciones ·
Todos los issues de dotnet/command-line-api
Issues similares
-
7.0
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
unoplatform/uno.extensions#3253 ·
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
Sector-Vestige/Sector-Vestige#476 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
PowerShell/platyPS#868 ·
-
S: Untriaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
Trauma-Station/Trauma-Station#5058 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 2 días