Skip to content

Commit bbea551

Browse files
authored
Add support for adding RIDs to runtime.json during build (#50818)
* Add support for adding RIDs to RidGraph during build * Address code review feedback * Ensure GenerateRuntimeJson runs before Pack The pack task itself doesn't do anything but depend on a sequence of tasks. As a result running a target `BeforeTargets="Pack"` actually runs after the package is created, since dependencies run before BeforeTargets. Sequence the target before GenerateNuspec instead. * Only add target RID to the RID graph
1 parent 03be59b commit bbea551

11 files changed

+1040
-53
lines changed

src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@ public ITaskItem[] RuntimeGroups
5353
set;
5454
}
5555

56+
/// <summary>
57+
/// Additional runtime identifiers to add to the graph.
58+
/// </summary>
59+
public string[] AdditionalRuntimeIdentifiers
60+
{
61+
get;
62+
set;
63+
}
64+
65+
/// <summary>
66+
/// Parent RID to use for any unknown AdditionalRuntimeIdentifer.
67+
/// </summary>
68+
public string AdditionalRuntimeIdentifierParent
69+
{
70+
get;
71+
set;
72+
}
73+
5674
/// <summary>
5775
/// Optional source Runtime.json to use as a starting point when merging additional RuntimeGroups
5876
/// </summary>
@@ -134,7 +152,11 @@ public override bool Execute()
134152
runtimeGraph = new RuntimeGraph();
135153
}
136154

137-
foreach (var runtimeGroup in RuntimeGroups.NullAsEmpty().Select(i => new RuntimeGroup(i)))
155+
List<RuntimeGroup> runtimeGroups = RuntimeGroups.NullAsEmpty().Select(i => new RuntimeGroup(i)).ToList();
156+
157+
AddRuntimeIdentifiers(runtimeGroups);
158+
159+
foreach (var runtimeGroup in runtimeGroups)
138160
{
139161
runtimeGraph = SafeMerge(runtimeGraph, runtimeGroup);
140162
}
@@ -291,6 +313,21 @@ private void ValidateImports(RuntimeGraph runtimeGraph, IDictionary<string, stri
291313
}
292314
}
293315

316+
private void AddRuntimeIdentifiers(ICollection<RuntimeGroup> runtimeGroups)
317+
{
318+
if (AdditionalRuntimeIdentifiers == null || AdditionalRuntimeIdentifiers.Length == 0)
319+
{
320+
return;
321+
}
322+
323+
RuntimeGroupCollection runtimeGroupCollection = new RuntimeGroupCollection(runtimeGroups);
324+
325+
foreach (string additionalRuntimeIdentifier in AdditionalRuntimeIdentifiers)
326+
{
327+
runtimeGroupCollection.AddRuntimeIdentifier(additionalRuntimeIdentifier, AdditionalRuntimeIdentifierParent);
328+
}
329+
}
330+
294331
private static IDictionary<string, IEnumerable<string>> GetCompatibilityMap(RuntimeGraph graph)
295332
{
296333
Dictionary<string, IEnumerable<string>> compatibilityMap = new Dictionary<string, IEnumerable<string>>();

src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.SDK">
1+
<Project Sdk="Microsoft.NET.SDK">
22
<!-- These are wrapper project files for packaging.-->
33
<PropertyGroup>
44
<TargetFrameworks>$(NetCoreAppToolCurrent);net472</TargetFrameworks>
@@ -12,6 +12,9 @@
1212
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
1313
<NoWarn>$(NoWarn);NU5128</NoWarn> <!-- No Dependencies-->
1414
<PackageDescription>Provides runtime information required to resolve target framework, platform, and runtime specific implementations of .NETCore packages.</PackageDescription>
15+
16+
<!-- When building from source, ensure the RID we're building for is part of the RID graph -->
17+
<AdditionalRuntimeIdentifiers Condition="'$(DotNetBuildFromSource)' == 'true'">$(AdditionalRuntimeIdentifiers);$(OutputRID)</AdditionalRuntimeIdentifiers>
1518
</PropertyGroup>
1619

1720
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
@@ -24,11 +27,14 @@
2427
<Compile Include="Extensions.cs" />
2528
<Compile Include="GenerateRuntimeGraph.cs" />
2629
<Compile Include="RID.cs" />
30+
<Compile Include="RuntimeGroupCollection.cs" />
2731
<Compile Include="RuntimeGroup.cs" />
32+
<Compile Include="RuntimeVersion.cs" />
2833
</ItemGroup>
2934

3035
<ItemGroup>
31-
<Content Include="runtime.json" PackagePath="/" />
36+
<Content Condition="'$(AdditionalRuntimeIdentifiers)' == ''" Include="runtime.json" PackagePath="/" />
37+
<Content Condition="'$(AdditionalRuntimeIdentifiers)' != ''" Include="$(IntermediateOutputPath)runtime.json" PackagePath="/" />
3238
<Content Include="_._" PackagePath="lib/netstandard1.0" />
3339
</ItemGroup>
3440

@@ -38,5 +44,14 @@
3844
<PackageReference Include="NuGet.ProjectModel" Version="$(RefOnlyNugetProjectModelVersion)" />
3945
</ItemGroup>
4046

47+
<Target Name="GenerateRuntimeJson" Condition="'$(AdditionalRuntimeIdentifiers)' != ''" BeforeTargets="GenerateNuspec">
48+
<MakeDir Directories="$(IntermediateOutputPath)" />
49+
<GenerateRuntimeGraph RuntimeGroups="@(RuntimeGroupWithQualifiers)"
50+
AdditionalRuntimeIdentifiers="$(AdditionalRuntimeIdentifiers)"
51+
AdditionalRuntimeIdentifierParent="$(AdditionalRuntimeIdentifierParent)"
52+
RuntimeJson="$(IntermediateOutputPath)runtime.json"
53+
UpdateRuntimeFiles="True" />
54+
</Target>
55+
4156
<Import Project="runtimeGroups.props" />
4257
</Project>
Lines changed: 156 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,44 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
5+
using System.Diagnostics;
46
using System.Text;
57

68
namespace Microsoft.NETCore.Platforms.BuildTasks
79
{
8-
internal class RID
10+
public class RID
911
{
12+
internal const char VersionDelimiter = '.';
13+
internal const char ArchitectureDelimiter = '-';
14+
internal const char QualifierDelimiter = '-';
15+
1016
public string BaseRID { get; set; }
11-
public string VersionDelimiter { get; set; }
12-
public string Version { get; set; }
13-
public string ArchitectureDelimiter { get; set; }
17+
public bool OmitVersionDelimiter { get; set; }
18+
public RuntimeVersion Version { get; set; }
1419
public string Architecture { get; set; }
15-
public string QualifierDelimiter { get; set; }
1620
public string Qualifier { get; set; }
1721

1822
public override string ToString()
1923
{
2024
StringBuilder builder = new StringBuilder(BaseRID);
2125

22-
if (HasVersion())
26+
if (HasVersion)
2327
{
24-
builder.Append(VersionDelimiter);
28+
if (!OmitVersionDelimiter)
29+
{
30+
builder.Append(VersionDelimiter);
31+
}
2532
builder.Append(Version);
2633
}
2734

28-
if (HasArchitecture())
35+
if (HasArchitecture)
2936
{
3037
builder.Append(ArchitectureDelimiter);
3138
builder.Append(Architecture);
3239
}
3340

34-
if (HasQualifier())
41+
if (HasQualifier)
3542
{
3643
builder.Append(QualifierDelimiter);
3744
builder.Append(Qualifier);
@@ -40,20 +47,153 @@ public override string ToString()
4047
return builder.ToString();
4148
}
4249

43-
public bool HasVersion()
50+
private enum RIDPart : int
51+
{
52+
Base = 0,
53+
Version,
54+
Architecture,
55+
Qualifier,
56+
Max = Qualifier
57+
}
58+
59+
public static RID Parse(string runtimeIdentifier)
60+
{
61+
string[] parts = new string[(int)RIDPart.Max + 1];
62+
bool omitVersionDelimiter = true;
63+
RIDPart parseState = RIDPart.Base;
64+
65+
int partStart = 0, partLength = 0;
66+
67+
// qualifier is indistinguishable from arch so we cannot distinguish it for parsing purposes
68+
Debug.Assert(ArchitectureDelimiter == QualifierDelimiter);
69+
70+
for (int i = 0; i < runtimeIdentifier.Length; i++)
71+
{
72+
char current = runtimeIdentifier[i];
73+
partLength = i - partStart;
74+
75+
switch (parseState)
76+
{
77+
case RIDPart.Base:
78+
// treat any number as the start of the version
79+
if (current == VersionDelimiter || (current >= '0' && current <= '9'))
80+
{
81+
SetPart();
82+
partStart = i;
83+
if (current == VersionDelimiter)
84+
{
85+
omitVersionDelimiter = false;
86+
partStart = i + 1;
87+
}
88+
parseState = RIDPart.Version;
89+
}
90+
// version might be omitted
91+
else if (current == ArchitectureDelimiter)
92+
{
93+
// ensure there's no version later in the string
94+
if (runtimeIdentifier.IndexOf(VersionDelimiter, i) != -1)
95+
{
96+
break;
97+
}
98+
SetPart();
99+
partStart = i + 1; // skip delimiter
100+
parseState = RIDPart.Architecture;
101+
}
102+
break;
103+
case RIDPart.Version:
104+
if (current == ArchitectureDelimiter)
105+
{
106+
SetPart();
107+
partStart = i + 1; // skip delimiter
108+
parseState = RIDPart.Architecture;
109+
}
110+
break;
111+
case RIDPart.Architecture:
112+
if (current == QualifierDelimiter)
113+
{
114+
SetPart();
115+
partStart = i + 1; // skip delimiter
116+
parseState = RIDPart.Qualifier;
117+
}
118+
break;
119+
default:
120+
break;
121+
}
122+
}
123+
124+
partLength = runtimeIdentifier.Length - partStart;
125+
if (partLength > 0)
126+
{
127+
SetPart();
128+
}
129+
130+
string GetPart(RIDPart part)
131+
{
132+
return parts[(int)part];
133+
}
134+
135+
void SetPart()
136+
{
137+
if (partLength == 0)
138+
{
139+
throw new ArgumentException($"Unexpected delimiter at position {partStart} in \"{runtimeIdentifier}\"");
140+
}
141+
142+
parts[(int)parseState] = runtimeIdentifier.Substring(partStart, partLength);
143+
}
144+
145+
string version = GetPart(RIDPart.Version);
146+
147+
if (version == null)
148+
{
149+
omitVersionDelimiter = false;
150+
}
151+
152+
return new RID()
153+
{
154+
BaseRID = GetPart(RIDPart.Base),
155+
OmitVersionDelimiter = omitVersionDelimiter,
156+
Version = version == null ? null : new RuntimeVersion(version),
157+
Architecture = GetPart(RIDPart.Architecture),
158+
Qualifier = GetPart(RIDPart.Qualifier)
159+
};
160+
}
161+
162+
public bool HasVersion => Version != null;
163+
164+
public bool HasArchitecture => Architecture != null;
165+
166+
public bool HasQualifier => Qualifier != null;
167+
168+
public override bool Equals(object obj)
44169
{
45-
return Version != null;
170+
return Equals(obj as RID);
46171
}
47172

48-
public bool HasArchitecture()
173+
public bool Equals(RID obj)
49174
{
50-
return Architecture != null;
175+
return object.ReferenceEquals(obj, this) ||
176+
(obj is not null &&
177+
BaseRID == obj.BaseRID &&
178+
(Version == null || OmitVersionDelimiter == obj.OmitVersionDelimiter) &&
179+
Version == obj.Version &&
180+
Architecture == obj.Architecture &&
181+
Qualifier == obj.Qualifier);
182+
51183
}
52184

53-
public bool HasQualifier()
185+
public override int GetHashCode()
54186
{
55-
return Qualifier != null;
187+
#if NETFRAMEWORK
188+
return BaseRID.GetHashCode();
189+
#else
190+
HashCode hashCode = default;
191+
hashCode.Add(BaseRID);
192+
hashCode.Add(Version);
193+
hashCode.Add(Architecture);
194+
hashCode.Add(Qualifier);
195+
return hashCode.ToHashCode();
196+
#endif
56197
}
57198
}
58-
59199
}

0 commit comments

Comments
 (0)