From b17626ff1c1172ce509ca1a2394c1a1115ab9a24 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 7 Aug 2023 20:19:30 +0200 Subject: [PATCH] [msbuild] Re-aot referencing assemblies. Fixes #17708. (#18509) If an assembly changes, then we must AOT compile that assembly again (which we already did), in addition to any assembly that references the modified assembly (which we didn't do). So rework the AOTCompile target: remove the Inputs and Outputs (because the dependency tracking is too complicated for MSBuild to resolve), and instead move the logic to detect if an assembly must be AOT-compiled again into the AOTCompile task. Note that this PR has a custom port to .NET 8: #18518. Fixes https://github.com/xamarin/xamarin-macios/issues/17708. --------- Co-authored-by: Alex Soto --- dotnet/targets/Xamarin.Shared.Sdk.targets | 3 +- .../MSBStrings.resx | 29 ++++ .../Tasks/AOTCompileTaskBase.cs | 135 ++++++++++++++++-- .../AppDelegate.cs | 18 +++ .../Library/Library.cs | 15 ++ .../Library/MacCatalyst/Library.csproj | 7 + .../Library/MacCatalyst/Makefile | 1 + .../Library/Makefile | 2 + .../Library/iOS/Library.csproj | 7 + .../Library/iOS/Makefile | 1 + .../Library/macOS/Library.csproj | 7 + .../Library/macOS/Makefile | 1 + .../Library/shared.csproj | 12 ++ .../Library/shared.mk | 2 + .../Library/tvOS/Library.csproj | 7 + .../Library/tvOS/Makefile | 1 + .../MacCatalyst/Makefile | 1 + .../RebuildTestAppWithLibraryReference.csproj | 7 + .../Makefile | 2 + .../iOS/Makefile | 1 + .../RebuildTestAppWithLibraryReference.csproj | 7 + .../macOS/Makefile | 1 + .../RebuildTestAppWithLibraryReference.csproj | 7 + .../shared.csproj | 19 +++ .../shared.mk | 2 + .../tvOS/Makefile | 1 + .../RebuildTestAppWithLibraryReference.csproj | 7 + tests/dotnet/UnitTests/RebuildTest.cs | 39 +++++ tests/dotnet/UnitTests/TestBaseClass.cs | 9 +- 29 files changed, 332 insertions(+), 19 deletions(-) create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/AppDelegate.cs create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/Library.cs create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/MacCatalyst/Library.csproj create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/MacCatalyst/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/iOS/Library.csproj create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/iOS/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/macOS/Library.csproj create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/macOS/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/shared.csproj create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/shared.mk create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/tvOS/Library.csproj create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Library/tvOS/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/MacCatalyst/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/MacCatalyst/RebuildTestAppWithLibraryReference.csproj create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/iOS/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/iOS/RebuildTestAppWithLibraryReference.csproj create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/macOS/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/macOS/RebuildTestAppWithLibraryReference.csproj create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/shared.csproj create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/shared.mk create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/tvOS/Makefile create mode 100644 tests/dotnet/RebuildTestAppWithLibraryReference/tvOS/RebuildTestAppWithLibraryReference.csproj create mode 100644 tests/dotnet/UnitTests/RebuildTest.cs diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index 475d8fae412..a64abd1d3bf 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -1053,8 +1053,7 @@ + > + + The assembly {0} does not provide an 'ObjectFile' metadata. + + Don't translate: 'ObjectFile'. + + + + + The assembly {0} was passed multiple times as an input assembly (referenced from {1}). + + {0}: the name of an assembly + {1}: the path to an assembly + + + + + Failed to AOT compile {0}, the AOT compiler exited with code {1}. + + {0}: the path to an assembly + {1}: the exit code of a process + + + + + Encountered an assembly reference cycle related to the assembly {0}. + + {0}: the path to an assembly + + diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/AOTCompileTaskBase.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/AOTCompileTaskBase.cs index 67e13be2bbf..c32ed8ebaca 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/AOTCompileTaskBase.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/AOTCompileTaskBase.cs @@ -7,6 +7,8 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Mono.Cecil; + using Xamarin.Localization.MSBuild; using Xamarin.Utils; @@ -42,11 +44,111 @@ public abstract class AOTCompileTaskBase : XamarinTask { public ITaskItem []? FileWrites { get; set; } #endregion + class AssemblyInfo { + public ITaskItem TaskItem; + public bool? IsUpToDate; + + public AssemblyInfo (ITaskItem item) + { + TaskItem = item; + } + } + + Dictionary assemblyInfos = new Dictionary (StringComparer.OrdinalIgnoreCase); + + string GetAssemblyName (ITaskItem item) + { + return Path.GetFileNameWithoutExtension (item.ItemSpec); + } + + bool IsUpToDate (ITaskItem assembly) + { + var assemblyPath = assembly.ItemSpec; + var key = GetAssemblyName (assembly); + if (assemblyInfos.TryGetValue (key, out var info)) { + if (!info.IsUpToDate.HasValue) { + Log.LogError (MSBStrings.E7119 /* Encountered an assembly reference cycle related to the assembly {0}. */, assemblyPath); + info.IsUpToDate = false; + return false; + } + return info.IsUpToDate.Value; + } + + info = new AssemblyInfo (assembly); + assemblyInfos [key] = info; + + var finfo = new FileInfo (assemblyPath); + if (!finfo.Exists) { + Log.LogError (MSBStrings.E0158 /* The file {0} does not exist. */, assemblyPath); + info.IsUpToDate = false; + return false; + } + + // ObjectFile is required + var objectFile = assembly.GetMetadata ("ObjectFile"); + if (string.IsNullOrEmpty (objectFile)) { + Log.LogError (MSBStrings.E7116 /* The assembly {0} does not provide an 'ObjectFile' metadata. */, assembly.ItemSpec); + info.IsUpToDate = false; + return false; + } + var objectFileInfo = new FileInfo (objectFile); + if (!IsUpToDate (finfo, objectFileInfo)) { + Log.LogMessage (MessageImportance.Low, "The assembly {0} is not up-to-date with regards to the object file {1}", assemblyPath, objectFile); + info.IsUpToDate = false; + return false; + } + + // LLVMFile is optional + var llvmFile = assembly.GetMetadata ("LLVMFile"); + if (!string.IsNullOrEmpty (llvmFile)) { + var llvmFileInfo = new FileInfo (llvmFile); + if (!IsUpToDate (finfo, llvmFileInfo)) { + Log.LogMessage (MessageImportance.Low, "The assembly {0} is not up-to-date with regards to the llvm file {1}", assemblyPath, llvmFile); + info.IsUpToDate = false; + return false; + } + } + + // We know now the assembly itself is up-to-date, but what about every referenced assembly? + // This assembly must be AOT-compiled again if any referenced assembly has changed as well. + using var ad = AssemblyDefinition.ReadAssembly (assembly.ItemSpec, new ReaderParameters { ReadingMode = ReadingMode.Deferred }); + foreach (var ar in ad.MainModule.AssemblyReferences) { + var referencedItems = Assemblies.Where (v => string.Equals (GetAssemblyName (v), ar.Name, StringComparison.OrdinalIgnoreCase)).ToArray (); + if (referencedItems.Length == 0) { + Log.LogMessage (MessageImportance.Low, $"Ignoring unresolved assembly {ar.Name} (referenced from {assemblyPath})."); + continue; + } else if (referencedItems.Length > 1) { + Log.LogError (MSBStrings.E7117 /* The assembly {0} was passed multiple times as an input assembly (referenced from {1}). */, ar.Name, assemblyPath); + info.IsUpToDate = false; + return false; + } + var referencedItem = referencedItems [0]; + if (!IsUpToDate (referencedItem)) { + info.IsUpToDate = false; + Log.LogMessage (MessageImportance.Low, "The assembly {0} is not up-to-date with regards to the reference {1}", assemblyPath, ar.Name); + return false; + } + } + + Log.LogMessage (MessageImportance.Low, $"The AOT-compiled code for {assemblyPath} is up-to-date."); + info.IsUpToDate = true; + return true; + } + + bool IsUpToDate (FileInfo input, FileInfo output) + { + if (!output.Exists) + return false; + return input.LastWriteTimeUtc <= output.LastWriteTimeUtc; + } + public override bool Execute () { var inputs = new List (Assemblies.Length); for (var i = 0; i < Assemblies.Length; i++) { - inputs.Add (Path.GetFullPath (Assemblies [i].ItemSpec)); + var input = Path.GetFullPath (Assemblies [i].ItemSpec); + inputs.Add (input); + Assemblies [i].SetMetadata ("Input", input); } // All the assemblies to AOT must be in the same directory @@ -64,24 +166,31 @@ public override bool Execute () InputDirectory = assemblyDirectories [0]; } + // Figure out which assemblies need to be aot'ed, and which are up-to-date. + var assembliesToAOT = Assemblies.Where (asm => !IsUpToDate (asm)).ToArray (); + if (assembliesToAOT.Length == 0) { + Log.LogMessage (MessageImportance.Low, $"All the AOT-compiled code is up-to-date."); + return !Log.HasLoggedErrors; + } + Directory.CreateDirectory (OutputDirectory); var aotAssemblyFiles = new List (); - var processes = new Task [Assemblies.Length]; + var processes = new Task [assembliesToAOT.Length]; var environment = new Dictionary { { "MONO_PATH", Path.GetFullPath (InputDirectory) }, }; var globalAotArguments = AotArguments?.Select (v => v.ItemSpec).ToList (); - for (var i = 0; i < Assemblies.Length; i++) { - var asm = Assemblies [i]; - var input = inputs [i]; - var arch = Assemblies [i].GetMetadata ("Arch"); - var aotArguments = Assemblies [i].GetMetadata ("Arguments"); - var processArguments = Assemblies [i].GetMetadata ("ProcessArguments"); - var aotData = Assemblies [i].GetMetadata ("AOTData"); - var aotAssembly = Assemblies [i].GetMetadata ("AOTAssembly"); + for (var i = 0; i < assembliesToAOT.Length; i++) { + var asm = assembliesToAOT [i]; + var input = asm.GetMetadata ("Input"); + var arch = asm.GetMetadata ("Arch"); + var aotArguments = asm.GetMetadata ("Arguments"); + var processArguments = asm.GetMetadata ("ProcessArguments"); + var aotData = asm.GetMetadata ("AOTData"); + var aotAssembly = asm.GetMetadata ("AOTAssembly"); var aotAssemblyItem = new TaskItem (aotAssembly); aotAssemblyItem.SetMetadata ("Arguments", "-Xlinker -rpath -Xlinker @executable_path/ -Qunused-arguments -x assembler -D DEBUG"); @@ -106,7 +215,7 @@ public override bool Execute () processes [i] = ExecuteAsync (AOTCompilerPath, arguments, environment: environment, sdkDevPath: SdkDevPath, showErrorIfFailure: false /* we show our own error below */) .ContinueWith ((v) => { if (v.Result.ExitCode != 0) - Log.LogError ("Failed to AOT compile {0}, the AOT compiler exited with code {1}", Path.GetFileName (input), v.Result.ExitCode); + Log.LogError (MSBStrings.E7118 /* Failed to AOT compile {0}, the AOT compiler exited with code {1} */, Path.GetFileName (input), v.Result.ExitCode); return System.Threading.Tasks.Task.FromResult (v.Result); }).Unwrap (); @@ -118,8 +227,8 @@ public override bool Execute () // For Windows support it's necessary to have the files we're going to create as an Output parameter, so that the files are // created on the windows side too, which makes the Inputs/Outputs logic work properly when working from Windows. - var objectFiles = Assemblies.Select (v => v.GetMetadata ("ObjectFile")).Where (v => !string.IsNullOrEmpty (v)); - var llvmFiles = Assemblies.Select (v => v.GetMetadata ("LLVMFile")).Where (v => !string.IsNullOrEmpty (v)); + var objectFiles = assembliesToAOT.Select (v => v.GetMetadata ("ObjectFile")).Where (v => !string.IsNullOrEmpty (v)); + var llvmFiles = assembliesToAOT.Select (v => v.GetMetadata ("LLVMFile")).Where (v => !string.IsNullOrEmpty (v)); FileWrites = objectFiles.Union (llvmFiles).Select (v => new TaskItem (v)).ToArray (); return !Log.HasLoggedErrors; diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/AppDelegate.cs b/tests/dotnet/RebuildTestAppWithLibraryReference/AppDelegate.cs new file mode 100644 index 00000000000..9c40ddb7276 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/AppDelegate.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.InteropServices; + +using Foundation; + +namespace MySimpleApp { + public class Program { + static int Main (string [] args) + { + GC.KeepAlive (typeof (NSObject)); // prevent linking away the platform assembly + + Console.WriteLine (Environment.GetEnvironmentVariable ("MAGIC_WORD")); + Console.WriteLine (Library.Class.SomeFunction ()); + + return args.Length; + } + } +} diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/Library.cs b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/Library.cs new file mode 100644 index 00000000000..7f457d7e7ad --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/Library.cs @@ -0,0 +1,15 @@ +using System; +using System.Runtime.InteropServices; + +namespace Library { + public class Class { + public static string SomeFunction () + { +#if FIRSTBUILD + return "FIRSTBUILD"; +#elif SECONDBUILD + return "SECONDBUILD"; +#endif + } + } +} diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/MacCatalyst/Library.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/MacCatalyst/Library.csproj new file mode 100644 index 00000000000..6390277d5f5 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/MacCatalyst/Library.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + + + diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/MacCatalyst/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/MacCatalyst/Makefile new file mode 100644 index 00000000000..110d078f457 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/MacCatalyst/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/Makefile new file mode 100644 index 00000000000..a97c2cbb3d5 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/Makefile @@ -0,0 +1,2 @@ +TOP=../../../.. +include $(TOP)/tests/common/shared-dotnet-test.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/iOS/Library.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/iOS/Library.csproj new file mode 100644 index 00000000000..6390277d5f5 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/iOS/Library.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + + + diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/iOS/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/iOS/Makefile new file mode 100644 index 00000000000..110d078f457 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/iOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/macOS/Library.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/macOS/Library.csproj new file mode 100644 index 00000000000..6390277d5f5 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/macOS/Library.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + + + diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/macOS/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/macOS/Makefile new file mode 100644 index 00000000000..110d078f457 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/macOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/shared.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/shared.csproj new file mode 100644 index 00000000000..b1a43cfef9e --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/shared.csproj @@ -0,0 +1,12 @@ + + + + Library + $(DefineConstants);FIRSTBUILD + $(DefineConstants);SECONDBUILD + + + + + + diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/shared.mk b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/shared.mk new file mode 100644 index 00000000000..3b1ea8d6cf3 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/shared.mk @@ -0,0 +1,2 @@ +TOP=../../../../.. +include $(TOP)/tests/common/shared-dotnet.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/tvOS/Library.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/tvOS/Library.csproj new file mode 100644 index 00000000000..6390277d5f5 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/tvOS/Library.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + + + diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Library/tvOS/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/tvOS/Makefile new file mode 100644 index 00000000000..110d078f457 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Library/tvOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/MacCatalyst/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/MacCatalyst/Makefile new file mode 100644 index 00000000000..110d078f457 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/MacCatalyst/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/MacCatalyst/RebuildTestAppWithLibraryReference.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/MacCatalyst/RebuildTestAppWithLibraryReference.csproj new file mode 100644 index 00000000000..6b0e2c77318 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/MacCatalyst/RebuildTestAppWithLibraryReference.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst + + + diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/Makefile new file mode 100644 index 00000000000..6affa45ff12 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/Makefile @@ -0,0 +1,2 @@ +TOP=../../.. +include $(TOP)/tests/common/shared-dotnet-test.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/iOS/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/iOS/Makefile new file mode 100644 index 00000000000..110d078f457 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/iOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/iOS/RebuildTestAppWithLibraryReference.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/iOS/RebuildTestAppWithLibraryReference.csproj new file mode 100644 index 00000000000..86d408734aa --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/iOS/RebuildTestAppWithLibraryReference.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-ios + + + diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/macOS/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/macOS/Makefile new file mode 100644 index 00000000000..110d078f457 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/macOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/macOS/RebuildTestAppWithLibraryReference.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/macOS/RebuildTestAppWithLibraryReference.csproj new file mode 100644 index 00000000000..a77287b9ba0 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/macOS/RebuildTestAppWithLibraryReference.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-macos + + + diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/shared.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/shared.csproj new file mode 100644 index 00000000000..532458ef45a --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/shared.csproj @@ -0,0 +1,19 @@ + + + + Exe + + RebuildTestAppWithLibraryReference + com.xamarin.rebuildtestappwithlibraryreference + + true + true + + + + + + + + + diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/shared.mk b/tests/dotnet/RebuildTestAppWithLibraryReference/shared.mk new file mode 100644 index 00000000000..f555cad4e80 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/shared.mk @@ -0,0 +1,2 @@ +TOP=../../../.. +include $(TOP)/tests/common/shared-dotnet.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/tvOS/Makefile b/tests/dotnet/RebuildTestAppWithLibraryReference/tvOS/Makefile new file mode 100644 index 00000000000..110d078f457 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/tvOS/Makefile @@ -0,0 +1 @@ +include ../shared.mk diff --git a/tests/dotnet/RebuildTestAppWithLibraryReference/tvOS/RebuildTestAppWithLibraryReference.csproj b/tests/dotnet/RebuildTestAppWithLibraryReference/tvOS/RebuildTestAppWithLibraryReference.csproj new file mode 100644 index 00000000000..bd487ddcd88 --- /dev/null +++ b/tests/dotnet/RebuildTestAppWithLibraryReference/tvOS/RebuildTestAppWithLibraryReference.csproj @@ -0,0 +1,7 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion)-tvos + + + diff --git a/tests/dotnet/UnitTests/RebuildTest.cs b/tests/dotnet/UnitTests/RebuildTest.cs new file mode 100644 index 00000000000..3fa72a7f2f6 --- /dev/null +++ b/tests/dotnet/UnitTests/RebuildTest.cs @@ -0,0 +1,39 @@ +namespace Xamarin.Tests { + [TestFixture] + public class RebuildTest : TestBaseClass { + [Test] + [TestCase (ApplePlatform.MacCatalyst, "maccatalyst-arm64")] + public void ReAotTest (ApplePlatform platform, string runtimeIdentifiers) + { + var project = "RebuildTestAppWithLibraryReference"; + Configuration.IgnoreIfIgnoredPlatform (platform); + Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers); + + var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath); + Clean (project_path); + + var properties = GetDefaultProperties (runtimeIdentifiers); + properties ["BuildCounter"] = "First"; + + DotNet.AssertBuild (project_path, properties); + + var appExecutable = Path.Combine (appPath, "Contents", "MacOS", Path.GetFileNameWithoutExtension (project_path)); + + Assert.That (appExecutable, Does.Exist, "There is an executable"); + if (CanExecute (platform, runtimeIdentifiers)) { + var output = ExecuteWithMagicWordAndAssert (appExecutable); + Assert.That (output, Does.Contain ("FIRSTBUILD"), "First build"); + } + + // Build again, changing something + properties ["BuildCounter"] = "Second"; + DotNet.AssertBuild (project_path, properties); + Assert.That (appExecutable, Does.Exist, "There is an executable"); + if (CanExecute (platform, runtimeIdentifiers)) { + var output = ExecuteWithMagicWordAndAssert (appExecutable); + Assert.That (output, Does.Contain ("SECONDBUILD"), "Second build"); + } + } + } +} + diff --git a/tests/dotnet/UnitTests/TestBaseClass.cs b/tests/dotnet/UnitTests/TestBaseClass.cs index 6c7346be334..ff589f6a57f 100644 --- a/tests/dotnet/UnitTests/TestBaseClass.cs +++ b/tests/dotnet/UnitTests/TestBaseClass.cs @@ -308,19 +308,20 @@ protected string GenerateProject (ApplePlatform platform, string name, string ru return csproj; } - protected void ExecuteWithMagicWordAndAssert (ApplePlatform platform, string runtimeIdentifiers, string executable) + protected string ExecuteWithMagicWordAndAssert (ApplePlatform platform, string runtimeIdentifiers, string executable) { if (!CanExecute (platform, runtimeIdentifiers)) - return; + return string.Empty; - ExecuteWithMagicWordAndAssert (executable); + return ExecuteWithMagicWordAndAssert (executable); } - protected void ExecuteWithMagicWordAndAssert (string executable) + protected string ExecuteWithMagicWordAndAssert (string executable) { var rv = Execute (executable, out var output, out string magicWord); Assert.That (output.ToString (), Does.Contain (magicWord), "Contains magic word"); Assert.AreEqual (0, rv.ExitCode, "ExitCode"); + return output.ToString (); } protected Execution Execute (string executable, out StringBuilder output, out string magicWord)