From e82404fca08383513e0b0b3c5308d4a9b18b7c7a Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Fri, 30 Dec 2022 18:25:47 +0100 Subject: [PATCH] Align TargetFramework pkg with NuGet Task (#12041) - Align the ChooseBestP2PTargetFrameworkTask more closely with the NuGet task that is used in the SDK to resolve best matching project references. - Enable nullable reference type warnings - Remove logic from BinPlace.targets that isn't needed anymore - Make the TargetFramework msbuild files pick up the SDK's runtime graph instead of passing it in explicitly. --- src/Common/Internal/AssemblyResolver.cs | 2 + src/Common/Internal/BuildTask.cs | 2 + .../README.md | 12 +- .../src/ChooseBestP2PTargetFrameworkTask.cs | 159 ++++++++++++------ .../src/ChooseBestTargetFrameworksTask.cs | 18 +- ....DotNet.Build.Tasks.TargetFramework.csproj | 3 +- .../src/TargetFrameworkResolver.cs | 34 ++-- .../src/build/BinPlace.targets | 81 +-------- ...DotNet.Build.Tasks.TargetFramework.targets | 91 ++++++++-- ...DotNet.Build.Tasks.TargetFramework.targets | 6 +- 10 files changed, 230 insertions(+), 178 deletions(-) diff --git a/src/Common/Internal/AssemblyResolver.cs b/src/Common/Internal/AssemblyResolver.cs index 6237f20c4fa..d398408cc29 100644 --- a/src/Common/Internal/AssemblyResolver.cs +++ b/src/Common/Internal/AssemblyResolver.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable disable + using System; using System.Diagnostics; using System.IO; diff --git a/src/Common/Internal/BuildTask.cs b/src/Common/Internal/BuildTask.cs index 0eb536efc7a..d57228a123e 100644 --- a/src/Common/Internal/BuildTask.cs +++ b/src/Common/Internal/BuildTask.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable disable + using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using System; diff --git a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/README.md b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/README.md index 77f231a699d..b69ce624c60 100644 --- a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/README.md +++ b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/README.md @@ -8,7 +8,7 @@ Supports copying to additional paths based on which `TargetFramework` among `Tar - BinPlaceItem - Typically computed by the BinPlacing targets to determine what assets to binplace. - - Identity: source of file to binplace. For example: the built output dll, pdb, content files, etc. + - Identity: source of file to binplace. For example: the built output dll, pdb, content files, etc. - Metadata: - TargetPath: when specified can indicate the relative path, including filename, to place the item. @@ -26,14 +26,10 @@ Supports copying to additional paths based on which `TargetFramework` among `Tar - RefPath: directory to copy `BinPlaceItem`s when `BinPlaceRef` is set to true. - RuntimePath: directory to copy `BinPlaceItem`s when `BinPlaceRuntime` is set to true. - TestPath: directory to copy `BinPlaceItem`s when `BinPlaceTest` is set to true. - - PackageFileNativePath: directory to write props file containing `BinPlaceItem`s when `BinPlaceNative` is set to true. - - PackageFileRefPath: directory to write props file containing `BinPlaceItem`s when `BinPlaceRef` is set to true. - - PackageFileRuntimePath: directory to write props file containing `BinPlaceItem`s when `BinPlaceRuntime` is set to true. - ItemName: An item name to use instead of `BinPlaceItem` for the source of items for this `BinPlaceTargetFramework`. - - SetProperties: Name=Value pairs of properties that should be set. ## BinPlacing Properties -- BinPlaceNative: When set to true `BinPlaceItem`s are copied to the `NativePath` of active `BinPlaceTargetFramework`s. Props are written to the `PackageFileNativePath` directory. -- BinPlaceRef: When set to true `BinPlaceItem`s are copied to the `RefPath` of active `BinPlaceTargetFramework`s. Props are written to the `PackageFileRefPath` directory. -- BinPlaceRuntime: When set to true `BinPlaceItem`s are copied to the `RuntimePath` of active `BinPlaceTargetFramework`s. Props are written to the `PackageFileRuntimePath` directory. +- BinPlaceNative: When set to true `BinPlaceItem`s are copied to the `NativePath` of active `BinPlaceTargetFramework`s. +- BinPlaceRef: When set to true `BinPlaceItem`s are copied to the `RefPath` of active `BinPlaceTargetFramework`s. +- BinPlaceRuntime: When set to true `BinPlaceItem`s are copied to the `RuntimePath` of active `BinPlaceTargetFramework`s. - BinPlaceTest: When set to true `BinPlaceItem`s are copied to the `TestPath` of active `BinPlaceTargetFramework`s. diff --git a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/ChooseBestP2PTargetFrameworkTask.cs b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/ChooseBestP2PTargetFrameworkTask.cs index ea5af527553..d98db883c6b 100644 --- a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/ChooseBestP2PTargetFrameworkTask.cs +++ b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/ChooseBestP2PTargetFrameworkTask.cs @@ -1,87 +1,134 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Build.Framework; +// Keep in sync with https://raw.githubusercontent.com/NuGet/NuGet.Client/dccbd304b11103e08b97abf4cf4bcc1499d9235a/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkUtility.cs + using System; using System.Collections.Generic; -using System.IO; +using System.Globalization; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using NuGet.Common; +using NuGet.Frameworks; namespace Microsoft.DotNet.Build.Tasks.TargetFramework { public class ChooseBestP2PTargetFrameworkTask : BuildTask { - [Required] - public ITaskItem[] ProjectReferencesWithTargetFrameworks { get; set; } + private const string NEAREST_TARGET_FRAMEWORK = "NearestTargetFramework"; + private const string TARGET_FRAMEWORKS = "TargetFrameworks"; [Required] - public string RuntimeGraph { get; set; } + public string? RuntimeGraph { get; set; } + /// + /// The current project's target framework. + /// [Required] - public string TargetFramework { get; set; } + public string? CurrentProjectTargetFramework { get; set; } + + /// + /// Optional TargetPlatformMoniker + /// + public string? CurrentProjectTargetPlatform { get; set; } public bool OmitIncompatibleProjectReferences { get; set; } + /// + /// The project references for property lookup. + /// + public ITaskItem[]? AnnotatedProjectReferences { get; set; } + + /// + /// The project references with assigned properties. + /// [Output] - public ITaskItem[] AnnotatedProjectReferencesWithSetTargetFramework { get; set; } + public ITaskItem[]? AssignedProjects { get; set; } public override bool Execute() { - var annotatedProjectReferencesWithSetTargetFramework = new List(ProjectReferencesWithTargetFrameworks.Length); - var targetFrameworkResolver = new TargetFrameworkResolver(RuntimeGraph); + if (AnnotatedProjectReferences == null) + { + return !Log.HasLoggedErrors; + } - for (int i = 0; i < ProjectReferencesWithTargetFrameworks.Length; i++) + // validate current project framework + string errorMessage = string.Format(CultureInfo.CurrentCulture, "The project target framework '{0}' is not a supported target framework.", $"TargetFrameworkMoniker: {CurrentProjectTargetFramework}, TargetPlatformMoniker:{CurrentProjectTargetPlatform}"); + if (!TryParseFramework(CurrentProjectTargetFramework!, CurrentProjectTargetPlatform, errorMessage, Log, out var projectNuGetFramework)) { - ITaskItem projectReference = ProjectReferencesWithTargetFrameworks[i]; - string targetFrameworksValue = projectReference.GetMetadata("TargetFrameworks"); + return false; + } + + TargetFrameworkResolver targetFrameworkResolver = TargetFrameworkResolver.CreateOrGet(RuntimeGraph!); + List assignedProjects = new(AnnotatedProjectReferences.Length); - // Allow referencing projects with TargetFrameworks explicitely cleared out, i.e. Microsoft.Build.Traversal. - if (!string.IsNullOrWhiteSpace(targetFrameworksValue)) + foreach (ITaskItem annotatedProjectReference in AnnotatedProjectReferences) + { + ITaskItem? assignedProject = AssignNearestFrameworkForSingleReference(annotatedProjectReference, projectNuGetFramework, targetFrameworkResolver); + if (assignedProject != null) { - string[] targetFrameworks = targetFrameworksValue.Split(';'); - - string referringTargetFramework = projectReference.GetMetadata("ReferringTargetFramework"); - if (string.IsNullOrWhiteSpace(referringTargetFramework)) - { - referringTargetFramework = TargetFramework; - } - - string bestTargetFramework = targetFrameworkResolver.GetBestSupportedTargetFramework(targetFrameworks, referringTargetFramework); - if (bestTargetFramework == null) - { - if (OmitIncompatibleProjectReferences) - { - continue; - } - Log.LogError($"Not able to find a compatible supported target framework for {referringTargetFramework} in Project {Path.GetFileName(projectReference.ItemSpec)}. The Supported Configurations are {string.Join(", ", targetFrameworks)}"); - } - - // Mimic msbuild's Common.targets behavior: https://github.com/dotnet/msbuild/blob/3c8fb11a080a5a15199df44fabf042a22e9ad4da/src/Tasks/Microsoft.Common.CurrentVersion.targets#L1842-L1853 - if (projectReference.GetMetadata("HasSingleTargetFramework") != "true") - { - projectReference.SetMetadata("SetTargetFramework", "TargetFramework=" + bestTargetFramework); - } - else - { - // If the project has a single TargetFramework, we need to Undefine TargetFramework to avoid another project evaluation. - string undefineProperties = projectReference.GetMetadata("UndefineProperties"); - projectReference.SetMetadata("UndefineProperties", undefineProperties + ";TargetFramework"); - } - - if (projectReference.GetMetadata("IsRidAgnostic") == "true") - { - // If the project is RID agnostic, undefine the RuntimeIdentifier property to avoid another evaluation. --> - string undefineProperties = projectReference.GetMetadata("UndefineProperties"); - projectReference.SetMetadata("UndefineProperties", undefineProperties + ";RuntimeIdentifier"); - } - - projectReference.SetMetadata("SkipGetTargetFrameworkProperties", "true"); + assignedProjects.Add(assignedProject); } - - annotatedProjectReferencesWithSetTargetFramework.Add(projectReference); } - AnnotatedProjectReferencesWithSetTargetFramework = annotatedProjectReferencesWithSetTargetFramework.ToArray(); + AssignedProjects = assignedProjects.ToArray(); return !Log.HasLoggedErrors; - } + } + + private ITaskItem? AssignNearestFrameworkForSingleReference(ITaskItem project, + NuGetFramework projectNuGetFramework, + TargetFrameworkResolver targetFrameworkResolver) + { + TaskItem itemWithProperties = new(project); + string referencedProjectFrameworkString = project.GetMetadata(TARGET_FRAMEWORKS); + + if (string.IsNullOrEmpty(referencedProjectFrameworkString)) + { + // No target frameworks set, nothing to do. + return itemWithProperties; + } + + string[] referencedProjectFrameworks = MSBuildStringUtility.Split(referencedProjectFrameworkString!); + + // try project framework + string? nearestNuGetFramework = targetFrameworkResolver.GetNearest(referencedProjectFrameworks, projectNuGetFramework); + if (nearestNuGetFramework != null) + { + itemWithProperties.SetMetadata(NEAREST_TARGET_FRAMEWORK, nearestNuGetFramework); + return itemWithProperties; + } + + if (OmitIncompatibleProjectReferences) + { + return null; + } + + // no match found + Log.LogError(string.Format(CultureInfo.CurrentCulture, "Project '{0}' targets '{1}'. It cannot be referenced by a project that targets '{2}{3}'.", project.ItemSpec, referencedProjectFrameworkString, projectNuGetFramework.DotNetFrameworkName, projectNuGetFramework.HasPlatform ? "-" + projectNuGetFramework.DotNetPlatformName : string.Empty)); + return itemWithProperties; + } + + private static bool TryParseFramework(string targetFrameworkMoniker, string? targetPlatformMoniker, string errorMessage, Log logger, out NuGetFramework nugetFramework) + { + // Check if we have a long name. +#if NETFRAMEWORK || NETSTANDARD + nugetFramework = targetFrameworkMoniker.Contains(",") + ? NuGetFramework.ParseComponents(targetFrameworkMoniker, targetPlatformMoniker) + : NuGetFramework.Parse(targetFrameworkMoniker); +#else + nugetFramework = targetFrameworkMoniker.Contains(',', System.StringComparison.Ordinal) + ? NuGetFramework.ParseComponents(targetFrameworkMoniker, targetPlatformMoniker) + : NuGetFramework.Parse(targetFrameworkMoniker); +#endif + + // validate framework + if (nugetFramework.IsUnsupported) + { + logger.LogError(errorMessage); + return false; + } + + return true; + } } } diff --git a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/ChooseBestTargetFrameworksTask.cs b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/ChooseBestTargetFrameworksTask.cs index 47a0cba2664..9af850ca166 100644 --- a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/ChooseBestTargetFrameworksTask.cs +++ b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/ChooseBestTargetFrameworksTask.cs @@ -3,6 +3,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using NuGet.Frameworks; using System.Collections.Generic; using System.Linq; @@ -11,31 +12,32 @@ namespace Microsoft.DotNet.Build.Tasks.TargetFramework public class ChooseBestTargetFrameworksTask : BuildTask { [Required] - public ITaskItem[] BuildTargetFrameworks { get; set; } + public ITaskItem[]? BuildTargetFrameworks { get; set; } [Required] - public string RuntimeGraph { get; set; } + public string? RuntimeGraph { get; set; } [Required] - public string[] SupportedTargetFrameworks { get; set; } + public string[]? SupportedTargetFrameworks { get; set; } // Returns distinct items only. Compares the include values. Metadata is ignored. public bool Distinct { get; set; } [Output] - public ITaskItem[] BestTargetFrameworks { get; set; } + public ITaskItem[]? BestTargetFrameworks { get; set; } public override bool Execute() { - var bestTargetFrameworkList = new List(BuildTargetFrameworks.Length); - var targetframeworkResolver = new TargetFrameworkResolver(RuntimeGraph); + List bestTargetFrameworkList = new(BuildTargetFrameworks!.Length); + TargetFrameworkResolver targetframeworkResolver = TargetFrameworkResolver.CreateOrGet(RuntimeGraph!); foreach (ITaskItem buildTargetFramework in BuildTargetFrameworks) { - string bestTargetFramework = targetframeworkResolver.GetBestSupportedTargetFramework(SupportedTargetFrameworks, buildTargetFramework.ItemSpec); + NuGetFramework framework = NuGetFramework.ParseFolder(buildTargetFramework.ItemSpec); + string? bestTargetFramework = targetframeworkResolver.GetNearest(SupportedTargetFrameworks!, framework); if (bestTargetFramework != null && (!Distinct || !bestTargetFrameworkList.Any(b => b.ItemSpec == bestTargetFramework))) { - var item = new TaskItem(bestTargetFramework); + TaskItem item = new(bestTargetFramework); buildTargetFramework.CopyMetadataTo(item); bestTargetFrameworkList.Add(item); } diff --git a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/Microsoft.DotNet.Build.Tasks.TargetFramework.csproj b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/Microsoft.DotNet.Build.Tasks.TargetFramework.csproj index dba748b041c..4e6ecf71bf2 100644 --- a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/Microsoft.DotNet.Build.Tasks.TargetFramework.csproj +++ b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/Microsoft.DotNet.Build.Tasks.TargetFramework.csproj @@ -8,6 +8,7 @@ Configuration system for cross-targeting projects. This package provides the following MSBuild tasks: ChooseBestTargetFrameworksTask and ChooseBestP2PTargetFrameworkTask. **/*.Desktop.* + enable @@ -24,7 +25,7 @@ - + diff --git a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/TargetFrameworkResolver.cs b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/TargetFrameworkResolver.cs index 71265b1994a..e0406d06b4b 100644 --- a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/TargetFrameworkResolver.cs +++ b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/TargetFrameworkResolver.cs @@ -14,19 +14,20 @@ namespace Microsoft.DotNet.Build.Tasks.TargetFramework /// This class uses NuGet's asset selection logic to choose the best TargetFramework given the list of supported TargetFrameworks. /// This behaves in a same way as NuGet selects lib files from a nuget package for a particular TargetFramework. /// - public class TargetFrameworkResolver + internal class TargetFrameworkResolver { + private static readonly Dictionary s_targetFrameworkResolverCache = new(); private readonly ManagedCodeConventions _conventions; private readonly PatternSet _configStringPattern; - public TargetFrameworkResolver(string runtimeGraph) + private TargetFrameworkResolver(string runtimeGraph) { _conventions = new ManagedCodeConventions(JsonRuntimeFormat.ReadRuntimeGraph(runtimeGraph)); _configStringPattern = new PatternSet( _conventions.Properties, groupPatterns: new PatternDefinition[] { - // In order to use Nuget's asset allocation, the input needs to file paths and should contain a trailing slash. + // In order to use Nuget's asset allocation, the input needs to be file paths and should contain a trailing slash. new PatternDefinition("{tfm}/"), new PatternDefinition("{tfm}-{rid}/") }, @@ -37,17 +38,28 @@ public TargetFrameworkResolver(string runtimeGraph) }); } - public string GetBestSupportedTargetFramework(IEnumerable supportedTargetFrameworks, string targetFramework) + public static TargetFrameworkResolver CreateOrGet(string runtimeGraph) { - var contentCollection = new ContentItemCollection(); - contentCollection.Load(supportedTargetFrameworks.Select(t => t + '/').ToArray()); + if (!s_targetFrameworkResolverCache.TryGetValue(runtimeGraph, out TargetFrameworkResolver? targetFrameworkResolver)) + { + targetFrameworkResolver = new TargetFrameworkResolver(runtimeGraph); + s_targetFrameworkResolverCache.Add(runtimeGraph, targetFrameworkResolver); + } - string[] splitStrings = targetFramework.Split('-'); - string targetFrameworkWithoutSuffix = splitStrings[0]; - string targetFrameworkSuffix = splitStrings.Length > 1 ? splitStrings[1] : string.Empty; + return targetFrameworkResolver!; + } + + public string? GetNearest(IEnumerable frameworks, NuGetFramework framework) + { + NuGetFramework frameworkWithoutPlatform = NuGetFramework.Parse(framework.DotNetFrameworkName); + + ContentItemCollection contentCollection = new(); + contentCollection.Load(frameworks.Select(f => f + '/').ToArray()); + + // The platform is expected to be passed-in lower-case but the SDK normalizes "windows" to "Windows" which is why it is lowered again. + SelectionCriteria criteria = _conventions.Criteria.ForFrameworkAndRuntime(frameworkWithoutPlatform, framework.Platform.ToLowerInvariant()); + string? bestTargetFrameworkString = contentCollection.FindBestItemGroup(criteria, _configStringPattern)?.Items[0].Path; - SelectionCriteria criteria = _conventions.Criteria.ForFrameworkAndRuntime(NuGetFramework.Parse(targetFrameworkWithoutSuffix), targetFrameworkSuffix); - string bestTargetFrameworkString = contentCollection.FindBestItemGroup(criteria, _configStringPattern)?.Items[0].Path; return bestTargetFrameworkString?.Remove(bestTargetFrameworkString.Length - 1); } } diff --git a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/build/BinPlace.targets b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/build/BinPlace.targets index 0ad46c8afbd..e36c962a737 100644 --- a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/build/BinPlace.targets +++ b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/build/BinPlace.targets @@ -7,7 +7,7 @@ @@ -45,57 +45,6 @@ - - - - - <_packageFileDir Include="@(PackageFileDir->Distinct())" /> - - - - - <_propsFilename>$(TargetName).$(TargetFramework) - <_propsFilename Condition="'$(TargetName)' == ''">$(MSBuildProjectName).$(TargetFramework) - <_projectDirLength>$(ProjectDir.Length) - - - - <_BinPlaceItemName>%(_packageFileDir.ItemName) - <_BinPlaceItemName Condition="'$(_BinPlaceItemName)' == ''">BinPlaceItem - - - - <_itemsToSave Include="@($(_BinPlaceItemName))"> - - - %(_packageFileDir.BuildConfiguration_NuGetTargetMonikerShort) - - - - <_docFiles Condition="'$(BinPlaceRef)' == 'true'" Include="$(XmlDocDir)/**/$(TargetName).xml" /> - <_docFiles> - /$([System.String]::new('%(RecursiveDir)').TrimEnd('\').TrimEnd('/')) - - <_docFiles> - %(_packageFileDir.BuildConfiguration_NuGetTargetMonikerShort) - - <_itemsToSave Include="@(_docFiles)"/> - - - - - - - - - $(GetBinPlaceItemsDependsOn); @@ -127,7 +76,7 @@ for each binplace targetFramework. --> + RuntimeGraph="$(BundledRuntimeIdentifierGraphFile)"> @@ -138,30 +87,7 @@ - - - NativeFile - - - RefFile - - - LibFile - - - - <_binplacePropertyTuples Include="%(_currentBinPlaceTargetFrameworks.SetProperties)" /> - - <_binplaceSetProperty Condition="'%(_binplacePropertyTuples.Identity)' != ''" - Include="$([System.String]::new('%(_binplacePropertyTuples.Identity)').Split('=')[0])"> - $([System.String]::new('%(_binplacePropertyTuples.Identity)').Split('=')[1]) - - - - - @@ -169,9 +95,6 @@ - - - diff --git a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/build/Microsoft.DotNet.Build.Tasks.TargetFramework.targets b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/build/Microsoft.DotNet.Build.Tasks.TargetFramework.targets index 5e43673b8a0..df09f4449b1 100644 --- a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/build/Microsoft.DotNet.Build.Tasks.TargetFramework.targets +++ b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/build/Microsoft.DotNet.Build.Tasks.TargetFramework.targets @@ -5,33 +5,100 @@ + DependsOnTargets="ResolvePackageDependenciesForBuild;_AddOutputPathToGlobalPropertiesToRemove"> + + + $(NuGetTargetMoniker) + $(TargetFrameworkMoniker) + + + + + + true + + + + - - <_ProjectReferenceWithTargetFrameworks Include="@(ProjectReference->WithMetadataValue('SkipGetTargetFrameworkProperties', 'true'))" /> - - - - + + + + + + - - + + + + + + + TargetFramework=%(AnnotatedProjects.NearestTargetFramework) + + + + + %(AnnotatedProjects.UndefineProperties);TargetFramework + + + + + %(AnnotatedProjects.UndefineProperties);RuntimeIdentifier;SelfContained + + + + + + diff --git a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/buildMultiTargeting/Microsoft.DotNet.Build.Tasks.TargetFramework.targets b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/buildMultiTargeting/Microsoft.DotNet.Build.Tasks.TargetFramework.targets index cba1954a8be..b777f123088 100644 --- a/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/buildMultiTargeting/Microsoft.DotNet.Build.Tasks.TargetFramework.targets +++ b/src/Microsoft.DotNet.Build.Tasks.TargetFramework/src/buildMultiTargeting/Microsoft.DotNet.Build.Tasks.TargetFramework.targets @@ -27,18 +27,18 @@ - <_BuildTargetFrameworkWithTargetOS Include="$(BuildTargetFramework)-$(TargetOS)" /> + <_BuildTargetFrameworkWithTargetOS Include="$(BuildTargetFramework)-$(TargetOS.ToLowerInvariant())" /> <_BuildTargetFrameworkWithoutOS Include="$([MSBuild]::Unescape($([System.Text.RegularExpressions.Regex]::Replace('$(TargetFrameworks)', '(-[^;]+)', ''))))" /> - <_BuildTargetFrameworkWithTargetOS Include="@(_BuildTargetFrameworkWithoutOS->Distinct()->'%(Identity)-$(TargetOS)')" + <_BuildTargetFrameworkWithTargetOS Include="@(_BuildTargetFrameworkWithoutOS->Distinct()->'%(Identity)-$(TargetOS.ToLowerInvariant())')" Condition="'$(TargetOS)' == 'windows' or !$([System.String]::Copy('%(Identity)').StartsWith('net4'))" />