diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/AddKeepAlivesStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/AddKeepAlivesStep.cs
index c916867cf2b..2c1382c5458 100644
--- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/AddKeepAlivesStep.cs
+++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/AddKeepAlivesStep.cs
@@ -11,6 +11,9 @@
namespace MonoDroid.Tuner
{
public class AddKeepAlivesStep : BaseStep
+#if !ILLINK
+ , IAssemblyModifierPipelineStep
+#endif // !ILLINK
{
protected override void ProcessAssembly (AssemblyDefinition assembly)
@@ -25,6 +28,17 @@ protected override void ProcessAssembly (AssemblyDefinition assembly)
}
}
+#if !ILLINK
+ public bool ProcessAssembly (AssemblyDefinition assembly, StepContext context)
+ {
+ // Only run this step on user Android assemblies
+ if (!context.IsAndroidUserAssembly)
+ return false;
+
+ return AddKeepAlives (assembly);
+ }
+#endif // !ILLINK
+
internal bool AddKeepAlives (AssemblyDefinition assembly)
{
if (!assembly.MainModule.HasTypeReference ("Java.Lang.Object"))
diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FindJavaObjectsStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FindJavaObjectsStep.cs
index bbf325a0154..5adc2e27582 100644
--- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FindJavaObjectsStep.cs
+++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FindJavaObjectsStep.cs
@@ -17,7 +17,7 @@ namespace MonoDroid.Tuner;
///
/// Scans an assembly for JLOs that need JCWs generated and writes them to an XML file.
///
-public class FindJavaObjectsStep : BaseStep
+public class FindJavaObjectsStep : BaseStep, IAssemblyModifierPipelineStep
{
public string ApplicationJavaClass { get; set; } = "";
@@ -29,8 +29,26 @@ public class FindJavaObjectsStep : BaseStep
public FindJavaObjectsStep (TaskLoggingHelper log) => Log = log;
- public bool ProcessAssembly (AssemblyDefinition assembly, string destinationJLOXml)
+ public bool ProcessAssembly (AssemblyDefinition assembly, StepContext context)
{
+ var destinationJLOXml = JavaObjectsXmlFile.GetJavaObjectsXmlFilePath (context.Destination.ItemSpec);
+ var scanned = ScanAssembly (assembly, context, destinationJLOXml);
+
+ if (!scanned) {
+ // We didn't scan for Java objects, so write an empty .xml file for later steps
+ JavaObjectsXmlFile.WriteEmptyFile (destinationJLOXml, Log);
+ return false;
+ }
+
+ // This step does not change the assembly
+ return false;
+ }
+
+ public bool ScanAssembly (AssemblyDefinition assembly, StepContext context, string destinationJLOXml)
+ {
+ if (!ShouldScan (context))
+ return false;
+
var action = Annotations.HasAction (assembly) ? Annotations.GetAction (assembly) : AssemblyAction.Skip;
if (action == AssemblyAction.Delete)
@@ -56,6 +74,25 @@ public bool ProcessAssembly (AssemblyDefinition assembly, string destinationJLOX
return true;
}
+ bool ShouldScan (StepContext context)
+ {
+ if (!context.IsAndroidAssembly)
+ return false;
+
+ // When marshal methods or non-JavaPeerStyle.XAJavaInterop1 are in use we do not want to skip non-user assemblies (such as Mono.Android) - we need to generate JCWs for them during
+ // application build, unlike in Debug configuration or when marshal methods are disabled, in which case we use JCWs generated during Xamarin.Android
+ // build and stored in a jar file.
+ var useMarshalMethods = !context.IsDebug && context.EnableMarshalMethods;
+ var shouldSkipNonUserAssemblies = !useMarshalMethods && context.CodeGenerationTarget == JavaPeerStyle.XAJavaInterop1;
+
+ if (shouldSkipNonUserAssemblies && !context.IsUserAssembly) {
+ Log.LogDebugMessage ($"Skipping assembly '{context.Source.ItemSpec}' because it is not a user assembly and we don't need JLOs from non-user assemblies");
+ return false;
+ }
+
+ return true;
+ }
+
List ScanForJavaTypes (AssemblyDefinition assembly)
{
var types = new List ();
diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs
index 83ceb21a7de..0f979714bf0 100644
--- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs
+++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs
@@ -21,6 +21,9 @@ namespace MonoDroid.Tuner
/// NOTE: this step is subclassed so it can be called directly from Xamarin.Android.Build.Tasks
///
public class FixAbstractMethodsStep : BaseMarkHandler
+#if !ILLINK
+ , IAssemblyModifierPipelineStep
+#endif // !ILLINK
{
public override void Initialize (LinkContext context, MarkContext markContext)
{
@@ -79,6 +82,17 @@ internal bool FixAbstractMethods (AssemblyDefinition assembly)
return changed;
}
+#if !ILLINK
+ public bool ProcessAssembly (AssemblyDefinition assembly, StepContext context)
+ {
+ // Only run this step on non-main user Android assemblies
+ if (context.IsMainAssembly || !context.IsAndroidUserAssembly)
+ return false;
+
+ return FixAbstractMethods (assembly);
+ }
+#endif // !ILLINK
+
readonly HashSet warnedAssemblies = new (StringComparer.Ordinal);
internal void CheckAppDomainUsage (AssemblyDefinition assembly, Action warn)
diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs
index 2aff9249130..1cfc19f2d9a 100644
--- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs
+++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs
@@ -21,6 +21,9 @@
namespace MonoDroid.Tuner
{
public class FixLegacyResourceDesignerStep : LinkDesignerBase
+#if !ILLINK
+ , Xamarin.Android.Tasks.IAssemblyModifierPipelineStep
+#endif // !ILLINK
{
internal const string DesignerAssemblyName = "_Microsoft.Android.Resource.Designer";
internal const string DesignerAssemblyNamespace = "_Microsoft.Android.Resource.Designer";
@@ -67,6 +70,17 @@ protected override void LoadDesigner ()
}
}
+#if !ILLINK
+ public bool ProcessAssembly (AssemblyDefinition assembly, Xamarin.Android.Tasks.StepContext context)
+ {
+ // Only run this step on non-main user Android assemblies
+ if (context.IsMainAssembly || !context.IsAndroidUserAssembly)
+ return false;
+
+ return ProcessAssemblyDesigner (assembly);
+ }
+#endif // !ILLINK
+
internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
{
if (!FindResourceDesigner (assembly, mainApplication: false, out TypeDefinition designer, out CustomAttribute designerAttribute)) {
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs
index 7eb2a5cc87c..18935bf8d77 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs
@@ -22,14 +22,6 @@ namespace Xamarin.Android.Tasks;
///
public class AssemblyModifierPipeline : AndroidTask
{
- // Names of assemblies which don't have Mono.Android.dll references, or are framework assemblies, but which must
- // be scanned for Java types.
- static readonly HashSet SpecialAssemblies = new HashSet (StringComparer.OrdinalIgnoreCase) {
- "Java.Interop.dll",
- "Mono.Android.dll",
- "Mono.Android.Runtime.dll",
- };
-
public override string TaskPrefix => "AMP";
public string ApplicationJavaClass { get; set; } = "";
@@ -66,6 +58,12 @@ public class AssemblyModifierPipeline : AndroidTask
[Required]
public ITaskItem [] SourceFiles { get; set; } = [];
+ ///
+ /// $(TargetName) would be "AndroidApp1" with no extension
+ ///
+ [Required]
+ public string TargetName { get; set; } = "";
+
protected JavaPeerStyle codeGenerationTarget;
public override bool RunTask ()
@@ -86,14 +84,14 @@ public override bool RunTask ()
Dictionary> perArchAssemblies = MonoAndroidHelper.GetPerArchAssemblies (ResolvedAssemblies, Array.Empty (), validate: false);
- RunState? runState = null;
+ AssemblyPipeline? pipeline = null;
var currentArch = AndroidTargetArch.None;
for (int i = 0; i < SourceFiles.Length; i++) {
ITaskItem source = SourceFiles [i];
- AndroidTargetArch sourceArch = GetValidArchitecture (source);
+ AndroidTargetArch sourceArch = MonoAndroidHelper.GetRequiredValidArchitecture (source);
ITaskItem destination = DestinationFiles [i];
- AndroidTargetArch destinationArch = GetValidArchitecture (destination);
+ AndroidTargetArch destinationArch = MonoAndroidHelper.GetRequiredValidArchitecture (destination);
if (sourceArch != destinationArch) {
throw new InvalidOperationException ($"Internal error: assembly '{sourceArch}' targets architecture '{sourceArch}', while destination assembly '{destination}' targets '{destinationArch}' instead");
@@ -102,38 +100,39 @@ public override bool RunTask ()
// Each architecture must have a different set of context classes, or otherwise only the first instance of the assembly may be rewritten.
if (currentArch != sourceArch) {
currentArch = sourceArch;
- runState?.Dispose ();
+ pipeline?.Dispose ();
var resolver = new DirectoryAssemblyResolver (this.CreateTaskLogger (), loadDebugSymbols: ReadSymbols, loadReaderParameters: readerParameters);
- runState = new RunState (resolver);
// Add SearchDirectories for the current architecture's ResolvedAssemblies
foreach (var kvp in perArchAssemblies [sourceArch]) {
ITaskItem assembly = kvp.Value;
var path = Path.GetFullPath (Path.GetDirectoryName (assembly.ItemSpec));
- if (!runState.resolver.SearchDirectories.Contains (path)) {
- runState.resolver.SearchDirectories.Add (path);
+ if (!resolver.SearchDirectories.Contains (path)) {
+ resolver.SearchDirectories.Add (path);
}
}
// Set up the FixAbstractMethodsStep and AddKeepAlivesStep
- var context = new MSBuildLinkContext (runState.resolver, Log);
+ var context = new MSBuildLinkContext (resolver, Log);
+ pipeline = new AssemblyPipeline (resolver);
- CreateRunState (runState, context);
+ BuildPipeline (pipeline, context);
}
Directory.CreateDirectory (Path.GetDirectoryName (destination.ItemSpec));
- RunPipeline (source, destination, runState!, writerParameters);
+ RunPipeline (pipeline!, source, destination, writerParameters);
}
- runState?.Dispose ();
+ pipeline?.Dispose ();
return !Log.HasLoggedErrors;
}
- protected virtual void CreateRunState (RunState runState, MSBuildLinkContext context)
+ protected virtual void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkContext context)
{
+ // FindJavaObjectsStep
var findJavaObjectsStep = new FindJavaObjectsStep (Log) {
ApplicationJavaClass = ApplicationJavaClass,
ErrorOnCustomJavaObject = ErrorOnCustomJavaObject,
@@ -141,108 +140,45 @@ protected virtual void CreateRunState (RunState runState, MSBuildLinkContext con
};
findJavaObjectsStep.Initialize (context);
-
- runState.findJavaObjectsStep = findJavaObjectsStep;
+ pipeline.Steps.Add (findJavaObjectsStep);
}
- protected virtual void RunPipeline (ITaskItem source, ITaskItem destination, RunState runState, WriterParameters writerParameters)
+ void RunPipeline (AssemblyPipeline pipeline, ITaskItem source, ITaskItem destination, WriterParameters writerParameters)
{
- var destinationJLOXml = JavaObjectsXmlFile.GetJavaObjectsXmlFilePath (destination.ItemSpec);
-
- if (!TryScanForJavaObjects (source, destination, runState, writerParameters)) {
- // Even if we didn't scan for Java objects, we still write an empty .xml file for later steps
- JavaObjectsXmlFile.WriteEmptyFile (destinationJLOXml, Log);
- }
- }
-
- bool TryScanForJavaObjects (ITaskItem source, ITaskItem destination, RunState runState, WriterParameters writerParameters)
- {
- if (!ShouldScanAssembly (source))
- return false;
-
- var destinationJLOXml = JavaObjectsXmlFile.GetJavaObjectsXmlFilePath (destination.ItemSpec);
- var assemblyDefinition = runState.resolver!.GetAssembly (source.ItemSpec);
-
- var scanned = runState.findJavaObjectsStep!.ProcessAssembly (assemblyDefinition, destinationJLOXml);
-
- return scanned;
- }
-
- bool ShouldScanAssembly (ITaskItem source)
- {
- // Skip this assembly if it is not an Android assembly
- if (!IsAndroidAssembly (source)) {
- Log.LogDebugMessage ($"Skipping assembly '{source.ItemSpec}' because it is not an Android assembly");
- return false;
- }
-
- // When marshal methods or non-JavaPeerStyle.XAJavaInterop1 are in use we do not want to skip non-user assemblies (such as Mono.Android) - we need to generate JCWs for them during
- // application build, unlike in Debug configuration or when marshal methods are disabled, in which case we use JCWs generated during Xamarin.Android
- // build and stored in a jar file.
- var useMarshalMethods = !Debug && EnableMarshalMethods;
- var shouldSkipNonUserAssemblies = !useMarshalMethods && codeGenerationTarget == JavaPeerStyle.XAJavaInterop1;
-
- if (shouldSkipNonUserAssemblies && !ResolvedUserAssemblies.Any (a => a.ItemSpec == source.ItemSpec)) {
- Log.LogDebugMessage ($"Skipping assembly '{source.ItemSpec}' because it is not a user assembly and we don't need JLOs from non-user assemblies");
- return false;
- }
-
- return true;
- }
-
- bool IsAndroidAssembly (ITaskItem source)
- {
- string name = Path.GetFileName (source.ItemSpec);
-
- if (SpecialAssemblies.Contains (name))
- return true;
+ var assembly = pipeline.Resolver.GetAssembly (source.ItemSpec);
+
+ var context = new StepContext (source, destination) {
+ CodeGenerationTarget = codeGenerationTarget,
+ EnableMarshalMethods = EnableMarshalMethods,
+ IsAndroidAssembly = MonoAndroidHelper.IsAndroidAssembly (source),
+ IsDebug = Debug,
+ IsFrameworkAssembly = MonoAndroidHelper.IsFrameworkAssembly (source),
+ IsMainAssembly = Path.GetFileNameWithoutExtension (source.ItemSpec) == TargetName,
+ IsUserAssembly = ResolvedUserAssemblies.Any (a => a.ItemSpec == source.ItemSpec),
+ };
- return MonoAndroidHelper.IsMonoAndroidAssembly (source);
- }
+ var changed = pipeline.Run (assembly, context);
- AndroidTargetArch GetValidArchitecture (ITaskItem item)
- {
- AndroidTargetArch ret = MonoAndroidHelper.GetTargetArch (item);
- if (ret == AndroidTargetArch.None) {
- throw new InvalidOperationException ($"Internal error: assembly '{item}' doesn't target any architecture.");
+ if (changed) {
+ Log.LogDebugMessage ($"Saving modified assembly: {destination.ItemSpec}");
+ Directory.CreateDirectory (Path.GetDirectoryName (destination.ItemSpec));
+ writerParameters.WriteSymbols = assembly.MainModule.HasSymbols;
+ assembly.Write (destination.ItemSpec, writerParameters);
+ } else {
+ // If we didn't write a modified file, copy the original to the destination
+ CopyIfChanged (source, destination);
}
-
- return ret;
}
- protected sealed class RunState : IDisposable
+ void CopyIfChanged (ITaskItem source, ITaskItem destination)
{
- public DirectoryAssemblyResolver resolver;
- public FixAbstractMethodsStep? fixAbstractMethodsStep = null;
- public AddKeepAlivesStep? addKeepAliveStep = null;
- public FixLegacyResourceDesignerStep? fixLegacyResourceDesignerStep = null;
- public FindJavaObjectsStep? findJavaObjectsStep = null;
- bool disposed_value;
-
- public RunState (DirectoryAssemblyResolver resolver)
- {
- this.resolver = resolver;
- }
-
- private void Dispose (bool disposing)
- {
- if (!disposed_value) {
- if (disposing) {
- resolver?.Dispose ();
- fixAbstractMethodsStep = null;
- fixLegacyResourceDesignerStep = null;
- addKeepAliveStep = null;
- findJavaObjectsStep = null;
- }
- disposed_value = true;
- }
- }
+ if (MonoAndroidHelper.CopyAssemblyAndSymbols (source.ItemSpec, destination.ItemSpec)) {
+ Log.LogDebugMessage ($"Copied: {destination.ItemSpec}");
+ } else {
+ Log.LogDebugMessage ($"Skipped unchanged file: {destination.ItemSpec}");
- public void Dispose ()
- {
- // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
- Dispose (disposing: true);
- GC.SuppressFinalize (this);
+ // NOTE: We still need to update the timestamp on this file, or this target would run again
+ File.SetLastWriteTimeUtc (destination.ItemSpec, DateTime.UtcNow);
}
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs
index 5e2e06578a6..c9c47b5adc7 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs
@@ -1,9 +1,5 @@
#nullable enable
using System;
-using System.IO;
-using Microsoft.Android.Build.Tasks;
-using Microsoft.Build.Framework;
-using Mono.Cecil;
using Mono.Linker.Steps;
using MonoDroid.Tuner;
@@ -15,91 +11,35 @@ namespace Xamarin.Android.Tasks
///
public class LinkAssembliesNoShrink : AssemblyModifierPipeline
{
- // Names of assemblies which don't have Mono.Android.dll references, or are framework assemblies, but which must
public override string TaskPrefix => "LNS";
- ///
- /// $(TargetName) would be "AndroidApp1" with no extension
- ///
- [Required]
- public string TargetName { get; set; } = "";
-
public bool AddKeepAlives { get; set; }
public bool UseDesignerAssembly { get; set; }
- protected override void CreateRunState (RunState runState, MSBuildLinkContext context)
+ protected override void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkContext context)
{
- // Create the additional steps that we want to run since ILLink won't be run
+ // FixAbstractMethodsStep
var fixAbstractMethodsStep = new FixAbstractMethodsStep ();
fixAbstractMethodsStep.Initialize (context, new EmptyMarkContext ());
- runState.fixAbstractMethodsStep = fixAbstractMethodsStep;
-
- var addKeepAliveStep = new AddKeepAlivesStep ();
- addKeepAliveStep.Initialize (context);
- runState.addKeepAliveStep = addKeepAliveStep;
-
- var fixLegacyResourceDesignerStep = new FixLegacyResourceDesignerStep ();
- fixLegacyResourceDesignerStep.Initialize (context);
- runState.fixLegacyResourceDesignerStep = fixLegacyResourceDesignerStep;
-
- // base must be called to initialize AssemblyModifierPipeline steps
- base.CreateRunState (runState, context);
- }
+ pipeline.Steps.Add (fixAbstractMethodsStep);
- protected override void RunPipeline (ITaskItem source, ITaskItem destination, RunState runState, WriterParameters writerParameters)
- {
- if (!TryModifyAssembly (source, destination, runState, writerParameters)) {
- // If we didn't write a modified file, copy the original to the destination
- CopyIfChanged (source, destination);
+ // FixLegacyResourceDesignerStep
+ if (UseDesignerAssembly) {
+ var fixLegacyResourceDesignerStep = new FixLegacyResourceDesignerStep ();
+ fixLegacyResourceDesignerStep.Initialize (context);
+ pipeline.Steps.Add (fixLegacyResourceDesignerStep);
}
- // base must be called to run AssemblyModifierPipeline steps
- base.RunPipeline (source, destination, runState, writerParameters);
- }
-
- bool TryModifyAssembly (ITaskItem source, ITaskItem destination, RunState runState, WriterParameters writerParameters)
- {
- var assemblyName = Path.GetFileNameWithoutExtension (source.ItemSpec);
-
- // In .NET 6+, we can skip the main assembly
- if (!AddKeepAlives && assemblyName == TargetName)
- return false;
-
- if (MonoAndroidHelper.IsFrameworkAssembly (source))
- return false;
-
- // Only run steps on "MonoAndroid" assemblies
- if (MonoAndroidHelper.IsMonoAndroidAssembly (source)) {
- AssemblyDefinition assemblyDefinition = runState.resolver!.GetAssembly (source.ItemSpec);
-
- bool save = runState.fixAbstractMethodsStep!.FixAbstractMethods (assemblyDefinition);
- if (UseDesignerAssembly)
- save |= runState.fixLegacyResourceDesignerStep!.ProcessAssemblyDesigner (assemblyDefinition);
- if (AddKeepAlives)
- save |= runState.addKeepAliveStep!.AddKeepAlives (assemblyDefinition);
- if (save) {
- Log.LogDebugMessage ($"Saving modified assembly: {destination.ItemSpec}");
- Directory.CreateDirectory (Path.GetDirectoryName (destination.ItemSpec));
- writerParameters.WriteSymbols = assemblyDefinition.MainModule.HasSymbols;
- assemblyDefinition.Write (destination.ItemSpec, writerParameters);
- return true;
- }
+ // AddKeepAlivesStep
+ if (AddKeepAlives) {
+ var addKeepAliveStep = new AddKeepAlivesStep ();
+ addKeepAliveStep.Initialize (context);
+ pipeline.Steps.Add (addKeepAliveStep);
}
- return false;
- }
-
- void CopyIfChanged (ITaskItem source, ITaskItem destination)
- {
- if (MonoAndroidHelper.CopyAssemblyAndSymbols (source.ItemSpec, destination.ItemSpec)) {
- Log.LogDebugMessage ($"Copied: {destination.ItemSpec}");
- } else {
- Log.LogDebugMessage ($"Skipped unchanged file: {destination.ItemSpec}");
-
- // NOTE: We still need to update the timestamp on this file, or this target would run again
- File.SetLastWriteTimeUtc (destination.ItemSpec, DateTime.UtcNow);
- }
+ // Ensure the task's steps are added
+ base.BuildPipeline (pipeline, context);
}
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyPipeline.cs b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyPipeline.cs
new file mode 100644
index 00000000000..5cf54ca78af
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyPipeline.cs
@@ -0,0 +1,76 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using Java.Interop.Tools.Cecil;
+using Java.Interop.Tools.JavaCallableWrappers;
+using Microsoft.Build.Framework;
+using Mono.Cecil;
+
+namespace Xamarin.Android.Tasks;
+
+public class AssemblyPipeline : IDisposable
+{
+ bool disposed_value;
+
+ public List Steps { get; } = [];
+ public DirectoryAssemblyResolver Resolver { get; }
+
+ public AssemblyPipeline (DirectoryAssemblyResolver resolver)
+ {
+ Resolver = resolver;
+ }
+
+ public bool Run (AssemblyDefinition assembly, StepContext context)
+ {
+ var changed = false;
+
+ foreach (var step in Steps)
+ changed |= step.ProcessAssembly (assembly, context);
+
+ return changed;
+ }
+
+ protected virtual void Dispose (bool disposing)
+ {
+ if (!disposed_value) {
+ if (disposing) {
+ Resolver?.Dispose ();
+ }
+
+ disposed_value = true;
+ }
+ }
+
+ public void Dispose ()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose (bool disposing)' method
+ Dispose (disposing: true);
+ GC.SuppressFinalize (this);
+ }
+}
+
+public interface IAssemblyModifierPipelineStep
+{
+ bool ProcessAssembly (AssemblyDefinition assembly, StepContext context);
+}
+
+public class StepContext
+{
+ public JavaPeerStyle CodeGenerationTarget { get; set; }
+ public ITaskItem Destination { get; }
+ public bool EnableMarshalMethods { get; set; }
+ public bool IsAndroidAssembly { get; set; }
+ public bool IsDebug { get; set; }
+ public bool IsFrameworkAssembly { get; set; }
+ public bool IsMainAssembly { get; set; }
+ public bool IsUserAssembly { get; set; }
+ public ITaskItem Source { get; }
+
+ public bool IsAndroidUserAssembly => IsAndroidAssembly && IsUserAssembly;
+
+ public StepContext (ITaskItem source, ITaskItem destination)
+ {
+ Source = source;
+ Destination = destination;
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.Linker.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.Linker.cs
index ec987463124..19d0d68421d 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.Linker.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.Linker.cs
@@ -12,6 +12,7 @@ public partial class MonoAndroidHelper
static readonly HashSet KnownAssemblyNames = new (StringComparer.OrdinalIgnoreCase) {
"Mono.Android",
"Mono.Android.Export",
+ "Mono.Android.Runtime",
"Java.Interop",
};
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
index bd5f567cae6..d528ea32e4e 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
@@ -278,6 +278,18 @@ public static void LogWarning (object log, string msg, params object [] args)
}
#if MSBUILD
+ public static bool IsAndroidAssembly (ITaskItem source)
+ {
+ string name = Path.GetFileNameWithoutExtension (source.ItemSpec);
+
+ // Check for assemblies which may not be built against the Android profile (`netXX-android`)
+ // but could still contain Android binding code (like Mono.Android).
+ if (KnownAssemblyNames.Contains (name))
+ return true;
+
+ return IsMonoAndroidAssembly (source);
+ }
+
public static bool IsMonoAndroidAssembly (ITaskItem assembly)
{
// NOTE: look for both MonoAndroid and Android
@@ -554,6 +566,18 @@ public static string GetAssemblyAbi (ITaskItem asmItem)
}
public static AndroidTargetArch GetTargetArch (ITaskItem asmItem) => AbiToTargetArch (GetAssemblyAbi (asmItem));
+
+
+ public static AndroidTargetArch GetRequiredValidArchitecture (ITaskItem item)
+ {
+ AndroidTargetArch ret = GetTargetArch (item);
+
+ if (ret == AndroidTargetArch.None) {
+ throw new InvalidOperationException ($"Internal error: assembly '{item}' doesn't target any architecture.");
+ }
+
+ return ret;
+ }
#endif // MSBUILD
static string GetToolsRootDirectoryRelativePath (string androidBinUtilsDirectory)
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
index 526bc869aaa..b4b2bde2a16 100644
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
@@ -1511,7 +1511,8 @@ because xbuild doesn't support framework reference assemblies.
ReadSymbols="$(_AndroidLinkAssembliesReadSymbols)"
ResolvedAssemblies="@(_AllResolvedAssemblies)"
ResolvedUserAssemblies="@(ResolvedUserAssemblies)"
- SourceFiles="@(ResolvedAssemblies)">
+ SourceFiles="@(ResolvedAssemblies)"
+ TargetName="$(TargetName)">