Use SDK NativeCompile hook for ILC publish integration#125629
Use SDK NativeCompile hook for ILC publish integration#125629sbomer merged 12 commits intodotnet:mainfrom
Conversation
…ILC publish integration Redefine the SDK's empty NativeCompile placeholder target with the actual NativeAOT compilation pipeline, replacing the fragile BeforeTargets/AfterTargets hooks that previously sequenced ILC into the publish flow. - Rename ComputeLinkedFilesToPublish to NativeCompile in Publish.targets, removing AfterTargets="ComputeResolvedFilesToPublishList" - Remove BeforeTargets="Publish" from SetupProperties, ComputeIlcCompileInputs, and ImportRuntimeIlcPackageTarget (all reachable via DependsOnTargets chains) - Keep empty ComputeLinkedFilesToPublish and CopyNativeBinary for back-compat
|
Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib |
There was a problem hiding this comment.
Pull request overview
This PR updates NativeAOT publish integration to use the .NET SDK’s NativeCompile hook as the primary publish pipeline entry point, replacing prior sequencing via BeforeTargets/AfterTargets to make ordering more explicit and less fragile.
Changes:
- Redefines the SDK
NativeCompileplaceholder target to run the NativeAOT pipeline and updateResolvedFileToPublish. - Renames the prior publish hook target (
ComputeLinkedFilesToPublish) out of the execution path and keeps it as an empty back-compat target. - Removes
BeforeTargets="Publish"from several targets, relying onDependsOnTargetschains instead.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets | Removes BeforeTargets="Publish" from SetupProperties and ComputeIlcCompileInputs, relying on dependency chains to run them when needed. |
| src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets | Implements NativeAOT publish integration by redefining NativeCompile; adds empty ComputeLinkedFilesToPublish for compatibility. |
| src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets | Removes BeforeTargets="Publish" from ImportRuntimeIlcPackageTarget, relying on dependency chains. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Refactors NativeAOT publish integration to use the .NET SDK’s NativeCompile hook (instead of BeforeTargets/AfterTargets-based sequencing) so the ILC + native link pipeline runs at an explicit, stable point in the publish target graph.
Changes:
- Replaces the previous
ComputeLinkedFilesToPublishpublish hook with an implementation ofNativeCompilethat runs NativeAOT compilation and updatesResolvedFileToPublish. - Removes
BeforeTargets="Publish"from several preparatory targets so they execute via dependency chains rather than fragile publish hooks. - Keeps
ComputeLinkedFilesToPublish(and existingCopyNativeBinary) as empty/back-compat targets.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets | Stops forcing SetupProperties/ComputeIlcCompileInputs to run via BeforeTargets="Publish"; relies on existing dependency flow. |
| src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets | Implements NativeCompile as the NativeAOT publish integration point; leaves ComputeLinkedFilesToPublish as an empty compatibility target. |
| src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets | Removes BeforeTargets="Publish" from runtime ILC pack import target to rely on dependency chains. |
The test infrastructure directly imported SingleEntry.targets from Directory.Build.targets, which is evaluated before Microsoft.NET.Sdk.targets. This caused the SDK's empty NativeCompile placeholder (in Publish.targets) to overwrite the NativeAOT redefinition (MSBuild last-definition-wins), resulting in no native compilation during NativeAOT test runs. Use AfterMicrosoftNETSdkTargets to defer the import to after all SDK targets, matching the pattern already used for ReadyToRun targets in tests.singlefile.targets.
agocke
left a comment
There was a problem hiding this comment.
Everything looks good except the stuff about AfterMicrosoftNETSdkTargets -- I'm not sure I understand well enough what's going on there to say whether or not it's correct.
|
The import order is:
We needed a way to ensure the repo testing imports SingleEntry.targets after the empty NativeCompile target is defined, like what happens in the nuget flow. Hopefully using AfterMicrosoftNETSdkTargets to delay the import doesn't cause other ordering issues... |
… load Deferring the ILC import via AfterMicrosoftNETSdkTargets also deferred the PublishTrimmed=true that Microsoft.NETCore.Native.targets sets at evaluation time. This caused liveILLink.targets to compute _RequiresLiveILLink=false (since PublishTrimmed was not yet set), so ILLink targets were never loaded, and _PrepareTrimConfiguration was missing at build time. Set PublishTrimmed=true explicitly in the NativeAOT test PropertyGroups before liveILLink.targets is imported. NativeAOT always enables trimming, so this is semantically correct.
There was a problem hiding this comment.
Pull request overview
Switches NativeAOT publish integration to use the SDK’s NativeCompile placeholder hook instead of BeforeTargets/AfterTargets, improving sequencing reliability in the publish pipeline.
Changes:
- Redefines
NativeCompileto run the NativeAOT compilation/linking pipeline and updates publish items accordingly. - Removes
BeforeTargets="Publish"hooks from several NativeAOT targets, relying on dependency chains instead. - Defers importing ILC targets via
AfterMicrosoftNETSdkTargetsand setsPublishTrimmedearlier to ensure trim configuration targets are available.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tests/Directory.Build.targets | Defers ILC target import via AfterMicrosoftNETSdkTargets and sets PublishTrimmed early for NativeAOT test builds. |
| src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets | Removes BeforeTargets="Publish" from NativeAOT setup/inputs targets. |
| src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets | Moves publish integration work into NativeCompile, leaving an empty ComputeLinkedFilesToPublish for back-compat. |
| src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets | Removes BeforeTargets="Publish" from runtime pack import target. |
| eng/testing/tests.singlefile.targets | Defers ILC import via AfterMicrosoftNETSdkTargets and sets PublishTrimmed early for single-file NativeAOT tests. |
|
@sbomer I believe this should fix the forward flow build failure in dotnet/dotnet#5506, correct? |
|
No, that's not the intention (though it might help) - I think that needs a separate fix. |
|
The failures in the _Bootstrapped legs look related |
…builds Replace the direct <Import> of ILC targets with the SDK's ILCompilerTargetsPath hook, fixing bootstrap builds where the SDK's empty NativeCompile placeholder was overriding the ILC NativeCompile target. Also set _IlcReferencedAsPackage to skip NuGet package resolution and remove the unnecessary RID-swapping workaround (all affected projects already use PortableTargetRid).
|
/azp run runtime-extra-platforms |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
@MichalStrehovsky PTAL, I think this is ready to go. |
MichalStrehovsky
left a comment
There was a problem hiding this comment.
Nice, thank you! Fingers crossed propagating this across repos will be smooth sailing.
|
/ba-g "Only unknown failure is unrelated failure in runtime-extra-platforms" |
...instead of BeforeTargets/AfterTargets. Redefine the SDK's empty NativeCompile placeholder target with the actual NativeAOT compilation pipeline, replacing the fragile BeforeTargets/AfterTargets hooks that previously sequenced ILC into the publish flow. - Rename ComputeLinkedFilesToPublish to NativeCompile in Publish.targets, removing AfterTargets="ComputeResolvedFilesToPublishList" - Remove BeforeTargets="Publish" from SetupProperties, ComputeIlcCompileInputs, and ImportRuntimeIlcPackageTarget (all reachable via DependsOnTargets chains) - Keep empty ComputeLinkedFilesToPublish and CopyNativeBinary for back-compat --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
dotnet/runtime#125629 moved the NativeAOT entry point from ComputeLinkedFilesToPublish (AfterTargets=ComputeResolvedFilesToPublishList) to NativeCompile (an SDK Publish hook). Since Android uses 'dotnet build' rather than 'dotnet publish', NativeCompile was never invoked and IlcCompile/LinkNative never ran. Add NativeCompile to the _AfterCompileDex hook so the ILC pipeline executes during Build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dotnet/runtime#125629 moved the NativeAOT entry point from ComputeLinkedFilesToPublish (AfterTargets=ComputeResolvedFilesToPublishList) to NativeCompile (an SDK Publish hook). Since Android uses 'dotnet build' rather than 'dotnet publish', NativeCompile was never invoked and IlcCompile/LinkNative never ran. Add _AndroidRunNativeCompile with AfterTargets=ComputeResolvedFilesToPublishList to restore the old scheduling. This runs in the inner per-RID build (via _ComputeFilesToPublishForRuntimeIdentifiers) where RuntimeIdentifier is set, matching the original ComputeLinkedFilesToPublish behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Changes: dotnet/dotnet@d702068...b94d039 - **Dependency Updates**: - From [11.0.0-beta.26179.102 to 11.0.0-beta.26214.103][3] - Microsoft.DotNet.Build.Tasks.Feed - From [0.11.5-preview.26179.102 to 0.11.5-preview.26214.103][3] - Microsoft.DotNet.Cecil - From [11.0.0-preview.3.26179.102 to 11.0.0-preview.4.26214.103][3] - Microsoft.NET.ILLink - Microsoft.NETCore.App.Ref - From [11.0.100-preview.3.26179.102 to 11.0.100-preview.4.26214.103][3] - Microsoft.NET.Sdk - Microsoft.TemplateEngine.Authoring.Tasks - From [11.0.100-preview.4.26210.111 to 11.0.100-preview.4.26214.103][4] - Microsoft.NET.Workload.Emscripten.Current.Manifest-11.0.100-preview.4 - Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest-11.0.100-preview.4 [3]: dotnet/dotnet@d702068...b94d039 [4]: dotnet/dotnet@5bed449...b94d039 ### Other Changes Manually updated version bands. * `darc update-dependencies --id 309919` ### Fix NativeAOT builds after dotnet/runtime#125629 dotnet/runtime#125629 moved the NativeAOT entry point from ComputeLinkedFilesToPublish (AfterTargets=ComputeResolvedFilesToPublishList) to NativeCompile (an SDK Publish hook). Since Android uses 'dotnet build' rather than 'dotnet publish', NativeCompile was never invoked and IlcCompile/LinkNative never ran. Add _AndroidRunNativeCompile with AfterTargets=ComputeResolvedFilesToPublishList to restore the old scheduling. This runs in the inner per-RID build (via _ComputeFilesToPublishForRuntimeIdentifiers) where RuntimeIdentifier is set, matching the original ComputeLinkedFilesToPublish behavior. Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…em from publish output (#127089) NativeAOT publish was leaking satellite `.dll` files from NuGet packages into the publish directory and failing to pass them to ILC via `--satellite:` (required for resource embedding at link time). **Root cause:** `ComputeManagedAssembliesToCompileToNative` classified satellites by PE-inspecting items flagged `PostprocessAssembly=true`. The SDK never sets this metadata on resource items (`AssetType=resources`), so the satellite branch was dead code — `SatelliteAssemblies` output was always empty. PR #111514 introduced this regression; PR #124192 partially fixed project-own satellites via `IntermediateSatelliteAssembliesWithTargetPath` but left the package case broken. ## Changes **`Microsoft.NETCore.Native.Publish.targets`** - Populate `IlcSatelliteAssembly` via `Condition="'%(ResolvedFileToPublish.AssetType)' == 'resources'"` (package satellites) in addition to the existing `IntermediateSatelliteAssembliesWithTargetPath` (project satellites) - Replace `Remove="@(IntermediateSatelliteAssembliesWithTargetPath)"` with `Remove="@(IlcSatelliteAssembly)"` so both package and project satellites are pruned from `ResolvedFileToPublish` - Drop the now-unused `<Output TaskParameter="SatelliteAssemblies" ...>` wire-up **`ComputeManagedAssembliesToCompileToNative.cs`** - Remove dead `SatelliteAssemblies` `[Output]` property, PE-reading culture classification loop, `PostprocessAssembly` guard block, and the now-unused `System.Reflection.Metadata` / `System.Reflection.PortableExecutable` usings (~45 lines removed, none of it ever ran) # Customer Impact NativeAOT apps that reference NuGet packages containing satellite assemblies (e.g. `System.CommandLine`) would fail to resolve localized resources at runtime and would have stray `.dll` files in the publish output, defeating the single-file native binary guarantee. # Regression Yes — regressed in PR #111514 ("Run ILC after ComputeResolvedFilesToPublishList"). PR #124192 closed the gap for project-own satellites but missed package satellites. # Testing End-to-end validated with a repro app (`PublishAot=true`, `System.CommandLine 2.0.6` package dependency, project `Strings.resx` + `Strings.es.resx`): publish output is clean (only native binary + debug symbols), ILC receives the expected `--satellite:` arguments for all cultures, and runtime correctly resolves `es`/`de`/`ja` resources from both package and project sources. # Risk Low. The targets change is purely additive for the satellite item population and replaces a narrower `Remove` with a broader one that is a strict superset of the previous behavior. The task change removes code that was provably never executed under the current SDK contract. # Package authoring no longer needed in .NET 9 IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version. Keep in mind that we still need package authoring in .NET 8 and older versions. <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> <analysis> Chronological review: 1. User asked to read dotnet/runtime PR #124192 + related SDK issue comments 2. Asked for a repro in /tmp 3. Asked to root cause 4. Asked for logging / both app and package resources in repro 5. Asked about runtime behavior 6. Asked how SDK populates ResolvedFileToPublish 7. Asked what gets passed as --satellite 8. Asked to fix 9. Asked if I tested 10. Asked about task's satellite filtering being moot → led to cleanup 11. Asked about `_IlcReferenceSatelliteAssemblies` naming → kept (consistency) 12. Then asked: "What if we consider IlcSatelliteAssembly to be the snapshot?" → simplified to inline include + `Remove="@(IlcSatelliteAssembly)"` 13. Asked to remove two comments 14. Asked to re-test 15. Asked to commit → committed as `e765e126dcb` 16. Most recent: asked "Why weren't we passing satellites, did that regress at some point?" → I investigated git history, found PR #111514 was the regression point. Recent commands and results: - `git log --oneline --all -S 'SatelliteAssemblies'` on task file → found b134fa2 (#86689, 2023, MichalStrehovsky, original satellite support) - `git show b134fa2` → showed original implementation: task read ALL items, PE-inspected, classified by Culture - `git log --oneline --all -S 'PostprocessAssembly'` → found e144870 "Skip non-PostprocessAssembly files..." (Feb 17 2026) and a5cee7c "Derive ILC inputs from ResolvedFileToPublish..." (Feb 25 2026), both by Sven Boemer - `git log --all --oneline --ancestry-path` → showed these were part of branch ilc-compile-order / ilcPublish - `git log main --grep='Run ILC after'` → confirmed squash-merged as `44e21b71149 Run ILC after ComputeResolvedFilesToPublishList (#111514)` Final answer given: PR #111514 regressed satellite passing. Original PR #86689 (2023) worked. PR #124192 partially fixed project-own case. This commit closes package case. Commit created: e765e126dcb on branch satellite-fix, not pushed. </analysis> <summary> 1. Conversation Overview: - Primary Objectives: Investigate dotnet/runtime PR #124192 / dotnet/sdk#53422 regression where NativeAOT publish leaks package satellite `.dll` files AND fails to embed them via ILC. Root-cause, fix, validate, commit. - Session Context: Progressed through reading comments → repro → root cause → logging → SDK trace → fix implementation → code review/cleanup iterations → test → commit → regression history. - User Intent Evolution: From fixing the surface bug to cleaning up dead code, then simplifying the fix (inlining `IlcSatelliteAssembly` as the snapshot), then historical attribution. 2. Technical Foundation: - .NET 11 preview 3 SDK (`11.0.100-preview.3.26172.108`) pinned via global.json in the repro - NativeAOT publish pipeline: `_ComputeIlcCompileInputs` → `NativeCompile` (post PR #125629 at HEAD) / `ComputeLinkedFilesToPublish` (pre #125629 in installed SDK) - SDK `_ComputeAssembliesToPostprocessOnPublish` deliberately excludes satellites from `PostprocessAssembly=true` metadata - Package/ref satellites land in `ResolvedFileToPublish` with `AssetType=='resources'`; project's own satellites arrive via `IntermediateSatelliteAssembliesWithTargetPath` - `IlcSatelliteAssembly` item → `--satellite:` ILC args (required for embedding; NAOT can't load from disk) 3. Codebase Status: - `/home/sven/src/satellite-fix/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets`: - Changed `<IlcSatelliteAssembly Include="@(_SatelliteAssembliesToPublish)" />` to `<IlcSatelliteAssembly Include="@(ResolvedFileToPublish)" Condition="'%(ResolvedFileToPublish.AssetType)' == 'resources'" />` - Changed `<ResolvedFileToPublish Remove="@(IntermediateSatelliteAssembliesWithTargetPath)" />` to `<ResolvedFileToPublish Remove="@(IlcSatelliteAssembly)" />` - Dropped `<Output TaskParameter="SatelliteAssemblies" ItemName="_SatelliteAssembliesToPublish" />` - `/home/sven/src/satellite-fix/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ComputeManagedAssembliesToCompileToNative.cs`: - Removed `SatelliteAssemblies` `[Output]` property, PE-reading culture classification, `PostprocessAssembly` late-filter check, `System.Reflection.Metadata`/`PortableExecutable` usings - Net −45 lines; +3/−3 on targets file - Repro at `/tmp/aot-satellite-repro/app/` with diagnostic dump targets, `PublishAot=true`, `System.CommandLine 2.0.6`, `Strings.resx`+`Strings.es.resx` 4. Problem Resolution: - Root cause: Task's classification logic gated on `PostprocessAssembly=true` which SDK never sets on satellites, making satellite branch unreachable - Fix: Classify via `AssetType=='resources'` in MSBuild directly; use `IlcSatelliteAssembly` as snapshot consumed by both ILC and the `ResolvedFileToPublish` remove - Dead code cleanup: task's `SatelliteAssemblies` output never produced anything under current SDK contract 5. Progress Tracking: - Completed: investigation, repro, fix, cleanup, end-to-end val... </details> Adding a test for this in dotnet/sdk#53971. <!-- START COPILOT CODING AGENT SUFFIX --> Created from Copilot CLI via the copilot delegate command. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>
...instead of BeforeTargets/AfterTargets.
Redefine the SDK's empty NativeCompile placeholder target with the actual NativeAOT compilation pipeline, replacing the fragile BeforeTargets/AfterTargets hooks that previously sequenced ILC into the publish flow.