Skip to content

Commit

Permalink
Add project target framework inference utility. Make NuGetFramework.P…
Browse files Browse the repository at this point in the history
…arseComponents more permissive
  • Loading branch information
nkolev92 committed Aug 5, 2020
1 parent 280abd3 commit c453e22
Show file tree
Hide file tree
Showing 10 changed files with 419 additions and 81 deletions.
1 change: 1 addition & 0 deletions src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<TargetFrameworkIdentifier>$(TargetFrameworkIdentifier)</TargetFrameworkIdentifier>
<TargetFrameworkVersion>$(TargetFrameworkVersion)</TargetFrameworkVersion>
<TargetFrameworkMoniker>$(TargetFrameworkMoniker)</TargetFrameworkMoniker>
<TargetFrameworkProfile>$(TargetFrameworkProfile)</TargetFrameworkProfile>
<TargetPlatformIdentifier>$(TargetPlatformIdentifier)</TargetPlatformIdentifier>
<TargetPlatformVersion>$(TargetPlatformVersion)</TargetPlatformVersion>
<TargetPlatformMinVersion>$(TargetPlatformMinVersion)</TargetPlatformMinVersion>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
static NuGet.Commands.MSBuildProjectFrameworkUtility.GetProjectFramework(string projectFilePath, string targetFrameworkMoniker, string targetFrameworkIdentifier, string targetFrameworkVersion, string targetFrameworkProfile, string targetPlatformIdentifier, string targetPlatformVersion, string targetPlatformMinVersion) -> NuGet.Frameworks.NuGetFramework
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
static NuGet.Commands.MSBuildProjectFrameworkUtility.GetProjectFramework(string projectFilePath, string targetFrameworkMoniker, string targetFrameworkIdentifier, string targetFrameworkVersion, string targetFrameworkProfile, string targetPlatformIdentifier, string targetPlatformVersion, string targetPlatformMinVersion) -> NuGet.Frameworks.NuGetFramework
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
static NuGet.Commands.MSBuildProjectFrameworkUtility.GetProjectFramework(string projectFilePath, string targetFrameworkMoniker, string targetFrameworkIdentifier, string targetFrameworkVersion, string targetFrameworkProfile, string targetPlatformIdentifier, string targetPlatformVersion, string targetPlatformMinVersion) -> NuGet.Frameworks.NuGetFramework
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ private static IEnumerable<TargetFrameworkInformation> GetTargetFrameworkInforma
var targetFrameworkIdentifier = item.GetProperty("TargetFrameworkIdentifier");
var targetFrameworkVersion = item.GetProperty("TargetFrameworkVersion");
var targetFrameworkMoniker = item.GetProperty("TargetFrameworkMoniker");
var targetFrameworkProfile = item.GetProperty("TargetFrameworkProfile");
var targetPlatformIdentifier = item.GetProperty("TargetPlatformIdentifier");
var targetPlatformVersion = item.GetProperty("TargetPlatformVersion");
var targetPlatformMinVersion = item.GetProperty("TargetPlatformMinVersion");
Expand All @@ -437,18 +438,19 @@ private static IEnumerable<TargetFrameworkInformation> GetTargetFrameworkInforma
}
uniqueIds.Add(targetAlias);

IEnumerable<string> targetFramework = MSBuildProjectFrameworkUtility.GetProjectFrameworkStrings(
projectFilePath: filePath,
targetFrameworks: null,
targetFramework: null,
targetFrameworkMoniker: targetFrameworkMoniker,
targetPlatformIdentifier: targetPlatformIdentifier,
targetPlatformVersion: targetPlatformVersion,
targetPlatformMinVersion: targetPlatformMinVersion);
NuGetFramework targetFramework = MSBuildProjectFrameworkUtility.GetProjectFramework(
projectFilePath: filePath,
targetFrameworkMoniker: targetFrameworkMoniker,
targetFrameworkIdentifier: targetFrameworkIdentifier,
targetFrameworkVersion: targetFrameworkVersion,
targetFrameworkProfile: targetFrameworkProfile,
targetPlatformIdentifier: targetPlatformIdentifier,
targetPlatformVersion: targetPlatformVersion,
targetPlatformMinVersion: targetPlatformMinVersion);

var targetFrameworkInfo = new TargetFrameworkInformation()
{
FrameworkName = NuGetFramework.Parse(targetFramework.Single()),
FrameworkName = targetFramework,
TargetAlias = targetAlias
};
if (restoreType == ProjectStyle.PackageReference)
Expand Down
180 changes: 139 additions & 41 deletions src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using NuGet.Common;
using NuGet.Frameworks;

Expand Down Expand Up @@ -34,6 +35,36 @@ public static IEnumerable<string> GetProjectFrameworkStrings(
isXnaWindowsPhoneProject: false);
}

/// <summary>
/// Given the properties from an msbuild project file and a the file path, infer the target framework.
/// This method prioritizes projects without a framework, such as vcxproj and accounts for the mismatching arguments in UAP projects, where the TFI and TFV are set but should be ignored.
/// Likewise, this method will *ignore* unnecessary properties, such as TPI and TPV when profiles are used, and frameworks that do not support platforms have some default values.
/// </summary>
/// <returns>The inferred framework. Unsupported otherwise.</returns>
public static NuGetFramework GetProjectFramework(
string projectFilePath,
string targetFrameworkMoniker,
string targetFrameworkIdentifier,
string targetFrameworkVersion,
string targetFrameworkProfile,
string targetPlatformIdentifier,
string targetPlatformVersion,
string targetPlatformMinVersion)
{
return GetProjectFramework(
projectFilePath,
targetFrameworkMoniker,
targetFrameworkIdentifier,
targetFrameworkVersion,
targetFrameworkProfile,
targetPlatformIdentifier,
targetPlatformVersion,
targetPlatformMinVersion,
isXnaWindowsPhoneProject: false,
isManagementPackProject: false,
GetAsNuGetFramework);
}

/// <summary>
/// Determine the target framework of an msbuild project.
/// </summary>
Expand All @@ -47,6 +78,37 @@ public static IEnumerable<string> GetProjectFrameworkStrings(
string targetPlatformMinVersion,
bool isXnaWindowsPhoneProject,
bool isManagementPackProject)
{
return GetProjectFrameworks(
projectFilePath,
targetFrameworks,
targetFramework,
targetFrameworkMoniker,
targetFrameworkIdentifier: null,
targetFrameworkVersion: null,
targetFrameworkProfile: null,
targetPlatformIdentifier,
targetPlatformVersion,
targetPlatformMinVersion,
isXnaWindowsPhoneProject,
isManagementPackProject,
GetAsFrameworkString);
}

internal static IEnumerable<T> GetProjectFrameworks<T>(
string projectFilePath,
string targetFrameworks,
string targetFramework,
string targetFrameworkMoniker,
string targetFrameworkIdentifier,
string targetFrameworkVersion,
string targetFrameworkProfile,
string targetPlatformIdentifier,
string targetPlatformVersion,
string targetPlatformMinVersion,
bool isXnaWindowsPhoneProject,
bool isManagementPackProject,
Func<object, T> valueFactory)
{
var frameworks = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);

Expand All @@ -55,7 +117,7 @@ public static IEnumerable<string> GetProjectFrameworkStrings(

if (frameworks.Count > 0)
{
return frameworks;
return frameworks.Select(e => valueFactory(e));
}

// TargetFramework property
Expand All @@ -65,26 +127,49 @@ public static IEnumerable<string> GetProjectFrameworkStrings(
{
frameworks.Add(currentFrameworkString);

return frameworks;
return frameworks.Select(e => valueFactory(e));
}

return new T[] { GetProjectFramework(
projectFilePath,
targetFrameworkMoniker,
targetFrameworkIdentifier,
targetFrameworkVersion,
targetFrameworkProfile,
targetPlatformIdentifier,
targetPlatformVersion,
targetPlatformMinVersion,
isXnaWindowsPhoneProject,
isManagementPackProject,
valueFactory) };
}

internal static T GetProjectFramework<T>(
string projectFilePath,
string targetFrameworkMoniker,
string targetFrameworkIdentifier,
string targetFrameworkVersion,
string targetFrameworkProfile,
string targetPlatformIdentifier,
string targetPlatformVersion,
string targetPlatformMinVersion,
bool isXnaWindowsPhoneProject,
bool isManagementPackProject,
Func<object, T> valueFactory)
{
// C++ check
if (projectFilePath?.EndsWith(".vcxproj", StringComparison.OrdinalIgnoreCase) == true)
{
// The C++ project does not have a TargetFrameworkMoniker property set.
// We hard-code the return value to Native.
frameworks.Add("Native, Version=0.0");

return frameworks;
return valueFactory("Native, Version=0.0");
}

// The MP project does not have a TargetFrameworkMoniker property set.
// We hard-code the return value to SCMPInfra.
if (isManagementPackProject)
{
frameworks.Add("SCMPInfra, Version=0.0");

return frameworks;
return valueFactory("SCMPInfra, Version=0.0");
}

// UAP/Windows store projects
Expand Down Expand Up @@ -113,55 +198,47 @@ public static IEnumerable<string> GetProjectFrameworkStrings(
platformIdentifier = FrameworkConstants.FrameworkIdentifiers.Windows;
}

frameworks.Add($"{platformIdentifier}, Version={platformVersion}");

return frameworks;
return valueFactory($"{platformIdentifier}, Version={platformVersion}");
}

if (!string.IsNullOrEmpty(platformVersion)
&& StringComparer.OrdinalIgnoreCase.Equals(platformIdentifier, "UAP"))
{
// Use the platform id and versions, this is done for UAP projects
frameworks.Add($"{platformIdentifier}, Version={platformVersion}");

return frameworks;
return valueFactory($"{platformIdentifier}, Version={platformVersion}");
}

// TargetFrameworkMoniker
currentFrameworkString = MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkMoniker);
var currentFrameworkString = MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkMoniker);
var trimmedTargetFrameworkIdentifier = MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkIdentifier);
var trimmedTargetFrameworkVersion = MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkVersion);
var hasTFIandTFV = !string.IsNullOrEmpty(trimmedTargetFrameworkIdentifier) && !string.IsNullOrEmpty(trimmedTargetFrameworkVersion);

if (!string.IsNullOrEmpty(currentFrameworkString))
if (!string.IsNullOrEmpty(currentFrameworkString) || hasTFIandTFV)
{
// XNA project lies about its true identity, reporting itself as a normal .NET 4.0 project.
// We detect it and changes its target framework to Silverlight4-WindowsPhone71
if (isXnaWindowsPhoneProject
&& ".NETFramework,Version=v4.0".Equals(currentFrameworkString, StringComparison.OrdinalIgnoreCase))
{
currentFrameworkString = "Silverlight,Version=v4.0,Profile=WindowsPhone71";
return valueFactory(currentFrameworkString);
}

// Workaround until https://github.com/NuGet/Home/issues/9871 is ready.
if (!string.IsNullOrEmpty(platformVersion))
{
var framework = NuGetFramework.Parse(currentFrameworkString);
if (framework.Version.Major >= 5 && framework.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, StringComparison.OrdinalIgnoreCase))
{
currentFrameworkString = framework.GetShortFolderName() + $"-{platformIdentifier}{platformVersion}";
}
}
NuGetFramework framework = hasTFIandTFV ?
NuGetFramework.ParseComponents(
trimmedTargetFrameworkIdentifier,
trimmedTargetFrameworkVersion,
MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkProfile),
MSBuildStringUtility.TrimAndGetNullForEmpty(targetPlatformIdentifier),
MSBuildStringUtility.TrimAndGetNullForEmpty(targetPlatformVersion)) :
NuGetFramework.Parse(currentFrameworkString);

frameworks.Add(currentFrameworkString);

return frameworks;
return valueFactory(framework);
}

// Default to unsupported it no framework can be found.
if (frameworks.Count < 1)
{
frameworks.Add(NuGetFramework.UnsupportedFramework.ToString());
}

return frameworks;
// Default to unsupported it no framework was found.
return valueFactory(NuGetFramework.UnsupportedFramework);
}

/// <summary>
Expand Down Expand Up @@ -221,16 +298,37 @@ public static NuGetFramework GetProjectFrameworkReplacement(NuGetFramework frame
return framework;
}

private static string GetPropertyOrNull(string propertyName, IDictionary<string, string> projectProperties)
/// <summary>
/// Get a NuGetFramework out of the passed object. The argument is expected to either be a <see cref="NuGetFramework"/> or <see cref="string"/>.
/// </summary>
private static NuGetFramework GetAsNuGetFramework(object arg)
{
if (arg is NuGetFramework)
{
return (NuGetFramework)arg;
}
if (arg is string frameworkString)
{
return NuGetFramework.Parse(frameworkString);
}
throw new ArgumentException("Unexpected object type");
}

/// <summary>
/// Get a roundtrippable framework string out of the passed object. The argument is expected to either be a <see cref="NuGetFramework"/> or <see cref="string"/>.
/// </summary>
private static string GetAsFrameworkString(object arg)
{
string value;
if (projectProperties.TryGetValue(propertyName, out value)
&& !string.IsNullOrEmpty(value))
if (arg is string)
{
return (string)arg;
}
if (arg is NuGetFramework framework)
{
return value;
return framework.DotNetFrameworkName;
}

return null;
throw new ArgumentException("Unexpected object type");
}
}
}
Loading

0 comments on commit c453e22

Please sign in to comment.