Skip to content

Comments

Decouple NativeAOT ILLink assembly input computation from ComputeIlcCompileInputs#124801

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/fix-ilc-compile-order
Draft

Decouple NativeAOT ILLink assembly input computation from ComputeIlcCompileInputs#124801
Copilot wants to merge 2 commits intomainfrom
copilot/fix-ilc-compile-order

Conversation

Copy link
Contributor

Copilot AI commented Feb 24, 2026

_ComputeManagedAssemblyForILLink in NativeAOT was indirectly dependent on ComputeIlcCompileInputs via @(ManagedBinary), which made PrepareForILLink ordering-sensitive and broke incremental ILLink behavior when ILLink is run before ILC input computation. This change removes that coupling so ILLink preparation no longer relies on ComputeIlcCompileInputs side effects.

  • What changed

    • Updated src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets in _ComputeManagedAssemblyForILLink.
    • Replaced @(ManagedBinary) with @(IntermediateAssembly) when reconstructing @(ManagedAssemblyToLink).
  • Why this matters

    • @(IntermediateAssembly) is available during PrepareForILLink, independent of ComputeIlcCompileInputs.
    • This preserves inclusion of the project assembly in @(ManagedAssemblyToLink) regardless of whether ComputeIlcCompileInputs runs before or after PrepareForILLink/ILLink.
  • Code change

    <ManagedAssemblyToLink Include="@(DefaultFrameworkAssemblies);@(_ManagedResolvedAssembliesToPublish);@(IntermediateAssembly)" />
Original prompt

This section details on the original issue you should resolve

<issue_title>ComputeIlcCompileInputs should not need to run before PrepareForILLink</issue_title>
<issue_description>## Description

The default IlcCompileDependsOn in Microsoft.NETCore.Native.targets orders ComputeIlcCompileInputs before PrepareForILLink:

Compile;ComputeIlcCompileInputs;SetupOSSpecificProps;PrepareForILLink

This couples ILC's input computation to ILLink's preparation phase. _ComputeManagedAssemblyForILLink (which runs AfterTargets="_ComputeManagedAssemblyToLink" during PrepareForILLink) consumes @(ManagedBinary), a side effect of ComputeIlcCompileInputs. This ordering works for the standard pipeline because it doesn't actually run ILLink (RunILLink=false), but it breaks consumers that need PrepareForILLink and ILLink to run before ComputeIlcCompileInputs.

Why a consumer would need the opposite order

The standard NativeAOT pipeline sets RunILLink=false — ILLink never actually runs, and @(ManagedAssemblyToLink) is only used as metadata for ILC. A consumer that sets RunILLink=true to actually trim assemblies before ILC needs ILLink to complete first, so that ILC consumes the trimmed output. This requires PrepareForILLink and ILLink to precede ComputeIlcCompileInputs — the opposite of the default order. This is the case in .NET for Android's NativeAOT pipeline.

Impact

When PrepareForILLink runs before ComputeIlcCompileInputs, _ComputeManagedAssemblyForILLink builds its replacement @(ManagedAssemblyToLink) list before @(ManagedBinary) has been populated. The project assembly ends up missing from @(ManagedAssemblyToLink), which is used as Inputs by _RunILLink (in Microsoft.NET.ILLink.targets). This causes ILLink's incremental build check to miss changes to the project assembly, so ILLink skips on rebuild even though the assembly changed.

First builds still succeed because PrepareForILLink independently adds @(IntermediateAssembly) as a TrimmerRootAssembly, so ILLink loads and processes it regardless. Only incremental builds are affected.

Reproduction

Create a dotnet new console project with the following csproj (note: explicit SDK imports are needed because Microsoft.NETCore.Native.targets unconditionally sets RunILLink=false, so it must be overridden after the SDK targets load):

<Project>
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net11.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <PublishAot>true</PublishAot>
    <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
  </PropertyGroup>

  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />

  <PropertyGroup>
    <RunILLink>true</RunILLink>
    <IlcCompileDependsOn>
      Compile;
      SetupOSSpecificProps;
      PrepareForILLink;
      ILLink;
      ComputeIlcCompileInputs
    </IlcCompileDependsOn>
  </PropertyGroup>
</Project>

Run dotnet publish, then change "Hello, World!" to "Hello, Changed!" in Program.cs and publish again. On the second publish, ILLink is skipped (no "Optimizing assemblies for size" message) and the trimmed assembly in obj/.../linked/ReproApp.dll still contains Hello, World!.

Suggestion

Decouple ComputeIlcCompileInputs from PrepareForILLink so neither depends on having run before the other. Ideally _ComputeManagedAssemblyForILLink should not rely on state produced by ComputeIlcCompileInputs, and ComputeIlcCompileInputs should be free to run after ILLink without breaking the ILLink preparation phase.

Workaround

.NET for Android can work around this by injecting @(IntermediateAssembly) into @(ManagedAssemblyToLink) after _ComputeManagedAssemblyForILLink replaces it:

<Target Name="_AndroidFixManagedAssemblyToLink"
    AfterTargets="_ComputeManagedAssemblyForILLink">
  <ItemGroup>
    <ManagedAssemblyToLink Include="@(IntermediateAssembly)" />
  </ItemGroup>
</Target>
```</issue_description>

<agent_instructions>Decouple these targets. The ILC inputs should not be resolved from ManagedAssemblyToLink.</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@github-actions github-actions bot added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Feb 24, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @agocke, @dotnet/illink
See info in area-owners.md if you want to be subscribed.

…puts

Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix ComputeIlcCompileInputs execution order before PrepareForILLink Decouple NativeAOT ILLink assembly input computation from ComputeIlcCompileInputs Feb 24, 2026
Copilot AI requested a review from sbomer February 24, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Tools-ILLink .NET linker development as well as trimming analyzers

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

ComputeIlcCompileInputs should not need to run before PrepareForILLink

2 participants