Apple platform
iOS, macOS, Mac Catalyst, tvOS
Framework version
net10.0-, net11.0-
Affected platform version
.NET 10 / .NET 11 (all current)
Description
.NET for iOS/tvOS/macOS/Mac Catalyst bakes the runtimeOptions.configProperties from *.runtimeconfig.json into the app at build time and hands them to the runtime directly (xamarin_bridge_vm_initialize, via the _CreateRuntimeConfiguration target in dotnet/targets/Xamarin.Shared.Sdk.targets). Because we do not go through hostfxr, the companion *.runtimeconfig.dev.json file is never merged on top of *.runtimeconfig.json.
Normally hostfxr layers *.runtimeconfig.dev.json over *.runtimeconfig.json at startup. The .NET SDK writes certain Debug-only switches (e.g. Hot Reload) into the dev file only, for example:
System.StartupHookProvider.IsSupported
System.Reflection.Metadata.MetadataUpdater.IsSupported
Since we bypass hostfxr, these switches never reach the runtime in our apps.
Root cause: the _CreateRuntimeConfiguration target invokes the shared RuntimeConfigParserTask (from dotnet/runtime, src/tasks/MonoTargetsTasks/RuntimeConfigParser) with a single RuntimeConfigFile input. That task reads only runtimeOptions.configProperties from the one file and has no notion of the dev file, so the dev values are dropped.
This is the same class of bug that dotnet/android fixed in dotnet/android#12249 (they have their own parser, RuntimePropertiesParser.cs, and layered the dev file's configProperties on top, with dev values winning — matching hostfxr).
Steps to Reproduce
- Create a Debug build of an iOS/macOS/etc. app.
- Set
System.StartupHookProvider.IsSupported / MetadataUpdater.IsSupported expectations, or simply read them at runtime:
AppContext.TryGetSwitch ("System.StartupHookProvider.IsSupported", out var startupHook);
AppContext.TryGetSwitch ("System.Reflection.Metadata.MetadataUpdater.IsSupported", out var metadataUpdater);
- Observe that the switches written by the SDK into
*.runtimeconfig.dev.json are not reflected at runtime (they don't get baked into runtimeconfig.bin).
Did you find any workaround?
Not a clean one. Setting the properties in the project so they land in *.runtimeconfig.json (rather than the dev file) would work around it, but that changes Release behavior too.
Relevant log output
No response
Fix direction
The proper fix is to merge the configProperties from $(ProjectRuntimeConfigDevFilePath) on top of those from $(ProjectRuntimeConfigFilePath) before/while producing runtimeconfig.bin, with dev values winning (matching hostfxr). Since we use the shared RuntimeConfigParserTask, the cleanest option is either:
- an upstream change to
RuntimeConfigParserTask to accept an optional dev-file input (benefits wasm and the other Mono-based mobile workloads too), or
- a macios-local merge step that produces a merged
runtimeconfig.json fed into the existing task.
Apple platform
iOS, macOS, Mac Catalyst, tvOS
Framework version
net10.0-, net11.0-
Affected platform version
.NET 10 / .NET 11 (all current)
Description
.NET for iOS/tvOS/macOS/Mac Catalyst bakes the
runtimeOptions.configPropertiesfrom*.runtimeconfig.jsoninto the app at build time and hands them to the runtime directly (xamarin_bridge_vm_initialize, via the_CreateRuntimeConfigurationtarget indotnet/targets/Xamarin.Shared.Sdk.targets). Because we do not go throughhostfxr, the companion*.runtimeconfig.dev.jsonfile is never merged on top of*.runtimeconfig.json.Normally
hostfxrlayers*.runtimeconfig.dev.jsonover*.runtimeconfig.jsonat startup. The .NET SDK writes certain Debug-only switches (e.g. Hot Reload) into the dev file only, for example:System.StartupHookProvider.IsSupportedSystem.Reflection.Metadata.MetadataUpdater.IsSupportedSince we bypass
hostfxr, these switches never reach the runtime in our apps.Root cause: the
_CreateRuntimeConfigurationtarget invokes the sharedRuntimeConfigParserTask(from dotnet/runtime,src/tasks/MonoTargetsTasks/RuntimeConfigParser) with a singleRuntimeConfigFileinput. That task reads onlyruntimeOptions.configPropertiesfrom the one file and has no notion of the dev file, so the dev values are dropped.This is the same class of bug that dotnet/android fixed in dotnet/android#12249 (they have their own parser,
RuntimePropertiesParser.cs, and layered the dev file'sconfigPropertieson top, with dev values winning — matchinghostfxr).Steps to Reproduce
System.StartupHookProvider.IsSupported/MetadataUpdater.IsSupportedexpectations, or simply read them at runtime:*.runtimeconfig.dev.jsonare not reflected at runtime (they don't get baked intoruntimeconfig.bin).Did you find any workaround?
Not a clean one. Setting the properties in the project so they land in
*.runtimeconfig.json(rather than the dev file) would work around it, but that changes Release behavior too.Relevant log output
No response
Fix direction
The proper fix is to merge the
configPropertiesfrom$(ProjectRuntimeConfigDevFilePath)on top of those from$(ProjectRuntimeConfigFilePath)before/while producingruntimeconfig.bin, with dev values winning (matchinghostfxr). Since we use the sharedRuntimeConfigParserTask, the cleanest option is either:RuntimeConfigParserTaskto accept an optional dev-file input (benefits wasm and the other Mono-based mobile workloads too), orruntimeconfig.jsonfed into the existing task.