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
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ void MarkFrameworkArrayEntryPeers (IEnumerable<JavaPeerInfo> 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);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
<_TrimmableRuntimeProviderJavaName Condition=" '$(_TrimmableRuntimeProviderJavaName)' == '' ">mono.MonoRuntimeProvider</_TrimmableRuntimeProviderJavaName>
</PropertyGroup>

<!-- TODO: Generate a focused ProGuard configuration from the ILLink-trimmed typemap. -->
<Target Name="_GenerateTrimmableTypeMapProguardConfiguration"
BeforeTargets="_CompileToDalvik"
Condition=" '$(PublishTrimmed)' == 'true' and '$(_ProguardProjectConfiguration)' != '' " />

<!-- Add TypeMap DLLs to ILLink input. ILLink natively understands TypeMapAttribute<T>. -->
<Target Name="_AddTrimmableTypeMapToLinker"
BeforeTargets="PrepareForILLink;_RunILLink"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@
Adds generated typemap assemblies to ILC inputs. -->
<Project>

<UsingTask TaskName="Xamarin.Android.Tasks.GenerateNativeAotProguardConfiguration" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />

<PropertyGroup>
<_TrimmableRuntimeProviderJavaName Condition=" '$(_TrimmableRuntimeProviderJavaName)' == '' ">net.dot.jni.nativeaot.NativeAotRuntimeProvider</_TrimmableRuntimeProviderJavaName>
<AndroidLinkTool Condition=" '$(AndroidLinkTool)' == '' ">r8</AndroidLinkTool>
<AndroidDexTool Condition=" '$(AndroidLinkTool)' == 'r8' ">d8</AndroidDexTool>
<AndroidEnableProguard Condition=" '$(AndroidLinkTool)' != '' ">True</AndroidEnableProguard>
<AndroidCreateProguardMappingFile Condition=" '$(AndroidCreateProguardMappingFile)' == '' and '$(AndroidLinkTool)' == 'r8' ">True</AndroidCreateProguardMappingFile>
<IlcGenerateDgmlFile Condition=" '$(AndroidLinkTool)' != '' and '$(IlcGenerateDgmlFile)' == '' ">true</IlcGenerateDgmlFile>
<_UseTrimmableNativeAotProguardConfiguration>true</_UseTrimmableNativeAotProguardConfiguration>
</PropertyGroup>

<!-- Before ILC input computation, add generated assemblies to ILC inputs.
Expand All @@ -14,21 +22,36 @@
DependsOnTargets="_ReadGeneratedTrimmableTypeMapAssemblies">
<ItemGroup>
<_TrimmableTypeMapIlcAssemblies Include="@(_GeneratedTypeMapAssembliesFromList)" />
<_TrimmableTypeMapFrameworkIlcAssemblies Include="@(ResolvedFrameworkAssemblies->'$(_TypeMapOutputDirectory)_%(Filename).TypeMap.dll')" />
<_TrimmableTypeMapFrameworkIlcAssemblies Include="@(PrivateSdkAssemblies->'$(_TypeMapOutputDirectory)_%(Filename).TypeMap.dll')" />
<_TrimmableTypeMapFrameworkIlcAssemblies Include="@(ReferencePath->'$(_TypeMapOutputDirectory)_%(Filename).TypeMap.dll')"
<_TrimmableTypeMapFrameworkIlcAssemblyNames Include="@(ResolvedFrameworkAssemblies->'_%(Filename).TypeMap')" />
<_TrimmableTypeMapFrameworkIlcAssemblyNames Include="@(PrivateSdkAssemblies->'_%(Filename).TypeMap')" />
<_TrimmableTypeMapFrameworkIlcAssemblyNames Include="@(ReferencePath->'_%(Filename).TypeMap')"
Condition=" '%(ReferencePath.FrameworkAssembly)' == 'true' " />
<!-- The root assembly (_Microsoft.Android.TypeMaps) only contains TypeMapLoader.Initialize()
and assembly-level attributes — no types that need vtable generation.
Framework per-assembly typemap DLLs must remain IlcReference inputs so ILC can read
their conditional TypeMap attributes, but they must not be unmanaged-entrypoint roots. -->
<_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)" />
<IlcReference Include="@(_TrimmableTypeMapIlcAssemblies)" />
<IlcArg Include="--typemap-entry-assembly:$(_TypeMapAssemblyName)" />
<UnmanagedEntryPointsAssembly Include="@(_TrimmableTypeMapUnmanagedEntryPointAssemblies->'%(Filename)')" />
<UnmanagedEntryPointsAssembly Include="@(_TrimmableTypeMapUnmanagedEntryPointAssemblyNames)" />
</ItemGroup>
</Target>

<Target Name="_GenerateTrimmableTypeMapProguardConfiguration"
AfterTargets="IlcCompile"
BeforeTargets="_CompileToDalvik"
Condition=" '$(PublishTrimmed)' == 'true' and '$(_ProguardProjectConfiguration)' != '' "
Inputs="$(NativeIntermediateOutputPath)$(TargetName).scan.dgml.xml;$(IntermediateOutputPath)acw-map.txt"
Outputs="$(_ProguardProjectConfiguration)">
<GenerateNativeAotProguardConfiguration
NativeAotDgmlFile="$(NativeIntermediateOutputPath)$(TargetName).scan.dgml.xml"
AcwMapFile="$(IntermediateOutputPath)acw-map.txt"
OutputFile="$(_ProguardProjectConfiguration)" />
<ItemGroup>
<FileWrites Include="$(_ProguardProjectConfiguration)" />
</ItemGroup>
</Target>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@
<Target Name="_ReadGeneratedTrimmableTypeMapAssemblies"
Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' "
DependsOnTargets="_GenerateTrimmableTypeMap">
<MakeDir
Directories="$(_TypeMapOutputDirectory)"
Condition=" '@(ReferencePath->Count())' == '0' and '$(_OuterIntermediateOutputPath)' == '' and !Exists('$(_TypeMapAssembliesListFile)')" />
<Touch
Files="$(_TypeMapAssembliesListFile)"
AlwaysCreate="true"
Condition=" '@(ReferencePath->Count())' == '0' and '$(_OuterIntermediateOutputPath)' == '' and !Exists('$(_TypeMapAssembliesListFile)')" />
<Error
Condition=" !Exists('$(_TypeMapAssembliesListFile)') "
Text="Trimmable typemap assembly list '$(_TypeMapAssembliesListFile)' was not found." />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Xamarin.Android NativeAOT trimmable typemap configuration.

-dontobfuscate

-keep class net.dot.jni.** { *; <init>(...); }
-keep class net.dot.android.crypto.** { *; <init>(...); }

-keepclassmembers class * extends android.view.View {
*** set*(...);
}

-keepclassmembers class * extends android.view.View {
<init>(android.content.Context,android.util.AttributeSet);
<init>(android.content.Context,android.util.AttributeSet,int);
}
Original file line number Diff line number Diff line change
@@ -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<string> LoadJavaTypesFromAcwMap (HashSet<string> retainedTypeKeys)
{
var javaTypes = new List<string> ();
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<string> LoadRetainedTypeKeysFromDgml ()
{
var typeKeys = new HashSet<string> (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;
}
}
}
11 changes: 9 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/R8.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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<string> (acwMap.Values.Count);
foreach (var v in acwMap.Values) {
Expand All @@ -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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down