Make Microsoft.Build.Tasks.CodeAnalysis AOT and single-file clean - #84335
Merged
333fred merged 7 commits intoJul 10, 2026
Merged
Conversation
A .NET copy of this task assembly ships in the .NET SDK's Native AOT CLI, so it must be clean for trimming, AOT, and single-file publishing. Enable IsAotCompatible (which turns on the trim, AOT, and single-file analyzers) and resolve the resulting warnings. - Delete the unused Utilities.TryGetAssemblyPath (it used Assembly.CodeBase/Location). - Guard the framework-to-core bridge csc.rsp/vbc.rsp lookups with #if NETFRAMEWORK; they only run on .NET Framework MSBuild and are dead code on .NET Core. - Scope-suppress the one unavoidable IL3000 in GetBuildTaskDirectory. Assembly.Location is required to locate the sibling compiler when loaded as loose files; single-file hosts fall back to AppContext.BaseDirectory and receive the compiler path out-of-band. [RequiresAssemblyFiles] cannot be used because it would flow onto the non-annotated ToolTask overrides (IL3003). - Annotate FatalError.CopyHandlersTo with [RequiresUnreferencedCode]; this shared Contracts file is compiled into the task and its reflection otherwise produces IL2026/IL2075.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates Microsoft.Build.Tasks.CodeAnalysis to be compatible with trimming, Native AOT, and single-file publishing scenarios by enabling the IsAotCompatible build setting for modern target frameworks and addressing the resulting IL linker/analyzer warnings via conditional compilation and targeted annotations/suppressions.
Changes:
- Enabled
IsAotCompatible(trim/AOT/single-file analyzers) for the .NET TFMs in the MSBuild task project. - Removed an unused helper (
Utilities.TryGetAssemblyPath) that relied onAssembly.CodeBase/Location. - Guarded
csc.rsp/vbc.rsplookup logic behind#if NETFRAMEWORK, added a scoped IL3000 suppression + runtime fallback for single-file/AOT hosts, and annotated reflection-heavy handler-copying withRequiresUnreferencedCode.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/Dependencies/Contracts/ErrorReporting/FatalError.cs | Annotates CopyHandlersTo with RequiresUnreferencedCode (on .NET builds) to satisfy trim analyzer around reflection use. |
| src/Compilers/Core/MSBuildTask/Vbc.cs | Wraps vbc.rsp lookup (via Assembly.Location) in #if NETFRAMEWORK to avoid single-file analyzer warnings on .NET. |
| src/Compilers/Core/MSBuildTask/Utilities.cs | Removes unused TryGetAssemblyPath (and System.Reflection using) to avoid assembly-path APIs. |
| src/Compilers/Core/MSBuildTask/MSBuild/Microsoft.Build.Tasks.CodeAnalysis.csproj | Enables IsAotCompatible for net8.0+ compatible TFMs (e.g., net10.0) while leaving net472 unaffected. |
| src/Compilers/Core/MSBuildTask/ManagedToolTask.cs | Adds a scoped IL3000 suppression and makes GetBuildTaskDirectory robust for single-file/AOT (fallback to AppContext.BaseDirectory). |
| src/Compilers/Core/MSBuildTask/Csc.cs | Wraps csc.rsp lookup (via Assembly.Location) in #if NETFRAMEWORK to avoid single-file analyzer warnings on .NET. |
jjonescz
reviewed
Jun 30, 2026
ReflectionUtilities.cs is linked into the task via Directory.Build.props; its trim annotations are required to fix the IL2057/IL2070 errors the Correctness legs hit (it was wrongly omitted from the first commit). Revert GetBuildTaskDirectory to its original throw-on-null behavior, keeping only the IL3000 suppression, so the change is purely analyzer cleanliness with no runtime behavior change. Assert the bridge-only response-file branch is unreachable on .NET Core via Debug.Assert(false) in the #else (Csc.cs, Vbc.cs). Gate IsAotCompatible on the TargetFramework matching NetRoslynSourceBuild (the .NET build) instead of an IsTargetFrameworkCompatible net8.0 floor, per review feedback.
JeremyKuhne
marked this pull request as draft
July 1, 2026 00:02
Member
Author
|
@jjonescz I've flipped this to draft until I fully rationalize the state coming from the muxer (dotnet.exe). I have dotnet/msbuild#14064 pending that I used the changes here to test with the SDK, but I think that I might be depending on the wrong directory info. |
Under the partially-AOT .NET SDK CLI, Assembly.Location is empty and AppContext.BaseDirectory is the muxer's install root, so GetBuildTaskDirectory could not find the versioned SDK directory. It now reads the Microsoft.DotNet.Sdk.Root AppContext value that the AOT host publishes (dotnet/sdk#55110) before falling back to Assembly.Location for loose-file MSBuild deployments. Also clarifies the ManagedToolTask class/member doc comments and adds the class-hierarchy and on-disk-layout diagrams.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…ildTaskDirectory Verifies that GetBuildTaskDirectory (and GetToolDirectory) resolve from the Microsoft.DotNet.Sdk.Root AppContext value the AOT host publishes, appending the Roslyn subfolder. The test derives a ManagedToolTask to observe the resolved folders, runs in a non-parallel collection (the AppContext value is process-global), asserts the value is unset beforehand, and restores it in a finally.
333fred
reviewed
Jul 9, 2026
Use Debug.Fail for the bridge-only branches in Csc and Vbc. Remove the hand-maintained ManagedToolTask hierarchy diagram and document ToolNameWithoutExtension at its declaration instead.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
333fred
approved these changes
Jul 10, 2026
RikkiGibson
approved these changes
Jul 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A .NET copy of
Microsoft.Build.Tasks.CodeAnalysisships in the .NET SDK's Native AOT CLI, so the assembly must be clean for trimming, AOT, and single-file publishing.This enables
IsAotCompatibleon the .NET build (which turns on the trim, AOT, and single-file analyzers) and resolves the resulting warnings. Most changes are annotations, dead-code guards, or a single scoped suppression; the one functional change isGetBuildTaskDirectorygaining an AOT-only resolution path (loose-file MSBuild behavior is unchanged).Changes
Utilities.TryGetAssemblyPath- deleted. It was unused and relied onAssembly.CodeBase/Location.csc.rsp/vbc.rsplookups (Csc.cs,Vbc.cs) - wrapped in#if NETFRAMEWORK. They only run insideif (IsSdkFrameworkToCoreBridgeTask), which is compile-timefalseon .NET Core, so theAssembly.Locationuse there is dead code on the AOT target framework. Behavior is unchanged for the net472 bridge task.ManagedToolTask.GetBuildTaskDirectory- resolves the build task directory (the anchor for the siblingbincorecompiler) and keeps one scoped[UnconditionalSuppressMessage]for the unavoidableIL3000. In the normal loose-file MSBuild scenario the directory isAssembly.Location. In the partially-AOT SDK CLI that is empty andAppContext.BaseDirectoryis the runtime muxer's install root, so the method first reads the versioned SDK directory the AOT host publishes as theMicrosoft.DotNet.Sdk.RootAppContextvalue (per dotnet/sdk#55110) and appends theRoslynsubfolder.[RequiresAssemblyFiles]cannot be used because it would have to flow onto the sealedToolTaskoverrides (GenerateFullPathToTool,GenerateCommandLineCommands,ToolName,ExecuteTool) whose base declarations are not annotated, which is itself an error (IL3003).ReflectionUtilities- added the trim annotations required to clear theIL2057/IL2070errors the Correctness legs hit. This shared Contracts file is linked into the task assembly viaDirectory.Build.props, so its reflection helpers must be annotated once the trim analyzer is enabled.FatalError.CopyHandlersTo- annotated[RequiresUnreferencedCode]. This shared Contracts file is globbed into the task assembly, and its reflection over the target type otherwise producesIL2026/IL2075once the trim analyzer is enabled.Validation
Built with
RunAnalyzers=trueand confirmed 0 IL warnings / 0 errors on:net10.0(the AOT target)net472Microsoft.Build.Tasks.CodeAnalysis.Sdkbridge variantMicrosoft Reviewers: Open in CodeFlow