diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs index 884f95521db..9865283360a 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs @@ -121,9 +121,9 @@ void MarkFrameworkArrayEntryPeers (IEnumerable peers) if (frameworkAssemblyNames.Contains (index.AssemblyName)) { continue; } - foreach (var frameworkAssemblyName in frameworkAssemblyNames) { - if (index.ReferencedTypeNamesByAssembly.TryGetValue (frameworkAssemblyName, out var typeNames)) { - referencedFrameworkTypes.UnionWith (typeNames); + foreach (var referencedTypeNames in index.ReferencedTypeNamesByAssembly) { + if (frameworkAssemblyNames.Contains (referencedTypeNames.Key)) { + referencedFrameworkTypes.UnionWith (referencedTypeNames.Value); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targets index d0ca9742e30..4246a9e95e0 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targets @@ -5,6 +5,11 @@ <_TrimmableRuntimeProviderJavaName Condition=" '$(_TrimmableRuntimeProviderJavaName)' == '' ">mono.MonoRuntimeProvider + + + + + <_TrimmableRuntimeProviderJavaName Condition=" '$(_TrimmableRuntimeProviderJavaName)' == '' ">net.dot.jni.nativeaot.NativeAotRuntimeProvider + r8 + d8 + True + True + true + <_UseTrimmableNativeAotProguardConfiguration>true - <_TrimmableTypeMapUnmanagedEntryPointAssemblies Include="@(_TrimmableTypeMapIlcAssemblies)" /> - <_TrimmableTypeMapUnmanagedEntryPointAssemblies Remove="$(_TypeMapOutputDirectory)$(_TypeMapAssemblyName).dll" /> - <_TrimmableTypeMapUnmanagedEntryPointAssemblies Remove="$(_TypeMapOutputDirectory)_Java.Interop.TypeMap.dll;$(_TypeMapOutputDirectory)_Mono.Android.TypeMap.dll" /> - <_TrimmableTypeMapUnmanagedEntryPointAssemblies Remove="@(_TrimmableTypeMapFrameworkIlcAssemblies)" /> + <_TrimmableTypeMapUnmanagedEntryPointAssemblyNames Include="@(_TrimmableTypeMapIlcAssemblies->'%(Filename)')" /> + <_TrimmableTypeMapUnmanagedEntryPointAssemblyNames Remove="$(_TypeMapAssemblyName)" /> + <_TrimmableTypeMapUnmanagedEntryPointAssemblyNames Remove="_Java.Interop.TypeMap;_Mono.Android.TypeMap" /> + <_TrimmableTypeMapUnmanagedEntryPointAssemblyNames Remove="@(_TrimmableTypeMapFrameworkIlcAssemblyNames)" /> - + + + + + + + + diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets index b040052577f..175c0496618 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets @@ -125,6 +125,13 @@ + + diff --git a/src/Xamarin.Android.Build.Tasks/Resources/proguard_trimmable_nativeaot.cfg b/src/Xamarin.Android.Build.Tasks/Resources/proguard_trimmable_nativeaot.cfg new file mode 100644 index 00000000000..6d54d809ae2 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Resources/proguard_trimmable_nativeaot.cfg @@ -0,0 +1,15 @@ +# Xamarin.Android NativeAOT trimmable typemap configuration. + +-dontobfuscate + +-keep class net.dot.jni.** { *; (...); } +-keep class net.dot.android.crypto.** { *; (...); } + +-keepclassmembers class * extends android.view.View { + *** set*(...); +} + +-keepclassmembers class * extends android.view.View { + (android.content.Context,android.util.AttributeSet); + (android.content.Context,android.util.AttributeSet,int); +} diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeAotProguardConfiguration.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeAotProguardConfiguration.cs new file mode 100644 index 00000000000..a803edf5fed --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeAotProguardConfiguration.cs @@ -0,0 +1,104 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using Microsoft.Build.Framework; +using Microsoft.Android.Build.Tasks; + +namespace Xamarin.Android.Tasks +{ + public class GenerateNativeAotProguardConfiguration : AndroidTask + { + public override string TaskPrefix => "GNAPC"; + + [Required] + public string NativeAotDgmlFile { get; set; } = ""; + + [Required] + public string AcwMapFile { get; set; } = ""; + + [Required] + public string OutputFile { get; set; } = ""; + + public override bool RunTask () + { + var dir = Path.GetDirectoryName (OutputFile); + if (!dir.IsNullOrEmpty () && !Directory.Exists (dir)) { + Directory.CreateDirectory (dir); + } + + if (!File.Exists (NativeAotDgmlFile)) { + Log.LogError ("NativeAOT DGML file '{0}' was not found.", NativeAotDgmlFile); + return false; + } + if (!File.Exists (AcwMapFile)) { + Log.LogError ("ACW map file '{0}' was not found.", AcwMapFile); + return false; + } + + var retainedTypeKeys = LoadRetainedTypeKeysFromDgml (); + var javaTypes = LoadJavaTypesFromAcwMap (retainedTypeKeys); + + using var writer = File.CreateText (OutputFile); + writer.WriteLine ("# ACWs retained by NativeAOT ILC"); + foreach (var javaTypeName in javaTypes) { + writer.WriteLine ($"-keep class {javaTypeName} {{ *; }}"); + } + + Log.LogMessage (MessageImportance.Low, $"Generated {javaTypes.Count} NativeAOT trimmable typemap ProGuard rules from '{NativeAotDgmlFile}'."); + return !Log.HasLoggedErrors; + } + + List LoadJavaTypesFromAcwMap (HashSet retainedTypeKeys) + { + var javaTypes = new List (); + foreach (var line in File.ReadLines (AcwMapFile)) { + var separator = line.IndexOf (';'); + if (separator <= 0 || separator == line.Length - 1) { + continue; + } + var managedTypeName = line.Substring (0, separator); + var javaTypeName = line.Substring (separator + 1); + if (retainedTypeKeys.Contains (managedTypeName) && !javaTypes.Contains (javaTypeName)) { + javaTypes.Add (javaTypeName); + } + } + return javaTypes; + } + + HashSet LoadRetainedTypeKeysFromDgml () + { + var typeKeys = new HashSet (StringComparer.Ordinal); + using var reader = XmlReader.Create (NativeAotDgmlFile, new XmlReaderSettings { + DtdProcessing = DtdProcessing.Prohibit, + XmlResolver = null, + }); + + while (reader.Read ()) { + if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "Node") { + continue; + } + + var label = reader.GetAttribute ("Label"); + if (label.IsNullOrEmpty () || !label.StartsWith ("Type metadata: [", StringComparison.Ordinal)) { + continue; + } + + var assemblyStart = "Type metadata: [".Length; + var assemblyEnd = label.IndexOf (']', assemblyStart); + if (assemblyEnd < 0 || assemblyEnd == label.Length - 1) { + continue; + } + + var assemblyName = label.Substring (assemblyStart, assemblyEnd - assemblyStart); + var managedTypeName = label.Substring (assemblyEnd + 1); + typeKeys.Add (managedTypeName); + typeKeys.Add ($"{managedTypeName}, {assemblyName}"); + } + + return typeKeys; + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/R8.cs b/src/Xamarin.Android.Build.Tasks/Tasks/R8.cs index fbf66e7bf48..ac91b59d705 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/R8.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/R8.cs @@ -35,6 +35,7 @@ public class R8 : D8 public string? ProguardCommonXamarinConfiguration { get; set; } public string? ProguardMappingFileOutput { get; set; } public string []? ProguardConfigurationFiles { get; set; } + public bool UseTrimmableNativeAotProguardConfiguration { get; set; } protected override string MainClass => "com.android.tools.r8.R8"; @@ -95,7 +96,9 @@ protected override string CreateResponseFile () } if (EnableShrinking) { - if (!AcwMapFile.IsNullOrEmpty ()) { + if (UseTrimmableNativeAotProguardConfiguration && !ProguardGeneratedApplicationConfiguration.IsNullOrEmpty ()) { + File.WriteAllText (ProguardGeneratedApplicationConfiguration, "# ACW keep rules are generated from NativeAOT ILC metadata.\n"); + } else if (!AcwMapFile.IsNullOrEmpty ()) { var acwMap = MonoAndroidHelper.LoadMapFile (BuildEngine4, Path.GetFullPath (AcwMapFile), StringComparer.OrdinalIgnoreCase); var javaTypes = new List (acwMap.Values.Count); foreach (var v in acwMap.Values) { @@ -110,7 +113,11 @@ protected override string CreateResponseFile () } if (!ProguardCommonXamarinConfiguration.IsNullOrWhiteSpace ()) { using (var xamcfg = File.CreateText (ProguardCommonXamarinConfiguration)) { - GetType ().Assembly.GetManifestResourceStream ("proguard_xamarin.cfg").CopyTo (xamcfg.BaseStream); + if (UseTrimmableNativeAotProguardConfiguration) { + GetType ().Assembly.GetManifestResourceStream ("proguard_trimmable_nativeaot.cfg").CopyTo (xamcfg.BaseStream); + } else { + GetType ().Assembly.GetManifestResourceStream ("proguard_xamarin.cfg").CopyTo (xamcfg.BaseStream); + } if (IgnoreWarnings) { xamcfg.WriteLine ("-ignorewarnings"); } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets index 0796e933155..40b45798c35 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets @@ -74,6 +74,7 @@ Copyright (C) 2018 Xamarin. All rights reserved. ProguardGeneratedApplicationConfiguration="$(IntermediateOutputPath)proguard\proguard_project_primary.cfg" ProguardMappingFileOutput="$(AndroidProguardMappingFile)" ProguardConfigurationFiles="@(_ProguardConfiguration)" + UseTrimmableNativeAotProguardConfiguration="$(_UseTrimmableNativeAotProguardConfiguration)" EnableShrinking="$(_R8EnableShrinking)" EnableMultiDex="$(AndroidEnableMultiDex)" MultiDexMainDexListFile="$(_AndroidMainDexListFile)"