Commit 033cfb1
Fix NativeAOT: pass package satellite assemblies to ILC and remove them 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>1 parent af44505 commit 033cfb1
2 files changed
Lines changed: 2 additions & 48 deletions
File tree
- src/coreclr
- nativeaot/BuildIntegration
- tools/aot/ILCompiler.Build.Tasks
Lines changed: 2 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
38 | | - | |
| 38 | + | |
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| |||
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
125 | | - | |
126 | 125 | | |
127 | 126 | | |
128 | 127 | | |
| |||
Lines changed: 0 additions & 45 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
11 | | - | |
12 | 10 | | |
13 | 11 | | |
14 | 12 | | |
| |||
74 | 72 | | |
75 | 73 | | |
76 | 74 | | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | 75 | | |
85 | 76 | | |
86 | 77 | | |
| |||
95 | 86 | | |
96 | 87 | | |
97 | 88 | | |
98 | | - | |
99 | 89 | | |
100 | 90 | | |
101 | 91 | | |
| |||
159 | 149 | | |
160 | 150 | | |
161 | 151 | | |
162 | | - | |
163 | | - | |
164 | | - | |
165 | | - | |
166 | | - | |
167 | | - | |
168 | | - | |
169 | | - | |
170 | | - | |
171 | | - | |
172 | | - | |
173 | | - | |
174 | | - | |
175 | | - | |
176 | | - | |
177 | | - | |
178 | | - | |
179 | | - | |
180 | | - | |
181 | | - | |
182 | | - | |
183 | | - | |
184 | | - | |
185 | | - | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
196 | 152 | | |
197 | 153 | | |
198 | 154 | | |
199 | | - | |
200 | 155 | | |
201 | 156 | | |
202 | 157 | | |
| |||
0 commit comments