From f5ab40756a92a0325a585e02e6c11e35d3ca16ad Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Oct 2026 08:34:46 +0200 Subject: [PATCH 1/3] Fix PE timestamp for deterministic resource designer assemblies (#13144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cecil’s wall-clock PE timestamp changes `_Microsoft.Android.Resource.Designer.dll` between identical builds, even with `Deterministic=true`. The timestamp also changes the content-derived MVID, cascading differences into application assemblies and downstream native artifacts. - **Generation:** Set `WriterParameters.Timestamp` to zero for deterministic output; retain Cecil’s default timestamp otherwise. ```csharp DeterministicMvid = Deterministic, Timestamp = Deterministic ? 0u : null, ``` - **Regression coverage:** Cover application and library designers in both modes, checking deterministic byte equality and matching MVIDs, plus preservation of non-deterministic timestamp and MVID behavior. - Fixes #13140 --- .../Tasks/GenerateResourceDesignerAssembly.cs | 1 + .../Tasks/AndroidResourceTests.cs | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerAssembly.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerAssembly.cs index 9b1983bdf76..0f23d2985ba 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerAssembly.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerAssembly.cs @@ -206,6 +206,7 @@ bool Run (DirectoryAssemblyResolver res) StrongNameAssembly (assembly.Name); var wp = new WriterParameters () { DeterministicMvid = Deterministic, + Timestamp = Deterministic ? 0u : null, }; var s = MemoryStreamPool.Shared.Rent (); try { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/AndroidResourceTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/AndroidResourceTests.cs index fc24e5348d2..163f7dd98a2 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/AndroidResourceTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/AndroidResourceTests.cs @@ -7,12 +7,72 @@ using Microsoft.Build.Framework; using Xamarin.Android.Tasks; using Microsoft.Build.Utilities; +using System.Reflection.PortableExecutable; +using Mono.Cecil; namespace Xamarin.Android.Build.Tests { [TestFixture] [Parallelizable (ParallelScope.Self)] public class AndroidResourceTests : BaseTest { + [TestCase (true, true)] + [TestCase (true, false)] + [TestCase (false, true)] + [TestCase (false, false)] + public void ResourceDesignerAssemblyDeterministic (bool deterministic, bool isApplication) + { + var path = Path.Combine (Root, "temp", TestName); + Directory.CreateDirectory (path); + var rtxt = Path.Combine (path, "R.txt"); + File.WriteAllText (rtxt, """ + int string app_name 0x7f010001 + int[] styleable TestView { 0x7f010001, 0x7f010002 } + int styleable TestView_text 0 + """); + var output = Path.Combine (path, "_Microsoft.Android.Resource.Designer.dll"); + var start = DateTimeOffset.UtcNow.ToUnixTimeSeconds (); + + void Generate () + { + var task = new GenerateResourceDesignerAssembly { + BuildEngine = new MockBuildEngine (TestContext.Out), + RTxtFile = new TaskItem (rtxt), + OutputFile = new TaskItem (output), + CaseMapFile = Path.Combine (path, "case_map.txt"), + ProjectDir = path, + ResourceDirectory = Path.Combine (path, "res"), + FrameworkDirectories = [new TaskItem (Path.GetDirectoryName (typeof (object).Assembly.Location))], + Deterministic = deterministic, + IsApplication = isApplication, + AssemblyName = "TestApp", + }; + Assert.IsTrue (task.Execute (), "Designer generation should succeed."); + using var stream = File.OpenRead (output); + using var reader = new PEReader (stream); + var timestamp = reader.PEHeaders.CoffHeader.TimeDateStamp; + if (deterministic) + Assert.AreEqual (0, timestamp, "Deterministic output must have a fixed PE timestamp."); + else + Assert.That ((long) timestamp, Is.InRange (start, DateTimeOffset.UtcNow.ToUnixTimeSeconds ()), "Non-deterministic output must retain the current PE timestamp."); + } + + Generate (); + var first = File.ReadAllBytes (output); + Guid firstMvid; + using (var firstAssembly = AssemblyDefinition.ReadAssembly (output)) + firstMvid = firstAssembly.MainModule.Mvid; + File.Delete (output); + Generate (); + var second = File.ReadAllBytes (output); + using var secondAssembly = AssemblyDefinition.ReadAssembly (output); + if (deterministic) { + CollectionAssert.AreEqual (first, second, "Clean generations with identical inputs must be byte-identical."); + Assert.AreEqual (firstMvid, secondAssembly.MainModule.Mvid); + } else { + Assert.AreNotEqual (firstMvid, secondAssembly.MainModule.Mvid); + } + } + [Test] public void RTxtParserTest () { From b79f628129bbb1e2cd21c2ccd99353def54df820 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 16 Oct 2026 08:24:01 -0500 Subject: [PATCH 2/3] Invalidate Android property cache when switching runtimes Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 109f55a1-56cd-4551-8f90-4c1704c22f4f --- src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 3a0c742f1d5..fa58fc653e4 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1005,6 +1005,7 @@ because xbuild doesn't support framework reference assemblies. <_PropertyCacheItems Include="ProjectFullPath=$(MSBuildProjectFullPath)" /> <_PropertyCacheItems Include="AndroidUseDesignerAssembly=$(AndroidUseDesignerAssembly)" /> <_PropertyCacheItems Include="AndroidTypeMapImplementation=$(AndroidTypeMapImplementation)" /> + <_PropertyCacheItems Include="_AndroidRuntime=$(_AndroidRuntime)" /> <_PropertyCacheItems Include="_AndroidUseMarshalMethods=$(_AndroidUseMarshalMethods)" /> <_PropertyCacheItems Include="_AndroidJcwCodegenTarget=$(_AndroidJcwCodegenTarget)" /> <_PropertyCacheItems Include="_AndroidAssemblyStoreCompressionLevel=$(_AndroidAssemblyStoreCompressionLevel)" /> From 3dd8d4eac6a60c8d6319130f018dec5ba97f18a8 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 16 Oct 2026 14:44:23 -0500 Subject: [PATCH 3/3] Invalidate linker cache when Android build properties change Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 109f55a1-56cd-4551-8f90-4c1704c22f4f --- .../Microsoft.Android.Sdk.AssemblyResolution.targets | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index 2b056f02870..b571d109f67 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -35,6 +35,14 @@ _ResolveAssemblies MSBuild target. <_RunAotMaybe Condition=" '$(_AndroidUseMarshalMethods)' != 'True' ">_AndroidAot + + + +