|
9 | 9 | using System.Xml.Linq; |
10 | 10 | using Microsoft.Build.Framework; |
11 | 11 | using Microsoft.Build.Utilities; |
12 | | -using NuGet.Versioning; |
| 12 | +using System.Collections.Generic; |
| 13 | +using NuGet.Frameworks; |
13 | 14 |
|
14 | 15 | namespace Microsoft.DotNet.Build.Tasks |
15 | 16 | { |
16 | 17 | /// <summary> |
17 | | - /// Use the runtime in dotnet/sdk instead of in the stage 0 to avoid circular dependency. |
18 | | - /// If there is a change depended on the latest runtime. Without override the runtime version in BundledNETCoreAppPackageVersion |
19 | | - /// we would need to somehow get this change in without the test, and then insertion dotnet/installer |
20 | | - /// and then update the stage 0 back. |
21 | | - /// |
22 | | - /// Override NETCoreSdkVersion to stage 0 sdk version like 6.0.100-dev |
23 | | - /// Override NETCoreSdkRuntimeIdentifier and NETCoreSdkPortableRuntimeIdentifier to match the target RID |
24 | | - /// |
25 | | - /// Use a task to override since it was generated as a string literal replace anyway. |
26 | | - /// And using C# can have better error when anything goes wrong. |
| 18 | + /// Updates version numbers in the stage 2 bundled versions file by copying values from the stage 0 file |
| 19 | + /// for items where the TargetFramework is not the latest supported. Matches and updates multiple item types. |
27 | 20 | /// </summary> |
28 | 21 | public sealed class OverrideAndCreateBundledNETCoreAppPackageVersion : Task |
29 | 22 | { |
30 | | - private static string _messageWhenMismatch = |
31 | | - "{0} version {1} does not match BundledNETCoreAppPackageVersion {2}. " + |
32 | | - "The schema of https://github.com/dotnet/installer/blob/main/src/redist/targets/GenerateBundledVersions.targets might change. " + |
33 | | - "We need to ensure we can swap the runtime version from what's in stage0 to what dotnet/sdk used successfully"; |
34 | | - |
35 | | - [Required] public string Stage0MicrosoftNETCoreAppRefPackageVersionPath { get; set; } |
36 | | - |
37 | | - [Required] public string MicrosoftNETCoreAppRefPackageVersion { get; set; } |
38 | | - |
39 | | - [Required] public string NewSDKVersion { get; set; } |
40 | | - |
41 | | - [Required] public string TargetRid { get; set; } |
42 | | - |
| 23 | + [Required] public string Stage0BundledVersionsPath { get; set; } |
| 24 | + [Required] public string Stage2BundledVersionsPath { get; set; } |
43 | 25 | [Required] public string OutputPath { get; set; } |
44 | 26 |
|
45 | 27 | public override bool Execute() |
46 | 28 | { |
47 | | - File.WriteAllText(OutputPath, |
48 | | - ExecuteInternal( |
49 | | - File.ReadAllText(Stage0MicrosoftNETCoreAppRefPackageVersionPath), |
50 | | - MicrosoftNETCoreAppRefPackageVersion, |
51 | | - NewSDKVersion, |
52 | | - TargetRid, |
53 | | - Log)); |
54 | | - return true; |
55 | | - } |
56 | | - |
57 | | - public static string ExecuteInternal( |
58 | | - string stage0MicrosoftNETCoreAppRefPackageVersionContent, |
59 | | - string microsoftNETCoreAppRefPackageVersion, |
60 | | - string newSDKVersion, |
61 | | - string targetRid, |
62 | | - TaskLoggingHelper log) |
63 | | - { |
64 | | - var projectXml = XDocument.Parse(stage0MicrosoftNETCoreAppRefPackageVersionContent); |
65 | | - |
66 | | - var ns = projectXml.Root.Name.Namespace; |
67 | | - |
68 | | - var propertyGroup = projectXml.Root.Elements(ns + "PropertyGroup").First(); |
69 | | - |
70 | | - propertyGroup.Element(ns + "NETCoreSdkVersion").Value = newSDKVersion; |
71 | | - propertyGroup.Element(ns + "NETCoreSdkRuntimeIdentifier").Value = targetRid; |
72 | | - propertyGroup.Element(ns + "NETCoreSdkPortableRuntimeIdentifier").Value = targetRid; |
73 | | - |
74 | | - |
75 | | - var originalBundledNETCoreAppPackageVersion = propertyGroup.Element(ns + "BundledNETCoreAppPackageVersion").Value; |
76 | | - var parsedOriginalBundledPackageVersion = SemanticVersion.Parse(originalBundledNETCoreAppPackageVersion); |
77 | | - var parsedMicrosoftNETCoreAppRefPackageVersion = |
78 | | - SemanticVersion.Parse(microsoftNETCoreAppRefPackageVersion); |
79 | | - |
80 | | - // In the case where we have a new major version, it'll come in first through the dotnet/runtime flow of the |
81 | | - // SDK's own package references. The Stage0 SDK's bundled version props file will still be on the older major version |
82 | | - // (and the older TFM that goes along with that). If we just replaced the bundled version with the new major version, |
83 | | - // apps that target the 'older' TFM would fail to build. So we need to keep the bundled version from the existing |
84 | | - // bundledversions.props in this one specific case. |
85 | | - |
86 | | - var newBundledPackageVersion = |
87 | | - parsedOriginalBundledPackageVersion.Major == parsedMicrosoftNETCoreAppRefPackageVersion.Major |
88 | | - ? microsoftNETCoreAppRefPackageVersion |
89 | | - : originalBundledNETCoreAppPackageVersion; |
90 | | - |
91 | | - propertyGroup.Element(ns + "BundledNETCoreAppPackageVersion").Value = newBundledPackageVersion; |
92 | | - |
93 | | - var isNETServicing = IsNETServicing(originalBundledNETCoreAppPackageVersion); |
94 | | - var currentTargetFramework = $"net{parsedMicrosoftNETCoreAppRefPackageVersion.Major}.0"; |
95 | | - |
96 | | - void CheckAndReplaceElement(XElement element) |
| 29 | + try |
97 | 30 | { |
98 | | - if (element.Value != originalBundledNETCoreAppPackageVersion) |
| 31 | + var stage0Doc = XDocument.Load(Stage0BundledVersionsPath); |
| 32 | + var stage2Doc = XDocument.Load(Stage2BundledVersionsPath); |
| 33 | + var ns = stage2Doc.Root.Name.Namespace; |
| 34 | + |
| 35 | + // Load all items from all ItemGroups |
| 36 | + var items2 = stage2Doc.Root.Elements(ns + "ItemGroup").SelectMany(ig => ig.Elements()).ToList(); |
| 37 | + var items0 = stage0Doc.Root.Elements(ns + "ItemGroup").SelectMany(ig => ig.Elements()).ToList(); |
| 38 | + |
| 39 | + // Find latest TargetFramework using NuGetFramework |
| 40 | + var allTargetFrameworks = items2 |
| 41 | + .Select(e => e.Attribute("TargetFramework")?.Value) |
| 42 | + .Where(v => !string.IsNullOrEmpty(v)) |
| 43 | + .Distinct() |
| 44 | + .Select(tf => new { Raw = tf, Parsed = NuGetFramework.Parse(tf) }) |
| 45 | + .ToList(); |
| 46 | + var latest = allTargetFrameworks |
| 47 | + .OrderByDescending(tf => tf.Parsed, NuGetFramework.Comparer) |
| 48 | + .FirstOrDefault(); |
| 49 | + var latestTargetFramework = latest?.Raw; |
| 50 | + |
| 51 | + // Helper for matching and updating |
| 52 | + void UpdateItems(string elementName, string[] matchAttrs, string[] updateAttrs) |
99 | 53 | { |
100 | | - throw new InvalidOperationException(string.Format( |
101 | | - _messageWhenMismatch, |
102 | | - element.ToString(), element.Value, originalBundledNETCoreAppPackageVersion)); |
| 54 | + var items2Filtered = items2 |
| 55 | + .Where(e => e.Name.LocalName == elementName && e.Attribute("TargetFramework")?.Value != latestTargetFramework) |
| 56 | + .ToList(); |
| 57 | + foreach (var item2 in items2Filtered) |
| 58 | + { |
| 59 | + var matches0 = items0 |
| 60 | + .Where(e => e.Name.LocalName == elementName && matchAttrs.All(attr => (e.Attribute(attr)?.Value ?? "") == (item2.Attribute(attr)?.Value ?? ""))) |
| 61 | + .ToList(); |
| 62 | + if (matches0.Count == 0) |
| 63 | + { |
| 64 | + Log.LogError($"No matching {elementName} in stage 0 for: {string.Join(", ", matchAttrs.Select(a => $"{a}={item2.Attribute(a)?.Value}"))}"); |
| 65 | + continue; |
| 66 | + } |
| 67 | + if (matches0.Count > 1) |
| 68 | + { |
| 69 | + Log.LogError($"Multiple matches for {elementName} in stage 0 for: {string.Join(", ", matchAttrs.Select(a => $"{a}={item2.Attribute(a)?.Value}"))}"); |
| 70 | + continue; |
| 71 | + } |
| 72 | + var item0 = matches0[0]; |
| 73 | + foreach (var updateAttr in updateAttrs) |
| 74 | + { |
| 75 | + var v0 = item0.Attribute(updateAttr)?.Value; |
| 76 | + var v2 = item2.Attribute(updateAttr)?.Value; |
| 77 | + if (v0 != null && v2 != v0) |
| 78 | + item2.SetAttributeValue(updateAttr, v0); |
| 79 | + } |
| 80 | + // Log if other metadata differs |
| 81 | + foreach (var attr in item2.Attributes()) |
| 82 | + { |
| 83 | + if (matchAttrs.Contains(attr.Name.LocalName) || updateAttrs.Contains(attr.Name.LocalName)) |
| 84 | + continue; |
| 85 | + var v0 = item0.Attribute(attr.Name)?.Value; |
| 86 | + if (v0 != null && v0 != attr.Value) |
| 87 | + Log.LogMessage(MessageImportance.Low, $"{elementName} {string.Join(", ", matchAttrs.Select(a => $"{a}={item2.Attribute(a)?.Value}"))}: Metadata '{attr.Name}' differs: stage0='{v0}', stage2='{attr.Value}'"); |
| 88 | + } |
| 89 | + } |
103 | 90 | } |
104 | 91 |
|
105 | | - log.LogMessage(MessageImportance.High, |
106 | | - $"Replacing element {element.Name} value '{element.Value}' with '{newBundledPackageVersion}'"); |
107 | | - element.Value = newBundledPackageVersion; |
108 | | - } |
| 92 | + UpdateItems("KnownFrameworkReference", new[] { "Include", "TargetFramework" }, new[] { "LatestRuntimeFrameworkVersion", "TargetingPackVersion" }); |
| 93 | + UpdateItems("KnownAppHostPack", new[] { "Include", "TargetFramework" }, new[] { "AppHostPackVersion" }); |
| 94 | + UpdateItems("KnownCrossgen2Pack", new[] { "Include", "TargetFramework" }, new[] { "Crossgen2PackVersion" }); |
| 95 | + UpdateItems("KnownILCompilerPack", new[] { "Include", "TargetFramework" }, new[] { "ILCompilerPackVersion" }); |
| 96 | + UpdateItems("KnownRuntimePack", new[] { "Include", "TargetFramework", "RuntimePackLabels" }, new[] { "LatestRuntimeFrameworkVersion" }); |
| 97 | + UpdateItems("KnownILLinkPack", new[] { "Include", "TargetFramework" }, new[] { "ILLinkPackVersion" }); |
109 | 98 |
|
110 | | - void CheckAndReplaceAttribute(XAttribute attribute) |
111 | | - { |
112 | | - if (attribute.Value != originalBundledNETCoreAppPackageVersion) |
113 | | - { |
114 | | - throw new InvalidOperationException(string.Format( |
115 | | - _messageWhenMismatch, |
116 | | - attribute.Parent.ToString() + " --- " + attribute.ToString(), attribute.Value, |
117 | | - originalBundledNETCoreAppPackageVersion)); |
118 | | - } |
119 | | - |
120 | | - log.LogMessage(MessageImportance.High, |
121 | | - $"Replacing attribute {attribute.Name} value '{attribute.Value}' with '{newBundledPackageVersion}' in element {attribute.Parent.Name}"); |
122 | | - attribute.Value = newBundledPackageVersion; |
| 99 | + stage2Doc.Save(OutputPath); |
| 100 | + return !Log.HasLoggedErrors; |
123 | 101 | } |
124 | | - |
125 | | - if (!isNETServicing) |
| 102 | + catch (Exception ex) |
126 | 103 | { |
127 | | - CheckAndReplaceElement(propertyGroup.Element(ns + "BundledNETCorePlatformsPackageVersion")); |
| 104 | + Log.LogErrorFromException(ex, true); |
| 105 | + return false; |
128 | 106 | } |
129 | | - |
130 | | - var itemGroup = projectXml.Root.Elements(ns + "ItemGroup").First(); |
131 | | - |
132 | | - if (!isNETServicing) |
133 | | - { |
134 | | - foreach (var element in itemGroup.Elements(ns + "KnownFrameworkReference") |
135 | | - .Where(e => e.Attribute("TargetFramework")?.Value == currentTargetFramework)) |
136 | | - { |
137 | | - CheckAndReplaceAttribute(element.Attribute("DefaultRuntimeFrameworkVersion")); |
138 | | - CheckAndReplaceAttribute(element.Attribute("TargetingPackVersion")); |
139 | | - } |
140 | | - } |
141 | | - |
142 | | - foreach (var element in itemGroup.Elements(ns + "KnownFrameworkReference") |
143 | | - .Where(e => e.Attribute("TargetFramework")?.Value == currentTargetFramework)) |
144 | | - { |
145 | | - CheckAndReplaceAttribute(element.Attribute("LatestRuntimeFrameworkVersion")); |
146 | | - } |
147 | | - foreach (var element in itemGroup.Elements(ns + "KnownAppHostPack") |
148 | | - .Where(e => e.Attribute("TargetFramework")?.Value == currentTargetFramework)) |
149 | | - { |
150 | | - CheckAndReplaceAttribute(element.Attribute("AppHostPackVersion")); |
151 | | - } |
152 | | - |
153 | | - foreach (var element in itemGroup.Elements(ns + "KnownCrossgen2Pack") |
154 | | - .Where(e => e.Attribute("TargetFramework")?.Value == currentTargetFramework)) |
155 | | - { |
156 | | - CheckAndReplaceAttribute(element.Attribute("Crossgen2PackVersion")); |
157 | | - } |
158 | | - |
159 | | - foreach (var element in itemGroup.Elements(ns + "KnownILCompilerPack") |
160 | | - .Where(e => e.Attribute("TargetFramework")?.Value == currentTargetFramework)) |
161 | | - { |
162 | | - CheckAndReplaceAttribute(element.Attribute("ILCompilerPackVersion")); |
163 | | - } |
164 | | - |
165 | | - foreach (var element in itemGroup.Elements(ns + "KnownILLinkPack") |
166 | | - .Where(e => e.Attribute("TargetFramework")?.Value == currentTargetFramework)) |
167 | | - { |
168 | | - CheckAndReplaceAttribute(element.Attribute("ILLinkPackVersion")); |
169 | | - } |
170 | | - |
171 | | - // web assembly packs always use the latest regardless of the TFM |
172 | | - foreach (var element in itemGroup.Elements(ns + "KnownWebAssemblySdkPack")) |
173 | | - { |
174 | | - CheckAndReplaceAttribute(element.Attribute("WebAssemblySdkPackVersion")); |
175 | | - } |
176 | | - |
177 | | - foreach (var element in itemGroup.Elements(ns + "KnownAspNetCorePack") |
178 | | - .Where(e => e.Attribute("TargetFramework")?.Value == currentTargetFramework)) |
179 | | - { |
180 | | - CheckAndReplaceAttribute(element.Attribute("AspNetCorePackVersion")); |
181 | | - } |
182 | | - |
183 | | - foreach (var element in itemGroup.Elements(ns + "KnownRuntimePack") |
184 | | - .Where(e => e.Attribute("TargetFramework")?.Value == currentTargetFramework)) |
185 | | - { |
186 | | - CheckAndReplaceAttribute(element.Attribute("LatestRuntimeFrameworkVersion")); |
187 | | - } |
188 | | - |
189 | | - return projectXml.ToString(); |
190 | | - } |
191 | | - |
192 | | - /// <summary> |
193 | | - /// For SDK servicing, few Attributes like "DefaultRuntimeFrameworkVersion" does not use the latest flowed version |
194 | | - /// so there is no need to replace them. |
195 | | - /// </summary> |
196 | | - /// <returns></returns> |
197 | | - private static bool IsNETServicing(string netVersion) |
198 | | - { |
199 | | - var parsedSdkVersion = NuGet.Versioning.NuGetVersion.Parse(netVersion); |
200 | | - |
201 | | - return !parsedSdkVersion.IsPrerelease; |
202 | 107 | } |
203 | 108 | } |
204 | 109 | } |
0 commit comments