Skip to content

[dotnet-linker] Use [DynamicDependency] attributes instead of manual marking when applying the [Preserve] attribute.#25001

Draft
rolfbjarne wants to merge 4 commits intodev/rolf/use-dynamic-dependency-attributes-optimizegeneratedcodefrom
dev/rolf/use-dynamic-dependency-attributes-applypreserveattribute
Draft

[dotnet-linker] Use [DynamicDependency] attributes instead of manual marking when applying the [Preserve] attribute.#25001
rolfbjarne wants to merge 4 commits intodev/rolf/use-dynamic-dependency-attributes-optimizegeneratedcodefrom
dev/rolf/use-dynamic-dependency-attributes-applypreserveattribute

Conversation

@rolfbjarne
Copy link
Member

This makes it easier to move this code out of a custom linker step in the future.

This was simplified a bit because we already had code to do this, but it was only
in effect when using NativeAOT. Generalize it, move it out of the marking phase of
the linker, and run it before marking.

Contributes towards #17693.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors how [Preserve] is handled in the dotnet linker pipeline by generating [DynamicDependency] attributes (so preservation intent is expressed in IL rather than via manual marking), aligning with the longer-term goal of reducing/removing custom linker steps (Issue #17693).

Changes:

  • Replaces the older ApplyPreserveAttribute implementation with a shared implementation (ApplyPreserveAttributeImpl) and introduces a new ApplyPreserveAttributeStep that emits [DynamicDependency] attributes before marking.
  • Refactors AssemblyModifierStep to support assembly-level modification hooks (ModifyAssembly) and ensures assemblies are only saved when modified.
  • Wires the new step into the build via MSBuild properties/step registration, and adds a link-context flag to prevent running both the old marking-based logic and the new dynamic-dependency logic.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/linker/ApplyPreserveAttribute.cs Removes the legacy linker-step implementation.
tools/dotnet-linker/dotnet-linker.csproj Drops compilation/linking of the removed legacy file.
tools/dotnet-linker/Steps/AssemblyModifierStep.cs Introduces ModifyAssembly and seals TryProcessAssembly to standardize assembly-save behavior.
tools/dotnet-linker/ApplyPreserveAttributeStep.cs New step that translates [Preserve] into [DynamicDependency] attributes pre-mark.
tools/dotnet-linker/ApplyPreserveAttributeBase.cs Refactors into ApplyPreserveAttribute substep + shared ApplyPreserveAttributeImpl used by both approaches.
tools/dotnet-linker/AppBundleRewriter.cs Exposes helpers used by the new step (GetOrCreateStaticConstructor, AddAttributeOnlyOnce).
tools/common/DerivedLinkContext.cs Adds a flag to coordinate whether the new step ran.
dotnet/targets/Xamarin.Shared.Sdk.targets Adds MSBuild property + registers ApplyPreserveAttributeStep before MarkStep.

MethodDefinition GetOrCreateModuleConstructor (ModuleDefinition @module)
{
var moduleType = @module.GetModuleType ();
return abr.GetOrCreateStaticConstructor (moduleType, out var modified);
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetOrCreateModuleConstructor calls abr.GetOrCreateStaticConstructor (moduleType, out var modified) but never uses modified. With TreatWarningsAsErrors=true, this unused local is likely to cause a build break. Consider using out _ (or otherwise consuming/propagating the flag) to avoid the unused-variable warning.

Suggested change
return abr.GetOrCreateStaticConstructor (moduleType, out var modified);
return abr.GetOrCreateStaticConstructor (moduleType, out _);

Copilot uses AI. Check for mistakes.
Comment on lines 279 to 288
for (int i = attributes.Count - 1; i >= 0; i--) {
var attribute = attributes [i];

bool remote_attribute;
if (!IsPreservedAttribute (provider, attribute, out remote_attribute))
if (!IsPreservedAttribute (provider, attribute, out var remove_attribute))
continue;

attrs.Add (attribute);
if (remote_attribute)
if (remove_attribute)
attributes.RemoveAt (i);
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetPreserveAttributes removes [Preserve] attributes from the provider (attributes.RemoveAt (i)), but this mutation isn't reflected in the modified value returned from Process/BrowseTypes/ProcessAssemblyAttributes. This means the dynamic-dependency step may decide the assembly wasn't modified (and skip SaveCurrentAssembly) even though attributes were removed (e.g., if the equivalent DynamicDependency attribute was already present so no new attributes get added). Consider tracking whether any attributes were removed and OR-ing that into the returned modified value.

Copilot uses AI. Check for mistakes.
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

rolfbjarne and others added 2 commits March 24, 2026 19:56
…marking when applying the [Preserve] attribute.

This makes it easier to move this code out of a custom linker step in the future.

This was simplified a bit because we already had code to do this, but it was only
in effect when using NativeAOT. Generalize it, move it out of the marking phase of
the linker, and run it before marking.

Contributes towards #17693.
@rolfbjarne rolfbjarne force-pushed the dev/rolf/use-dynamic-dependency-attributes-applypreserveattribute branch from d09d3b4 to 81457b4 Compare March 24, 2026 18:56
@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ [CI Build #81457b4] Build passed (Build packages) ✅

Pipeline on Agent
Hash: 81457b40464470aaf3f1d5132daee60c84a99e38 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

🔥 [CI Build #81457b4] Test results 🔥

Test results

❌ Tests failed on VSTS: test results

5 tests crashed, 93 tests failed, 38 tests passed.

Failures

❌ dotnettests tests (iOS)

1 tests failed, 0 tests passed.

Failed tests

  • DotNet tests: Failed (Execution failed with exit code 1)
    • Xamarin.Tests.AppIconTest.AlternateAppIcon(iOS,"ios-arm64"): 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • Xamarin.Tests.AppIconTest.AlternateAppIcons(iOS,"ios-arm64"): 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • Xamarin.Tests.AppIconTest.AppIcon_1(iOS,"ios-arm64"): 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • ... and 75 more

Html Report (VSDrops) Download

❌ dotnettests tests (MacCatalyst)

1 tests failed, 0 tests passed.

Failed tests

  • DotNet tests: Failed (Execution failed with exit code 1)
    • Xamarin.Tests.AppIconTest.AlternateAppIcon(MacCatalyst,"maccatal...: 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • Xamarin.Tests.AppIconTest.AlternateAppIcons(MacCatalyst,"maccata...: 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • Xamarin.Tests.AppIconTest.AppIcon_1(MacCatalyst,"maccatalyst-arm...: 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • ... and 62 more

Html Report (VSDrops) Download

❌ dotnettests tests (macOS)

1 tests failed, 0 tests passed.

Failed tests

  • DotNet tests: Failed (Execution failed with exit code 1)
    • Xamarin.Tests.DotNetProjectTest.BuildAndExecuteAppWithNativeDyna...: 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • Xamarin.Tests.DotNetProjectTest.BuildProjectsWithExtensions(MacO...: 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • Xamarin.Tests.DotNetProjectTest.BuildProjectsWithExtensions(MacO...: 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • ... and 15 more

Html Report (VSDrops) Download

❌ dotnettests tests (Multiple platforms)

1 tests failed, 0 tests passed.

Failed tests

  • DotNet tests: Failed (Execution failed with exit code 1)
    • Xamarin.Tests.DotNetProjectTest.AppWithGenericLibraryReference(i...: 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/3/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...

Html Report (VSDrops) Download

❌ dotnettests tests (tvOS)

1 tests failed, 0 tests passed.

Failed tests

  • DotNet tests: Failed (Execution failed with exit code 1)
    • Xamarin.Tests.AppIconTest.AlternateAppIcon(TVOS,"tvos-arm64"): 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/2/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • Xamarin.Tests.AppIconTest.AlternateAppIcons(TVOS,"tvos-arm64"): 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/2/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • Xamarin.Tests.AppIconTest.AppIcon_1(TVOS,"tvos-arm64"): 'dotnet build' failed with exit code 1
      Full command: /Users/builder/azdo/_work/2/s/macios/builds/downloads/dotnet-sdk-10.0.300-p...
    • ... and 43 more

Html Report (VSDrops) Download

❌ framework tests

1 tests failed, 1 tests passed.

Failed tests

  • framework-test/Mac Catalyst/Debug: BuildFailure

Html Report (VSDrops) Download

❌ fsharp tests

3 tests failed, 1 tests passed.

Failed tests

  • fsharp/Mac Catalyst/Debug: BuildFailure
  • fsharp/iOS - simulator/Debug: BuildFailure
  • fsharp/tvOS - simulator/Debug: BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))

Html Report (VSDrops) Download

❌ interdependent-binding-projects tests

3 tests failed, 1 tests passed.

Failed tests

  • interdependent-binding-projects/Mac Catalyst/Debug: BuildFailure
  • interdependent-binding-projects/iOS - simulator/Debug: BuildFailure
  • interdependent-binding-projects/tvOS - simulator/Debug: BuildFailure

Html Report (VSDrops) Download

❌ linker tests

28 tests failed, 16 tests passed.

Failed tests

  • link sdk/macOS/Debug: BuildFailure
  • link sdk/macOS/Release: BuildFailure
  • link sdk/Mac Catalyst/Debug: BuildFailure
  • link sdk/Mac Catalyst/Release: BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • link sdk/iOS - simulator/Debug: BuildFailure
  • link sdk/iOS - simulator/Release: BuildFailure
  • link sdk/tvOS - simulator/Debug: BuildFailure
  • link sdk/tvOS - simulator/Release: BuildFailure
  • link all/macOS/Debug: BuildFailure
  • link all/macOS/Debug (don't bundle original resources): BuildFailure
  • link all/macOS/Release: BuildFailure
  • link all/Mac Catalyst/Debug: BuildFailure
  • link all/Mac Catalyst/Debug (don't bundle original resources): BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • link all/Mac Catalyst/Release: BuildFailure
  • link all/iOS - simulator/Debug: BuildFailure
  • link all/iOS - simulator/Release: BuildFailure
  • link all/iOS - simulator/Debug (don't bundle original resources): BuildFailure
  • link all/tvOS - simulator/Debug: BuildFailure
  • link all/tvOS - simulator/Release: BuildFailure
  • link all/tvOS - simulator/Debug (don't bundle original resources): BuildFailure
  • trimmode link/macOS/Debug: BuildFailure
  • trimmode link/macOS/Release: BuildFailure
  • trimmode link/Mac Catalyst/Debug: BuildFailure
  • trimmode link/Mac Catalyst/Release: BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • trimmode link/iOS - simulator/Debug: BuildFailure
  • trimmode link/iOS - simulator/Release: BuildFailure
  • trimmode link/tvOS - simulator/Debug: BuildFailure
  • trimmode link/tvOS - simulator/Release: BuildFailure

Html Report (VSDrops) Download

❌ monotouch tests (iOS)

11 tests failed, 0 tests passed.

Failed tests

  • monotouch-test/iOS - simulator/Debug: BuildFailure
  • monotouch-test/iOS - simulator/Debug (LinkSdk): BuildFailure
  • monotouch-test/iOS - simulator/Debug (static registrar): BuildFailure
  • monotouch-test/iOS - simulator/Release (all optimizations): BuildFailure
  • monotouch-test/iOS - simulator/Debug (ARM64): BuildFailure
  • monotouch-test/iOS - simulator/Release (NativeAOT, ARM64): BuildFailure
  • monotouch-test/iOS - simulator/Debug (managed static registrar): BuildFailure
  • monotouch-test/iOS - simulator/Release (managed static registrar, all optimizations): BuildFailure
  • monotouch-test/iOS - simulator/Release (NativeAOT, x64): BuildFailure
  • monotouch-test/iOS - simulator/Debug (interpreter): BuildFailure
  • monotouch-test/iOS - simulator/Release (interpreter): BuildFailure

Html Report (VSDrops) Download

❌ monotouch tests (MacCatalyst)

15 tests failed, 0 tests passed.

Failed tests

  • monotouch-test/Mac Catalyst/Debug: BuildFailure
  • monotouch-test/Mac Catalyst/Debug (ARM64): BuildFailure
  • monotouch-test/Mac Catalyst/Debug (managed static registrar): BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • monotouch-test/Mac Catalyst/Debug (static registrar): BuildFailure
  • monotouch-test/Mac Catalyst/Debug (static registrar, ARM64): BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • monotouch-test/Mac Catalyst/Release (managed static registrar): BuildFailure
  • monotouch-test/Mac Catalyst/Release (managed static registrar, all optimizations): BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • monotouch-test/Mac Catalyst/Release (NativeAOT): BuildFailure
  • monotouch-test/Mac Catalyst/Release (NativeAOT, ARM64): BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • monotouch-test/Mac Catalyst/Release (NativeAOT, x64): BuildFailure
  • monotouch-test/Mac Catalyst/Release (static registrar): BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • monotouch-test/Mac Catalyst/Release (static registrar, all optimizations): BuildFailure
  • monotouch-test/Mac Catalyst/Release (ARM64, LLVM): BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • monotouch-test/Mac Catalyst/Debug (interpreter): BuildFailure
  • monotouch-test/Mac Catalyst/Release (interpreter): BuildFailure

Html Report (VSDrops) Download

❌ monotouch tests (macOS)

12 tests failed, 0 tests passed.

Failed tests

  • monotouch-test/macOS/Debug: Failed (Test run failed.
    Tests run: 3671 Passed: 3568 Inconclusive: 4 Failed: 1 Ignored: 102)
  • monotouch-test/macOS/Debug (ARM64): Failed (Test run failed.
    Tests run: 3671 Passed: 3568 Inconclusive: 4 Failed: 1 Ignored: 102)
  • monotouch-test/macOS/Debug (managed static registrar): Failed (Test run failed.
    Tests run: 3668 Passed: 3565 Inconclusive: 4 Failed: 1 Ignored: 102)
  • monotouch-test/macOS/Debug (static registrar): Failed (Test run failed.
    Tests run: 3668 Passed: 3566 Inconclusive: 4 Failed: 1 Ignored: 101)
  • monotouch-test/macOS/Debug (static registrar, ARM64): Failed (Test run failed.
    Tests run: 3668 Passed: 3566 Inconclusive: 4 Failed: 1 Ignored: 101)
  • monotouch-test/macOS/Release (managed static registrar): Failed (Test run failed.
    Tests run: 3668 Passed: 3564 Inconclusive: 4 Failed: 1 Ignored: 103)
  • monotouch-test/macOS/Release (managed static registrar, all optimizations): Failed (Test run failed.
    Tests run: 3668 Passed: 3562 Inconclusive: 4 Failed: 1 Ignored: 105)
  • monotouch-test/macOS/Release (NativeAOT): BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • monotouch-test/macOS/Release (NativeAOT, ARM64): BuildFailure
  • monotouch-test/macOS/Release (NativeAOT, x64): BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))
  • monotouch-test/macOS/Release (static registrar): Failed (Test run failed.
    Tests run: 3668 Passed: 3564 Inconclusive: 4 Failed: 1 Ignored: 103)
  • monotouch-test/macOS/Release (static registrar, all optimizations): Failed (Test run failed.
    Tests run: 3668 Passed: 3561 Inconclusive: 4 Failed: 1 Ignored: 106)

Html Report (VSDrops) Download

❌ monotouch tests (tvOS)

11 tests failed, 0 tests passed.

Failed tests

  • monotouch-test/tvOS - simulator/Debug: BuildFailure
  • monotouch-test/tvOS - simulator/Debug (LinkSdk): BuildFailure
  • monotouch-test/tvOS - simulator/Debug (static registrar): BuildFailure
  • monotouch-test/tvOS - simulator/Release (all optimizations): BuildFailure
  • monotouch-test/tvOS - simulator/Debug (ARM64): BuildFailure
  • monotouch-test/tvOS - simulator/Release (NativeAOT, ARM64): BuildFailure
  • monotouch-test/tvOS - simulator/Debug (managed static registrar): BuildFailure
  • monotouch-test/tvOS - simulator/Release (managed static registrar, all optimizations): BuildFailure
  • monotouch-test/tvOS - simulator/Release (NativeAOT, x64): BuildFailure
  • monotouch-test/tvOS - simulator/Debug (interpreter): BuildFailure
  • monotouch-test/tvOS - simulator/Release (interpreter): BuildFailure

Html Report (VSDrops) Download

❌ windows tests

1 tests failed, 2 tests passed.

Failed tests

  • Remote .NET tests/Xamarin.Tests.DotNetProjectTest.AppWithLibraryWithResourcesReferenceOnRemoteWindows(iOS,"ios-arm64",False): Failed: 'dotnet build' failed with exit code 1

  • Remote .NET tests/Xamarin.Tests.WindowsTest.StripTest(iOS,"ios-arm64","Release"): Failed: 'dotnet build' failed with exit code 1

  • Remote .NET tests/Xamarin.Tests.WindowsTest.PluralRuntimeIdentifiersWithRemoteMac(iOS,"ios-arm64"): Failed: 'dotnet build' timed out after 00:10:00

  • Remote .NET tests/Xamarin.Tests.DotNetProjectTest.AppWithLibraryWithResourcesReferenceOnRemoteWindows(iOS,"ios-arm64",True): Failed: 'dotnet build' failed with exit code 1

  • Remote .NET tests/Xamarin.Tests.WindowsTest.BundleStructureWithRemoteMac(iOS,"ios-arm64",All,"Debug"): Failed: 'dotnet build' failed with exit code 1

  • Remote .NET tests/Xamarin.Tests.WindowsTest.RemoteTest(iOS,"ios-arm64"): Failed: 'dotnet build' failed with exit code 1

  • Remote .NET tests/Xamarin.Tests.WindowsTest.PluralRuntimeIdentifiersWithRemoteMac(iOS,"iossimulator-arm64;iossimulator-x64"): Failed: 'dotnet build' timed out after 00:10:00

  • Remote .NET tests/Xamarin.Tests.WindowsTest.BuildEmbeddedFrameworkInBindingProjectApp(iOS,"iossimulator-arm64"): Failed: 'dotnet build' failed with exit code 1

Html Report (VSDrops) Download

❌ xcframework tests

3 tests failed, 1 tests passed.

Failed tests

  • xcframework-test/Mac Catalyst/Debug: BuildFailure
  • xcframework-test/iOS - simulator/Debug: BuildFailure
  • xcframework-test/tvOS - simulator/Debug: BuildFailure ( (failed to parse the logs: The Writer is closed or in error state.))

Html Report (VSDrops) Download

❌ Tests on macOS Monterey (12) tests

🔥 Failed catastrophically on VSTS: test results - mac_monterey (no summary found).

Html Report (VSDrops) Download

❌ Tests on macOS Ventura (13) tests

🔥 Failed catastrophically on VSTS: test results - mac_ventura (no summary found).

Html Report (VSDrops) Download

❌ Tests on macOS Sonoma (14) tests

🔥 Failed catastrophically on VSTS: test results - mac_sonoma (no summary found).

Html Report (VSDrops) Download

❌ Tests on macOS Sequoia (15) tests

🔥 Failed catastrophically on VSTS: test results - mac_sequoia (no summary found).

Html Report (VSDrops) Download

❌ Tests on macOS Tahoe (26) tests

🔥 Failed catastrophically on VSTS: test results - mac_tahoe (no summary found).

Html Report (VSDrops) Download

Successes

✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ generator: All 5 tests passed. Html Report (VSDrops) Download
✅ introspection: All 6 tests passed. Html Report (VSDrops) Download
✅ msbuild: All 2 tests passed. Html Report (VSDrops) Download
✅ sharpie: All 1 tests passed. Html Report (VSDrops) Download
✅ xtro: All 1 tests passed. Html Report (VSDrops) Download

macOS tests

Linux Build Verification

Linux build succeeded

Pipeline on Agent
Hash: 81457b40464470aaf3f1d5132daee60c84a99e38 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ [PR Build #4036efb] Build passed (Detect API changes) ✅

Pipeline on Agent
Hash: 4036efb2889fb0cd8e72a063706b7d47e8df9142 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ API diff for current PR / commit

NET (empty diffs)

✅ API diff vs stable

NET (empty diffs)

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: 4036efb2889fb0cd8e72a063706b7d47e8df9142 [PR build]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants