diff --git a/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs b/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs index 329ee6aa67a..17bbb17d273 100644 --- a/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs +++ b/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs @@ -336,9 +336,7 @@ static IWebProxy GetDefaultProxy () [DynamicDependency (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof (Xamarin.Android.Net.AndroidMessageHandler))] static object GetHttpMessageHandler () { - // FIXME: https://github.com/xamarin/xamarin-android/issues/8797 - // Note that this is a problem for custom $(AndroidHttpClientHandlerType) or $XA_HTTP_CLIENT_HANDLER_TYPE - [UnconditionalSuppressMessage ("Trimming", "IL2057", Justification = "DynamicDependency should preserve AndroidMessageHandler.")] + [UnconditionalSuppressMessage ("Trimming", "IL2057", Justification = "Rooted by the MSBuild task.")] [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] static Type TypeGetType (string typeName) => Type.GetType (typeName, throwOnError: false); diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index 05c6c56639f..775370ac8d4 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -81,7 +81,8 @@ _ResolveAssemblies MSBuild target. - + <_RIDs Include="$(RuntimeIdentifier)" Condition=" '$(RuntimeIdentifiers)' == '' " /> <_RIDs Include="$(RuntimeIdentifiers)" Condition=" '$(RuntimeIdentifiers)' != '' " /> @@ -96,6 +97,7 @@ _ResolveAssemblies MSBuild target. ;_OuterIntermediateAssembly=@(IntermediateAssembly) ;_OuterOutputPath=$(OutputPath) ;_OuterIntermediateOutputPath=$(IntermediateOutputPath) + ;_AndroidGeneratedRootDescriptor=$(_AndroidGeneratedRootDescriptor) <_AndroidBuildRuntimeIdentifiersInParallel Condition=" '$(_AndroidBuildRuntimeIdentifiersInParallel)' == '' ">true diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets index df4dab646d8..429386f977d 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets @@ -55,6 +55,7 @@ properties that determine build ordering. _SetLatestTargetFrameworkVersion; _GetLibraryImports; _RemoveRegisterAttribute; + _GenerateILLinkXml; _ResolveAssemblies; _ResolveSatellitePaths; _CreatePackageWorkspace; diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets index d9478aed722..36dd8c3f985 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets @@ -9,6 +9,8 @@ This file contains the .NET 5-specific targets to customize ILLink + + + + + + + + + <_AndroidGeneratedRootDescriptor>$(IntermediateOutputPath)net-android-trimmer.xml + + + + diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateILLinkXml.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateILLinkXml.cs new file mode 100644 index 00000000000..263a85833c7 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateILLinkXml.cs @@ -0,0 +1,70 @@ +using System.Xml.Linq; +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Framework; + +namespace Xamarin.Android.Tasks; + +public class GenerateILLinkXml : AndroidTask +{ + public override string TaskPrefix => "GILX"; + + [Required] + public string AndroidHttpClientHandlerType { get; set; } + + [Required] + public string CustomViewMapFile { get; set; } + + [Required] + public string OutputFile { get; set; } + + public override bool RunTask () + { + string assemblyName, typeName; + + var index = AndroidHttpClientHandlerType.IndexOf (','); + if (index != -1) { + typeName = AndroidHttpClientHandlerType.Substring (0, index).Trim (); + assemblyName = AndroidHttpClientHandlerType.Substring (index + 1).Trim (); + } else { + typeName = AndroidHttpClientHandlerType; + assemblyName = "Mono.Android"; + } + + // public parameterless constructors + // example: https://github.com/dotnet/runtime/blob/039d2ecb46687e89337d6d629c295687cfe226be/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml + var ctor = new XElement ("method", new XAttribute("signature", "System.Void .ctor()")); + + XElement linker; + var doc = new XDocument ( + linker = new XElement ("linker", + new XElement ("assembly", + new XAttribute ("fullname", assemblyName), + new XElement ("type", new XAttribute ("fullname", typeName), ctor) + ) + ) + ); + + var customViewMap = MonoAndroidHelper.LoadCustomViewMapFile (BuildEngine4, CustomViewMapFile); + foreach (var pair in customViewMap) { + index = pair.Key.IndexOf (','); + if (index == -1) + continue; + + typeName = pair.Key.Substring (0, index).Trim (); + assemblyName = pair.Key.Substring (index + 1).Trim (); + + linker.Add (new XElement ("assembly", + new XAttribute ("fullname", assemblyName), + new XElement ("type", new XAttribute ("fullname", typeName), ctor) + )); + } + + if (doc.SaveIfChanged (OutputFile)) { + Log.LogDebugMessage ($"Saving {OutputFile}"); + } else { + Log.LogDebugMessage ($"{OutputFile} is unchanged. Skipping."); + } + + return !Log.HasLoggedErrors; + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index 86f2a319cc0..5aebe1b4a6f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -156,14 +156,45 @@ static AssemblyDefinition CreateFauxMonoAndroidAssembly () return assm; } - private void PreserveCustomHttpClientHandler (string handlerType, string handlerAssembly, string testProjectName, string assemblyPath) + private void PreserveCustomHttpClientHandler ( + string handlerType, + string handlerAssembly, + string testProjectName, + string assemblyPath, + TrimMode trimMode) { - var proj = new XamarinAndroidApplicationProject () { IsRelease = true }; + testProjectName += trimMode.ToString (); + + var class_library = new XamarinAndroidLibraryProject { + IsRelease = true, + ProjectName = "MyClassLibrary", + Sources = { + new BuildItem.Source ("MyCustomHandler.cs") { + TextContent = () => """ + class MyCustomHandler : System.Net.Http.HttpMessageHandler + { + protected override Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken) => + throw new NotImplementedException (); + } + """ + } + } + }; + using (var libBuilder = CreateDllBuilder ($"{testProjectName}/{class_library.ProjectName}")) { + Assert.IsTrue (libBuilder.Build (class_library), $"Build for {class_library.ProjectName} should have succeeded."); + } + + var proj = new XamarinAndroidApplicationProject { + ProjectName = "MyApp", + IsRelease = true, + TrimModeRelease = trimMode + }; + proj.AddReference (class_library); proj.AddReferences ("System.Net.Http"); string handlerTypeFullName = string.IsNullOrEmpty(handlerAssembly) ? handlerType : handlerType + ", " + handlerAssembly; proj.SetProperty (proj.ActiveConfigurationProperties, "AndroidHttpClientHandlerType", handlerTypeFullName); proj.MainActivity = proj.DefaultMainActivity.Replace ("base.OnCreate (bundle);", "base.OnCreate (bundle);\nvar client = new System.Net.Http.HttpClient ();"); - using (var b = CreateApkBuilder (testProjectName)) { + using (var b = CreateApkBuilder ($"{testProjectName}/{proj.ProjectName}")) { Assert.IsTrue (b.Build (proj), "Build should have succeeded."); using (var assembly = AssemblyDefinition.ReadAssembly (Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, assemblyPath))) { @@ -173,12 +204,14 @@ private void PreserveCustomHttpClientHandler (string handlerType, string handler } [Test] - public void PreserveCustomHttpClientHandlers () + public void PreserveCustomHttpClientHandlers ([Values (TrimMode.Partial, TrimMode.Full)] TrimMode trimMode) { PreserveCustomHttpClientHandler ("Xamarin.Android.Net.AndroidMessageHandler", "", - "temp/PreserveAndroidMessageHandler", "android-arm64/linked/Mono.Android.dll"); + "temp/PreserveAndroidMessageHandler", "android-arm64/linked/Mono.Android.dll", trimMode); PreserveCustomHttpClientHandler ("System.Net.Http.SocketsHttpHandler", "System.Net.Http", - "temp/PreserveSocketsHttpHandler", "android-arm64/linked/System.Net.Http.dll"); + "temp/PreserveSocketsHttpHandler", "android-arm64/linked/System.Net.Http.dll", trimMode); + PreserveCustomHttpClientHandler ("MyCustomHandler", "MyClassLibrary", + "temp/MyCustomHandler", "android-arm64/linked/MyClassLibrary.dll", trimMode); } [Test] diff --git a/tests/Mono.Android-Tests/Android.Widget/CustomWidgetTests.cs b/tests/Mono.Android-Tests/Android.Widget/CustomWidgetTests.cs index ec2ab5bdc1d..9ffd36a7328 100644 --- a/tests/Mono.Android-Tests/Android.Widget/CustomWidgetTests.cs +++ b/tests/Mono.Android-Tests/Android.Widget/CustomWidgetTests.cs @@ -1,11 +1,9 @@ -using System.Diagnostics.CodeAnalysis; -using Android.App; +using Android.App; using Android.Content; using Android.Util; using Android.Views; using Android.Widget; using NUnit.Framework; -using Mono.Android_Test.Library; namespace Xamarin.Android.RuntimeTests { @@ -14,7 +12,6 @@ public class CustomWidgetTests { // https://bugzilla.xamarin.com/show_bug.cgi?id=23880 [Test] - [DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (CustomTextView))] public void UpperCaseCustomWidget_ShouldNotThrowInflateException () { Assert.DoesNotThrow (() => { @@ -24,7 +21,6 @@ public void UpperCaseCustomWidget_ShouldNotThrowInflateException () } [Test] - [DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (CustomTextView))] public void LowerCaseCustomWidget_ShouldNotThrowInflateException () { Assert.DoesNotThrow (() => { @@ -34,7 +30,6 @@ public void LowerCaseCustomWidget_ShouldNotThrowInflateException () } [Test] - [DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (CustomTextView))] public void UpperAndLowerCaseCustomWidget_FromLibrary_ShouldNotThrowInflateException () { Assert.DoesNotThrow (() => {