Skip to content
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

[Xamarin.Android.Build.Tasks] Enable 'AndroidLinkResources' for .NET 6 Release Applications #6696

Merged
merged 1 commit into from
Feb 16, 2022
Merged
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
@@ -0,0 +1,151 @@
using Mono.Cecil;
using Mono.Linker;
using Mono.Linker.Steps;
using System;
using System.Linq;
using Xamarin.Android.Tasks;
using System.Collections.Generic;
using Mono.Cecil.Cil;
using System.Text.RegularExpressions;
#if ILLINK
using Microsoft.Android.Sdk.ILLink;
#endif


namespace MonoDroid.Tuner {
public abstract class LinkDesignerBase : BaseStep {
public virtual void LogMessage (string message)
{
Context.LogMessage (message);
}

public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
{
return Context.Resolve (name);
}

protected bool FindResourceDesigner (AssemblyDefinition assembly, bool mainApplication, out TypeDefinition designer, out CustomAttribute designerAttribute)
{
string designerFullName = null;
designer = null;
designerAttribute = null;
foreach (CustomAttribute attribute in assembly.CustomAttributes)
{
if (attribute.AttributeType.FullName == "Android.Runtime.ResourceDesignerAttribute")
{
designerAttribute = attribute;
if (attribute.HasProperties)
{
foreach (var p in attribute.Properties)
{
if (p.Name == "IsApplication" && (bool)p.Argument.Value == (mainApplication ? mainApplication : (bool)p.Argument.Value))
{
designerFullName = attribute.ConstructorArguments[0].Value.ToString ();
break;
}
}
}
break;

}
}
if (string.IsNullOrEmpty(designerFullName))
return false;

foreach (ModuleDefinition module in assembly.Modules)
{
foreach (TypeDefinition type in module.Types)
{
if (type.FullName == designerFullName)
{
designer = type;
return true;
}
}
}
return false;
}

protected void ClearDesignerClass (TypeDefinition designer)
{
LogMessage ($" TryRemoving {designer.FullName}");
designer.NestedTypes.Clear ();
designer.Methods.Clear ();
designer.Fields.Clear ();
designer.Properties.Clear ();
designer.CustomAttributes.Clear ();
designer.Interfaces.Clear ();
designer.Events.Clear ();
}
protected Dictionary<string, int> BuildResourceDesignerFieldLookup (TypeDefinition type)
{
var output = new Dictionary<string, int> ();
foreach (TypeDefinition definition in type.NestedTypes)
{
foreach (FieldDefinition field in definition.Fields)
{
string key = $"{definition.Name}::{field.Name}";
if (!output.ContainsKey (key))
output.Add(key, int.Parse (field.Constant?.ToString () ?? "0"));
}
}
return output;
}

protected void FixType (TypeDefinition type, TypeDefinition localDesigner)
{
foreach (MethodDefinition method in type.Methods)
{
if (!method.HasBody)
continue;
FixBody (method.Body, localDesigner);
}
foreach (PropertyDefinition property in type.Properties)
{
if (property.GetMethod != null && property.GetMethod.HasBody)
{
FixBody (property.GetMethod.Body, localDesigner);
}
if (property.SetMethod != null && property.SetMethod.HasBody)
{
FixBody (property.SetMethod.Body, localDesigner);
}
}
foreach (TypeDefinition nestedType in type.NestedTypes)
{
FixType (nestedType, localDesigner);
}
}

protected void FixupAssemblyTypes (AssemblyDefinition assembly, TypeDefinition designer)
{
foreach (ModuleDefinition module in assembly.Modules)
{
foreach (TypeDefinition type in module.Types)
{
if (type.FullName == designer.FullName)
continue;
FixType (type, designer);
}
}
}

protected override void ProcessAssembly (AssemblyDefinition assembly)
{
LoadDesigner ();

var action = Annotations.HasAction (assembly) ? Annotations.GetAction (assembly) : AssemblyAction.Skip;
if (action == AssemblyAction.Delete)
return;

if (ProcessAssemblyDesigner (assembly)) {
if (action == AssemblyAction.Skip || action == AssemblyAction.Copy)
Annotations.SetAction (assembly, AssemblyAction.Save);
}
}

internal abstract bool ProcessAssemblyDesigner (AssemblyDefinition assemblyDefinition);
protected abstract void LoadDesigner ();
protected abstract void FixBody (MethodBody body, TypeDefinition designer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,23 @@
using System.Collections.Generic;
using Mono.Cecil.Cil;
using System.Text.RegularExpressions;
#if ILLINK
using Microsoft.Android.Sdk.ILLink;
#endif

namespace MonoDroid.Tuner
{
public class RemoveResourceDesignerStep : BaseStep
public class RemoveResourceDesignerStep : LinkDesignerBase
{
TypeDefinition mainDesigner = null;
AssemblyDefinition mainAssembly = null;
CustomAttribute mainDesignerAttribute;
Dictionary<string, int> designerConstants;
Regex opCodeRegex = new Regex (@"([\w]+): ([\w]+) ([\w.]+) ([\w:./]+)");

protected override void Process ()
protected override void LoadDesigner ()
{
if (mainAssembly != null)
return;
// resolve the MainAssembly Resource designer TypeDefinition
AndroidLinkConfiguration config = AndroidLinkConfiguration.GetInstance (Context);
if (config == null)
Expand All @@ -31,95 +35,26 @@ protected override void Process ()
}
}
if (mainDesigner == null) {
Context.LogMessage ($" Main Designer not found.");
LogMessage ($" Main Designer not found.");
return;
}
Context.LogMessage ($" Main Designer found {mainDesigner.FullName}.");
LogMessage ($" Main Designer found {mainDesigner.FullName}.");
designerConstants = BuildResourceDesignerFieldLookup (mainDesigner);
}

protected override void EndProcess ()
{
if (mainDesigner != null) {
Context.LogMessage ($" Setting Action on {mainAssembly.Name} to Save.");
LogMessage ($" Setting Action on {mainAssembly.Name} to Save.");
Annotations.SetAction (mainAssembly, AssemblyAction.Save);
}
}

bool FindResourceDesigner (AssemblyDefinition assembly, bool mainApplication, out TypeDefinition designer, out CustomAttribute designerAttribute)
{
string designerFullName = null;
designer = null;
designerAttribute = null;
foreach (CustomAttribute attribute in assembly.CustomAttributes)
{
if (attribute.AttributeType.FullName == "Android.Runtime.ResourceDesignerAttribute")
{
designerAttribute = attribute;
if (attribute.HasProperties)
{
foreach (var p in attribute.Properties)
{
if (p.Name == "IsApplication" && (bool)p.Argument.Value == (mainApplication ? mainApplication : (bool)p.Argument.Value))
{
designerFullName = attribute.ConstructorArguments[0].Value.ToString ();
break;
}
}
}
break;

}
}
if (string.IsNullOrEmpty(designerFullName))
return false;

foreach (ModuleDefinition module in assembly.Modules)
{
foreach (TypeDefinition type in module.Types)
{
if (type.FullName == designerFullName)
{
designer = type;
return true;
}
}
}
return false;
}

Dictionary<string, int> BuildResourceDesignerFieldLookup (TypeDefinition type)
{
var output = new Dictionary<string, int> ();
foreach (TypeDefinition definition in type.NestedTypes)
{
foreach (FieldDefinition field in definition.Fields)
{
string key = $"{definition.Name}::{field.Name}";
if (!output.ContainsKey (key))
output.Add(key, int.Parse (field.Constant?.ToString () ?? "0"));
}
}
return output;
}

void ClearDesignerClass (TypeDefinition designer)
{
Context.LogMessage ($" TryRemoving {designer.FullName}");
designer.NestedTypes.Clear ();
designer.Methods.Clear ();
designer.Fields.Clear ();
designer.Properties.Clear ();
designer.CustomAttributes.Clear ();
designer.Interfaces.Clear ();
designer.Events.Clear ();
}

void FixBody (MethodBody body, TypeDefinition localDesigner)
protected override void FixBody (MethodBody body, TypeDefinition designer)
{
Dictionary<Instruction, int> instructions = new Dictionary<Instruction, int>();
var processor = body.GetILProcessor ();
string designerFullName = $"{localDesigner.FullName}/";
string designerFullName = $"{designer.FullName}/";
foreach (var i in body.Instructions)
{
string line = i.ToString ();
Expand All @@ -134,77 +69,48 @@ void FixBody (MethodBody body, TypeDefinition localDesigner)
}
}
if (instructions.Count > 0)
Context.LogMessage ($" Fixing up {body.Method.FullName}");
LogMessage ($" Fixing up {body.Method.FullName}");
foreach (var i in instructions)
{
var newCode = Extensions.CreateLoadArraySizeOrOffsetInstruction (i.Value);
Context.LogMessage ($" Replacing {i.Key}");
Context.LogMessage ($" With {newCode}");
LogMessage ($" Replacing {i.Key}");
LogMessage ($" With {newCode}");
processor.Replace(i.Key, newCode);
}
}

void FixType (TypeDefinition type, TypeDefinition localDesigner)
{
foreach (MethodDefinition method in type.Methods)
{
if (!method.HasBody)
continue;
FixBody (method.Body, localDesigner);
}
foreach (PropertyDefinition property in type.Properties)
{
if (property.GetMethod != null && property.GetMethod.HasBody)
{
FixBody (property.GetMethod.Body, localDesigner);
}
if (property.SetMethod != null && property.SetMethod.HasBody)
{
FixBody (property.SetMethod.Body, localDesigner);
}
}
foreach (TypeDefinition nestedType in type.NestedTypes)
{
FixType (nestedType, localDesigner);
}
}

protected override void ProcessAssembly (AssemblyDefinition assembly)
internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
{
if (mainDesigner == null)
return;
return false;
var fileName = assembly.Name.Name + ".dll";
if (MonoAndroidHelper.IsFrameworkAssembly (fileName))
return;
return false;

Context.LogMessage ($" Fixing up {assembly.Name.Name}");
LogMessage ($" Fixing up {assembly.Name.Name}");
TypeDefinition localDesigner = null;
CustomAttribute designerAttribute;
if (assembly != mainAssembly) {
Context.LogMessage ($" {assembly.Name.Name} is not the main assembly. ");
LogMessage ($" {assembly.Name.Name} is not the main assembly. ");
if (!FindResourceDesigner (assembly, mainApplication: false, designer: out localDesigner, designerAttribute: out designerAttribute)) {
Context.LogMessage ($" {assembly.Name.Name} does not have a designer file.");
return;
return false;
}
} else {
Context.LogMessage ($" {assembly.Name.Name} is the main assembly. ");
LogMessage ($" {assembly.Name.Name} is the main assembly. ");
localDesigner = mainDesigner;
designerAttribute = mainDesignerAttribute;
}

Context.LogMessage ($" {assembly.Name.Name} has designer {localDesigner.FullName}.");
LogMessage ($" {assembly.Name.Name} has designer {localDesigner.FullName}.");

FixupAssemblyTypes (assembly, localDesigner);

foreach (var mod in assembly.Modules) {
foreach (var type in mod.Types) {
if (type == localDesigner)
continue;
FixType (type, localDesigner);
}
}
ClearDesignerClass (localDesigner);
if (designerAttribute != null) {
assembly.CustomAttributes.Remove (designerAttribute);
}
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@
<InvariantGlobalization Condition="'$(InvariantGlobalization)' == ''">false</InvariantGlobalization>
<StartupHookSupport Condition="'$(StartupHookSupport)' == ''">false</StartupHookSupport>
<UseNativeHttpHandler Condition="'$(UseNativeHttpHandler)' == ''">true</UseNativeHttpHandler>
<_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true</_AggressiveAttributeTrimming>
<_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true</_AggressiveAttributeTrimming>
<NullabilityInfoContextSupport Condition="'$(NullabilityInfoContextSupport)' == ''">false</NullabilityInfoContextSupport>
<BuiltInComInteropSupport Condition="'$(BuiltInComInteropSupport)' == ''">false</BuiltInComInteropSupport>
<BuiltInComInteropSupport Condition="'$(BuiltInComInteropSupport)' == ''">false</BuiltInComInteropSupport>
<!-- Verify DI trimmability at development-time, but turn the validation off for production/trimmed builds. -->
<VerifyDependencyInjectionOpenGenericServiceTrimmability Condition="'$(VerifyDependencyInjectionOpenGenericServiceTrimmability)' == '' and '$(PublishTrimmed)' == 'true'">false</VerifyDependencyInjectionOpenGenericServiceTrimmability>
<VerifyDependencyInjectionOpenGenericServiceTrimmability Condition="'$(VerifyDependencyInjectionOpenGenericServiceTrimmability)' == ''">true</VerifyDependencyInjectionOpenGenericServiceTrimmability>
Expand Down
Loading