Skip to content

PublishAot / PublishSingleFile apps do not automatically strip resx satellite assemblies #127086

@nagilson

Description

@nagilson

Summary

When publishing an application with PublishAot=true, PublishSingleFile=true, or as self-contained, satellite resource assemblies (localized .resources.dll files in per-culture folders like cs/, de/, fr/, etc.) are not automatically excluded from the publish output directory. I believe these resources are already embedded in the single-file bundle or are unnecessary alongside a native AOT binary, yet they still appear as loose files in the output. This is confusing and made me think they were not correctly embedded.

Users must manually set <SatelliteResourceLanguages>en</SatelliteResourceLanguages> in their project file to suppress them. This property should not be required — the default behavior for PublishSingleFile and PublishAot scenarios should be to exclude satellite assembly folders from the publish output.

SatelliteResourceLanguages also does not seem to be properly excluding everything when I set the property - folders are still being generated when I set it to en in the dotnetup publish when I tried to set it locally.

Reproduction

  1. Create a console app with <PublishAot>true</PublishAot> that references any NuGet package shipping satellite assemblies (e.g., System.CommandLine, Spectre.Console).
  2. Run dotnet publish -r win-x64 -c Release.
  3. Observe the publish output directory contains 13+ language subdirectories (cs/, de/, es/, fr/, it/, ja/, ko/, pl/, pt-BR/, ru/, tr/, zh-Hans/, zh-Hant/), each containing .resources.dll files that are unnecessary alongside the native binary.

Current behavior

  • PublishAot: The AOT compiler produces a native binary. Satellite .resources.dll files from the project and its NuGet dependencies are still copied to the publish directory as loose files in per-culture subdirectories. They serve no purpose — the native binary does not load them.
  • PublishSingleFile: Satellite assemblies are bundled into the single-file binary (they are not marked ExcludeFromSingleFile=true), yet their per-culture folders and .resources.dll files are also emitted as loose files in the publish output.
  • Self-contained: Same issue — satellite assemblies from runtime packs and NuGet packages are copied out even when they are not needed.

In all three cases, the only way I found to suppress the extra output is to manually set this variable, which does not seem to always work:

<SatelliteResourceLanguages>en</SatelliteResourceLanguages>

Expected behavior

  1. SatelliteResourceLanguages should not be required. The SDK should automatically suppress satellite assembly output for PublishAot and PublishSingleFile scenarios, since the resources are either embedded or unused.
  2. The default for PublishSingleFile and PublishAot should behave as if SatelliteResourceLanguages is set to the project's neutral language (typically en), so that no extra per-culture folders are produced in the publish output.
  3. Users who explicitly need satellite assemblies alongside the binary should be able to opt back in.

Impact

Disk space and artifact bloat

For a project like dotnetup (PublishAot=true) that references System.CommandLine, Microsoft.Deployment.DotNet.Releases, and Spectre.Console, the publish output contains 13 language directories × 3 .resources.dll files = 39 extra files and their parent directories. This bloats CI artifacts and wastes disk space, particularly in constrained environments like cross-build containers.

XML documentation files

A related concern: XML documentation files (.xml) from NuGet packages should also be excluded from publish output by default. These are developer-time assets, not runtime assets, and should not ship in a self-contained or AOT-published app.

Related

  • Fixing this would clean up the dotnetup executable publish output (Produce AOT, Runtime Specific, Cross Build Executables of dotnetup sdk#53143), which currently ships 39 unnecessary satellite assembly files per RID alongside the native AOT binary.
  • Example CI artifacts showing the problem: official-dnup-ci build #2925196 — each per-RID dotnetup-executable-* artifact contains cs/, de/, es/, fr/, it/, ja/, ko/, pl/, pt-BR/, ru/, tr/, zh-Hans/, zh-Hant/ folders with satellite .resources.dll files that should not be present alongside the native AOT binary. Binlogs are also included in the build artifacts for investigation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions