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..706a73ae31e 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 @@ -25,66 +25,39 @@ - - + + - <_TrimmableTypeMapAbi Include="@(_BuildTargetAbis)" /> - <_TrimmableTypeMapAbi Update="@(_TrimmableTypeMapAbi)" Condition=" '%(_TrimmableTypeMapAbi.Identity)' == 'arm64-v8a' "> - android-arm64 - - <_TrimmableTypeMapAbi Update="@(_TrimmableTypeMapAbi)" Condition=" '%(_TrimmableTypeMapAbi.Identity)' == 'armeabi-v7a' "> - android-arm - - <_TrimmableTypeMapAbi Update="@(_TrimmableTypeMapAbi)" Condition=" '%(_TrimmableTypeMapAbi.Identity)' == 'x86_64' "> - android-x64 - - <_TrimmableTypeMapAbi Update="@(_TrimmableTypeMapAbi)" Condition=" '%(_TrimmableTypeMapAbi.Identity)' == 'x86' "> - android-x86 - + <_TrimmableTypeMapLinkedAssemblies Include="$(IntermediateOutputPath)linked\_*.TypeMap.dll;$(IntermediateOutputPath)linked\_Microsoft.Android.TypeMap*.dll" /> + <_TrimmableTypeMapAssembliesToPublish Include="@(_TrimmableTypeMapLinkedAssemblies)" /> + <_TrimmableTypeMapAssembliesToPublish Include="@(_GeneratedTypeMapAssembliesFromList)" Condition=" '@(_TrimmableTypeMapLinkedAssemblies->Count())' == '0' " /> + + %(Filename)%(Extension) + PreserveNewest + true + $(RuntimeIdentifier) + + - - - <_LinkedTypeMapDlls Include="$(IntermediateOutputPath)%(_TrimmableTypeMapAbi.RuntimeIdentifier)/linked/_*.TypeMap.dll;$(IntermediateOutputPath)%(_TrimmableTypeMapAbi.RuntimeIdentifier)/linked/_Microsoft.Android.TypeMap*.dll"> - %(_TrimmableTypeMapAbi.Identity) - %(_TrimmableTypeMapAbi.RuntimeIdentifier) - %(_TrimmableTypeMapAbi.Identity)/%(_LinkedTypeMapDlls.Filename)%(_LinkedTypeMapDlls.Extension) - %(_TrimmableTypeMapAbi.Identity)/ - - - - <_BuildApkResolvedUserAssemblies Include="@(_LinkedTypeMapDlls)"> - %(_LinkedTypeMapDlls.Abi) - %(_LinkedTypeMapDlls.RuntimeIdentifier) - %(_LinkedTypeMapDlls.DestinationSubPath) - %(_LinkedTypeMapDlls.DestinationSubDirectory) - - - - - <_TypeMapDlls Include="$(_TypeMapOutputDirectory)*.dll"> - %(_TrimmableTypeMapAbi.Identity) - %(_TrimmableTypeMapAbi.RuntimeIdentifier) - %(_TrimmableTypeMapAbi.Identity)/%(_TypeMapDlls.Filename)%(_TypeMapDlls.Extension) - %(_TrimmableTypeMapAbi.Identity)/ - - - - <_BuildApkResolvedUserAssemblies Include="@(_TypeMapDlls)"> - %(_TypeMapDlls.Abi) - %(_TypeMapDlls.RuntimeIdentifier) - %(_TypeMapDlls.DestinationSubPath) - %(_TypeMapDlls.DestinationSubDirectory) - - + - <_LinkedTypeMapDlls Remove="@(_LinkedTypeMapDlls)" /> - <_TypeMapDlls Remove="@(_TypeMapDlls)" /> - <_TrimmableTypeMapAbi Remove="@(_TrimmableTypeMapAbi)" /> + <_TrimmableTypeMapLinkedAssemblies Include="$(IntermediateOutputPath)linked\_*.TypeMap.dll;$(IntermediateOutputPath)linked\_Microsoft.Android.TypeMap*.dll" /> + <_TrimmableTypeMapReadyToRunAssemblies Include="$(IntermediateOutputPath)R2R\_*.TypeMap.dll;$(IntermediateOutputPath)R2R\_Microsoft.Android.TypeMap*.dll" /> + + + + %(Filename)%(Extension) + PreserveNewest + $(RuntimeIdentifier) + diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs index 93195ece435..838a6290bb6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs @@ -5,6 +5,7 @@ using System.Text.RegularExpressions; using NUnit.Framework; using Xamarin.Android.Tasks; +using Xamarin.Android.Tools; using Xamarin.ProjectTools; namespace Xamarin.Android.Build.Tests { @@ -197,6 +198,62 @@ public void CoreClrTrimmableTypeMap_PackagesJavaProxyThrowable () $"`{dexFile}` should include `android.runtime.JavaProxyThrowable`."); } + [Test] + public void CoreClrTrimmableTypeMap_PackagesReadyToRunTypeMap () + { + if (IgnoreUnsupportedConfiguration (AndroidRuntime.CoreCLR, release: true)) { + return; + } + + var proj = new XamarinAndroidApplicationProject { + IsRelease = true, + }; + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.SetProperty ("_AndroidTypeMapImplementation", "trimmable"); + proj.SetProperty ("RuntimeIdentifier", "android-arm64"); + proj.SetProperty ("AndroidEnableAssemblyCompression", "false"); + + using var builder = CreateApkBuilder (); + Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); + + var r2rTypeMap = builder.Output.GetIntermediaryPath (Path.Combine ("android-arm64", "R2R", "_Microsoft.Android.TypeMaps.dll")); + FileAssert.Exists (r2rTypeMap, "ReadyToRun should compile the generated TypeMap entry assembly."); + using (var r2rStream = File.OpenRead (r2rTypeMap)) { + using var r2rReader = new System.Reflection.PortableExecutable.PEReader (r2rStream); + Assert.IsTrue ( + r2rReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory.Size > 0, + "ReadyToRun output for _Microsoft.Android.TypeMaps.dll should have a managed native header."); + } + + var apk = Path.Combine (Root, builder.ProjectDirectory, proj.OutputPath, "android-arm64", $"{proj.PackageName}-Signed.apk"); + FileAssert.Exists (apk); + + var helper = new ArchiveAssemblyHelper (apk, useAssemblyStores: true); + var packagedTypeMapEntries = helper.ListArchiveContents ("lib/", arch: AndroidTargetArch.Arm64) + .Where (entry => entry.StartsWith ("lib/arm64-v8a/lib__", StringComparison.Ordinal) && + entry.EndsWith (".dll.so", StringComparison.Ordinal) && + !entry.EndsWith (".ni.dll.so", StringComparison.Ordinal) && + entry.Contains ("TypeMap", StringComparison.Ordinal)) + .ToArray (); + Assert.AreEqual ( + packagedTypeMapEntries.Distinct ().Count (), + packagedTypeMapEntries.Length, + "TypeMap assemblies should be packaged only once; do not include both linked IL and ReadyToRun copies."); + Assert.AreEqual ( + 1, + packagedTypeMapEntries.Count (entry => entry == "lib/arm64-v8a/lib__Microsoft.Android.TypeMaps.dll.so"), + "_Microsoft.Android.TypeMaps.dll should be packaged only once."); + + Assert.IsTrue (helper.Exists ("assemblies/arm64-v8a/_Microsoft.Android.TypeMaps.dll"), "_Microsoft.Android.TypeMaps.dll should exist in the APK."); + using (var packagedTypeMap = helper.ReadEntry ("assemblies/arm64-v8a/_Microsoft.Android.TypeMaps.dll", AndroidTargetArch.Arm64)) { + Assert.IsNotNull (packagedTypeMap, "_Microsoft.Android.TypeMaps.dll should be readable from the APK."); + using var packagedReader = new System.Reflection.PortableExecutable.PEReader (packagedTypeMap); + Assert.IsTrue ( + packagedReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory.Size > 0, + "Packaged _Microsoft.Android.TypeMaps.dll should be the ReadyToRun image, not the linked IL image."); + } + } + [Test] public void TrimmableTypeMap_PreserveLists_ArePackagedInSdk () {