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