Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ public ITaskItem[] RuntimeGroups
set;
}

/// <summary>
/// Additional runtime identifiers to add to the graph.
/// </summary>
public string[] AdditionalRuntimeIdentifiers
{
get;
set;
}

/// <summary>
/// Parent RID to use for any unknown AdditionalRuntimeIdentifer.
/// </summary>
public string AdditionalRuntimeIdentifierParent
{
get;
set;
}

/// <summary>
/// Optional source Runtime.json to use as a starting point when merging additional RuntimeGroups
/// </summary>
Expand Down Expand Up @@ -134,7 +152,11 @@ public override bool Execute()
runtimeGraph = new RuntimeGraph();
}

foreach (var runtimeGroup in RuntimeGroups.NullAsEmpty().Select(i => new RuntimeGroup(i)))
List<RuntimeGroup> runtimeGroups = RuntimeGroups.NullAsEmpty().Select(i => new RuntimeGroup(i)).ToList();

AddRuntimeIdentifiers(runtimeGroups);

foreach (var runtimeGroup in runtimeGroups)
{
runtimeGraph = SafeMerge(runtimeGraph, runtimeGroup);
}
Expand Down Expand Up @@ -291,6 +313,21 @@ private void ValidateImports(RuntimeGraph runtimeGraph, IDictionary<string, stri
}
}

private void AddRuntimeIdentifiers(ICollection<RuntimeGroup> runtimeGroups)
{
if (AdditionalRuntimeIdentifiers == null || AdditionalRuntimeIdentifiers.Length == 0)
{
return;
}

RuntimeGroupCollection runtimeGroupCollection = new RuntimeGroupCollection(runtimeGroups);

foreach (string additionalRuntimeIdentifier in AdditionalRuntimeIdentifiers)
{
runtimeGroupCollection.AddRuntimeIdentifier(additionalRuntimeIdentifier, AdditionalRuntimeIdentifierParent);
}
}

private static IDictionary<string, IEnumerable<string>> GetCompatibilityMap(RuntimeGraph graph)
{
Dictionary<string, IEnumerable<string>> compatibilityMap = new Dictionary<string, IEnumerable<string>>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.SDK">
<Project Sdk="Microsoft.NET.SDK">
<!-- These are wrapper project files for packaging.-->
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppToolCurrent);net472</TargetFrameworks>
Expand All @@ -12,6 +12,9 @@
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<NoWarn>$(NoWarn);NU5128</NoWarn> <!-- No Dependencies-->
<PackageDescription>Provides runtime information required to resolve target framework, platform, and runtime specific implementations of .NETCore packages.</PackageDescription>

<!-- When building from source, ensure the RID we're building for is part of the RID graph -->
<AdditionalRuntimeIdentifiers Condition="'$(DotNetBuildFromSource)' == 'true'">$(AdditionalRuntimeIdentifiers);$(OutputRID)</AdditionalRuntimeIdentifiers>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good we're appending the OutputRID at the end here. The AdditionalRuntimeIdentifiers will be added in order, so we want them to be ordered from least to most specific.

Copy link
Member Author

@ericstj ericstj Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That shouldn’t matter since the only way to establish a parent RID is through the parent parameter. New RIDs are added recursively following the rules in the runtime groups and parent. When written the graph is sorted.

In other words if you are imagining a case where order matters then you probably don’t need to specify that RID since it will be discovered recursively.

</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
Expand All @@ -24,11 +27,14 @@
<Compile Include="Extensions.cs" />
<Compile Include="GenerateRuntimeGraph.cs" />
<Compile Include="RID.cs" />
<Compile Include="RuntimeGroupCollection.cs" />
<Compile Include="RuntimeGroup.cs" />
<Compile Include="RuntimeVersion.cs" />
</ItemGroup>

<ItemGroup>
<Content Include="runtime.json" PackagePath="/" />
<Content Condition="'$(AdditionalRuntimeIdentifiers)' == ''" Include="runtime.json" PackagePath="/" />
<Content Condition="'$(AdditionalRuntimeIdentifiers)' != ''" Include="$(IntermediateOutputPath)runtime.json" PackagePath="/" />
<Content Include="_._" PackagePath="lib/netstandard1.0" />
</ItemGroup>

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

<Target Name="GenerateRuntimeJson" Condition="'$(AdditionalRuntimeIdentifiers)' != ''" BeforeTargets="GenerateNuspec">
<MakeDir Directories="$(IntermediateOutputPath)" />
<GenerateRuntimeGraph RuntimeGroups="@(RuntimeGroupWithQualifiers)"
AdditionalRuntimeIdentifiers="$(AdditionalRuntimeIdentifiers)"
AdditionalRuntimeIdentifierParent="$(AdditionalRuntimeIdentifierParent)"
RuntimeJson="$(IntermediateOutputPath)runtime.json"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider defining a property for the path to the runtime.json file. You are currently hardcoding it in two places.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do so in a follow up PR

UpdateRuntimeFiles="True" />
</Target>

<Import Project="runtimeGroups.props" />
</Project>
172 changes: 156 additions & 16 deletions src/libraries/Microsoft.NETCore.Platforms/src/RID.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Text;

namespace Microsoft.NETCore.Platforms.BuildTasks
{
internal class RID
public class RID
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the access modifier changed because of tests depending on the RID class now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I didn't bother setting up internals visible to since this is an msbuild task and the only consumer of it is our build and tests.

{
internal const char VersionDelimiter = '.';
internal const char ArchitectureDelimiter = '-';
internal const char QualifierDelimiter = '-';

public string BaseRID { get; set; }
public string VersionDelimiter { get; set; }
public string Version { get; set; }
public string ArchitectureDelimiter { get; set; }
public bool OmitVersionDelimiter { get; set; }
public RuntimeVersion Version { get; set; }
public string Architecture { get; set; }
public string QualifierDelimiter { get; set; }
public string Qualifier { get; set; }

public override string ToString()
{
StringBuilder builder = new StringBuilder(BaseRID);

if (HasVersion())
if (HasVersion)
{
builder.Append(VersionDelimiter);
if (!OmitVersionDelimiter)
{
builder.Append(VersionDelimiter);
}
builder.Append(Version);
}

if (HasArchitecture())
if (HasArchitecture)
{
builder.Append(ArchitectureDelimiter);
builder.Append(Architecture);
}

if (HasQualifier())
if (HasQualifier)
{
builder.Append(QualifierDelimiter);
builder.Append(Qualifier);
Expand All @@ -40,20 +47,153 @@ public override string ToString()
return builder.ToString();
}

public bool HasVersion()
private enum RIDPart : int
{
Base = 0,
Version,
Architecture,
Qualifier,
Max = Qualifier
}

public static RID Parse(string runtimeIdentifier)
{
string[] parts = new string[(int)RIDPart.Max + 1];
bool omitVersionDelimiter = true;
RIDPart parseState = RIDPart.Base;

int partStart = 0, partLength = 0;

// qualifier is indistinguishable from arch so we cannot distinguish it for parsing purposes
Debug.Assert(ArchitectureDelimiter == QualifierDelimiter);

for (int i = 0; i < runtimeIdentifier.Length; i++)
{
char current = runtimeIdentifier[i];
partLength = i - partStart;

switch (parseState)
{
case RIDPart.Base:
// treat any number as the start of the version
if (current == VersionDelimiter || (current >= '0' && current <= '9'))
{
SetPart();
partStart = i;
if (current == VersionDelimiter)
{
omitVersionDelimiter = false;
partStart = i + 1;
}
parseState = RIDPart.Version;
}
// version might be omitted
else if (current == ArchitectureDelimiter)
{
// ensure there's no version later in the string
if (runtimeIdentifier.IndexOf(VersionDelimiter, i) != -1)
{
break;
}
SetPart();
partStart = i + 1; // skip delimiter
parseState = RIDPart.Architecture;
}
break;
case RIDPart.Version:
if (current == ArchitectureDelimiter)
{
SetPart();
partStart = i + 1; // skip delimiter
parseState = RIDPart.Architecture;
}
break;
case RIDPart.Architecture:
if (current == QualifierDelimiter)
{
SetPart();
partStart = i + 1; // skip delimiter
parseState = RIDPart.Qualifier;
}
break;
default:
break;
}
}

partLength = runtimeIdentifier.Length - partStart;
if (partLength > 0)
{
SetPart();
}

string GetPart(RIDPart part)
{
return parts[(int)part];
}

void SetPart()
{
if (partLength == 0)
{
throw new ArgumentException($"Unexpected delimiter at position {partStart} in \"{runtimeIdentifier}\"");
}

parts[(int)parseState] = runtimeIdentifier.Substring(partStart, partLength);
}

string version = GetPart(RIDPart.Version);

if (version == null)
{
omitVersionDelimiter = false;
}

return new RID()
{
BaseRID = GetPart(RIDPart.Base),
OmitVersionDelimiter = omitVersionDelimiter,
Version = version == null ? null : new RuntimeVersion(version),
Architecture = GetPart(RIDPart.Architecture),
Qualifier = GetPart(RIDPart.Qualifier)
};
}

public bool HasVersion => Version != null;

public bool HasArchitecture => Architecture != null;

public bool HasQualifier => Qualifier != null;

public override bool Equals(object obj)
{
return Version != null;
return Equals(obj as RID);
}

public bool HasArchitecture()
public bool Equals(RID obj)
{
return Architecture != null;
return object.ReferenceEquals(obj, this) ||
(obj is not null &&
BaseRID == obj.BaseRID &&
(Version == null || OmitVersionDelimiter == obj.OmitVersionDelimiter) &&
Version == obj.Version &&
Architecture == obj.Architecture &&
Qualifier == obj.Qualifier);

}

public bool HasQualifier()
public override int GetHashCode()
{
return Qualifier != null;
#if NETFRAMEWORK
return BaseRID.GetHashCode();
#else
HashCode hashCode = default;
hashCode.Add(BaseRID);
hashCode.Add(Version);
hashCode.Add(Architecture);
hashCode.Add(Qualifier);
return hashCode.ToHashCode();
#endif
}
}

}
Loading