Skip to content

Commit d4f7c6b

Browse files
committed
Add support for matching runtime packs based on labels
1 parent c051961 commit d4f7c6b

File tree

3 files changed

+142
-20
lines changed

3 files changed

+142
-20
lines changed

src/Tasks/Common/MetadataKeys.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ internal static class MetadataKeys
8080
// Targeting packs
8181
public const string PackageConflictPreferredPackages = "PackageConflictPreferredPackages";
8282

83+
// Runtime packs
84+
public const string RuntimePackLabels = "RuntimePackLabels";
85+
8386
// Content files
8487
public const string PPOutputPath = "PPOutputPath";
8588
public const string CodeLanguage = "CodeLanguage";

src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs

Lines changed: 138 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
using System.IO;
77
using System.Linq;
88
using System.Runtime.InteropServices;
9+
using System.Text.RegularExpressions;
910
using Microsoft.Build.Framework;
1011
using Microsoft.Build.Utilities;
12+
using Newtonsoft.Json;
1113
using NuGet.Frameworks;
1214

1315
namespace Microsoft.NET.Build.Tasks
@@ -50,6 +52,8 @@ public class ProcessFrameworkReferences : TaskBase
5052

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

55+
public ITaskItem[] KnownRuntimePacks { get; set; } = Array.Empty<ITaskItem>();
56+
5357
public ITaskItem[] KnownCrossgen2Packs { get; set; } = Array.Empty<ITaskItem>();
5458

5559
[Required]
@@ -90,6 +94,21 @@ protected override void ExecuteCore()
9094
NormalizeVersion(kfr.TargetFramework.Version) == normalizedTargetFrameworkVersion)
9195
.ToList();
9296

97+
// Get known runtime packs from known framework references.
98+
// Only use items where the framework reference name matches the RuntimeFrameworkName.
99+
// This will filter out known framework references for "profiles", ie WindowsForms and WPF
100+
var knownRuntimePacksForTargetFramework =
101+
knownFrameworkReferencesForTargetFramework
102+
.Where(kfr => kfr.Name == kfr.RuntimeFrameworkName)
103+
.Select(kfr => kfr.ToKnownRuntimePack())
104+
.ToList();
105+
106+
// Add additional known runtime packs
107+
knownRuntimePacksForTargetFramework.AddRange(
108+
KnownRuntimePacks.Select(item => new KnownRuntimePack(item))
109+
.Where(krp => krp.TargetFramework.Framework.Equals(TargetFrameworkIdentifier, StringComparison.OrdinalIgnoreCase) &&
110+
NormalizeVersion(krp.TargetFramework.Version) == normalizedTargetFrameworkVersion));
111+
93112
var frameworkReferenceMap = FrameworkReferences.ToDictionary(fr => fr.ItemSpec, StringComparer.OrdinalIgnoreCase);
94113

95114
List<ITaskItem> packagesToDownload = new List<ITaskItem>();
@@ -120,20 +139,23 @@ protected override void ExecuteCore()
120139
continue;
121140
}
122141

142+
KnownRuntimePack selectedRuntimePack = SelectRuntimePack(frameworkReference, knownFrameworkReference, knownRuntimePacksForTargetFramework);
143+
144+
// Add targeting pack and all known runtime packs to "preferred packages" list.
145+
// These are packages that will win in conflict resolution for assets that have identical assembly and file versions
123146
List<string> preferredPackages = new List<string>();
124147
preferredPackages.Add(knownFrameworkReference.TargetingPackName);
125148

126-
var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = knownFrameworkReference.RuntimePackRuntimeIdentifiers.Split(';');
149+
var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = selectedRuntimePack.RuntimePackRuntimeIdentifiers.Split(';');
127150
foreach (var runtimeIdentifier in knownFrameworkReferenceRuntimePackRuntimeIdentifiers)
128151
{
129-
foreach (var runtimePackNamePattern in knownFrameworkReference.RuntimePackNamePatterns.Split(';'))
152+
foreach (var runtimePackNamePattern in selectedRuntimePack.RuntimePackNamePatterns.Split(';'))
130153
{
131154
string runtimePackName = runtimePackNamePattern.Replace("**RID**", runtimeIdentifier);
132155
preferredPackages.Add(runtimePackName);
133156
}
134157
}
135-
136-
// Get the path of the targeting pack in the targeting pack root (e.g. dotnet/ref)
158+
137159
TaskItem targetingPack = new TaskItem(knownFrameworkReference.Name);
138160
targetingPack.SetMetadata(MetadataKeys.NuGetPackageId, knownFrameworkReference.TargetingPackName);
139161
targetingPack.SetMetadata(MetadataKeys.PackageConflictPreferredPackages, string.Join(";", preferredPackages));
@@ -152,13 +174,14 @@ protected override void ExecuteCore()
152174
targetingPack.SetMetadata("TargetingPackFormat", knownFrameworkReference.TargetingPackFormat);
153175
targetingPack.SetMetadata("TargetFramework", knownFrameworkReference.TargetFramework.GetShortFolderName());
154176
targetingPack.SetMetadata(MetadataKeys.RuntimeFrameworkName, knownFrameworkReference.RuntimeFrameworkName);
155-
targetingPack.SetMetadata(MetadataKeys.RuntimePackRuntimeIdentifiers, knownFrameworkReference.RuntimePackRuntimeIdentifiers);
177+
targetingPack.SetMetadata(MetadataKeys.RuntimePackRuntimeIdentifiers, selectedRuntimePack.RuntimePackRuntimeIdentifiers);
156178

157179
if (!string.IsNullOrEmpty(knownFrameworkReference.Profile))
158180
{
159181
targetingPack.SetMetadata("Profile", knownFrameworkReference.Profile);
160182
}
161183

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

187210
var runtimeFrameworkVersion = GetRuntimeFrameworkVersion(
188211
frameworkReference,
189-
knownFrameworkReference,
212+
knownFrameworkReference,
213+
selectedRuntimePack,
190214
out string runtimePackVersion);
191215

192216
string isTrimmable = null;
@@ -197,16 +221,16 @@ protected override void ExecuteCore()
197221
}
198222
if (string.IsNullOrEmpty(isTrimmable))
199223
{
200-
isTrimmable = knownFrameworkReference.IsTrimmable;
224+
isTrimmable = selectedRuntimePack.IsTrimmable;
201225
}
202226

203227
bool processedPrimaryRuntimeIdentifier = false;
204228

205229
if ((SelfContained || ReadyToRunEnabled) &&
206230
!string.IsNullOrEmpty(RuntimeIdentifier) &&
207-
!string.IsNullOrEmpty(knownFrameworkReference.RuntimePackNamePatterns))
231+
!string.IsNullOrEmpty(selectedRuntimePack.RuntimePackNamePatterns))
208232
{
209-
ProcessRuntimeIdentifier(RuntimeIdentifier, knownFrameworkReference, runtimePackVersion,
233+
ProcessRuntimeIdentifier(RuntimeIdentifier, selectedRuntimePack, runtimePackVersion,
210234
unrecognizedRuntimeIdentifiers, unavailableRuntimePacks, runtimePacks, packagesToDownload, isTrimmable);
211235

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

225249
// Pass in null for the runtimePacks list, as for these runtime identifiers we only want to
226250
// download the runtime packs, but not use the assets from them
227-
ProcessRuntimeIdentifier(runtimeIdentifier, knownFrameworkReference, runtimePackVersion,
251+
ProcessRuntimeIdentifier(runtimeIdentifier, selectedRuntimePack, runtimePackVersion,
228252
unrecognizedRuntimeIdentifiers, unavailableRuntimePacks, runtimePacks: null, packagesToDownload, isTrimmable);
229253
}
230254
}
@@ -280,9 +304,57 @@ protected override void ExecuteCore()
280304
}
281305
}
282306

307+
private KnownRuntimePack SelectRuntimePack(ITaskItem frameworkReference, KnownFrameworkReference knownFrameworkReference, List<KnownRuntimePack> knownRuntimePacks)
308+
{
309+
var requiredLabelsMetadata = frameworkReference?.GetMetadata(MetadataKeys.RuntimePackLabels) ?? "";
310+
311+
HashSet<string> requiredRuntimePackLabels = null;
312+
if (frameworkReference != null)
313+
{
314+
requiredRuntimePackLabels = new HashSet<string>(requiredLabelsMetadata.Split(new [] { ';' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
315+
}
316+
317+
// The runtime pack name matches the RuntimeFrameworkName on the KnownFrameworkReference
318+
var matchingRuntimePacks = knownRuntimePacks.Where(krp => krp.Name.Equals(knownFrameworkReference.RuntimeFrameworkName, StringComparison.OrdinalIgnoreCase))
319+
.Where(krp =>
320+
{
321+
if (requiredRuntimePackLabels == null)
322+
{
323+
return krp.RuntimePackLabels.Length == 0;
324+
}
325+
else
326+
{
327+
return requiredRuntimePackLabels.SetEquals(krp.RuntimePackLabels);
328+
}
329+
})
330+
.ToList();
331+
332+
if (matchingRuntimePacks.Count == 1)
333+
{
334+
return matchingRuntimePacks[0];
335+
}
336+
else
337+
{
338+
string runtimePackDescriptionForErrorMessage = knownFrameworkReference.RuntimeFrameworkName +
339+
(requiredLabelsMetadata == string.Empty ? string.Empty : ":" + requiredLabelsMetadata);
340+
341+
if (matchingRuntimePacks.Count == 0)
342+
{
343+
Log.LogError(Strings.NoRuntimePackInformation, runtimePackDescriptionForErrorMessage);
344+
}
345+
else
346+
{
347+
Log.LogError(Strings.ConflictingRuntimePackInformation, runtimePackDescriptionForErrorMessage,
348+
string.Join(Environment.NewLine, matchingRuntimePacks.Select(rp => rp.RuntimePackNamePatterns)));
349+
}
350+
351+
return knownFrameworkReference.ToKnownRuntimePack();
352+
}
353+
}
354+
283355
private void ProcessRuntimeIdentifier(
284356
string runtimeIdentifier,
285-
KnownFrameworkReference knownFrameworkReference,
357+
KnownRuntimePack selectedRuntimePack,
286358
string runtimePackVersion,
287359
HashSet<string> unrecognizedRuntimeIdentifiers,
288360
List<ITaskItem> unavailableRuntimePacks,
@@ -291,7 +363,7 @@ private void ProcessRuntimeIdentifier(
291363
string isTrimmable)
292364
{
293365
var runtimeGraph = new RuntimeGraphCache(this).GetRuntimeGraph(RuntimeGraphPath);
294-
var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = knownFrameworkReference.RuntimePackRuntimeIdentifiers.Split(';');
366+
var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = selectedRuntimePack.RuntimePackRuntimeIdentifiers.Split(';');
295367

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

@@ -330,7 +402,7 @@ private void ProcessRuntimeIdentifier(
330402
TaskItem runtimePackItem = new TaskItem(runtimePackName);
331403
runtimePackItem.SetMetadata(MetadataKeys.NuGetPackageId, runtimePackName);
332404
runtimePackItem.SetMetadata(MetadataKeys.NuGetPackageVersion, runtimePackVersion);
333-
runtimePackItem.SetMetadata(MetadataKeys.FrameworkName, knownFrameworkReference.Name);
405+
runtimePackItem.SetMetadata(MetadataKeys.FrameworkName, selectedRuntimePack.Name);
334406
runtimePackItem.SetMetadata(MetadataKeys.RuntimeIdentifier, runtimePackRuntimeIdentifier);
335407
runtimePackItem.SetMetadata(MetadataKeys.IsTrimmable, isTrimmable);
336408

@@ -392,6 +464,7 @@ private bool AddCrossgen2Package(Version normalizedTargetFrameworkVersion, List<
392464
private string GetRuntimeFrameworkVersion(
393465
ITaskItem frameworkReference,
394466
KnownFrameworkReference knownFrameworkReference,
467+
KnownRuntimePack knownRuntimePack,
395468
out string runtimePackVersion)
396469
{
397470
// Precedence order for selecting runtime framework version
@@ -417,11 +490,11 @@ private string GetRuntimeFrameworkVersion(
417490
return knownFrameworkReference.DefaultRuntimeFrameworkVersion;
418491

419492
case RuntimePatchRequest.UseLatestVersion:
420-
runtimePackVersion = knownFrameworkReference.LatestRuntimeFrameworkVersion;
421-
return knownFrameworkReference.LatestRuntimeFrameworkVersion;
493+
runtimePackVersion = knownRuntimePack.LatestRuntimeFrameworkVersion;
494+
return knownRuntimePack.LatestRuntimeFrameworkVersion;
422495

423496
case RuntimePatchRequest.UseDefaultVersionWithLatestRuntimePack:
424-
runtimePackVersion = knownFrameworkReference.LatestRuntimeFrameworkVersion;
497+
runtimePackVersion = knownRuntimePack.LatestRuntimeFrameworkVersion;
425498
return knownFrameworkReference.DefaultRuntimeFrameworkVersion;
426499

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

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

584+
//public string RuntimePackNamePatterns => _item.GetMetadata("RuntimePackNamePatterns");
585+
586+
//public string RuntimePackRuntimeIdentifiers => _item.GetMetadata(MetadataKeys.RuntimePackRuntimeIdentifiers);
587+
588+
//public string IsTrimmable => _item.GetMetadata(MetadataKeys.IsTrimmable);
589+
590+
public bool IsWindowsOnly => _item.HasMetadataValue("IsWindowsOnly", "true");
591+
592+
public string Profile => _item.GetMetadata("Profile");
593+
594+
public NuGetFramework TargetFramework { get; }
595+
596+
public KnownRuntimePack ToKnownRuntimePack()
597+
{
598+
return new KnownRuntimePack(_item);
599+
}
600+
}
601+
602+
private struct KnownRuntimePack
603+
{
604+
ITaskItem _item;
605+
606+
public KnownRuntimePack(ITaskItem item)
607+
{
608+
_item = item;
609+
TargetFramework = NuGetFramework.Parse(item.GetMetadata("TargetFramework"));
610+
string runtimePackLabels = item.GetMetadata(MetadataKeys.RuntimePackLabels);
611+
if (string.IsNullOrEmpty(runtimePackLabels))
612+
{
613+
RuntimePackLabels = Array.Empty<string>();
614+
}
615+
else
616+
{
617+
RuntimePackLabels = runtimePackLabels.Split(';');
618+
}
619+
}
620+
621+
// The name / itemspec of the FrameworkReference used in the project
622+
public string Name => _item.ItemSpec;
623+
624+
//// The framework name to write to the runtimeconfig file (and the name of the folder under dotnet/shared)
625+
//public string RuntimeFrameworkName => _item.GetMetadata(MetadataKeys.RuntimeFrameworkName);
626+
//public string DefaultRuntimeFrameworkVersion => _item.GetMetadata("DefaultRuntimeFrameworkVersion");
627+
public string LatestRuntimeFrameworkVersion => _item.GetMetadata("LatestRuntimeFrameworkVersion");
628+
511629
public string RuntimePackNamePatterns => _item.GetMetadata("RuntimePackNamePatterns");
512630

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

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

519-
public string Profile => _item.GetMetadata("Profile");
637+
public string [] RuntimePackLabels { get; }
520638

521639
public NuGetFramework TargetFramework { get; }
522640
}

src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Copyright (c) .NET Foundation. All rights reserved.
5858

5959
<ProcessFrameworkReferences FrameworkReferences="@(FrameworkReference)"
6060
KnownFrameworkReferences="@(KnownFrameworkReference)"
61+
KnownRuntimePacks="@(KnownRuntimePack)"
6162
TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)"
6263
TargetFrameworkVersion="$(_TargetFrameworkVersionWithoutV)"
6364
TargetingPackRoot="$(NetCoreTargetingPackRoot)"

0 commit comments

Comments
 (0)