diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/CheckForObsoletePreserveAttributeStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/CheckForObsoletePreserveAttributeStep.cs index fb80578ef9a..9099132cf6c 100644 --- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/CheckForObsoletePreserveAttributeStep.cs +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/CheckForObsoletePreserveAttributeStep.cs @@ -9,7 +9,7 @@ namespace MonoDroid.Tuner; /// /// Post-trimming step that warns when an assembly references the obsolete -/// Android.Runtime.PreserveAttribute. Runs as part of PostTrimmingPipeline +/// Android.Runtime.PreserveAttribute. Runs as part of AssemblyModifierPipeline /// so the assemblies are already loaded by Mono.Cecil and the check is free. /// class CheckForObsoletePreserveAttributeStep : IAssemblyModifierPipelineStep diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index b4ecd0f5fe6..ce892d97165 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -126,6 +126,23 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. + + + + + + + @@ -150,6 +167,8 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_AndroidILLinkAssemblies Include="@(ManagedAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" Condition="Exists('$(IntermediateLinkDir)%(Filename)%(Extension)')" /> + + <_AndroidILLinkAssemblies Remove="$(IntermediateLinkDir)$(TargetName)$(TargetExt)" /> diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets index 9b8870a8295..8de11e61c0c 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets @@ -7,7 +7,6 @@ - <_RemoveRegisterFlag>$(MonoAndroidIntermediateAssemblyDir)shrunk\shrunk.flag @@ -232,25 +231,6 @@ - - - - <_PostTrimmingAssembly Include="@(ResolvedFileToPublish)" Condition=" '%(Extension)' == '.dll' " /> - - - - diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs index 08a558de918..14fc35fce0d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs @@ -18,14 +18,24 @@ namespace Xamarin.Android.Tasks; /// -/// This task runs additional "linker steps" that are not part of ILLink. These steps -/// are run *after* the linker has run. Additionally, this task is run by -/// LinkAssembliesNoShrink to modify assemblies when ILLink is not used. +/// This task runs assembly modification steps that are not part of ILLink. +/// +/// For trimmed builds, this runs after ILLink and includes post-trimming steps +/// (CheckForObsoletePreserveAttribute, StripEmbeddedLibraries, AddKeepAlives, +/// RemoveResourceDesigner) followed by common steps (FindJavaObjects, +/// SaveChangedAssembly, FindTypeMapObjects). +/// +/// For non-trimmed builds, LinkAssembliesNoShrink extends this task and overrides +/// BuildAssemblyModificationSteps to add non-trimmed-specific steps instead. /// public class AssemblyModifierPipeline : AndroidTask { public override string TaskPrefix => "AMP"; + public bool AddKeepAlives { get; set; } + + public bool AndroidLinkResources { get; set; } + public string ApplicationJavaClass { get; set; } = ""; public string CodeGenerationTarget { get; set; } = ""; @@ -111,7 +121,6 @@ public override bool RunTask () } } - // Set up the FixAbstractMethodsStep and AddKeepAlivesStep var context = new MSBuildLinkContext (resolver, Log); pipeline = new AssemblyPipeline (resolver); @@ -130,6 +139,8 @@ public override bool RunTask () protected virtual void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkContext context) { + BuildAssemblyModificationSteps (pipeline, context); + // FindJavaObjectsStep var findJavaObjectsStep = new FindJavaObjectsStep (Log) { ApplicationJavaClass = ApplicationJavaClass, @@ -158,6 +169,53 @@ protected virtual void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkCont pipeline.Steps.Add (findTypeMapObjectsStep); } + /// + /// Builds the assembly modification steps that run before FindJavaObjects/Save/FindTypeMapObjects. + /// For trimmed builds (default), this adds post-trimming steps. + /// LinkAssembliesNoShrink overrides this for non-trimmed builds. + /// + protected virtual void BuildAssemblyModificationSteps (AssemblyPipeline pipeline, MSBuildLinkContext context) + { + // CheckForObsoletePreserveAttributeStep + pipeline.Steps.Add (new CheckForObsoletePreserveAttributeStep (Log)); + + // StripEmbeddedLibrariesStep + pipeline.Steps.Add (new StripEmbeddedLibrariesStep (Log)); + + // PostTrimmingAddKeepAlivesStep + if (AddKeepAlives) { + var cache = new TypeDefinitionCache (); + + // Memoize the corlib resolution so the attempt (and any error logging) happens at most once, + // regardless of how many assemblies/methods need KeepAlive injection. + AssemblyDefinition? corlibAssembly = null; + bool corlibResolutionAttempted = false; + + pipeline.Steps.Add (new PostTrimmingAddKeepAlivesStep (cache, + () => { + if (!corlibResolutionAttempted) { + corlibResolutionAttempted = true; + try { + corlibAssembly = pipeline.Resolver.Resolve (AssemblyNameReference.Parse ("System.Private.CoreLib")); + } catch (AssemblyResolutionException ex) { + Log.LogErrorFromException (ex, showStackTrace: false); + } + } + return corlibAssembly; + }, + (msg) => Log.LogDebugMessage (msg))); + } + + // RemoveResourceDesignerStep + if (AndroidLinkResources) { + var allAssemblies = new List (SourceFiles.Length); + foreach (var item in SourceFiles) { + allAssemblies.Add (pipeline.Resolver.GetAssembly (item.ItemSpec)); + } + pipeline.Steps.Add (new RemoveResourceDesignerStep (allAssemblies, (msg) => Log.LogDebugMessage (msg))); + } + } + void RunPipeline (AssemblyPipeline pipeline, ITaskItem source, ITaskItem destination) { var assembly = pipeline.Resolver.GetAssembly (source.ItemSpec); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs index 9dc7e99e635..70deda9c8a5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs @@ -14,11 +14,9 @@ public class LinkAssembliesNoShrink : AssemblyModifierPipeline { public override string TaskPrefix => "LNS"; - public bool AddKeepAlives { get; set; } - public bool UseDesignerAssembly { get; set; } - protected override void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkContext context) + protected override void BuildAssemblyModificationSteps (AssemblyPipeline pipeline, MSBuildLinkContext context) { // FixAbstractMethodsStep var fixAbstractMethodsStep = new FixAbstractMethodsStep (); @@ -38,9 +36,6 @@ protected override void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkCon addKeepAliveStep.Initialize (context); pipeline.Steps.Add (addKeepAliveStep); } - - // Ensure the task's steps are added - base.BuildPipeline (pipeline, context); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs deleted file mode 100644 index 56fb01b6485..00000000000 --- a/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs +++ /dev/null @@ -1,98 +0,0 @@ -#nullable enable - -using System.Collections.Generic; -using System.IO; -using Java.Interop.Tools.Cecil; -using Microsoft.Android.Build.Tasks; -using Microsoft.Build.Framework; -using Mono.Cecil; -using MonoDroid.Tuner; - -namespace Xamarin.Android.Tasks; - -/// -/// An MSBuild task that runs post-trimming assembly modifications in a single pass. -/// -/// This opens each assembly once (via DirectoryAssemblyResolver with ReadWrite) and -/// runs all registered steps on it, then writes modified assemblies in-place. Currently -/// runs CheckForObsoletePreserveAttributeStep, StripEmbeddedLibrariesStep and -/// (optionally) AddKeepAlivesStep. -/// -/// Runs in the inner build after ILLink but before ReadyToRun/crossgen2 compilation, -/// so that R2R images are generated from the already-modified assemblies. -/// -public class PostTrimmingPipeline : AndroidTask -{ - public override string TaskPrefix => "PTP"; - - [Required] - public ITaskItem [] Assemblies { get; set; } = []; - - public bool AddKeepAlives { get; set; } - - public bool AndroidLinkResources { get; set; } - - public bool Deterministic { get; set; } - - public override bool RunTask () - { - using var resolver = new DirectoryAssemblyResolver ( - this.CreateTaskLogger (), loadDebugSymbols: true, - loadReaderParameters: new ReaderParameters { ReadWrite = true }); - var cache = new TypeDefinitionCache (); - - foreach (var assembly in Assemblies) { - var dir = Path.GetFullPath (Path.GetDirectoryName (assembly.ItemSpec) ?? ""); - if (!resolver.SearchDirectories.Contains (dir)) { - resolver.SearchDirectories.Add (dir); - } - } - - var steps = new List (); - steps.Add (new CheckForObsoletePreserveAttributeStep (Log)); - steps.Add (new StripEmbeddedLibrariesStep (Log)); - if (AddKeepAlives) { - // Memoize the corlib resolution so the attempt (and any error logging) happens at most once, - // regardless of how many assemblies/methods need KeepAlive injection. - AssemblyDefinition? corlibAssembly = null; - bool corlibResolutionAttempted = false; - steps.Add (new PostTrimmingAddKeepAlivesStep (cache, - () => { - if (!corlibResolutionAttempted) { - corlibResolutionAttempted = true; - try { - corlibAssembly = resolver.Resolve (AssemblyNameReference.Parse ("System.Private.CoreLib")); - } catch (AssemblyResolutionException ex) { - Log.LogErrorFromException (ex, showStackTrace: false); - } - } - return corlibAssembly; - }, - (msg) => Log.LogDebugMessage (msg))); - } - if (AndroidLinkResources) { - var allAssemblies = new List (Assemblies.Length); - foreach (var item in Assemblies) { - allAssemblies.Add (resolver.GetAssembly (item.ItemSpec)); - } - steps.Add (new RemoveResourceDesignerStep (allAssemblies, (msg) => Log.LogDebugMessage (msg))); - } - - foreach (var item in Assemblies) { - var assembly = resolver.GetAssembly (item.ItemSpec); - var context = new StepContext (item, item); - foreach (var step in steps) { - step.ProcessAssembly (assembly, context); - } - if (context.IsAssemblyModified) { - Log.LogDebugMessage ($" Writing modified assembly: {item.ItemSpec}"); - assembly.Write (new WriterParameters { - WriteSymbols = assembly.MainModule.HasSymbols, - DeterministicMvid = Deterministic, - }); - } - } - - return !Log.HasLoggedErrors; - } -} diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs index b0dcc115436..dc7591d44e9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs @@ -560,11 +560,9 @@ public void CheckSignApk ([Values] bool useApkSigner, [Values] bool perAbiApk, [ item.TextContent = () => proj.StringsXml.Replace ("${PROJECT_NAME}", "Foo"); item.Timestamp = null; Assert.IsTrue (b.Build (proj), "Second build failed"); - if (runtime != AndroidRuntime.NativeAOT) { - b.AssertHasNoWarnings (); - } else { - StringAssertEx.Contains ("2 Warning(s)", b.LastBuildOutput, "NativeAOT should produce two IL3053 warnings"); - } + // Only Strings.xml changed, so assemblies are unchanged and ILLink + // correctly skips — no IL3053 warnings are expected for any runtime. + b.AssertHasNoWarnings (); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles (bin, "*-Signed.apk")) { diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj index a0ed0a47ead..d63b6bfdc4c 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj @@ -44,8 +44,8 @@ - - + + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index c2527d1954e..6d187855e88 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1454,33 +1454,261 @@ because xbuild doesn't support framework reference assemblies. - + + + + + + + + + + <_AfterILLinkAssembliesRaw + Include="@(ManagedAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" + Condition=" Exists('$(IntermediateLinkDir)%(Filename)%(Extension)') " /> + + + + + + + + <_AfterILLinkAssemblies + Include="$(IntermediateLinkDir)$(TargetName)$(TargetExt)" + Exclude="@(_AfterILLinkAssemblies)" + Condition=" Exists('$(IntermediateLinkDir)$(TargetName)$(TargetExt)') " /> + + + + + <_AfterILLinkAssemblies Update="@(_AfterILLinkAssemblies)" Abi="$(_AfterILLinkAbi)" /> + <_AfterILLinkAssemblies + Update="$(IntermediateLinkDir)$(TargetName)$(TargetExt)" + HasMonoAndroidReference="true" /> + + + + + <_AfterILLinkUserAssemblies Include="@(_AfterILLinkAssemblies)" + Condition=" '%(Filename)' != 'Mono.Android' and '%(Filename)' != 'Mono.Android.Export' and '%(Filename)' != 'Mono.Android.Runtime' and '%(Filename)' != 'Java.Interop' " /> + + + + + + + + + + + <_AfterILLinkAssembliesRaw Remove="@(_AfterILLinkAssembliesRaw)" /> + <_AfterILLinkAssemblies Remove="@(_AfterILLinkAssemblies)" /> + <_AfterILLinkUserAssemblies Remove="@(_AfterILLinkUserAssemblies)" /> + + + + + + + <_R2RCompositeAssemblies Include="@(_ResolvedAssemblies)" Condition=" $([System.String]::Copy('%(Filename)').EndsWith('.r2r')) " /> + <_NonCompositeAssemblies Include="@(_ResolvedAssemblies)" Condition=" !$([System.String]::Copy('%(Filename)').EndsWith('.r2r')) " /> + + + + + <_R2RCompositeSidecarFiles Include="@(_R2RCompositeAssemblies->'%(RootDir)%(Directory)%(Filename).jlo.xml')" /> + <_R2RCompositeSidecarFiles Include="@(_R2RCompositeAssemblies->'%(RootDir)%(Directory)%(Filename).typemap.xml')" /> + + + + + <_MissingR2RCompositeSidecarFiles Include="@(_R2RCompositeSidecarFiles)" Condition=" !Exists('%(Identity)') " /> + + + + + + + + <_SidecarLinkedDir Condition=" $(IntermediateOutputPath.Replace('\','/').TrimEnd('/').EndsWith('$(RuntimeIdentifier)')) ">$(IntermediateOutputPath)linked\ + <_SidecarLinkedDir Condition=" '$(_SidecarLinkedDir)' == '' ">$(IntermediateOutputPath)$(RuntimeIdentifier)\linked\ + + + <_SidecarXmlCopySource Include="@(_NonCompositeAssemblies->'$(IntermediateOutputPath)%(RuntimeIdentifier)\linked\%(Filename).jlo.xml')" /> + <_SidecarXmlCopySource Include="@(_NonCompositeAssemblies->'$(IntermediateOutputPath)%(RuntimeIdentifier)\linked\%(Filename).typemap.xml')" /> + + + <_SidecarXmlCopySource Include="@(_NonCompositeAssemblies->'$(_SidecarLinkedDir)%(Filename).jlo.xml')" /> + <_SidecarXmlCopySource Include="@(_NonCompositeAssemblies->'$(_SidecarLinkedDir)%(Filename).typemap.xml')" /> + + + + + + <_MissingSidecarXmlCopySource Include="@(_SidecarXmlCopySource)" Condition=" !Exists('%(Identity)') " /> + + + + + <_SidecarXmlCopyDestination Include="@(_NonCompositeAssemblies->'%(RootDir)%(Directory)%(Filename).jlo.xml')" /> + <_SidecarXmlCopyDestination Include="@(_NonCompositeAssemblies->'%(RootDir)%(Directory)%(Filename).typemap.xml')" /> + + + + + + + + + + <_SidecarXmlCopySource Remove="@(_SidecarXmlCopySource)" /> + <_MissingSidecarXmlCopySource Remove="@(_MissingSidecarXmlCopySource)" /> + <_SidecarXmlCopyDestination Remove="@(_SidecarXmlCopyDestination)" /> + <_NonCompositeAssemblies Remove="@(_NonCompositeAssemblies)" /> + <_R2RCompositeAssemblies Remove="@(_R2RCompositeAssemblies)" /> + <_R2RCompositeSidecarFiles Remove="@(_R2RCompositeSidecarFiles)" /> + <_MissingR2RCompositeSidecarFiles Remove="@(_MissingR2RCompositeSidecarFiles)" /> + + + <_GenerateJavaStubsInputs Include="@(_AndroidMSBuildAllProjects)" />