Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a project framework inference/parsing utility #3562

Merged
merged 3 commits into from
Aug 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -929,6 +929,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
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -423,6 +423,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 @@ -434,18 +435,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
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 = trimmedTargetFrameworkIdentifier != null && trimmedTargetFrameworkVersion != null;

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),
platformIdentifier,
platformVersion) :
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)
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
{
if (arg is NuGetFramework nugetFramework)
{
return nugetFramework;
}
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)
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
{
string value;
if (projectProperties.TryGetValue(propertyName, out value)
&& !string.IsNullOrEmpty(value))
if (arg is string str)
{
return str;
}
if (arg is NuGetFramework framework)
{
return value;
return framework.DotNetFrameworkName;
}

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