From 066ab68bc6d256a4ece8e85c9b3590ce2d437229 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 4 May 2023 17:09:50 +0200 Subject: [PATCH] [xharness] Append to MtouchExtraArgs/MonoBundlingExtraArgs instead of just setting the value. (#18200) When generating/cloning test projects and modifying MtouchExtraArgs/MonoBundlingExtraArgs, we always want to add to any existing properties instead of overwriting them, so do exactly that. With this change we now find the latest top-level PropertyGroup in the project file with no Condition, and add a MtouchExtraArgs/MonoBundlingArgs that adds to any existing property. --- .../xharness/Jenkins/TestVariationsFactory.cs | 10 ++- tests/xharness/ProjectFileExtensions.cs | 86 +++++++++++++++++++ 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/tests/xharness/Jenkins/TestVariationsFactory.cs b/tests/xharness/Jenkins/TestVariationsFactory.cs index 2d7de0f6bf..f2691bce58 100644 --- a/tests/xharness/Jenkins/TestVariationsFactory.cs +++ b/tests/xharness/Jenkins/TestVariationsFactory.cs @@ -225,11 +225,13 @@ namespace Xharness.Jenkins { var canSymlink = task.Platform.CanSymlink (); if (!string.IsNullOrEmpty (mtouch_extra_args)) - clone.Xml.AddExtraMtouchArgs (mtouch_extra_args, task.ProjectPlatform, configuration); + clone.Xml.AppendExtraMtouchArgs (mtouch_extra_args); if (!string.IsNullOrEmpty (bundling_extra_args)) - clone.Xml.AddMonoBundlingExtraArgs (bundling_extra_args, task.ProjectPlatform, configuration); - if (!string.IsNullOrEmpty (link_mode)) - clone.Xml.SetNode (isMac ? "LinkMode" : "MtouchLink", link_mode, task.ProjectPlatform, configuration); + clone.Xml.AppendMonoBundlingExtraArgs (bundling_extra_args); + if (!string.IsNullOrEmpty (link_mode)) { + clone.Xml.SetProperty ("LinkMode", link_mode); + clone.Xml.SetProperty ("MtouchLink", link_mode); + } if (!string.IsNullOrEmpty (defines)) { clone.Xml.AddAdditionalDefines (defines, task.ProjectPlatform, configuration); if (clone.ProjectReferences != null) { diff --git a/tests/xharness/ProjectFileExtensions.cs b/tests/xharness/ProjectFileExtensions.cs index 403be04e12..0fca5d0855 100644 --- a/tests/xharness/ProjectFileExtensions.cs +++ b/tests/xharness/ProjectFileExtensions.cs @@ -17,6 +17,92 @@ using Xamarin.Utils; namespace Xharness { public static class EvolvedProjectFileExtensions { + public static void SetProperty (this XmlDocument csproj, string key, string value) + { + // Set all existing properties + var xmlNodeList = csproj.SelectNodes ("/*[local-name() = 'Project']/*[local-name() = 'PropertyGroup']/*[local-name() = '" + key + "']").Cast (); + foreach (var item in xmlNodeList) + item.InnerText = value; + + // Create a new one as well, in case any of the other ones are for a different configuration. + var propertyGroup = GetLastPropertyGroup (csproj); + var mea = csproj.CreateElement (key, csproj.GetNamespace ()); + mea.InnerText = value; + propertyGroup.AppendChild (mea); + propertyGroup.InsertBefore (csproj.CreateComment ($" This property was created by xharness "), mea); + } + + public static void AppendToProperty (this XmlDocument csproj, string node, string value, string separator) + { + var propertyGroup = GetLastPropertyGroup (csproj); + var newNode = csproj.CreateElement (node, csproj.GetNamespace ()); + newNode.InnerText = $"$({node}){separator}{value}"; + propertyGroup.AppendChild (newNode); + propertyGroup.InsertBefore (csproj.CreateComment ($" This property was created by xharness "), newNode); + } + + public static void AppendExtraMtouchArgs (this XmlDocument csproj, string value) + { + csproj.AppendToProperty ("MtouchExtraArgs", value, " "); + } + + public static void AppendMonoBundlingExtraArgs (this XmlDocument csproj, string value) + { + csproj.AppendToProperty ("MonoBundlingExtraArgs", value, " "); + } + + static int IndexOf (this XmlNodeList @this, XmlNode node) + { + for (var i = 0; i < @this.Count; i++) { + if ((object) node == (object) @this [i]) + return i; + } + return -1; + } + + static XmlElement GetLastPropertyGroup (this XmlDocument csproj) + { + // Is the last property group Condition-less? If so, return it. + // Definition of last: the last PropertyGroup before an Import node (or last in file if there are no Import nodes) + var propertyGroups = csproj.SelectNodes ("/*[local-name() = 'Project']/*[local-name() = 'PropertyGroup']").Cast (); + var imports = csproj.SelectNodes ("/*[local-name() = 'Project']/*[local-name() = 'Import']").Cast (); + if (propertyGroups.Any ()) { + XmlElement? last = null; + + if (imports.Any ()) { + var firstImport = imports.First (); + var firstImportIndex = firstImport.ParentNode.ChildNodes.IndexOf (firstImport); + foreach (var pg in propertyGroups) { + var pgIndex = pg.ParentNode.ChildNodes.IndexOf (pg); + if (pgIndex < firstImportIndex) { + last = pg; + } else { + break; + } + } + } else { + last = propertyGroups.Last (); + } + + if (last?.HasAttribute ("Condition") == false) + return last; + } + + // Create a new PropertyGroup, and add it either: + // * Just before the first Import node + // * If no Import node, then after the last PropertyGroup. + var projectNode = csproj.SelectSingleNode ("//*[local-name() = 'Project']"); + var newPropertyGroup = csproj.CreateElement ("PropertyGroup", csproj.GetNamespace ()); + if (imports.Any ()) { + projectNode.InsertBefore (newPropertyGroup, imports.First ()); + } else { + var lastPropertyGroup = csproj.SelectNodes ("/*[local-name() = 'Project']/*[local-name() = 'PropertyGroup']").Cast ().Last (); + projectNode.InsertAfter (newPropertyGroup, lastPropertyGroup); + } + projectNode.InsertBefore (csproj.CreateComment ($" This property group was created by xharness "), newPropertyGroup); + return newPropertyGroup; + } + // Evaluates a text and replaces '$(Variable)' with the property value specified in the 'properties' dictionary. // Contrary to what MSBuild does, if the variable can't be found in the dictionary, it's not replaced with // an empty string, instead the variable reference stays as-is.