Skip to content

Make Microsoft.Build.Tasks.CodeAnalysis AOT and single-file clean#84335

Draft
JeremyKuhne wants to merge 1 commit into
dotnet:mainfrom
JeremyKuhne:msbuild-tasks-aot-single-file-clean
Draft

Make Microsoft.Build.Tasks.CodeAnalysis AOT and single-file clean#84335
JeremyKuhne wants to merge 1 commit into
dotnet:mainfrom
JeremyKuhne:msbuild-tasks-aot-single-file-clean

Conversation

@JeremyKuhne

@JeremyKuhne JeremyKuhne commented Jun 30, 2026

Copy link
Copy Markdown
Member

A .NET copy of Microsoft.Build.Tasks.CodeAnalysis ships in the .NET SDK's Native AOT CLI, so the assembly must be clean for trimming, AOT, and single-file publishing.

This enables IsAotCompatible on the .NET build (which turns on the trim, AOT, and single-file analyzers) and resolves the resulting warnings. All changes are annotations, dead-code guards, or a single scoped suppression - there is no runtime behavior change.

Changes

  • Utilities.TryGetAssemblyPath - deleted. It was unused and relied on Assembly.CodeBase/Location.
  • csc.rsp / vbc.rsp lookups (Csc.cs, Vbc.cs) - wrapped in #if NETFRAMEWORK. They only run inside if (IsSdkFrameworkToCoreBridgeTask), which is compile-time false on .NET Core, so the Assembly.Location use there is dead code on the AOT target framework. Behavior is unchanged for the net472 bridge task.
  • ManagedToolTask.GetBuildTaskDirectory - one scoped [UnconditionalSuppressMessage] for the single unavoidable IL3000. The task needs its own on-disk directory to locate the sibling bincore compiler when deployed as loose files (the normal MSBuild scenario). Single-file / native AOT hosts fall back to AppContext.BaseDirectory and receive the compiler path out-of-band (CscToolPath / VbcToolPath). [RequiresAssemblyFiles] cannot be used because it would have to flow onto the sealed ToolTask overrides (GenerateFullPathToTool, GenerateCommandLineCommands, ToolName, ExecuteTool) whose base declarations are not annotated, which is itself an error (IL3003).
  • FatalError.CopyHandlersTo - annotated [RequiresUnreferencedCode]. This shared Contracts file is globbed into the task assembly, and its reflection over the target type otherwise produces IL2026 / IL2075 once the trim analyzer is enabled.

Validation

Built with RunAnalyzers=true and confirmed 0 IL warnings / 0 errors on:

  • net10.0 (the AOT target)
  • net472
  • the net472 Microsoft.Build.Tasks.CodeAnalysis.Sdk bridge variant
Microsoft Reviewers: Open in CodeFlow
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.
Copilot AI review requested due to automatic review settings June 30, 2026 01:28
@JeremyKuhne JeremyKuhne requested a review from a team as a code owner June 30, 2026 01:28
@dotnet-policy-service dotnet-policy-service Bot added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Jun 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 on Assembly.CodeBase/Location.
  • Guarded csc.rsp/vbc.rsp lookup logic behind #if NETFRAMEWORK, added a scoped IL3000 suppression + runtime fallback for single-file/AOT hosts, and annotated reflection-heavy handler-copying with RequiresUnreferencedCode.

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.
clean for trimming, AOT, and single-file publishing. IsAotCompatible enables all three
analyzers (trim, AOT, and single-file).
-->
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsAotCompatible>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsTargetFrameworkCompatible(net10.0, net8.0) is going to be true, right? But perhaps instead of "hard-coding" net8.0 here, we could check "is .net core" somehow?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using IsTargetFrameworkCompatible is the recommended way to do this (there is '$(TargetFrameworkIdentifier)' == '.NETCoreApp' but it doesn't always work due to MSBuild evaluation rules).

Another way is to write '$(TargetFramework)' == '$(NetRoslynSourceBuild)'.

Comment thread src/Compilers/Core/MSBuildTask/Csc.cs
//
// The single-file / native AOT case is handled at run time: Assembly.Location returns an empty
// string and we fall back to AppContext.BaseDirectory below. In those hosts the real compiler
// location is supplied out-of-band anyway (e.g. via the CscToolPath/VbcToolPath MSBuild property),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate what exactly is the single-file / native AOT scenario where the compiler is supplied via CscToolPath only?

// through these members.
// This should not happen in supported product scenarios but could happen if a non-supported
// scenario tried to load our task and call through these members.
throw new InvalidOperationException("Unable to determine the location of the build task assembly.");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this reachable now? Can it happen that AppContext.BaseDirectory is ever null or empty?

Comment thread src/Compilers/Core/MSBuildTask/ManagedToolTask.cs
@JeremyKuhne JeremyKuhne marked this pull request as draft July 1, 2026 00:02
@JeremyKuhne

Copy link
Copy Markdown
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee.

4 participants