Skip to content

Commit dd5846a

Browse files
authored
Respect the profile attribute on runtime packs (#39402)
1 parent 8a3fb06 commit dd5846a

15 files changed

+334
-5
lines changed

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"tools": {
3-
"dotnet": "9.0.100-preview.1.24101.2",
3+
"dotnet": "9.0.100-preview.3.24175.24",
44
"runtimes": {
55
"dotnet": [
66
"$(VSRedistCommonNetCoreSharedFrameworkx6490PackageVersion)"

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ var runtimeRequiredByDeployment
379379

380380
runtimeFramework.SetMetadata(MetadataKeys.Version, runtimeFrameworkVersion);
381381
runtimeFramework.SetMetadata(MetadataKeys.FrameworkName, knownFrameworkReference.Name);
382+
runtimeFramework.SetMetadata("Profile", knownFrameworkReference.Profile);
382383

383384
runtimeFrameworks.Add(runtimeFramework);
384385
}

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

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public class ResolveRuntimePackAssets : TaskBase
1616

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

19+
public ITaskItem[] RuntimeFrameworks { get; set; }
20+
1921
public bool DesignTimeBuild { get; set; }
2022

2123
public bool DisableTransitiveFrameworkReferenceDownloads { get; set; }
@@ -27,6 +29,18 @@ protected override void ExecuteCore()
2729
{
2830
var runtimePackAssets = new List<ITaskItem>();
2931

32+
// Find any RuntimeFrameworks that matches with FrameworkReferences, so that we can apply that RuntimeFrameworks profile to the corresponding RuntimePack.
33+
// This is done in 2 parts, First part (see comments for 2nd part further below), we match the RuntimeFramework with the FrameworkReference by using the following metadata.
34+
// RuntimeFrameworks.GetMetadata("FrameworkName")==FrameworkReferences.ItemSpec AND RuntimeFrameworks.GetMetadata("Profile") is not empty
35+
// For example, A WinForms app that uses useWindowsForms (and useWPF will be set to false) has the following values that will result in a match of the below RuntimeFramework.
36+
// FrameworkReferences with an ItemSpec "Microsoft.WindowsDesktop.App.WindowsForms" will match with
37+
// RuntimeFramework with an ItemSpec => "Microsoft.WindowsDesktop.App", GetMetadata("FrameworkName") => "Microsoft.WindowsDesktop.App.WindowsForms", GetMetadata("Profile") => "WindowsForms"
38+
List<ITaskItem> matchingRuntimeFrameworks = RuntimeFrameworks != null ? FrameworkReferences
39+
.SelectMany(fxReference => RuntimeFrameworks.Where(rtFx =>
40+
fxReference.ItemSpec.Equals(rtFx.GetMetadata(MetadataKeys.FrameworkName), StringComparison.OrdinalIgnoreCase) &&
41+
!string.IsNullOrEmpty(rtFx.GetMetadata("Profile"))))
42+
.ToList() : null;
43+
3044
HashSet<string> frameworkReferenceNames = new(FrameworkReferences.Select(item => item.ItemSpec), StringComparer.OrdinalIgnoreCase);
3145

3246
foreach (var unavailableRuntimePack in UnavailableRuntimePacks)
@@ -55,6 +69,15 @@ protected override void ExecuteCore()
5569
}
5670
}
5771

72+
// For any RuntimeFrameworks that matches with FrameworkReferences, we can apply that RuntimeFrameworks profile to the corresponding RuntimePack.
73+
// This is done in 2 parts, second part (see comments for 1st part above), Matches the RuntimeFramework with the ResolvedRuntimePacks by comparing the following metadata.
74+
// RuntimeFrameworks.ItemSpec == ResolvedRuntimePacks.GetMetadata("FrameworkName")
75+
// For example, A WinForms app that uses useWindowsForms (and useWPF will be set to false) has the following values that will result in a match of the below RuntimeFramework
76+
// matchingRTReference.GetMetadata("Profile") will be "WindowsForms". 'Profile' will be an empty string if no matching RuntimeFramework is found
77+
HashSet<string> profiles = matchingRuntimeFrameworks?
78+
.Where(matchingRTReference => runtimePack.GetMetadata("FrameworkName").Equals(matchingRTReference.ItemSpec))
79+
.Select(matchingRTReference => matchingRTReference.GetMetadata("Profile")).ToHashSet() ?? [];
80+
5881
string runtimePackRoot = runtimePack.GetMetadata(MetadataKeys.PackageDirectory);
5982

6083
if (string.IsNullOrEmpty(runtimePackRoot) || !Directory.Exists(runtimePackRoot))
@@ -91,7 +114,7 @@ protected override void ExecuteCore()
91114
{
92115
var runtimePackAlwaysCopyLocal = runtimePack.HasMetadataValue(MetadataKeys.RuntimePackAlwaysCopyLocal, "true");
93116

94-
AddRuntimePackAssetsFromManifest(runtimePackAssets, runtimePackRoot, runtimeListPath, runtimePack, runtimePackAlwaysCopyLocal);
117+
AddRuntimePackAssetsFromManifest(runtimePackAssets, runtimePackRoot, runtimeListPath, runtimePack, runtimePackAlwaysCopyLocal, profiles);
95118
}
96119
else
97120
{
@@ -103,13 +126,39 @@ protected override void ExecuteCore()
103126
}
104127

105128
private void AddRuntimePackAssetsFromManifest(List<ITaskItem> runtimePackAssets, string runtimePackRoot,
106-
string runtimeListPath, ITaskItem runtimePack, bool runtimePackAlwaysCopyLocal)
129+
string runtimeListPath, ITaskItem runtimePack, bool runtimePackAlwaysCopyLocal, HashSet<string> profiles)
107130
{
108131
var assetSubPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
109132

110133
XDocument frameworkListDoc = XDocument.Load(runtimeListPath);
134+
// profile feature is only supported in net9.0 and later. We would ignore it for previous versions.
135+
bool profileSupported = false;
136+
string targetFrameworkVersion = frameworkListDoc.Root.Attribute("TargetFrameworkVersion")?.Value;
137+
if (!string.IsNullOrEmpty(targetFrameworkVersion))
138+
{
139+
string[] parts = targetFrameworkVersion.Split('.');
140+
if (parts.Length > 0 && int.TryParse(parts[0], out int versionNumber))
141+
{
142+
if (versionNumber >= 9)
143+
{
144+
profileSupported = true;
145+
}
146+
}
147+
}
111148
foreach (var fileElement in frameworkListDoc.Root.Elements("File"))
112149
{
150+
if (profileSupported && profiles.Count != 0)
151+
{
152+
var profileAttributeValue = fileElement.Attribute("Profile")?.Value;
153+
154+
var assemblyProfiles = profileAttributeValue?.Split(';');
155+
if (profileAttributeValue == null || !assemblyProfiles.Any(p => profiles.Contains(p)))
156+
{
157+
// Assembly wasn't in the profile specified, so don't reference it
158+
continue;
159+
}
160+
}
161+
113162
// Call GetFullPath to normalize slashes
114163
string assetPath = Path.GetFullPath(Path.Combine(runtimePackRoot, fileElement.Attribute("Path").Value));
115164

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
@@ -492,6 +492,7 @@ Copyright (c) .NET Foundation. All rights reserved.
492492
Condition="'@(RuntimePack)' != ''">
493493

494494
<ResolveRuntimePackAssets FrameworkReferences="@(FrameworkReference)"
495+
RuntimeFrameworks="@(RuntimeFramework)"
495496
ResolvedRuntimePacks="@(ResolvedRuntimePack)"
496497
UnavailableRuntimePacks="@(UnavailableRuntimePack)"
497498
SatelliteResourceLanguages="$(SatelliteResourceLanguages)"

test/Microsoft.NET.Build.Tests/GivenFrameworkReferences.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ public void WindowsFormsFrameworkReference(bool selfContained)
887887
TestFrameworkReferenceProfiles(
888888
frameworkReferences: new[] { "Microsoft.WindowsDesktop.App.WindowsForms" },
889889
expectedReferenceNames: new[] { "Microsoft.Win32.Registry", "System.Windows.Forms" },
890-
notExpectedReferenceNames: new[] { "System.Windows.Presentation", "WindowsFormsIntegration" },
890+
notExpectedReferenceNames: new[] { "WindowsFormsIntegration" },
891891
selfContained);
892892
}
893893

@@ -899,7 +899,7 @@ public void WPFFrameworkReference(bool selfContained)
899899
TestFrameworkReferenceProfiles(
900900
frameworkReferences: new[] { "Microsoft.WindowsDesktop.App.WPF" },
901901
expectedReferenceNames: new[] { "Microsoft.Win32.Registry", "System.Windows.Presentation" },
902-
notExpectedReferenceNames: new[] { "System.Windows.Forms", "WindowsFormsIntegration" },
902+
notExpectedReferenceNames: new[] { "WindowsFormsIntegration" },
903903
selfContained);
904904
}
905905

0 commit comments

Comments
 (0)