Skip to content

[trimming] preserve custom views and $(AndroidHttpClientHandlerType) #8944

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
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
4 changes: 1 addition & 3 deletions src/Mono.Android/Android.Runtime/AndroidEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <GenerateILLinkXml/> MSBuild task.")]
[return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
static Type TypeGetType (string typeName) =>
Type.GetType (typeName, throwOnError: false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ _ResolveAssemblies MSBuild target.
</ItemGroup>
</Target>

<Target Name="_ResolveAssemblies">
<Target Name="_ResolveAssemblies"
DependsOnTargets="_GenerateILLinkXml">
<ItemGroup>
<_RIDs Include="$(RuntimeIdentifier)" Condition=" '$(RuntimeIdentifiers)' == '' " />
<_RIDs Include="$(RuntimeIdentifiers)" Condition=" '$(RuntimeIdentifiers)' != '' " />
Expand All @@ -96,6 +97,7 @@ _ResolveAssemblies MSBuild target.
;_OuterIntermediateAssembly=@(IntermediateAssembly)
;_OuterOutputPath=$(OutputPath)
;_OuterIntermediateOutputPath=$(IntermediateOutputPath)
;_AndroidGeneratedRootDescriptor=$(_AndroidGeneratedRootDescriptor)
</_AdditionalProperties>
<_AndroidBuildRuntimeIdentifiersInParallel Condition=" '$(_AndroidBuildRuntimeIdentifiersInParallel)' == '' ">true</_AndroidBuildRuntimeIdentifiersInParallel>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ properties that determine build ordering.
_SetLatestTargetFrameworkVersion;
_GetLibraryImports;
_RemoveRegisterAttribute;
_GenerateILLinkXml;
_ResolveAssemblies;
_ResolveSatellitePaths;
_CreatePackageWorkspace;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ This file contains the .NET 5-specific targets to customize ILLink

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

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

<Target Name="_PrepareLinking"
Condition=" '$(PublishTrimmed)' == 'true' "
AfterTargets="ComputeResolvedFilesToPublishList"
Expand Down Expand Up @@ -94,6 +96,25 @@ This file contains the .NET 5-specific targets to customize ILLink
Condition=" '@(ResolvedFileToPublish->Count())' != '0' and '%(Filename)' != '' "
Include="@(_PreserveLists)" />
<TrimmerRootDescriptor Include="@(LinkDescription)" />
<TrimmerRootDescriptor
Condition=" Exists('$(_AndroidGeneratedRootDescriptor)') "
Include="$(_AndroidGeneratedRootDescriptor)"
/>
</ItemGroup>
</Target>

<Target Name="_GenerateILLinkXml"
Condition=" '$(PublishTrimmed)' == 'true' ">
<PropertyGroup>
<_AndroidGeneratedRootDescriptor>$(IntermediateOutputPath)net-android-trimmer.xml</_AndroidGeneratedRootDescriptor>
</PropertyGroup>
<GenerateILLinkXml
AndroidHttpClientHandlerType="$(AndroidHttpClientHandlerType)"
CustomViewMapFile="$(_CustomViewMapFile)"
OutputFile="$(_AndroidGeneratedRootDescriptor)"
/>
<ItemGroup>
<FileWrites Include="$(_AndroidGeneratedRootDescriptor)" />
</ItemGroup>
</Target>

Expand Down
70 changes: 70 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Tasks/GenerateILLinkXml.cs
Original file line number Diff line number Diff line change
@@ -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)
));
}
Comment on lines +47 to +60
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code doesn't work yet, as the file contains:

mono.android_test.library.CustomTextView;D:\src\xamarin-android\tests\Mono.Android-Tests\Runtime-Microsoft.Android.Sdk\obj\Release\net9.0-android\lp\6\jl\res\layout\upper_lower_custom.xml
mono.android_test.library.CustomTextView;obj\Release\net9.0-android\res\layout\upper_lower_custom.xml
Mono.Android_Test.Library.CustomTextView;D:\src\xamarin-android\tests\Mono.Android-Tests\Runtime-Microsoft.Android.Sdk\obj\Release\net9.0-android\lp\6\jl\res\layout\upper_lower_custom.xml
Mono.Android_Test.Library.CustomTextView;obj\Release\net9.0-android\res\layout\upper_lower_custom.xml
Mono.Android_Test.Library.CustomTextView;obj\Release\net9.0-android\res\layout\uppercase_custom.xml

I need the full type name like Mono.Android_Test.Library.CustomTextView, Mono.Android_Test.Library.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the acw_map.txt file might allow you to map that info back. You will probably need a new file though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to piece together the information with existing files:

  • customview-map.txt has an entry with a type name and file name like upper_lower_custom.xml
  • <ConvertResourcesCases/> has $(MonoAndroidResDirIntermediate);@(_LibraryResourceDirectories)
  • @(_LibraryResourceDirectories) has OriginalFile = D:\src\xamarin-android\tests\Mono.Android-Tests\Mono.Android-Test.Library\bin\Release\net9.0-android\Mono.Android-Test.Library.NET.aar
  • Mono.Android-Test.Library.NET.aar is created based on AndroidResource files in Mono.Android-Test.Library.NET.dll

So, it doesn't seem like we even have data to map back to the assembly name. I'll keep thinking about this one.


if (doc.SaveIfChanged (OutputFile)) {
Log.LogDebugMessage ($"Saving {OutputFile}");
} else {
Log.LogDebugMessage ($"{OutputFile} is unchanged. Skipping.");
}

return !Log.HasLoggedErrors;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 <HttpResponseMessage> 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))) {
Expand All @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -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 (() => {
Expand All @@ -24,7 +21,6 @@ public void UpperCaseCustomWidget_ShouldNotThrowInflateException ()
}

[Test]
[DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (CustomTextView))]
public void LowerCaseCustomWidget_ShouldNotThrowInflateException ()
{
Assert.DoesNotThrow (() => {
Expand All @@ -34,7 +30,6 @@ public void LowerCaseCustomWidget_ShouldNotThrowInflateException ()
}

[Test]
[DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (CustomTextView))]
public void UpperAndLowerCaseCustomWidget_FromLibrary_ShouldNotThrowInflateException ()
{
Assert.DoesNotThrow (() => {
Expand Down
Loading