From 19284a38bc567cccc2610d0f3220358c98eb519b Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Wed, 30 Dec 2020 18:51:27 -0800 Subject: [PATCH 1/4] Support gathering additional arbitrary properties from referenced projects --- src/Tasks/Microsoft.Common.CrossTargeting.targets | 1 + src/Tasks/Microsoft.Common.CurrentVersion.targets | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/Tasks/Microsoft.Common.CrossTargeting.targets b/src/Tasks/Microsoft.Common.CrossTargeting.targets index 51ef08021b0..0573610a896 100644 --- a/src/Tasks/Microsoft.Common.CrossTargeting.targets +++ b/src/Tasks/Microsoft.Common.CrossTargeting.targets @@ -32,6 +32,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. @(_TargetFrameworkInfo) @(_TargetFrameworkInfo->'%(TargetFrameworkMonikers)') @(_TargetFrameworkInfo->'%(TargetPlatformMonikers)') + @(_TargetFrameworkInfo->'%(AdditionalPropertiesFromProject)', ';;') false diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 0192bdd8e9c..7dcff35ce7f 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -1730,6 +1730,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. @(_TargetFrameworkInfo) @(_TargetFrameworkInfo->'%(TargetFrameworkMonikers)') @(_TargetFrameworkInfo->'%(TargetPlatformMonikers)') + @(_TargetFrameworkInfo->'%(AdditionalPropertiesFromProject)', ';;') true @@ -1743,12 +1744,23 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + <_AdditionalTargetFrameworkInfoPropertyWithValue Include="@(AdditionalTargetFrameworkInfoProperty)"> + @(AdditionalTargetFrameworkInfoProperty->'%(Identity)')=$(%(AdditionalTargetFrameworkInfoProperty.Identity)) + + + + + <_AdditionalTargetFrameworkInfoProperties>@(_AdditionalTargetFrameworkInfoPropertyWithValue->'%(PropertyAndValue)') + + <_TargetFrameworkInfo Include="$(TargetFramework)"> $(TargetFramework) $(TargetFrameworkMoniker) $(TargetPlatformMoniker) None + $(_AdditionalTargetFrameworkInfoProperties) From 9ca4269df444fc19a6dbc429b6d1f49a42b7ba4f Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Mon, 25 Jan 2021 22:48:33 -0800 Subject: [PATCH 2/4] Use Xml for gathering additional properties from referenced projects --- .../net/Microsoft.Build.Tasks.Core.cs | 18 ++++++++++ .../netstandard/Microsoft.Build.Tasks.Core.cs | 18 ++++++++++ .../CombineTargetFrameworkInfoProperties.cs | 34 +++++++++++++++++++ src/Tasks/CombineXmlElements.cs | 34 +++++++++++++++++++ src/Tasks/Microsoft.Build.Tasks.csproj | 2 ++ .../Microsoft.Common.CrossTargeting.targets | 9 ++++- .../Microsoft.Common.CurrentVersion.targets | 22 ++++++++---- src/Tasks/Microsoft.Common.tasks | 2 ++ 8 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 src/Tasks/CombineTargetFrameworkInfoProperties.cs create mode 100644 src/Tasks/CombineXmlElements.cs diff --git a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs index cbc25f139be..e7563cddf44 100644 --- a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs @@ -157,6 +157,24 @@ public partial class CombinePath : Microsoft.Build.Tasks.TaskExtension public Microsoft.Build.Framework.ITaskItem[] Paths { get { throw null; } set { } } public override bool Execute() { throw null; } } + public partial class CombineTargetFrameworkInfoProperties : Microsoft.Build.Tasks.TaskExtension + { + public CombineTargetFrameworkInfoProperties() { } + public Microsoft.Build.Framework.ITaskItem[] PropertiesAndValues { get { throw null; } set { } } + [Microsoft.Build.Framework.OutputAttribute] + public string Result { get { throw null; } set { } } + public string RootElementName { get { throw null; } set { } } + public override bool Execute() { throw null; } + } + public partial class CombineXmlElements : Microsoft.Build.Tasks.TaskExtension + { + public CombineXmlElements() { } + [Microsoft.Build.Framework.OutputAttribute] + public string Result { get { throw null; } set { } } + public string RootElementName { get { throw null; } set { } } + public Microsoft.Build.Framework.ITaskItem[] XmlElements { get { throw null; } set { } } + public override bool Execute() { throw null; } + } public partial class CommandLineBuilderExtension : Microsoft.Build.Utilities.CommandLineBuilder { public CommandLineBuilderExtension() { } diff --git a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs index 349308aac70..01fdb88cb0f 100644 --- a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs @@ -87,6 +87,24 @@ public partial class CombinePath : Microsoft.Build.Tasks.TaskExtension public Microsoft.Build.Framework.ITaskItem[] Paths { get { throw null; } set { } } public override bool Execute() { throw null; } } + public partial class CombineTargetFrameworkInfoProperties : Microsoft.Build.Tasks.TaskExtension + { + public CombineTargetFrameworkInfoProperties() { } + public Microsoft.Build.Framework.ITaskItem[] PropertiesAndValues { get { throw null; } set { } } + [Microsoft.Build.Framework.OutputAttribute] + public string Result { get { throw null; } set { } } + public string RootElementName { get { throw null; } set { } } + public override bool Execute() { throw null; } + } + public partial class CombineXmlElements : Microsoft.Build.Tasks.TaskExtension + { + public CombineXmlElements() { } + [Microsoft.Build.Framework.OutputAttribute] + public string Result { get { throw null; } set { } } + public string RootElementName { get { throw null; } set { } } + public Microsoft.Build.Framework.ITaskItem[] XmlElements { get { throw null; } set { } } + public override bool Execute() { throw null; } + } public partial class CommandLineBuilderExtension : Microsoft.Build.Utilities.CommandLineBuilder { public CommandLineBuilderExtension() { } diff --git a/src/Tasks/CombineTargetFrameworkInfoProperties.cs b/src/Tasks/CombineTargetFrameworkInfoProperties.cs new file mode 100644 index 00000000000..d09a3fd8d93 --- /dev/null +++ b/src/Tasks/CombineTargetFrameworkInfoProperties.cs @@ -0,0 +1,34 @@ +using Microsoft.Build.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Microsoft.Build.Tasks +{ + public class CombineTargetFrameworkInfoProperties : TaskExtension + { + public string RootElementName { get; set; } + + public ITaskItem[] PropertiesAndValues { get; set; } + + [Output] + public string Result { get; set; } + + public override bool Execute() + { + XElement root = new XElement(RootElementName); + + foreach (var item in PropertiesAndValues) + { + root.Add(new XElement(item.ItemSpec, item.GetMetadata("Value"))); + } + + Result = root.ToString(); + + return true; + } + } +} diff --git a/src/Tasks/CombineXmlElements.cs b/src/Tasks/CombineXmlElements.cs new file mode 100644 index 00000000000..28bac3204d7 --- /dev/null +++ b/src/Tasks/CombineXmlElements.cs @@ -0,0 +1,34 @@ +using Microsoft.Build.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Microsoft.Build.Tasks +{ + public class CombineXmlElements : TaskExtension + { + public string RootElementName { get; set; } + + public ITaskItem [] XmlElements { get; set; } + + [Output] + public string Result { get; set; } + + public override bool Execute() + { + XElement root = new XElement(RootElementName); + + foreach (var item in XmlElements) + { + root.Add(XElement.Parse(item.ItemSpec)); + } + + Result = root.ToString(); + + return true; + } + } +} diff --git a/src/Tasks/Microsoft.Build.Tasks.csproj b/src/Tasks/Microsoft.Build.Tasks.csproj index f70cb0e0dc6..f3351a6434b 100644 --- a/src/Tasks/Microsoft.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.Build.Tasks.csproj @@ -61,6 +61,8 @@ True + + true diff --git a/src/Tasks/Microsoft.Common.CrossTargeting.targets b/src/Tasks/Microsoft.Common.CrossTargeting.targets index 0573610a896..1346b258253 100644 --- a/src/Tasks/Microsoft.Common.CrossTargeting.targets +++ b/src/Tasks/Microsoft.Common.CrossTargeting.targets @@ -26,13 +26,20 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + + + <_ThisProjectBuildMetadata Include="$(MSBuildProjectFullPath)"> @(_TargetFrameworkInfo) @(_TargetFrameworkInfo->'%(TargetFrameworkMonikers)') @(_TargetFrameworkInfo->'%(TargetPlatformMonikers)') - @(_TargetFrameworkInfo->'%(AdditionalPropertiesFromProject)', ';;') + $(_AdditionalPropertiesFromProject) false diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 7dcff35ce7f..45acda50d66 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -1725,12 +1725,19 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + + + <_ThisProjectBuildMetadata Include="$(MSBuildProjectFullPath)"> @(_TargetFrameworkInfo) @(_TargetFrameworkInfo->'%(TargetFrameworkMonikers)') @(_TargetFrameworkInfo->'%(TargetPlatformMonikers)') - @(_TargetFrameworkInfo->'%(AdditionalPropertiesFromProject)', ';;') + $(_AdditionalPropertiesFromProject) true @@ -1746,14 +1753,17 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_AdditionalTargetFrameworkInfoPropertyWithValue Include="@(AdditionalTargetFrameworkInfoProperty)"> - @(AdditionalTargetFrameworkInfoProperty->'%(Identity)')=$(%(AdditionalTargetFrameworkInfoProperty.Identity)) + $(%(AdditionalTargetFrameworkInfoProperty.Identity)) - - <_AdditionalTargetFrameworkInfoProperties>@(_AdditionalTargetFrameworkInfoPropertyWithValue->'%(PropertyAndValue)') - - + + + + <_TargetFrameworkInfo Include="$(TargetFramework)"> $(TargetFramework) diff --git a/src/Tasks/Microsoft.Common.tasks b/src/Tasks/Microsoft.Common.tasks index 9f7f4620cd2..a1eb793d5ee 100644 --- a/src/Tasks/Microsoft.Common.tasks +++ b/src/Tasks/Microsoft.Common.tasks @@ -95,6 +95,8 @@ + + From 63cd06efa36f70d815a266b3dc7f5292ef75cbfe Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 26 Jan 2021 18:42:03 -0800 Subject: [PATCH 3/4] Fix tasks when inputs not set --- src/Tasks/CombineTargetFrameworkInfoProperties.cs | 15 +++++++++------ src/Tasks/CombineXmlElements.cs | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/Tasks/CombineTargetFrameworkInfoProperties.cs b/src/Tasks/CombineTargetFrameworkInfoProperties.cs index d09a3fd8d93..4b582273270 100644 --- a/src/Tasks/CombineTargetFrameworkInfoProperties.cs +++ b/src/Tasks/CombineTargetFrameworkInfoProperties.cs @@ -19,14 +19,17 @@ public class CombineTargetFrameworkInfoProperties : TaskExtension public override bool Execute() { - XElement root = new XElement(RootElementName); - - foreach (var item in PropertiesAndValues) + if (PropertiesAndValues != null) { - root.Add(new XElement(item.ItemSpec, item.GetMetadata("Value"))); - } + XElement root = new XElement(RootElementName); + + foreach (var item in PropertiesAndValues) + { + root.Add(new XElement(item.ItemSpec, item.GetMetadata("Value"))); + } - Result = root.ToString(); + Result = root.ToString(); + } return true; } diff --git a/src/Tasks/CombineXmlElements.cs b/src/Tasks/CombineXmlElements.cs index 28bac3204d7..1bfa4bf6248 100644 --- a/src/Tasks/CombineXmlElements.cs +++ b/src/Tasks/CombineXmlElements.cs @@ -19,14 +19,17 @@ public class CombineXmlElements : TaskExtension public override bool Execute() { - XElement root = new XElement(RootElementName); - - foreach (var item in XmlElements) + if (XmlElements != null) { - root.Add(XElement.Parse(item.ItemSpec)); - } + XElement root = new XElement(RootElementName); - Result = root.ToString(); + foreach (var item in XmlElements) + { + root.Add(XElement.Parse(item.ItemSpec)); + } + + Result = root.ToString(); + } return true; } From 8682eb1fdbfe57b0f50defe97caa7e08478543e2 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Fri, 29 Jan 2021 12:10:35 -0800 Subject: [PATCH 4/4] Apply code review feedback --- documentation/ProjectReference-Protocol.md | 29 +++++++++++++++++++ .../CombineTargetFrameworkInfoProperties.cs | 22 +++++++++++--- src/Tasks/CombineXmlElements.cs | 19 ++++++++++-- 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/documentation/ProjectReference-Protocol.md b/documentation/ProjectReference-Protocol.md index f464ca17374..8f6a76fd036 100644 --- a/documentation/ProjectReference-Protocol.md +++ b/documentation/ProjectReference-Protocol.md @@ -61,6 +61,7 @@ If implementing a project with an “outer” (determine what properties to pass * The `GetReferenceNearestTargetFrameworkTask` (provided by NuGet) is responsible for selecting the best matching `TargetFramework` of the referenced project * This target is _optional_. If not present, the reference will be built with no additional properties. * **New** in MSBuild 15.5. (`TargetFrameworkMonikers` and `TargetPlatformMonikers` metadata is new in MSBuild 16.8) + * It is possible to gather additional information from referenced projects. See the below section on "Getting additional properties from referenced projects" for more information * `GetTargetFrameworkProperties` determines what properties should be passed to the “main” target for a given `ReferringTargetFramework`. * **Deprecated** in MSBuild 15.5. * New for MSBuild 15/Visual Studio 2017. Supports the cross-targeting feature allowing a project to have multiple `TargetFrameworks`. @@ -91,3 +92,31 @@ As with all MSBuild logic, targets can be added to do other work with `ProjectRe In particular, NuGet depends on being able to identify referenced projects' package dependencies, and calls some targets that are imported through `Microsoft.Common.targets` to do so. At the time of writing this this is in [`NuGet.targets`](https://github.com/NuGet/NuGet.Client/blob/79264a74262354c1a8f899c2c9ddcaff58afaf62/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets). `Microsoft.AppxPackage.targets` adds a dependency on the target `GetPackagingOutputs`. + +## Getting additional properties from referenced projects + +As of MSBuild 16.10, it is possible to gather additional properties from referenced projects. To do this, the referenced project should declare an `AdditionalTargetFrameworkInfoProperty` item for each property that should be gathered for referencing projects. For example: + +```xml + + + + +``` + +These properties will then be gathered via the `GetTargetFrameworks` call. They will be available to the referencing project via the `AdditionalPropertiesFromProject` metadata on the `_MSBuildProjectReferenceExistent` item. The `AdditionalPropertiesFromProject` value will be an XML string which contains the values of the properties for each `TargetFramework` in the referenced project. For example: + +```xml + + + true + <_IsExecutable>true + + + false + <_IsExecutable>true + + +``` + +The `NearestTargetFramework` metadata will be the target framework which was selected as the best one to use for the reference (via `GetReferenceNearestTargetFrameworkTask`). This can be used to select which set of properties were used in the target framework that was active for the reference. \ No newline at end of file diff --git a/src/Tasks/CombineTargetFrameworkInfoProperties.cs b/src/Tasks/CombineTargetFrameworkInfoProperties.cs index 4b582273270..612f27d3b88 100644 --- a/src/Tasks/CombineTargetFrameworkInfoProperties.cs +++ b/src/Tasks/CombineTargetFrameworkInfoProperties.cs @@ -1,4 +1,7 @@ -using Microsoft.Build.Framework; +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Framework; using System; using System.Collections.Generic; using System.Linq; @@ -8,12 +11,24 @@ namespace Microsoft.Build.Tasks { + /// + /// Combines items that represent properties and values into an XML representation. + /// public class CombineTargetFrameworkInfoProperties : TaskExtension { + /// + /// The root element name to use for the generated XML string + /// public string RootElementName { get; set; } + /// + /// Items to include in the XML. The ItemSpec should be the property name, and it should have Value metadata for its value. + /// public ITaskItem[] PropertiesAndValues { get; set; } + /// + /// The generated XML representation of the properties and values. + /// [Output] public string Result { get; set; } @@ -29,9 +44,8 @@ public override bool Execute() } Result = root.ToString(); - } - - return true; + } + return !Log.HasLoggedErrors; } } } diff --git a/src/Tasks/CombineXmlElements.cs b/src/Tasks/CombineXmlElements.cs index 1bfa4bf6248..c42aed7f1bd 100644 --- a/src/Tasks/CombineXmlElements.cs +++ b/src/Tasks/CombineXmlElements.cs @@ -1,4 +1,7 @@ -using Microsoft.Build.Framework; +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Framework; using System; using System.Collections.Generic; using System.Linq; @@ -8,12 +11,24 @@ namespace Microsoft.Build.Tasks { + /// + /// Combines multiple XML elements + /// public class CombineXmlElements : TaskExtension { + /// + /// The root element name to use for the generated XML string + /// public string RootElementName { get; set; } + /// + /// The XML elements to include as children of the root element + /// public ITaskItem [] XmlElements { get; set; } + /// + /// The generated XML + /// [Output] public string Result { get; set; } @@ -31,7 +46,7 @@ public override bool Execute() Result = root.ToString(); } - return true; + return !Log.HasLoggedErrors; } } }