Add NativeAOT + satellite assembly integration test#53971
Open
Add NativeAOT + satellite assembly integration test#53971
Conversation
Agent-Logs-Url: https://github.com/dotnet/sdk/sessions/afa29c4d-fe1c-41ac-a756-7b4e929dc2b9 Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Add NativeAOT test for satellite assemblies in publishing
Add NativeAOT + satellite assembly integration test
Apr 17, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
Adds end-to-end test coverage in the publish test suite for NativeAOT publish scenarios involving satellite assemblies, ensuring both app-authored and package-provided satellites are produced and usable at runtime.
Changes:
- Add
NativeAot_app_publishes_with_app_and_package_satellite_assembliesto validate satellite assembly publish output underPublishAot=true. - Validate
SatelliteResourceLanguagesfiltering for package satellites (presence forfr/de, absence forja). - Execute the produced native binary and assert the French resource value is resolved at runtime.
Member
|
@copilot apply changes based on the comments in this thread |
Agent-Logs-Url: https://github.com/dotnet/sdk/sessions/c47cab5e-0546-408b-9437-a902ecfe4d7c Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>
Contributor
Author
Done in 981ab55. The source file assignment now uses an interpolated string ( |
sbomer
added a commit
to dotnet/runtime
that referenced
this pull request
Apr 21, 2026
…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>
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.
NativeAot_app_publishes_with_app_and_package_satellite_assembliestest toGivenThatWeWantToPublishAnAotApp.csProgram.csusingResourceManager+CultureInfo("fr")to verify French resource at runtimeStrings.resx(neutral fallback) andStrings.fr.resx(French) embedded resourcesSystem.Spatial5.8.3 package reference (ships de/es/fr/it/ja/ko/ru/zh-Hans/zh-Hant satellites)SatelliteResourceLanguages=fr;defilterPublishAot=true,UseCurrentRuntimeIdentifier=true,SelfContained=trueStripSymbols=trueon non-Windowsfr/{projectName}.resources.dllexists (app satellite)fr/System.Spatial.resources.dllandde/System.Spatial.resources.dllexist (package satellites)ja/System.Spatial.resources.dlldoes NOT exist (filtered)ResourceManagerbase name fromprojectNamevariable (use interpolated string) so the test stays self-consistent ifprojectNameever changesOriginal prompt
The conversation is about investigating test coverage for satellite assemblies in the dotnet/sdk repository, specifically under the context of a branch named `sdk-satellite-fix`. User progressively asked: 1. Are there tests for publishing with satellite assemblies (app-defined + package reference)? 2. Any NativeAOT tests? 3. Add a single NativeAOT test covering both app-defined and package-reference satellites. 4. Can the test run the native image to verify resources work at runtime?I identified existing tests, then added a new test to
test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.csnamedNativeAot_app_publishes_with_app_and_package_satellite_assemblies. In the final step, I enhanced it to run the produced native exe and assert runtime resource loading.Key technical details:
CreateHelloWorldTestProjecthelper creates a basic test project with aProgram.csthat prints "Hello World"TestProjecthasEmbeddedResourcesdict (auto-written like SourceFiles); SDK default globs include**/*.resxasEmbeddedResourceSystem.Spatial5.8.3 ships satellites for de, es, fr, it, ja, ko, ru, zh-Hans, zh-Hant (perGivenThatWeWantToFilterSatelliteAssemblies)/p:UseCurrentRuntimeIdentifier=true, recordsNETCoreSdkPortableRuntimeIdentifier, setsStripSymbols=trueon non-Windows, and usesRunExeCommand./build.shwas attempted but aborted (long bootstrap); systemdotnetalone is insufficient because repo tests depend on redistRecent operations: edited the file twice. First edit added a basic file-existence test. Second (final) edit replaced it with a runnable version that customizes
Program.csto use ResourceManager + CultureInfo("fr") and runs the native exe asserting "Bonjour".1. Conversation Overview: - Primary Objectives: - "are there tests for publishing with satellite assemblies (either defined by the app or from package references)?" - "any nativeaot tests?" - "Ah can we add a nativeaot test that has satellite assemblies? both satellites defined by the app and from package reference? a single test ideally." - "can the test run the native image to ensure it can process the resources? (do other tests run)?" - Session Context: Investigation of satellite-assembly test coverage in the dotnet/sdk repo (branch `sdk-satellite-fix`), culminating in authoring a new NativeAOT publish test that exercises both app-defined satellites and package-reference satellites, then upgrading it to run the native image. - User Intent Evolution: From discovery (what tests exist?) → gap identification (no AOT+satellite coverage) → implementation (add single covering test) → strengthen (execute native image to validate runtime resource resolution).-
- Repo:
- Test framework: xUnit via
- NativeAOT publishing:
- Satellite filtering:
- Reference NuGet:
-
- Purpose: Primary NativeAOT publish test suite
- Current State: Added new test method
- Key Code Segments: New
- Calls
- Records
- Sets
- Adds
- Overrides
- Adds
- Publishes with
- Gets rid from recorded properties,...
Technical Foundation:
/home/sven/src/sdk-satellite-fix(dotnet/sdk)SdkTestbase class;TestAssetsManager.CreateTestProject(TestProject);PublishCommand,RunExeCommand,GetValuesCommandTestProjectmodel:SourceFiles,EmbeddedResources(both dumped to disk; SDK default globs pick up.resx),PackageReferences,AdditionalProperties,RecordProperties/p:PublishAot=true,/p:UseCurrentRuntimeIdentifier=true,/p:SelfContained=true,NETCoreSdkPortableRuntimeIdentifierproperty recording,StripSymbols=trueon non-Windows,IsNativeImage()helperSatelliteResourceLanguagesMSBuild propertySystem.Spatial5.8.3 (ships de/es/fr/it/ja/ko/ru/zh-Hans/zh-Hant satellites)Codebase Status:
test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs:NativeAot_app_publishes_with_app_and_package_satellite_assembliesinserted just before the private helperCheckIlcVersions(around line 1061)[RequiresMSBuildVersionFact("17.0.0.32901")]test that:CreateHelloWorldTestProject(CurrentTargetFramework, "NativeAotAppWithSatellites", true)NETCoreSdkPortableRuntimeIdentifier, setsPublishAot=true, conditionallyStripSymbols=trueon non-WindowsSatelliteResourceLanguages=fr;denew TestPackageReference("System.Spatial", "5.8.3")SourceFiles[$"{projectName}.cs"]with aMainthat usesnew ResourceManager("{projectName}.Strings", typeof(Test).Assembly).GetString("HelloWorld", CultureInfo.GetCultureInfo("fr")), prints value, returns 0 iff equals "Bonjour"EmbeddedResources["Strings.resx"](value "Hello") andEmbeddedResources["Strings.fr.resx"](value "Bonjour") using minimal resx content/p:UseCurrentRuntimeIdentifier=true /p:SelfContained=trueCreated from Copilot CLI via the copilot delegate command.