Skip to content

Commit a022f5d

Browse files
[One .NET] *.runtimeconfig.json support (#6009)
Context: https://github.com/dotnet/runtime/blob/d95bfea59ed7d19e9b1db096c9d332005989296b/docs/design/mono/mobile-runtimeconfig-json.md .NET allows including a [`runtimeconfig.json`][0] file in an application, which can be used to control runtime configuration options, provide "backing data" to [`AppContext.GetData()`][1], control configuration values, and more. Update the Xamarin.Android-for-.NET 6 build process to call the new [`<RuntimeConfigParser/>` task][2], which will process the `runtimeconfig.json` files into an efficient binary blob, which will be added to the `.apk` as `assemblies/rc.bin`. During process startup, `assemblies/rc.bin` will be proved to `monovm_runtimeconfig_initialize()`, which will parse `rc.bin`. Certain `runtimeconfig.json` properties are *reserved*: if they're specified within `runtimeconfig.json`, then the `<RuntimeConfigParser/>` task will throw an `ArgumentException`, and the build will fail. The reserved properties are controlled by the `@(_RuntimeConfigReservedProperties)` item group, and are reserved because the Xamarin.Android runtime needs to control the value of these properties for proper app execution, e.g. `PINVOKE_OVERRIDE` (cf84e1b, 0cd890b). Co-authored-by: Marek Habersack <[email protected]> [0]: https://docs.microsoft.com/en-us/dotnet/core/run-time-config/#runtimeconfigjson [1]: https://docs.microsoft.com/en-us/dotnet/api/system.appcontext.getdata?view=net-5.0 [2]: https://github.com/dotnet/runtime/blob/01b7e73cd378145264a7cb7a09365b41ed42b240/src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs
1 parent 5da92bd commit a022f5d

20 files changed

+195
-10
lines changed

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ This file is imported *after* the Microsoft.NET.Sdk/Sdk.targets.
2323
<Import Project="Microsoft.Android.Sdk.ILLink.targets" />
2424
<Import Project="Microsoft.Android.Sdk.ProjectCapabilities.targets" />
2525
<Import Project="Microsoft.Android.Sdk.Publish.targets" />
26+
<Import Project="Microsoft.Android.Sdk.RuntimeConfig.targets" />
2627
<Import Project="Microsoft.Android.Sdk.Tooling.targets" />
2728
</Project>

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
<AndroidHttpClientHandlerType Condition=" '$(AndroidHttpClientHandlerType)' == '' ">Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
1313
<AndroidUseIntermediateDesignerFile Condition=" '$(AndroidUseIntermediateDesignerFile)' == '' ">true</AndroidUseIntermediateDesignerFile>
1414
<GenerateDependencyFile Condition=" '$(GenerateDependencyFile)' == '' ">false</GenerateDependencyFile>
15-
<GenerateRuntimeConfigurationFiles Condition=" '$(GenerateRuntimeConfigurationFiles)' == '' ">false</GenerateRuntimeConfigurationFiles>
1615
<CopyLocalLockFileAssemblies Condition=" '$(CopyLocalLockFileAssemblies)' == '' ">false</CopyLocalLockFileAssemblies>
1716
<ComputeNETCoreBuildOutputFiles Condition=" '$(ComputeNETCoreBuildOutputFiles)' == '' ">false</ComputeNETCoreBuildOutputFiles>
1817
<!-- jar2xml is not supported -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!--
2+
***********************************************************************************************
3+
Microsoft.Android.Sdk.RuntimeConfig.targets
4+
5+
MSBuild logic related to *.runtimeconfig.json files.
6+
7+
See: https://github.com/dotnet/runtime/blob/b13715b6984889a709ba29ea8a1961db469f8805/src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/README.md
8+
9+
***********************************************************************************************
10+
-->
11+
<Project>
12+
13+
<Import Sdk="Microsoft.NET.Runtime.RuntimeConfigParser.Task" Project="Sdk.props" />
14+
15+
<PropertyGroup>
16+
<!-- HACK: workaround https://github.com/dotnet/runtime/issues/53811 -->
17+
<RuntimeConfigParserTasksAssemblyPath>$([System.IO.Path]::GetDirectoryName($(RuntimeConfigParserTasksAssemblyPath)))/net6.0/$([System.IO.Path]::GetFileName($(RuntimeConfigParserTasksAssemblyPath)))</RuntimeConfigParserTasksAssemblyPath>
18+
<_BinaryRuntimeConfigPath>$(IntermediateOutputPath)$(ProjectRuntimeConfigFileName).bin</_BinaryRuntimeConfigPath>
19+
</PropertyGroup>
20+
21+
<ItemGroup>
22+
<!--
23+
See: https://docs.microsoft.com/en-us/dotnet/core/tutorials/netcore-hosting#step-3-%2D-prepare-runtime-properties
24+
These properties shouldn't be used in Xamarin.Android apps as there are no directories the runtime can search,
25+
everything related to assemblies or shared libraries must go through the Xamarin.Android native runtime.
26+
-->
27+
<_RuntimeConfigReservedProperties Include="TRUSTED_PLATFORM_ASSEMBLIES"/>
28+
<_RuntimeConfigReservedProperties Include="APP_PATHS"/>
29+
<_RuntimeConfigReservedProperties Include="APP_NI_PATHS"/>
30+
<_RuntimeConfigReservedProperties Include="NATIVE_DLL_SEARCH_DIRECTORIES"/>
31+
<_RuntimeConfigReservedProperties Include="PLATFORM_RESOURCE_ROOTS"/>
32+
<_RuntimeConfigReservedProperties Include="PINVOKE_OVERRIDE"/>
33+
</ItemGroup>
34+
35+
<Target Name="_ParseRuntimeConfigFiles"
36+
AfterTargets="GenerateBuildRuntimeConfigurationFiles"
37+
Condition=" '$(GenerateRuntimeConfigurationFiles)' == 'true' "
38+
Inputs="$(ProjectRuntimeConfigFilePath)"
39+
Outputs="$(_BinaryRuntimeConfigPath)">
40+
<RuntimeConfigParserTask
41+
RuntimeConfigFile="$(ProjectRuntimeConfigFilePath)"
42+
OutputFile="$(_BinaryRuntimeConfigPath)"
43+
RuntimeConfigReservedProperties="@(_RuntimeConfigReservedProperties)"
44+
/>
45+
<ItemGroup>
46+
<FileWrites Include="$(_BinaryRuntimeConfigPath)" />
47+
</ItemGroup>
48+
</Target>
49+
50+
</Project>

src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ public class BuildApk : AndroidTask
8989

9090
public string CheckedBuild { get; set; }
9191

92+
public string RuntimeConfigBinFilePath { get; set; }
93+
9294
[Required]
9395
public string ProjectFullPath { get; set; }
9496

@@ -190,6 +192,10 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
190192
}
191193
}
192194

195+
if (!String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath)) {
196+
AddFileToArchiveIfNewer (apk, RuntimeConfigBinFilePath, $"{AssembliesPath}rc.bin", compressionMethod: UncompressedMethod);
197+
}
198+
193199
int count = 0;
194200
foreach (var file in files) {
195201
var item = Path.Combine (file.archivePath.Replace (Path.DirectorySeparatorChar, '/'));

src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public class GeneratePackageManagerJava : AndroidTask
5959
[Required]
6060
public bool InstantRunEnabled { get; set; }
6161

62+
public string RuntimeConfigBinFilePath { get; set; }
6263
public string BoundExceptionType { get; set; }
6364

6465
public string PackageNamingPolicy { get; set; }
@@ -261,6 +262,7 @@ void AddEnvironment ()
261262
throw new InvalidOperationException ($"Unsupported BoundExceptionType value '{BoundExceptionType}'");
262263
}
263264

265+
bool haveRuntimeConfigBlob = !String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath);
264266
var appConfState = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<ApplicationConfigTaskState> (ApplicationConfigTaskState.RegisterTaskObjectKey, RegisteredTaskObjectLifetime.Build);
265267
foreach (string abi in SupportedAbis) {
266268
NativeAssemblerTargetProvider asmTargetProvider = GetAssemblyTargetProvider (abi);
@@ -279,6 +281,7 @@ void AddEnvironment ()
279281
BoundExceptionType = boundExceptionType,
280282
InstantRunEnabled = InstantRunEnabled,
281283
JniAddNativeMethodRegistrationAttributePresent = appConfState != null ? appConfState.JniAddNativeMethodRegistrationAttributePresent : false,
284+
HaveRuntimeConfigBlob = haveRuntimeConfigBlob,
282285
};
283286

284287
using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) {

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public void CheckIncludedAssemblies ()
8686
new [] {
8787
"Java.Interop.dll",
8888
"Mono.Android.dll",
89+
"rc.bin",
8990
"System.Private.CoreLib.dll",
9091
"System.Runtime.dll",
9192
"System.Linq.dll",

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ public sealed class ApplicationConfig
2525
public bool broken_exception_transitions;
2626
public bool instant_run_enabled;
2727
public bool jni_add_native_method_registration_attribute_present;
28+
public bool have_runtime_config_blob;
2829
public byte bound_stream_io_exception_type;
2930
public uint package_naming_policy;
3031
public uint environment_variable_count;
3132
public uint system_property_count;
3233
public string android_package_name;
3334
};
34-
const uint ApplicationConfigFieldCount = 12;
35+
const uint ApplicationConfigFieldCount = 13;
3536

3637
static readonly object ndkInitLock = new object ();
3738
static readonly char[] readElfFieldSeparator = new [] { ' ', '\t' };
@@ -150,27 +151,32 @@ static ApplicationConfig ReadApplicationConfig (string envFile)
150151
ret.jni_add_native_method_registration_attribute_present = ConvertFieldToBool ("jni_add_native_method_registration_attribute_present", envFile, i, field [1]);
151152
break;
152153

153-
case 7: // bound_stream_io_exception_type: byte / .byte
154+
case 7: // have_runtime_config_blob: bool / .byte
155+
AssertFieldType (envFile, ".byte", field [0], i);
156+
ret.have_runtime_config_blob = ConvertFieldToBool ("have_runtime_config_blob", envFile, i, field [1]);
157+
break;
158+
159+
case 8: // bound_stream_io_exception_type: byte / .byte
154160
AssertFieldType (envFile, ".byte", field [0], i);
155161
ret.bound_stream_io_exception_type = ConvertFieldToByte ("bound_stream_io_exception_type", envFile, i, field [1]);
156162
break;
157163

158-
case 8: // package_naming_policy: uint32_t / .word | .long
164+
case 9: // package_naming_policy: uint32_t / .word | .long
159165
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile}:{i}': {field [0]}");
160166
ret.package_naming_policy = ConvertFieldToUInt32 ("package_naming_policy", envFile, i, field [1]);
161167
break;
162168

163-
case 9: // environment_variable_count: uint32_t / .word | .long
169+
case 10: // environment_variable_count: uint32_t / .word | .long
164170
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile}:{i}': {field [0]}");
165171
ret.environment_variable_count = ConvertFieldToUInt32 ("environment_variable_count", envFile, i, field [1]);
166172
break;
167173

168-
case 10: // system_property_count: uint32_t / .word | .long
174+
case 11: // system_property_count: uint32_t / .word | .long
169175
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile}:{i}': {field [0]}");
170176
ret.system_property_count = ConvertFieldToUInt32 ("system_property_count", envFile, i, field [1]);
171177
break;
172178

173-
case 11: // android_package_name: string / [pointer type]
179+
case 12: // android_package_name: string / [pointer type]
174180
Assert.IsTrue (expectedPointerTypes.Contains (field [0]), $"Unexpected pointer field type in '{envFile}:{i}': {field [0]}");
175181
pointers.Add (field [1].Trim ());
176182
break;

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease)
412412
"es",
413413
$"{proj.ProjectName}.dll",
414414
$"{proj.ProjectName}.pdb",
415+
$"{proj.ProjectName}.runtimeconfig.json",
415416
$"{proj.ProjectName}.xml",
416417
};
417418
CollectionAssert.AreEqual (expectedFiles, files, $"Expected: {string.Join (";", expectedFiles)}\n Found: {string.Join (";", files)}");
@@ -574,7 +575,7 @@ public abstract class Foo<TVirtualView, TNativeView> : AbstractViewHandler<TVirt
574575
where TNativeView : Android.Views.View
575576
#else
576577
where TNativeView : class
577-
#endif
578+
#endif
578579
{
579580
protected Foo (PropertyMapper mapper) : base(mapper)
580581
{

src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class ApplicationConfigNativeAssemblyGenerator : NativeAssemblyGenerator
2222
public global::Android.Runtime.BoundExceptionType BoundExceptionType { get; set; }
2323
public bool InstantRunEnabled { get; set; }
2424
public bool JniAddNativeMethodRegistrationAttributePresent { get; set; }
25+
public bool HaveRuntimeConfigBlob { get; set; }
2526

2627
public PackageNamingPolicy PackageNamingPolicy { get; set; }
2728

@@ -70,6 +71,9 @@ protected override void WriteSymbols (StreamWriter output)
7071
WriteCommentLine (output, "jni_add_native_method_registration_attribute_present");
7172
size += WriteData (output, JniAddNativeMethodRegistrationAttributePresent);
7273

74+
WriteCommentLine (output, "have_runtime_config_blob");
75+
size += WriteData (output, HaveRuntimeConfigBlob);
76+
7377
WriteCommentLine (output, "bound_exception_type");
7478
size += WriteData (output, (byte)BoundExceptionType);
7579

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,7 @@ because xbuild doesn't support framework reference assemblies.
15671567
PackageNamingPolicy="$(AndroidPackageNamingPolicy)"
15681568
BoundExceptionType="$(AndroidBoundExceptionType)"
15691569
InstantRunEnabled="$(_InstantRunEnabled)"
1570+
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
15701571
>
15711572
<Output TaskParameter="BuildId" PropertyName="_XamarinBuildId" />
15721573
</GeneratePackageManagerJava>
@@ -2036,7 +2037,8 @@ because xbuild doesn't support framework reference assemblies.
20362037
UncompressedFileExtensions="$(AndroidStoreUncompressedFileExtensions)"
20372038
ProjectFullPath="$(MSBuildProjectFullPath)"
20382039
IncludeWrapSh="$(AndroidIncludeWrapSh)"
2039-
CheckedBuild="$(_AndroidCheckedBuild)">
2040+
CheckedBuild="$(_AndroidCheckedBuild)"
2041+
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)">
20402042
<Output TaskParameter="OutputFiles" ItemName="ApkFiles" />
20412043
</BuildApk>
20422044
<BuildBaseAppBundle
@@ -2067,7 +2069,8 @@ because xbuild doesn't support framework reference assemblies.
20672069
UncompressedFileExtensions="$(AndroidStoreUncompressedFileExtensions)"
20682070
ProjectFullPath="$(MSBuildProjectFullPath)"
20692071
IncludeWrapSh="$(AndroidIncludeWrapSh)"
2070-
CheckedBuild="$(_AndroidCheckedBuild)">
2072+
CheckedBuild="$(_AndroidCheckedBuild)"
2073+
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)">
20712074
<Output TaskParameter="OutputFiles" ItemName="BaseZipFile" />
20722075
</BuildBaseAppBundle>
20732076
<BuildAppBundle

0 commit comments

Comments
 (0)