Skip to content

[Hot Reload] Add assembly modification safety net. Fixes #26072 - #26417

Open
rolfbjarne wants to merge 25 commits into
mainfrom
dev/rolf/hot-reload-safety-net
Open

rolfbjarne wants to merge 25 commits into
mainfrom
dev/rolf/hot-reload-safety-net

Conversation

@rolfbjarne

@rolfbjarne rolfbjarne commented Aug 6, 2026

Copy link
Copy Markdown
Member

Add the HotReloadCompatibleBuild MSBuild default and enforce that reloadable user assemblies are not modified during compatible builds.

The safety net reports a normal linker error when a Copy assembly would be saved. NativeAOT builds default HotReloadCompatibleBuild to false, and the build-property documentation now describes the behavior.

Fixes #26072

🤖 Pull request created by Copilot

rolfbjarne and others added 2 commits August 6, 2026 16:31
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 6, 2026 14:33
@rolfbjarne
rolfbjarne requested a review from dalexsoto as a code owner August 6, 2026 14:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a Hot Reload compatibility “safety net” to prevent reloadable (Copy) assemblies from being re-saved during HotReloadCompatibleBuild builds, and updates build defaults/documentation so NativeAOT builds don’t opt into this mode by default.

Changes:

  • Add a guard in AppBundleRewriter.SaveAssembly that reports an error and skips saving when HotReloadCompatibleBuild is enabled for a reloadable (Copy) assembly.
  • Default $(HotReloadCompatibleBuild) to Debug for non-NativeAOT builds, and to false for NativeAOT.
  • Update build-property documentation to reflect the NativeAOT default behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
tools/dotnet-linker/AppBundleRewriter.cs Adds the enforcement check that blocks saving reloadable assemblies during Hot Reload compatible builds.
dotnet/targets/Xamarin.Shared.Sdk.targets Adjusts MSBuild defaults so NativeAOT doesn’t enable HotReloadCompatibleBuild by default.
docs/building-apps/build-properties.md Documents the updated default (non-NativeAOT Debug => true; otherwise false).
Suppressed comments (2)

tools/dotnet-linker/AppBundleRewriter.cs:1507

  • 🤖 💡 Error message — The error text says the assembly “was modified”, but this guard is actually about preventing any reloadable assembly from being saved/re-serialized (which can happen even for metadata-preservation reasons). Consider wording that explains the byte-for-byte constraint and points to $(HotReloadCompatibleBuild) as the controlling switch.
				configuration.Logger.LogError (ErrorHelper.CreateError (99, $"The assembly '{assembly.Name.Name}' is reloadable, but was modified during a Hot Reload compatible build."));

tools/dotnet-linker/AppBundleRewriter.cs:1509

  • 🤖 💡 Testing — This new safety-net behavior (failing when a reloadable/Copy assembly would be saved under HotReloadCompatibleBuild) doesn’t appear to have a regression test. Given there’s already a dedicated assembly-preparer test harness (tests/assembly-preparer/BaseClass.cs), it would be good to add a focused test that exercises this specific failure mode and asserts on the emitted error (and code, once it’s dedicated).
			if (configuration.HotReloadCompatibleBuild && action == AssemblyAction.Copy && assembly != PlatformAssembly) {
				configuration.Logger.LogError (ErrorHelper.CreateError (99, $"The assembly '{assembly.Name.Name}' is reloadable, but was modified during a Hot Reload compatible build."));
				return;
			}

Comment thread tools/dotnet-linker/AppBundleRewriter.cs Outdated
@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.

…o reloadable assemblies

PreserveSmartEnumConversionsStep injects [DynamicDependency] attributes into the
assembly that references a smart enum. That assembly may be reloadable (i.e. not
trimmed), in which case the injection breaks Hot Reload - and it now trips the
newly added safety net (MT0099), which broke the 'link sdk' test build:

    ILLINK error MT0099: The assembly 'bindings-test' is reloadable, but was
    modified during a Hot Reload compatible build.

Use the mark handler (PreserveSmartEnumConversionsHandler) instead when doing a
Hot Reload compatible build without the assembly-preparer: it marks the
conversion methods when the referencing method is marked, without modifying any
assemblies (and without any app size cost). When the assembly-preparer is used,
the step already emits a root descriptor xml file instead, so nothing changes
there.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

rolfbjarne and others added 2 commits August 28, 2026 10:48
…static registrar

The managed static registrar emits registrar code into every user assembly, which
breaks Hot Reload (and trips the recently added safety net, which broke the
'dont link' test build):

    MSBuild : error MT99: The assembly 'dont link' is reloadable, but was modified
    during a Hot Reload compatible build.

Hot Reload can't work in that case at all (unlike with the trimmable static
registrar, which emits this code into separate companion assemblies), so default
$(HotReloadCompatibleBuild) to false when the managed static registrar is used (a
user-specified value still wins, as before).

This means the property can't be computed at evaluation time anymore, because the
registrar isn't known until the SelectRegistrar target has run, so compute it in a
new _ComputeHotReloadCompatibleBuild target instead, and make the consumers depend
on that target:

* _ComputeLinkerInputs in the SDK (it computes
  $(_UseDynamicDependenciesForSmartEnumPreservation) and the linker's
  HotReloadCompatibleBuild option from it).
* _AddHotReloadCompatibleBuildDefine in the tests (it defines
  HOTRELOAD_COMPATIBLE_BUILD). The condition had to be moved from the target to
  the property group, because a target's condition is evaluated before its
  dependencies are built.

Additionally, SelectRegistrar only runs for projects that produce an app bundle,
so the referenced test library projects would keep the (potentially different)
default, even though it's the app's value that decides whether their assemblies
are modified. So pass the app's value on to every referenced project.

Also update the documentation for $(HotReloadCompatibleBuild) accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…afety-net

# Conflicts:
#	dotnet/targets/Xamarin.Shared.Sdk.targets
#	tests/common/shared-dotnet.csproj
@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 September 3, 2026 07:59
Constructor factory methods modify their containing assembly, so skip generating them for copied assemblies in Hot Reload compatible builds. Add coverage that verifies a reloadable assembly remains unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

rolfbjarne and others added 2 commits September 3, 2026 15:16
Only treat ProjectReference items with an Include attribute as project references. This allows cloned projects to retain valid Update items without crashing XHarness project traversal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

rolfbjarne and others added 3 commits September 4, 2026 09:14
Grant relocated callbacks access to helper assemblies instead of relying on visibility rewrites, and construct generic wrappers through reflection when reloadable assemblies cannot contain injected factories.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Gate the generic constructor reflection fallbacks behind the HotReloadCompatible trimmer feature switch so non-Hot-Reload builds remove them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0bb3455e-b9bf-452b-80f7-53e6b3079583
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

⚠️ AppSizeTest expected files changed ⚠️

The AppSizeTest detected changes in the expected app size files.

To update the expected files, add a comment with the following command:

/apply-gist https://gist.github.com/vs-mobiletools-engineering-service2/9952f297bcbd6412873215b267f412a8
Updated files
  • iOS-CoreCLR-Interpreter-preservedapis.txt
  • iOS-CoreCLR-R2R-preservedapis.txt
  • iOS-MonoVM-interpreter-preservedapis.txt
  • iOS-MonoVM-preservedapis.txt
  • MacCatalyst-CoreCLR-Interpreter-preservedapis.txt
  • MacCatalyst-CoreCLR-R2R-preservedapis.txt
  • MacCatalyst-MonoVM-interpreter-preservedapis.txt
  • MacCatalyst-MonoVM-preservedapis.txt
  • TVOS-CoreCLR-Interpreter-preservedapis.txt
  • TVOS-CoreCLR-R2R-preservedapis.txt
  • TVOS-MonoVM-interpreter-preservedapis.txt
  • TVOS-MonoVM-preservedapis.txt

Pipeline on Agent
Hash: 74964bbc318361ea7f278a85fbbf6e03a3cc0e1f [PR build]

rolfbjarne and others added 3 commits September 7, 2026 13:44
Only run the shared test-project Hot Reload propagation targets for .NET 11 and newer, where the workload defines the computation target.

Set the registrar using the Registrar property for the P/Invoke wrapper generator test so that Hot Reload compatibility is determined correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…afety-net

# Conflicts:
#	tests/assembly-preparer/ManagedRegistrarStepTests.cs
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

Per-assembly type map companions previously embedded the user assembly name verbatim. A comma was interpreted as the start of assembly display-name qualifiers, so ILLink could not resolve the companion and then modified the root type map as if it referenced a missing assembly. Encode ambiguous names into a collision-safe hexadecimal form and use the resulting identity consistently when creating, referencing, and locating companion assemblies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2bdd0ea7-c3c5-45ed-97c0-7ca42d9f5b05
@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.

HotReloadCompatibleBuild propagation gives transitive project references multiple MSBuild configurations that still share their output paths. Parallel project-reference builds can consequently compile MonoTouch.Dialog twice into the same DLL or PDB, causing intermittent MSB3026 copy retries.

Disable parallel project-reference builds for BundleStructure so the first configuration finishes before the equivalent configuration is checked and skipped as up-to-date. This prevents the conflicting writers without hiding successful-copy retry warnings globally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2bdd0ea7-c3c5-45ed-97c0-7ca42d9f5b05
@vs-mobiletools-engineering-service2

This comment has been minimized.

rolfbjarne and others added 2 commits September 9, 2026 19:16
… net

Add an undocumented $(_HotReloadModifiedAssemblySafetyNet) MSBuild property (default: true)
that can be set to false to disable the error raised when a reloadable assembly is
modified during a Hot Reload compatible build. This is only meant as an escape hatch
in case the safety net itself produces a false positive.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne rolfbjarne added ready-to-review This PR is ready to review/merge. and removed ready-to-review This PR is ready to review/merge. labels Sep 15, 2026
Ensure the trimming state is initialized before Hot Reload compatibility selects the registrar. This prevents early target execution from caching assemblies as untrimmed and disabling export attribute removal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c2a5ee38-078a-480d-b532-f862155b3fd9
@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
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: 6bfada4c1500d143fa0c9d2965e04fe17f139d1b [PR build]

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

🔥 [CI Build #6bfada4] Test results 🔥

Test results

❌ Tests failed on VSTS: test results

0 tests crashed, 1 tests failed, 263 tests passed.

Failures

❌ monotouch tests (iOS)

1 tests failed, 24 tests passed.

Failed tests

  • monotouch-test/iOS - simulator/Release (trimmable static registrar, all optimizations): TimedOut

Html Report (VSDrops) Download

Successes

✅ assembly-processing: All 1 tests passed. Html Report (VSDrops) Download
✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (iOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (MacCatalyst): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (macOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (Multiple platforms): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (tvOS): All 1 tests passed. Html Report (VSDrops) Download
✅ framework: All 2 tests passed. Html Report (VSDrops) Download
✅ fsharp: All 4 tests passed. Html Report (VSDrops) Download
✅ generator: All 5 tests passed. Html Report (VSDrops) Download
✅ interdependent-binding-projects: All 4 tests passed. Html Report (VSDrops) Download
✅ introspection: All 7 tests passed. Html Report (VSDrops) Download
✅ linker (iOS): All 31 tests passed. Html Report (VSDrops) Download
✅ linker (MacCatalyst): All 31 tests passed. Html Report (VSDrops) Download
✅ linker (macOS): All 21 tests passed. Html Report (VSDrops) Download
✅ linker (tvOS): All 31 tests passed. Html Report (VSDrops) Download
✅ monotouch (MacCatalyst): All 25 tests passed. Html Report (VSDrops) Download
✅ monotouch (macOS): All 20 tests passed. Html Report (VSDrops) Download
✅ monotouch (tvOS): All 25 tests passed. Html Report (VSDrops) Download
✅ msbuild: All 2 tests passed. Html Report (VSDrops) Download
✅ sharpie: All 1 tests passed. Html Report (VSDrops) Download
✅ windows: All 3 tests passed. Html Report (VSDrops) Download
✅ xcframework: All 4 tests passed. Html Report (VSDrops) Download
✅ xtro: All 1 tests passed. Html Report (VSDrops) Download

macOS tests

✅ Tests on macOS Sonoma (14): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Sequoia (15): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Tahoe (26): All 5 tests passed. Html Report (VSDrops) Download
⚠️ Tests on macOS Golden Gate (27): Tests skipped, incorrect beta version. Html Report (VSDrops) Download

Linux Build Verification

Linux build succeeded

Pipeline on Agent
Hash: 6bfada4c1500d143fa0c9d2965e04fe17f139d1b [PR build]

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Hot Reload] Add $(HotReloadCompatibleBuild) MSBuild property + assembly-preparer safety net

3 participants