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)">