Skip to content

Commit

Permalink
Add support for matching runtime packs based on labels
Browse files Browse the repository at this point in the history
  • Loading branch information
dsplaisted committed Jun 1, 2020
1 parent c051961 commit d4f7c6b
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 20 deletions.
3 changes: 3 additions & 0 deletions src/Tasks/Common/MetadataKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ internal static class MetadataKeys
// Targeting packs
public const string PackageConflictPreferredPackages = "PackageConflictPreferredPackages";

// Runtime packs
public const string RuntimePackLabels = "RuntimePackLabels";

// Content files
public const string PPOutputPath = "PPOutputPath";
public const string CodeLanguage = "CodeLanguage";
Expand Down
158 changes: 138 additions & 20 deletions src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Newtonsoft.Json;
using NuGet.Frameworks;

namespace Microsoft.NET.Build.Tasks
Expand Down Expand Up @@ -50,6 +52,8 @@ public class ProcessFrameworkReferences : TaskBase

public ITaskItem[] KnownFrameworkReferences { get; set; } = Array.Empty<ITaskItem>();

public ITaskItem[] KnownRuntimePacks { get; set; } = Array.Empty<ITaskItem>();

public ITaskItem[] KnownCrossgen2Packs { get; set; } = Array.Empty<ITaskItem>();

[Required]
Expand Down Expand Up @@ -90,6 +94,21 @@ protected override void ExecuteCore()
NormalizeVersion(kfr.TargetFramework.Version) == normalizedTargetFrameworkVersion)
.ToList();

// Get known runtime packs from known framework references.
// Only use items where the framework reference name matches the RuntimeFrameworkName.
// This will filter out known framework references for "profiles", ie WindowsForms and WPF
var knownRuntimePacksForTargetFramework =
knownFrameworkReferencesForTargetFramework
.Where(kfr => kfr.Name == kfr.RuntimeFrameworkName)
.Select(kfr => kfr.ToKnownRuntimePack())
.ToList();

// Add additional known runtime packs
knownRuntimePacksForTargetFramework.AddRange(
KnownRuntimePacks.Select(item => new KnownRuntimePack(item))
.Where(krp => krp.TargetFramework.Framework.Equals(TargetFrameworkIdentifier, StringComparison.OrdinalIgnoreCase) &&
NormalizeVersion(krp.TargetFramework.Version) == normalizedTargetFrameworkVersion));

var frameworkReferenceMap = FrameworkReferences.ToDictionary(fr => fr.ItemSpec, StringComparer.OrdinalIgnoreCase);

List<ITaskItem> packagesToDownload = new List<ITaskItem>();
Expand Down Expand Up @@ -120,20 +139,23 @@ protected override void ExecuteCore()
continue;
}

KnownRuntimePack selectedRuntimePack = SelectRuntimePack(frameworkReference, knownFrameworkReference, knownRuntimePacksForTargetFramework);

// Add targeting pack and all known runtime packs to "preferred packages" list.
// These are packages that will win in conflict resolution for assets that have identical assembly and file versions
List<string> preferredPackages = new List<string>();
preferredPackages.Add(knownFrameworkReference.TargetingPackName);

var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = knownFrameworkReference.RuntimePackRuntimeIdentifiers.Split(';');
var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = selectedRuntimePack.RuntimePackRuntimeIdentifiers.Split(';');
foreach (var runtimeIdentifier in knownFrameworkReferenceRuntimePackRuntimeIdentifiers)
{
foreach (var runtimePackNamePattern in knownFrameworkReference.RuntimePackNamePatterns.Split(';'))
foreach (var runtimePackNamePattern in selectedRuntimePack.RuntimePackNamePatterns.Split(';'))
{
string runtimePackName = runtimePackNamePattern.Replace("**RID**", runtimeIdentifier);
preferredPackages.Add(runtimePackName);
}
}

// Get the path of the targeting pack in the targeting pack root (e.g. dotnet/ref)

TaskItem targetingPack = new TaskItem(knownFrameworkReference.Name);
targetingPack.SetMetadata(MetadataKeys.NuGetPackageId, knownFrameworkReference.TargetingPackName);
targetingPack.SetMetadata(MetadataKeys.PackageConflictPreferredPackages, string.Join(";", preferredPackages));
Expand All @@ -152,13 +174,14 @@ protected override void ExecuteCore()
targetingPack.SetMetadata("TargetingPackFormat", knownFrameworkReference.TargetingPackFormat);
targetingPack.SetMetadata("TargetFramework", knownFrameworkReference.TargetFramework.GetShortFolderName());
targetingPack.SetMetadata(MetadataKeys.RuntimeFrameworkName, knownFrameworkReference.RuntimeFrameworkName);
targetingPack.SetMetadata(MetadataKeys.RuntimePackRuntimeIdentifiers, knownFrameworkReference.RuntimePackRuntimeIdentifiers);
targetingPack.SetMetadata(MetadataKeys.RuntimePackRuntimeIdentifiers, selectedRuntimePack.RuntimePackRuntimeIdentifiers);

if (!string.IsNullOrEmpty(knownFrameworkReference.Profile))
{
targetingPack.SetMetadata("Profile", knownFrameworkReference.Profile);
}

// Get the path of the targeting pack in the targeting pack root (e.g. dotnet/packs)
string targetingPackPath = null;
if (!string.IsNullOrEmpty(TargetingPackRoot))
{
Expand Down Expand Up @@ -186,7 +209,8 @@ protected override void ExecuteCore()

var runtimeFrameworkVersion = GetRuntimeFrameworkVersion(
frameworkReference,
knownFrameworkReference,
knownFrameworkReference,
selectedRuntimePack,
out string runtimePackVersion);

string isTrimmable = null;
Expand All @@ -197,16 +221,16 @@ protected override void ExecuteCore()
}
if (string.IsNullOrEmpty(isTrimmable))
{
isTrimmable = knownFrameworkReference.IsTrimmable;
isTrimmable = selectedRuntimePack.IsTrimmable;
}

bool processedPrimaryRuntimeIdentifier = false;

if ((SelfContained || ReadyToRunEnabled) &&
!string.IsNullOrEmpty(RuntimeIdentifier) &&
!string.IsNullOrEmpty(knownFrameworkReference.RuntimePackNamePatterns))
!string.IsNullOrEmpty(selectedRuntimePack.RuntimePackNamePatterns))
{
ProcessRuntimeIdentifier(RuntimeIdentifier, knownFrameworkReference, runtimePackVersion,
ProcessRuntimeIdentifier(RuntimeIdentifier, selectedRuntimePack, runtimePackVersion,
unrecognizedRuntimeIdentifiers, unavailableRuntimePacks, runtimePacks, packagesToDownload, isTrimmable);

processedPrimaryRuntimeIdentifier = true;
Expand All @@ -224,7 +248,7 @@ protected override void ExecuteCore()

// Pass in null for the runtimePacks list, as for these runtime identifiers we only want to
// download the runtime packs, but not use the assets from them
ProcessRuntimeIdentifier(runtimeIdentifier, knownFrameworkReference, runtimePackVersion,
ProcessRuntimeIdentifier(runtimeIdentifier, selectedRuntimePack, runtimePackVersion,
unrecognizedRuntimeIdentifiers, unavailableRuntimePacks, runtimePacks: null, packagesToDownload, isTrimmable);
}
}
Expand Down Expand Up @@ -280,9 +304,57 @@ protected override void ExecuteCore()
}
}

private KnownRuntimePack SelectRuntimePack(ITaskItem frameworkReference, KnownFrameworkReference knownFrameworkReference, List<KnownRuntimePack> knownRuntimePacks)
{
var requiredLabelsMetadata = frameworkReference?.GetMetadata(MetadataKeys.RuntimePackLabels) ?? "";

HashSet<string> requiredRuntimePackLabels = null;
if (frameworkReference != null)
{
requiredRuntimePackLabels = new HashSet<string>(requiredLabelsMetadata.Split(new [] { ';' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
}

// The runtime pack name matches the RuntimeFrameworkName on the KnownFrameworkReference
var matchingRuntimePacks = knownRuntimePacks.Where(krp => krp.Name.Equals(knownFrameworkReference.RuntimeFrameworkName, StringComparison.OrdinalIgnoreCase))
.Where(krp =>
{
if (requiredRuntimePackLabels == null)
{
return krp.RuntimePackLabels.Length == 0;
}
else
{
return requiredRuntimePackLabels.SetEquals(krp.RuntimePackLabels);
}
})
.ToList();

if (matchingRuntimePacks.Count == 1)
{
return matchingRuntimePacks[0];
}
else
{
string runtimePackDescriptionForErrorMessage = knownFrameworkReference.RuntimeFrameworkName +
(requiredLabelsMetadata == string.Empty ? string.Empty : ":" + requiredLabelsMetadata);

if (matchingRuntimePacks.Count == 0)
{
Log.LogError(Strings.NoRuntimePackInformation, runtimePackDescriptionForErrorMessage);
}
else
{
Log.LogError(Strings.ConflictingRuntimePackInformation, runtimePackDescriptionForErrorMessage,
string.Join(Environment.NewLine, matchingRuntimePacks.Select(rp => rp.RuntimePackNamePatterns)));
}

return knownFrameworkReference.ToKnownRuntimePack();
}
}

private void ProcessRuntimeIdentifier(
string runtimeIdentifier,
KnownFrameworkReference knownFrameworkReference,
KnownRuntimePack selectedRuntimePack,
string runtimePackVersion,
HashSet<string> unrecognizedRuntimeIdentifiers,
List<ITaskItem> unavailableRuntimePacks,
Expand All @@ -291,7 +363,7 @@ private void ProcessRuntimeIdentifier(
string isTrimmable)
{
var runtimeGraph = new RuntimeGraphCache(this).GetRuntimeGraph(RuntimeGraphPath);
var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = knownFrameworkReference.RuntimePackRuntimeIdentifiers.Split(';');
var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = selectedRuntimePack.RuntimePackRuntimeIdentifiers.Split(';');

string runtimePackRuntimeIdentifier = NuGetUtils.GetBestMatchingRid(
runtimeGraph,
Expand All @@ -308,7 +380,7 @@ private void ProcessRuntimeIdentifier(
// framework we don't directly reference. But we don't want to immediately error out
// here if a runtime pack that we might not need to reference isn't available for the
// targeted RID (e.g. Microsoft.WindowsDesktop.App for a linux RID).
var unavailableRuntimePack = new TaskItem(knownFrameworkReference.Name);
var unavailableRuntimePack = new TaskItem(selectedRuntimePack.Name);
unavailableRuntimePack.SetMetadata(MetadataKeys.RuntimeIdentifier, runtimeIdentifier);
unavailableRuntimePacks.Add(unavailableRuntimePack);
}
Expand All @@ -321,7 +393,7 @@ private void ProcessRuntimeIdentifier(
}
else
{
foreach (var runtimePackNamePattern in knownFrameworkReference.RuntimePackNamePatterns.Split(';'))
foreach (var runtimePackNamePattern in selectedRuntimePack.RuntimePackNamePatterns.Split(';'))
{
string runtimePackName = runtimePackNamePattern.Replace("**RID**", runtimePackRuntimeIdentifier);

Expand All @@ -330,7 +402,7 @@ private void ProcessRuntimeIdentifier(
TaskItem runtimePackItem = new TaskItem(runtimePackName);
runtimePackItem.SetMetadata(MetadataKeys.NuGetPackageId, runtimePackName);
runtimePackItem.SetMetadata(MetadataKeys.NuGetPackageVersion, runtimePackVersion);
runtimePackItem.SetMetadata(MetadataKeys.FrameworkName, knownFrameworkReference.Name);
runtimePackItem.SetMetadata(MetadataKeys.FrameworkName, selectedRuntimePack.Name);
runtimePackItem.SetMetadata(MetadataKeys.RuntimeIdentifier, runtimePackRuntimeIdentifier);
runtimePackItem.SetMetadata(MetadataKeys.IsTrimmable, isTrimmable);

Expand Down Expand Up @@ -392,6 +464,7 @@ private bool AddCrossgen2Package(Version normalizedTargetFrameworkVersion, List<
private string GetRuntimeFrameworkVersion(
ITaskItem frameworkReference,
KnownFrameworkReference knownFrameworkReference,
KnownRuntimePack knownRuntimePack,
out string runtimePackVersion)
{
// Precedence order for selecting runtime framework version
Expand All @@ -417,11 +490,11 @@ private string GetRuntimeFrameworkVersion(
return knownFrameworkReference.DefaultRuntimeFrameworkVersion;

case RuntimePatchRequest.UseLatestVersion:
runtimePackVersion = knownFrameworkReference.LatestRuntimeFrameworkVersion;
return knownFrameworkReference.LatestRuntimeFrameworkVersion;
runtimePackVersion = knownRuntimePack.LatestRuntimeFrameworkVersion;
return knownRuntimePack.LatestRuntimeFrameworkVersion;

case RuntimePatchRequest.UseDefaultVersionWithLatestRuntimePack:
runtimePackVersion = knownFrameworkReference.LatestRuntimeFrameworkVersion;
runtimePackVersion = knownRuntimePack.LatestRuntimeFrameworkVersion;
return knownFrameworkReference.DefaultRuntimeFrameworkVersion;

default:
Expand Down Expand Up @@ -501,13 +574,58 @@ public KnownFrameworkReference(ITaskItem item)
// The framework name to write to the runtimeconfig file (and the name of the folder under dotnet/shared)
public string RuntimeFrameworkName => _item.GetMetadata(MetadataKeys.RuntimeFrameworkName);
public string DefaultRuntimeFrameworkVersion => _item.GetMetadata("DefaultRuntimeFrameworkVersion");
public string LatestRuntimeFrameworkVersion => _item.GetMetadata("LatestRuntimeFrameworkVersion");
//public string LatestRuntimeFrameworkVersion => _item.GetMetadata("LatestRuntimeFrameworkVersion");

// The ID of the targeting pack NuGet package to reference
public string TargetingPackName => _item.GetMetadata("TargetingPackName");
public string TargetingPackVersion => _item.GetMetadata("TargetingPackVersion");
public string TargetingPackFormat => _item.GetMetadata("TargetingPackFormat");

//public string RuntimePackNamePatterns => _item.GetMetadata("RuntimePackNamePatterns");

//public string RuntimePackRuntimeIdentifiers => _item.GetMetadata(MetadataKeys.RuntimePackRuntimeIdentifiers);

//public string IsTrimmable => _item.GetMetadata(MetadataKeys.IsTrimmable);

public bool IsWindowsOnly => _item.HasMetadataValue("IsWindowsOnly", "true");

public string Profile => _item.GetMetadata("Profile");

public NuGetFramework TargetFramework { get; }

public KnownRuntimePack ToKnownRuntimePack()
{
return new KnownRuntimePack(_item);
}
}

private struct KnownRuntimePack
{
ITaskItem _item;

public KnownRuntimePack(ITaskItem item)
{
_item = item;
TargetFramework = NuGetFramework.Parse(item.GetMetadata("TargetFramework"));
string runtimePackLabels = item.GetMetadata(MetadataKeys.RuntimePackLabels);
if (string.IsNullOrEmpty(runtimePackLabels))
{
RuntimePackLabels = Array.Empty<string>();
}
else
{
RuntimePackLabels = runtimePackLabels.Split(';');
}
}

// The name / itemspec of the FrameworkReference used in the project
public string Name => _item.ItemSpec;

//// The framework name to write to the runtimeconfig file (and the name of the folder under dotnet/shared)
//public string RuntimeFrameworkName => _item.GetMetadata(MetadataKeys.RuntimeFrameworkName);
//public string DefaultRuntimeFrameworkVersion => _item.GetMetadata("DefaultRuntimeFrameworkVersion");
public string LatestRuntimeFrameworkVersion => _item.GetMetadata("LatestRuntimeFrameworkVersion");

public string RuntimePackNamePatterns => _item.GetMetadata("RuntimePackNamePatterns");

public string RuntimePackRuntimeIdentifiers => _item.GetMetadata(MetadataKeys.RuntimePackRuntimeIdentifiers);
Expand All @@ -516,7 +634,7 @@ public KnownFrameworkReference(ITaskItem item)

public bool IsWindowsOnly => _item.HasMetadataValue("IsWindowsOnly", "true");

public string Profile => _item.GetMetadata("Profile");
public string [] RuntimePackLabels { get; }

public NuGetFramework TargetFramework { get; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Copyright (c) .NET Foundation. All rights reserved.

<ProcessFrameworkReferences FrameworkReferences="@(FrameworkReference)"
KnownFrameworkReferences="@(KnownFrameworkReference)"
KnownRuntimePacks="@(KnownRuntimePack)"
TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)"
TargetFrameworkVersion="$(_TargetFrameworkVersionWithoutV)"
TargetingPackRoot="$(NetCoreTargetingPackRoot)"
Expand Down

0 comments on commit d4f7c6b

Please sign in to comment.