Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
56 changes: 56 additions & 0 deletions Documentation/guides/building-apps/build-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,62 @@ installing app bundles.

This build action was introduced in Xamarin.Android 11.3.

## AndroidKnownDesignerAssemblies

This is an ItemGroup that is used to work around issues when upgrading the old `Resource.designer.cs`
system to the new Resource Assembly system. This can happen if an assembly is referencing a
`Resource.designer.cs` class from anther assembly which was built on an older framework.
If you see runtime errors such as

```
System.TypeLoadException: 'Could not resolve type with token 010001d8 from typeref (expected class 'Style' in assembly '')'
```

or build errors such as

```
error NETSDK1144: Optimizing assemblies for size failed. Optimization can be disabled by setting the PublishTrimmed property to false
```

or

```
Unhandled exception. Mono.Linker.LinkerFatalErrorException: ILLink: error IL1013: Error processing '…/dotnet-sdk-8.0.100-rc.1.23455.8-osx-x64/packs/Microsoft.Android.Sdk.Darwin/34.0.0-rc.1.432/targets/../PreserveLists/Mono.Android.xml'.
---> System.ArgumentNullException: Value cannot be null. (Parameter 'key')
at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at Mono.Linker.AssemblyResolver.GetAssembly(String file)
at Mono.Linker.AssemblyResolver.ResolveFromReferences(AssemblyNameReference name)
at Mono.Linker.AssemblyResolver.Resolve(AssemblyNameReference name, Boolean probing)
at Mono.Linker.LinkContext.TryResolve(AssemblyNameReference name)
at Mono.Linker.Steps.ProcessLinkerXmlBase.ProcessAssemblies(XPathNavigator nav)
at Mono.Linker.Steps.ProcessLinkerXmlBase.ProcessXml(Boolean stripResource, Boolean ignoreResource)
--- End of inner exception stack trace ---
at Mono.Linker.Steps.ProcessLinkerXmlBase.ProcessXml(Boolean stripResource, Boolean ignoreResource)
at Mono.Linker.Steps.DescriptorMarker.Mark()
at Mono.Linker.Steps.ResolveFromXmlStep.Process()
at Mono.Linker.Steps.BaseStep.Process(LinkContext context)
at Mono.Linker.Pipeline.ProcessStep(LinkContext context, IStep step)
at Mono.Linker.Pipeline.Process(LinkContext context)
at Mono.Linker.Driver.Run(ILogger customLogger)
at Mono.Linker.Driver.Main(String[] args)
The command exited with code 134.
```

You can make use of this ItemGroup to work around these issues.

You need to figure out which assembly is missing the designer and add its name to this
ItemGroup

```
<AndroidKnownDesignerAssemblies Include="Some.Assembly" />
```

This will enable some additional processing to ensure the IL is fixed.

This build item was introduced in .NET 8.


## AndroidNativeLibrary

[Native libraries](~/android/platform/native-libraries.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public class FixLegacyResourceDesignerStep : LinkDesignerBase

protected override void EndProcess ()
{
base.EndProcess ();

if (designerAssembly != null) {
LogMessage ($" Setting Action on {designerAssembly.Name} to Link.");
Annotations.SetAction (designerAssembly, AssemblyAction.Link);
Expand Down Expand Up @@ -68,18 +70,20 @@ protected override void LoadDesigner ()
}
}

internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly, TypeDefinition designer = null)
{
if (!FindResourceDesigner (assembly, mainApplication: false, out TypeDefinition designer, out CustomAttribute designerAttribute)) {
if (designer is not null) {
LogMessage ($" {assembly.Name.Name} has an assembly reference with a designer.");
} else if (!FindResourceDesigner (assembly, mainApplication: false, out designer, out _)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is hitting "perfect is the enemy of the good" territory -- so don't take this comment as a suggestion to change anything -- but I don't like the code flow here:

  • It requires that FindResourceDesigner create a new TypeDefinition instance, even though that instance shouldn't be used for anything, and ideally should be linked away entirely.
  • I suspect we could have/create a scenario in which an assembly references multiple Resource types, in which case returning one type -- if that type were used for anything -- would be inappropriate.

This complaint likely isn't worth addressing now, I just wanted to comment on it.

LogMessage ($" {assembly.Name.Name} has no designer. ");
return false;
}

LogMessage ($" {assembly.Name.Name} has a designer. ");
LogMessage ($" BaseType: {designer.BaseType.FullName}. ");
if (designer.BaseType.FullName == $"{DesignerAssemblyNamespace}.Resource") {
LogMessage ($" {assembly.Name.Name} has already been processed. ");
return false;
} else {
LogMessage ($" {assembly.Name.Name} has a designer. ");
LogMessage ($" BaseType: {designer.BaseType.FullName}. ");
if (designer.BaseType.FullName == $"{DesignerAssemblyNamespace}.Resource") {
LogMessage ($" {assembly.Name.Name} has already been processed. ");
return false;
}
}

// This is expected for the first call, in <LinkAssembliesNoShrink/>
Expand Down Expand Up @@ -163,7 +167,7 @@ protected override void FixBody (MethodBody body, TypeDefinition designer)
found = lookupCaseInsensitive.TryGetValue (key, out method);
}
if (found) {
var importedMethod = designer.Module.ImportReference (method);
var importedMethod = body.Method.Module.ImportReference (method);
var newIn = Instruction.Create (OpCodes.Call, importedMethod);
instructions.Add (i, newIn);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@

namespace MonoDroid.Tuner {
public abstract class LinkDesignerBase : BaseStep {
HashSet<AssemblyDefinition> allAssemblies = new ();
HashSet<AssemblyDefinition> processedAssemblies = new ();

public string[] KnownDesignerAssemblies { get; set; } = Array.Empty<string> ();

public virtual void LogMessage (string message)
{
Context.LogMessage (message);
Expand All @@ -35,7 +40,7 @@ public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
return Context.Resolve (name);
}

protected bool FindResourceDesigner (AssemblyDefinition assembly, bool mainApplication, out TypeDefinition designer, out CustomAttribute designerAttribute)
internal bool FindResourceDesigner (AssemblyDefinition assembly, bool mainApplication, out TypeDefinition designer, out CustomAttribute designerAttribute)
{
string designerFullName = null;
designer = null;
Expand All @@ -60,8 +65,17 @@ protected bool FindResourceDesigner (AssemblyDefinition assembly, bool mainAppli

}
}
if (string.IsNullOrEmpty(designerFullName))
if (string.IsNullOrEmpty(designerFullName)) {
// Check for known designers which have been removed.
foreach (var knownDesigner in KnownDesignerAssemblies) {
if (string.Compare (knownDesigner, assembly.Name.Name, StringComparison.Ordinal) == 0) {
designer = new TypeDefinition (knownDesigner, "Resource", TypeAttributes.Public | TypeAttributes.AnsiClass);
designer.BaseType = new TypeDefinition ("System", "Object", TypeAttributes.Public | TypeAttributes.AnsiClass);
return true;
}
}
return false;
}

foreach (ModuleDefinition module in assembly.Modules)
{
Expand All @@ -74,6 +88,7 @@ protected bool FindResourceDesigner (AssemblyDefinition assembly, bool mainAppli
}
}
}

return false;
}

Expand Down Expand Up @@ -198,19 +213,67 @@ protected void FixupAssemblyTypes (AssemblyDefinition assembly, TypeDefinition d

protected override void ProcessAssembly (AssemblyDefinition assembly)
{
allAssemblies.Add (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)
if (action == AssemblyAction.Skip || action == AssemblyAction.Copy) {
Annotations.SetAction (assembly, AssemblyAction.Save);
processedAssemblies.Add (assembly);
}
}
}

protected override void Process ()
{
#if ILLINK
if (Context.TryGetCustomData ("AndroidKnownDesignerAssemblies", out string knownDesignerAssemblies)) {
KnownDesignerAssemblies = knownDesignerAssemblies.Split (';');
}
#endif
}

protected override void EndProcess ()
{
// This is a "second pass" to fix assemblies with references to assemblies with a designer
if (processedAssemblies.Count > 0) {
foreach (var assembly in allAssemblies) {
if (processedAssemblies.Contains (assembly))
continue;

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

foreach (var processedAssembly in processedAssemblies) {
if (ProcessAssemblyDesignerSecondPass (assembly, processedAssembly) &&
(action == AssemblyAction.Skip || action == AssemblyAction.Copy)) {
Annotations.SetAction (assembly, AssemblyAction.Save);
}
}
}
}
}

public bool ProcessAssemblyDesignerSecondPass (AssemblyDefinition assembly, AssemblyDefinition processedAssembly)
{
if (assembly.MainModule.AssemblyReferences.Any (r => r.FullName == processedAssembly.Name.FullName)) {
LogMessage ($" {assembly.Name.Name} has an assembly reference to {processedAssembly.Name}");
if (FindResourceDesigner (processedAssembly, mainApplication: false, out TypeDefinition designer, out _) &&
ProcessAssemblyDesigner (assembly, designer)) {
return true;
} else {
LogMessage ($" {processedAssembly.Name} did not have a designer");
}
}
return false;
}

internal abstract bool ProcessAssemblyDesigner (AssemblyDefinition assemblyDefinition);
internal abstract bool ProcessAssemblyDesigner (AssemblyDefinition assemblyDefinition, TypeDefinition designer = null);
protected abstract void LoadDesigner ();
protected abstract void FixBody (MethodBody body, TypeDefinition designer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ protected override void FixBody (MethodBody body, TypeDefinition designer)
}
}

internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly, TypeDefinition designer = null)
{
if (mainDesigner == null)
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ This file contains the .NET 5-specific targets to customize ILLink
Include="ProguardConfiguration"
Value="$(_ProguardProjectConfiguration)"
/>
<_TrimmerCustomData Include="AndroidKnownDesignerAssemblies" Value="@(AndroidKnownDesignerAssemblies)" />

<!--
Used for the <ILLink CustomSteps="@(_TrimmerCustomSteps)" /> value:
Expand Down
35 changes: 32 additions & 3 deletions src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Mono.Cecil;
using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.Android.Build.Tasks;

namespace Xamarin.Android.Tasks
Expand Down Expand Up @@ -42,6 +43,8 @@ public class LinkAssembliesNoShrink : AndroidTask

public bool Deterministic { get; set; }

public string[] KnownDesignerAssemblies { get; set; }

public override bool RunTask ()
{
if (SourceFiles.Length != DestinationFiles.Length)
Expand All @@ -66,7 +69,9 @@ public override bool RunTask ()
var cache = new TypeDefinitionCache ();
var fixAbstractMethodsStep = new FixAbstractMethodsStep (resolver, cache, Log);
var addKeepAliveStep = new AddKeepAlivesStep (resolver, cache, Log, UsingAndroidNETSdk);
var fixLegacyResourceDesignerStep = new FixLegacyResourceDesignerStep (resolver, Log);
var fixLegacyResourceDesignerStep = new FixLegacyResourceDesignerStep (resolver, Log, KnownDesignerAssemblies);
Dictionary<AssemblyDefinition, ITaskItem> allAssemblies = new ();
HashSet<AssemblyDefinition> processedAssemblies = new ();
for (int i = 0; i < SourceFiles.Length; i++) {
var source = SourceFiles [i];
var destination = DestinationFiles [i];
Expand Down Expand Up @@ -94,9 +99,14 @@ public override bool RunTask ()
if (assemblyDefinition == null)
assemblyDefinition = resolver.GetAssembly (source.ItemSpec);

allAssemblies.Add (assemblyDefinition, destination);

bool save = fixAbstractMethodsStep.FixAbstractMethods (assemblyDefinition);
if (UseDesignerAssembly)
if (UseDesignerAssembly) {
save |= fixLegacyResourceDesignerStep.ProcessAssemblyDesigner (assemblyDefinition);
if (save)
processedAssemblies.Add (assemblyDefinition);
}
if (AddKeepAlives)
save |= addKeepAliveStep.AddKeepAlives (assemblyDefinition);
if (save) {
Expand All @@ -109,6 +119,24 @@ public override bool RunTask ()

CopyIfChanged (source, destination);
}

// Run a "second pass" for the FixLegacyResourceDesignerStep
if (UseDesignerAssembly && processedAssemblies.Count > 0) {
foreach (var kvp in allAssemblies) {
var assembly = kvp.Key;
var destination = kvp.Value;
if (processedAssemblies.Contains (assembly))
continue;
bool save = false;
foreach (var processedAssembly in processedAssemblies) {
save |= fixLegacyResourceDesignerStep.ProcessAssemblyDesignerSecondPass (assembly, processedAssembly);
}
if (save) {
writerParameters.WriteSymbols = assembly.MainModule.HasSymbols;
assembly.Write (destination.ItemSpec, writerParameters);
}
}
}
}

return !Log.HasLoggedErrors;
Expand All @@ -131,10 +159,11 @@ class FixLegacyResourceDesignerStep : MonoDroid.Tuner.FixLegacyResourceDesignerS
readonly DirectoryAssemblyResolver resolver;
readonly TaskLoggingHelper logger;

public FixLegacyResourceDesignerStep (DirectoryAssemblyResolver resolver, TaskLoggingHelper logger)
public FixLegacyResourceDesignerStep (DirectoryAssemblyResolver resolver, TaskLoggingHelper logger, string[] knownDesignerAssemblies)
{
this.resolver = resolver;
this.logger = logger;
this.KnownDesignerAssemblies = knownDesignerAssemblies;
}

public override void LogMessage (string message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<ItemGroup>
<AndroidPackagingOptionsExclude Include="DebugProbesKt.bin" />
<AndroidPackagingOptionsExclude Include="$([MSBuild]::Escape('*.kotlin_*'))" />
<AndroidKnownDesignerAssemblies Include="Microsoft.Maui;Microsoft.Maui.Core;Microsoft.Maui.Controls;Microsoft.Maui.Graphics" />
</ItemGroup>

<!-- Assets build properties -->
Expand Down Expand Up @@ -1464,6 +1465,7 @@ because xbuild doesn't support framework reference assemblies.
UseDesignerAssembly="$(AndroidUseDesignerAssembly)"
Deterministic="$(Deterministic)"
UsingAndroidNETSdk="$(UsingAndroidNETSdk)"
KnownDesignerAssemblies="@(AndroidKnownDesignerAssemblies)"
/>
<ItemGroup>
<FileWrites Include="$(MonoAndroidIntermediateAssemblyDir)**" />
Expand Down
Loading