Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/building-apps/build-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,9 @@ assemblies so they stay byte-for-byte unchanged (a requirement for Hot
Reload). This will disable a few minor optimizations, but will otherwies not
affect anything.

The default value is `true` for debug builds and `false` otherwise.
The default value is `true` for non-NativeAOT debug builds that don't use the
managed static registrar, and `false` otherwise (the managed static registrar
modifies user assemblies, which is incompatible with Hot Reload).

## IBToolPath

Expand Down
32 changes: 23 additions & 9 deletions dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,6 @@
<!-- As a last resort, the default is false for all platforms -->
<_BundlerDebug Condition="'$(_BundlerDebug)' == ''">false</_BundlerDebug>

<!--
Hot Reload requires that we don't modify user (reloadable) assemblies. It's a debug-only
feature, so default $(HotReloadCompatibleBuild) to the debug state: this keeps the current
behaviour (and app size) for release builds, where the extra unconditional preservation of
smart-enum conversion methods would otherwise cost a bit of app size.
-->
<HotReloadCompatibleBuild Condition="'$(HotReloadCompatibleBuild)' == ''">$(_BundlerDebug)</HotReloadCompatibleBuild>

<!-- On the other hand, we want the linker to link away debug support in the actual code unless we're building for Debug -->
<DebuggerSupport Condition="'$(DebuggerSupport)' == ''">$(_BundlerDebug)</DebuggerSupport>

Expand Down Expand Up @@ -651,7 +643,21 @@

<Target Name="_ComputeLinkerArguments" DependsOnTargets="$(_ComputeLinkerArgumentsDependsOn)" />

<Target Name="_ComputeLinkerInputs">
<Target Name="_ComputeHotReloadCompatibleBuild" DependsOnTargets="SelectRegistrar">
<PropertyGroup>
<!--
Hot Reload requires that we don't modify user (reloadable) assemblies. It's a debug-only
feature, so default $(HotReloadCompatibleBuild) to the debug state: this keeps the current
behaviour (and app size) for release builds, where the extra unconditional preservation of
smart-enum conversion methods would otherwise cost a bit of app size.
-->
<HotReloadCompatibleBuild Condition="'$(HotReloadCompatibleBuild)' == '' And '$(Registrar)' == 'managed-static'">false</HotReloadCompatibleBuild>
<HotReloadCompatibleBuild Condition="'$(HotReloadCompatibleBuild)' == '' And '$(_UseNativeAot)' != 'true'">$(_BundlerDebug)</HotReloadCompatibleBuild>
<HotReloadCompatibleBuild Condition="'$(HotReloadCompatibleBuild)' == ''">false</HotReloadCompatibleBuild>
</PropertyGroup>
</Target>

<Target Name="_ComputeLinkerInputs" DependsOnTargets="_ComputeHotReloadCompatibleBuild">
<!-- Validate the linker mode -->
<Error Text="Invalid link mode: '$(_LinkMode)'. Valid link modes are: 'None', 'SdkOnly' and 'Full'" Condition="'$(_LinkMode)' != 'None' And '$(_LinkMode)' != 'SdkOnly' And '$(_LinkMode)' != 'Full' And '$(_LinkMode)' != 'TrimMode'" />

Expand All @@ -665,6 +671,14 @@
<PropertyGroup>
<_UseDynamicDependenciesInsteadOfMarking Condition="'$(_UseDynamicDependenciesInsteadOfMarking)' == ''">true</_UseDynamicDependenciesInsteadOfMarking>
<_UseDynamicDependenciesForProtocolPreservation Condition="'$(_UseDynamicDependenciesForProtocolPreservation)' == ''">$(_UseDynamicDependenciesInsteadOfMarking)</_UseDynamicDependenciesForProtocolPreservation>
<!--
PreserveSmartEnumConversionsStep injects [DynamicDependency] attributes into the assembly that
references the smart enum, and that assembly may be reloadable (i.e. not trimmed), which would
break Hot Reload. So use the mark handler instead (it doesn't modify any assemblies) when we're
doing a Hot Reload compatible build (this doesn't apply when the assembly-preparer is used,
because it emits a root descriptor xml file instead - see PreserveSmartEnumConversionsStep).
-->
<_UseDynamicDependenciesForSmartEnumPreservation Condition="'$(_UseDynamicDependenciesForSmartEnumPreservation)' == '' And '$(HotReloadCompatibleBuild)' == 'true' And '$(PrepareAssemblies)' != 'true'">false</_UseDynamicDependenciesForSmartEnumPreservation>
<_UseDynamicDependenciesForSmartEnumPreservation Condition="'$(_UseDynamicDependenciesForSmartEnumPreservation)' == ''">$(_UseDynamicDependenciesInsteadOfMarking)</_UseDynamicDependenciesForSmartEnumPreservation>
<_UseDynamicDependenciesForBlockCodePreservation Condition="'$(_UseDynamicDependenciesForBlockCodePreservation)' == ''">$(_UseDynamicDependenciesInsteadOfMarking)</_UseDynamicDependenciesForBlockCodePreservation>
<_UseDynamicDependenciesForGeneratedCodeOptimizations Condition="'$(_UseDynamicDependenciesForGeneratedCodeOptimizations)' == ''">$(_UseDynamicDependenciesInsteadOfMarking)</_UseDynamicDependenciesForGeneratedCodeOptimizations>
Expand Down
17 changes: 15 additions & 2 deletions tests/common/shared-dotnet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,25 @@
happen in a target (and not in a plain PropertyGroup), because $(HotReloadCompatibleBuild)
is computed in the platform SDK targets, which are imported after this file.
-->
<Target Name="_AddHotReloadCompatibleBuildDefine" BeforeTargets="CoreCompile" Condition="'$(HotReloadCompatibleBuild)' == 'true'">
<PropertyGroup>
<Target Name="_AddHotReloadCompatibleBuildDefine" BeforeTargets="CoreCompile" DependsOnTargets="_ComputeHotReloadCompatibleBuild">
<PropertyGroup Condition="'$(HotReloadCompatibleBuild)' == 'true'">
<DefineConstants>$(DefineConstants);HOTRELOAD_COMPATIBLE_BUILD</DefineConstants>
</PropertyGroup>
</Target>

<!--
Whether the build is hot-reload-compatible is an app-level decision (it depends on the
registrar, which is only selected for projects that produce an app bundle), but it affects
the library projects the app references as well (their assemblies are the ones that must be
left unmodified). So pass the app's value on to every referenced project, to make sure they
all agree (in particular when defining HOTRELOAD_COMPATIBLE_BUILD above).
-->
<Target Name="_PassHotReloadCompatibleBuildToProjectReferences" BeforeTargets="AssignProjectConfiguration" DependsOnTargets="_ComputeHotReloadCompatibleBuild" Condition="'$(_CanOutputAppBundle)' == 'true'">
<ItemGroup>
<ProjectReference Update="@(ProjectReference)" AdditionalProperties="%(ProjectReference.AdditionalProperties);HotReloadCompatibleBuild=$(HotReloadCompatibleBuild)" />
</ItemGroup>
</Target>

<Import Project="$(MSBuildThisFileDirectory)/../ComputeRegistrarConstant.targets" />
<Import Project="$(MSBuildThisFileDirectory)/../nunit.framework.targets" Condition="'$(ExcludeNUnitLiteReference)' != 'true'" />

Expand Down
6 changes: 5 additions & 1 deletion tools/dotnet-linker/AppBundleRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1501,9 +1501,13 @@ void SaveAssembly (AssemblyDefinition assembly)
{
if (assembly != CurrentAssembly && assembly != PlatformAssembly)
throw new InvalidOperationException ($"Can't save assembly {assembly.Name} because it's not the current assembly ({CurrentAssembly.Name}) or the platform assembly ({PlatformAssembly.Name}).");
AssemblySaved?.Invoke (assembly);
var annotations = configuration.Context.Annotations;
var action = annotations.GetAction (assembly);
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
rolfbjarne marked this conversation as resolved.
}
AssemblySaved?.Invoke (assembly);
if (action == AssemblyAction.Copy) {
#if !ASSEMBLY_PREPARER
// Preserve TypeForwardedTo which would the linker sweep otherwise
Expand Down