From d6874e3fbaa5f237268b5ba6f3c6e334cf61f909 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Thu, 5 Jan 2023 09:27:22 +0000 Subject: [PATCH 1/4] Add support for Project Specific RegisterTaskObject. Context https://github.com/dotnet/maui/issues/11605 If a solution has more than one Android "App" project there is the potential that objects registered using the `RegisterTaskObject` will be reused between the projects. In most cases this is NOT the desired outcome. There are a few instances where it is safe to share the registered objects between projects. But for most of the time it is specific to that project that is being built. Historically we have probably got away with this because "most" users only have one project..... So lets update the MSBuildExtensions extension methods to include an additional parameter. This will control if the registered object will be "project" scope or "solution" scope. Yes those are terms I just made up :D. We do this by including the `engine.ProjectFileOfTaskNode` as part of the key. Initially the thought was that everything would be "solution" scope if the `LiftTime` was set to `AppDomain`. However there are instances if "solution" scope usage for non "AppDomain" entires. A good example is the `aapt2` daemon which has a "LiftTime" of "Build" but can be "solution" scope. This is why we add a new parameter to control this. The default for this new parameter is `true`, this is to reduce the number of changes. We assume most items will be "project" scope. --- .../MSBuildExtensions.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs b/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs index d50b3d9..4a87584 100644 --- a/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs +++ b/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs @@ -252,34 +252,34 @@ public static void SetDestinationSubPath (this ITaskItem assembly) /// /// IBuildEngine4.RegisterTaskObject, but adds the current assembly path into the key /// - public static void RegisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, object value, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection = false) => - engine.RegisterTaskObject ((AssemblyLocation, key), value, lifetime, allowEarlyCollection); + public static void RegisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, object value, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection = false, bool projectSpecific = true) => + engine.RegisterTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), value, lifetime, allowEarlyCollection); /// /// IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key /// - public static object GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) => - engine.GetRegisteredTaskObject ((AssemblyLocation, key), lifetime); + public static object GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) => + engine.GetRegisteredTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime); /// /// Generic version of IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key /// - public static T GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) + public static T GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) where T : class => - engine.GetRegisteredTaskObject ((AssemblyLocation, key), lifetime) as T; + engine.GetRegisteredTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime) as T; /// /// IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key /// - public static object UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) => - engine.UnregisterTaskObject ((AssemblyLocation, key), lifetime); + public static object UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) => + engine.UnregisterTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime); /// /// Generic version of IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key /// - public static T UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) + public static T UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) where T : class => - engine.UnregisterTaskObject ((AssemblyLocation, key), lifetime) as T; + engine.UnregisterTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime) as T; } } From db9934c5d2321f02d907a9921cc68fd3a3b5b402 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Mon, 9 Jan 2023 10:23:35 +0000 Subject: [PATCH 2/4] Add Overrides and Obsolete methods --- .../MSBuildExtensions.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs b/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs index 4a87584..b9145a3 100644 --- a/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs +++ b/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs @@ -249,18 +249,40 @@ public static void SetDestinationSubPath (this ITaskItem assembly) static readonly string AssemblyLocation = typeof (MSBuildExtensions).Assembly.Location; + /// + /// IBuildEngine4.RegisterTaskObject, but adds the current assembly path into the key + /// + [Obsolete ("Use RegisterTaskObjectAssemblyLocal (engine, key, value, allowEarlyCollection, lifetime, projectSpecific) instead.")] + public static void RegisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, object value, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection = false) => + RegisterTaskObjectAssemblyLocal (engine, key, value, lifetime, allowEarlyCollection: false, projectSpecific: true); + /// /// IBuildEngine4.RegisterTaskObject, but adds the current assembly path into the key /// public static void RegisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, object value, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection = false, bool projectSpecific = true) => engine.RegisterTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), value, lifetime, allowEarlyCollection); + /// + /// IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key + /// + [Obsolete ("Use GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific) instead.")] + public static object GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) => + GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific: true); + /// /// IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key /// public static object GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) => engine.GetRegisteredTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime); + + /// + /// Generic version of IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key + /// + [Obsolete ("Use GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific) instead.")] + public static T GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) + where T : class => GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific: true); + /// /// Generic version of IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key /// @@ -268,6 +290,12 @@ public static T GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engi where T : class => engine.GetRegisteredTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime) as T; + /// + /// IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key + /// + [Obsolete ("Use UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific) instead.")] + public static object UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) => + UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific: true); /// /// IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key @@ -275,6 +303,13 @@ public static T GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engi public static object UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) => engine.UnregisterTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime); + /// + /// Generic version of IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key + /// + [Obsolete ("Use UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific) instead.")] + public static T UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) + where T : class => UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific: true); + /// /// Generic version of IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key /// From 83da56fad519721bf053022cf6796fd09d36ffb6 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Tue, 10 Jan 2023 10:31:44 +0000 Subject: [PATCH 3/4] Switch to using RegisterTaskObjectKeyFlags --- .../MSBuildExtensions.cs | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs b/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs index b9145a3..63ae6b4 100644 --- a/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs +++ b/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs @@ -13,6 +13,12 @@ namespace Microsoft.Android.Build.Tasks { + [Flags] + public enum RegisterTaskObjectKeyFlags { + None = 0, + IncludeProjectFile = 1 << 0, + } + public static class MSBuildExtensions { public static void LogDebugMessage (this TaskLoggingHelper log, string message, params object[] messageArgs) @@ -252,69 +258,80 @@ public static void SetDestinationSubPath (this ITaskItem assembly) /// /// IBuildEngine4.RegisterTaskObject, but adds the current assembly path into the key /// - [Obsolete ("Use RegisterTaskObjectAssemblyLocal (engine, key, value, allowEarlyCollection, lifetime, projectSpecific) instead.")] + [Obsolete ("Use RegisterTaskObjectAssemblyLocal (engine, key, value, allowEarlyCollection, lifetime, flags) instead.")] public static void RegisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, object value, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection = false) => - RegisterTaskObjectAssemblyLocal (engine, key, value, lifetime, allowEarlyCollection: false, projectSpecific: true); + RegisterTaskObjectAssemblyLocal (engine, key, value, lifetime, allowEarlyCollection: false, flags: RegisterTaskObjectKeyFlags.None); /// /// IBuildEngine4.RegisterTaskObject, but adds the current assembly path into the key /// - public static void RegisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, object value, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection = false, bool projectSpecific = true) => - engine.RegisterTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), value, lifetime, allowEarlyCollection); + public static void RegisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, object value, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection = false, RegisterTaskObjectKeyFlags flags = RegisterTaskObjectKeyFlags.IncludeProjectFile) => + engine.RegisterTaskObject (engine.GetKey (AssemblyLocation, key, flags), value, lifetime, allowEarlyCollection); /// /// IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key /// - [Obsolete ("Use GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific) instead.")] + [Obsolete ("Use GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, flags) instead.")] public static object GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) => - GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific: true); + GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, flags: RegisterTaskObjectKeyFlags.IncludeProjectFile); /// /// IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key /// - public static object GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) => - engine.GetRegisteredTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime); + public static object GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, RegisterTaskObjectKeyFlags flags = RegisterTaskObjectKeyFlags.IncludeProjectFile) => + engine.GetRegisteredTaskObject (engine.GetKey (AssemblyLocation, key, flags), lifetime); /// /// Generic version of IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key /// - [Obsolete ("Use GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific) instead.")] + [Obsolete ("Use GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, flags) instead.")] public static T GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) - where T : class => GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific: true); + where T : class => GetRegisteredTaskObjectAssemblyLocal (engine, key, lifetime, flags: RegisterTaskObjectKeyFlags.IncludeProjectFile); /// /// Generic version of IBuildEngine4.GetRegisteredTaskObject, but adds the current assembly path into the key /// - public static T GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) + public static T GetRegisteredTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, RegisterTaskObjectKeyFlags flags = RegisterTaskObjectKeyFlags.IncludeProjectFile) where T : class => - engine.GetRegisteredTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime) as T; + engine.GetRegisteredTaskObject (engine.GetKey (AssemblyLocation, key, flags), lifetime) as T; /// /// IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key /// - [Obsolete ("Use UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific) instead.")] + [Obsolete ("Use UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, flags) instead.")] public static object UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) => - UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific: true); + UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, flags: RegisterTaskObjectKeyFlags.IncludeProjectFile); /// /// IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key /// - public static object UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) => - engine.UnregisterTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime); + public static object UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, RegisterTaskObjectKeyFlags flags = RegisterTaskObjectKeyFlags.IncludeProjectFile) => + engine.UnregisterTaskObject (engine.GetKey (AssemblyLocation, key, flags), lifetime); /// /// Generic version of IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key /// - [Obsolete ("Use UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific) instead.")] + [Obsolete ("Use UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, flags) instead.")] public static T UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime) - where T : class => UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, projectSpecific: true); + where T : class => UnregisterTaskObjectAssemblyLocal (engine, key, lifetime, flags: RegisterTaskObjectKeyFlags.IncludeProjectFile); /// /// Generic version of IBuildEngine4.UnregisterTaskObject, but adds the current assembly path into the key /// - public static T UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, bool projectSpecific = true) + public static T UnregisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, RegisteredTaskObjectLifetime lifetime, RegisterTaskObjectKeyFlags flags = RegisterTaskObjectKeyFlags.IncludeProjectFile) where T : class => - engine.UnregisterTaskObject ((AssemblyLocation, key, (projectSpecific ? engine.ProjectFileOfTaskNode : string.Empty)), lifetime) as T; + engine.UnregisterTaskObject (engine.GetKey (AssemblyLocation, key, flags), lifetime) as T; + + /// + /// Method to calculate the key for the RegisterTaskObject. This is based on the + /// RegisterTaskObjectKeyFlags which are passed. + /// + static object GetKey (this IBuildEngine4 engine, string location, object key, RegisterTaskObjectKeyFlags flags) + { + return ((flags & RegisterTaskObjectKeyFlags.IncludeProjectFile) != 0) + ? (location, key, engine.ProjectFileOfTaskNode) + : (location, key, string.Empty); + } } } From de1a279849fd67187095ee21e78b509e66857b1e Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Tue, 10 Jan 2023 16:14:52 +0000 Subject: [PATCH 4/4] Fix wrong value for RegisterTaskObjectAssemblyLocal --- src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs b/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs index 63ae6b4..757b754 100644 --- a/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs +++ b/src/Microsoft.Android.Build.BaseTasks/MSBuildExtensions.cs @@ -260,7 +260,7 @@ public static void SetDestinationSubPath (this ITaskItem assembly) /// [Obsolete ("Use RegisterTaskObjectAssemblyLocal (engine, key, value, allowEarlyCollection, lifetime, flags) instead.")] public static void RegisterTaskObjectAssemblyLocal (this IBuildEngine4 engine, object key, object value, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection = false) => - RegisterTaskObjectAssemblyLocal (engine, key, value, lifetime, allowEarlyCollection: false, flags: RegisterTaskObjectKeyFlags.None); + RegisterTaskObjectAssemblyLocal (engine, key, value, lifetime, allowEarlyCollection: false, flags: RegisterTaskObjectKeyFlags.IncludeProjectFile); /// /// IBuildEngine4.RegisterTaskObject, but adds the current assembly path into the key