Enable NativeAOT CLI command handling by default (DOTNET_CLI_ENABLEAOT)#55144
Conversation
There was a problem hiding this comment.
Pull request overview
This PR enables the NativeAOT CLI command-handling fast path by default by flipping the default value of DOTNET_CLI_ENABLEAOT from false to true, while preserving the ability to opt out via falsy values (e.g. false, 0, no, off). This aligns the native entry point behavior with the stated parity goals and broadens real-world coverage of the AOT path.
Changes:
- Enable the AOT fast path by default in the native entry point (opt-out via
DOTNET_CLI_ENABLEAOT=false/0/no/off). - Update design documentation to describe on-by-default gating and add an opt-out section.
- Update and extend tests to reflect the new default behavior and validate opt-out formats.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Cli/dotnet-aot/NativeEntryPoint.cs | Flips DOTNET_CLI_ENABLEAOT default to enabled and updates related comments. |
| src/Cli/dotnet-aot/DESIGN.md | Updates gating language/diagrams and documents the opt-out behavior. |
| test/dotnet-aot.Tests/NativeEntryPointTests.cs | Adjusts tests for new default; adds data-driven opt-out format coverage. |
| test/dotnet-aot.Tests/AotIntegrationTests.cs | Ensures “disabled” integration runs explicitly set DOTNET_CLI_ENABLEAOT=false; updates commentary accordingly. |
|
The S.CL fix has flowed to the VMR in dotnet/dotnet#7611, so once VMR flows to main we can rebase this and enable NAOT command invocation for all platforms. |
f111f2e to
d2c1d56
Compare
|
Confirmed that with these changes the macOS NAOT binaries no longer crash, and the Apple Crypto library conflicts that the Runtime team fixed for NAOT hosted libraries are also no longer causing errors to be emitted to stdout! |
af5ac99 to
d4ec304
Compare
|
@dotnet/illink the Linker on the windows/arm64 AOT leg is complaining about an arm64-specific issue:
Is this something that the ILLink Targets should be automatically handling for us, or something that the SDK needs to help detect? |
|
@marcpopMSFT I've disabled the failing windows-arm64 leg and raised #55298 to track fixing and re-enabling that leg. |
nagilson
left a comment
There was a problem hiding this comment.
Thank you for the thoughtful PR description. Included some thoughts below.
Normally I would approve but I think we need some changes and my approval won't matter after that point anyways, so waiting on this.
5f541db to
33f96b4
Compare
Gate the size-analysis Copy/Publish steps on categoryName == 'AoT' instead of runAoTTests. runAoTTests is the Blazor WebAssembly AoT test switch (its only env-var consumer is Microsoft.NET.Sdk.BlazorWebAssembly.AoT.Tests); the NativeAOT dll and its mstat/dgml size-analysis output are produced by every AoT leg, so the AoT category is the honest gate and it now runs on the build-only cross-arch legs too. With size-analysis decoupled, the now-dead runAoTTests: true on the build-only cross-arch legs (win-arm64, linux-arm64; runTests: false) is removed - it drove nothing there since the Helix test block is gated on runTests. runAoTTests now appears only on legs that run Blazor WebAssembly AoT tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b7c0dc09-64f3-40b8-9c5f-45b0fce2e461
Turn on the NativeAOT CLI fast path by default across Windows, macOS, and Linux (#55078). The native entry point handles supported commands directly and defers unsupported ones (e.g. the 'new' subtree, 'test --help') back to the managed CLI. Present the AOT CLI as 'dotnet' in help output via the System.CommandLine executable-name escape hatch set during the build rather than in code. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b7c0dc09-64f3-40b8-9c5f-45b0fce2e461
Update the NativeAOT CLI tests for the on-by-default behavior: make the test classes partial as required by the MSTest reflection-free source generator (used for NativeAOT test discovery), cover the help fall-back-to-managed action, and use a single space-delimited string for the DataRow cases (params string[] cannot be stored into the DataRow object[] under NativeAOT). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b7c0dc09-64f3-40b8-9c5f-45b0fce2e461
33f96b4 to
9ac6f8d
Compare
This is not ILLink, this is link.exe from MSVC. "IL Linker" term is a constant source of confusion, the tool should have never been named like that. What Visual Studio version is on this build machine? The branch is getting force pushed so I can't find previous CI results in GitHub UI. The MSVC team claims this check has been removed as of January (https://developercommunity.visualstudio.com/t/Remove-checking-for-and-fixing-Cortex-A/10905134). There's also an undocumented command line switch that can be passed to link.exe to disable this we could try ( Cc @dotnet/ilc-contrib |
This is your problem. You are building with fairly old native toolset in the CI. .NET 11 official product builds and dotnet/runtime repo run on windows.vs2026.amd64 . Is it intentional that you are stuck with vs2022? |
Well that's interesting. Will see if we can bump as part of fixing the linked follow-up issue. |
Fixes #55078.
What
Flips the default of
DOTNET_CLI_ENABLEAOTin the NativeAOT entry point fromfalsetotrue, so the AOT command-handling fast path runs by default on all platforms. Users opt out by settingDOTNET_CLI_ENABLEAOTto a falsy value (false/0/no/off), which routes every invocation to the managed CLI exactly as before.Now that the native path has reached parity with the managed CLI, enabling it by default gives broad real-world testing and improves CLI startup for common commands.
History: macOS + Linux were temporarily gated
Earlier revisions of this PR restricted the default to Windows only, because the AOT path was blocked on macOS and Linux by a command-line parsing crash: dotnet/command-line-api#2812. When the AOT CLI is loaded as a NativeAOT shared library,
Environment.GetCommandLineArgs()returned an empty array on those platforms, soSystem.CommandLine'sRootCommandconstructor threw anIndexOutOfRangeException. (Although the upstream issue was titled for macOS, it was empirically confirmed to reproduce identically on Linux with a focused NativeAOT shared-library repro.)That crash is now fixed upstream in dotnet/command-line-api#2820 —
RootCommand.ExecutablePathguards the empty-args case andExecutableNamefalls back to the build-injectedSystem.CommandLine.ExecutableNamefromAppContextfor the native-library scenario. The fix has flowed into the SDK viaSystem.CommandLine3.0.0-preview.7.26359.117, so the platform gate has been removed and the default is now unconditional (ParseBool(..., defaultValue: true)). ExplicitDOTNET_CLI_ENABLEAOTvalues still override on every platform.Changes
The branch is organized into five focused commits:
MSTest.Sdkin sync with the MSTest framework (from @dsplaisted, #55302) —global.json+eng/Version.Details.xml. The reflection-free MSTest source generator (required for NativeAOT test discovery) emits zero tests whenMSTest.Sdk(inmsbuild-sdks) drifts from the darc-flowed MSTest framework version — which is why the "Run NativeAOT CLI Tests" step reported "Zero tests ran". BumpsMSTest.Sdkto match and tracks it as a darc dependency so the two stay in lockstep on every testfx flow. This is the root-cause fix that replaces the earlier CI workarounds.disableJob: truein.vsts-pr.yml/.vsts-ci.yml. The win-x64 → win-arm64 ILC cross-link fails persistently; tracked by Windows arm64 NativeAOT cross-build (dotnet-aot) AoT CI leg persistently failing #55298 for the dotnet/illink (ILCompiler) team. Re-enable once fixed.categoryName == 'AoT'instead ofrunAoTTests(sdk-build.yml), and drop the now-deadrunAoTTests: truefrom the build-only cross-arch legs (they haverunTests: false, so the Helix block never consumed it).runAoTTestsremains the Blazor WebAssembly AoT test switch.src/Cli/dotnet-aot/NativeEntryPoint.cs: unconditional default —ParseBool(..., defaultValue: true).src/Cli/dotnet/Parser.cs: newAotPrintHelpAction(underCLI_AOT) renders help from the shared command tree but defers thenewandtestsubtrees to the managed CLI, whose help for those is generated dynamically (template-engine short-name/args + per-template options; Microsoft.Testing.Platform "Extension Options:" + per-extension options) and has no static equivalent.src/Cli/dotnet-aot/dotnet-aot.csproj: present the CLI asdotnetin usage output via theSystem.CommandLineexecutable-name escape hatch (_SystemCommandLineExecutableName) rather than the defaultdotnet-aot; and remove the now-unneeded Apple-crypto NativeAOT link workaround (the fixed runtime has flowed to the SDK).src/Cli/dotnet-aot/DESIGN.md: on-by-default gating text/diagrams and the Opting out (DOTNET_CLI_ENABLEAOT) section.test/dotnet-aot.Tests/*: make the test classespartial(required by the MSTest source generator under NativeAOT), cover the help fall-back-to-managed action, and use a single space-delimited string for theDataRowcases (params string[]cannot be stored into theDataRowobject[]under NativeAOT).Verification
Built
dotnet-aot.Testsand exercised the managed test classes locally (NativeEntryPointTests,AotParserTests); integration tests skip without the nativednbinary in the layout, as expected. The #2812 fix was verified present inSystem.CommandLine3.0.0-preview.7.26359.117onmainbefore removing the gate. Full NativeAOT test discovery/run is validated by the "Run NativeAOT CLI Tests" step on the AoT CI legs.Breaking change
This is a behavioral breaking change (label
breaking-change). Related documentation work in dotnet/docs:The opt-out is documented at:
https://learn.microsoft.com/dotnet/core/tools/dotnet-environment-variables#dotnet_cli_enableaot