Skip to content

Instantly share code, notes, and snippets.

@0xced
Last active April 17, 2022 08:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0xced/8c23cda2a58e0744560fac802db404dd to your computer and use it in GitHub Desktop.
Save 0xced/8c23cda2a58e0744560fac802db404dd to your computer and use it in GitHub Desktop.
Graphing the .NET RID Catalog with Graphviz
*.user
bin/
obj/
.idea/
.vs/

Generate visualizable .NET runtime identifier graphs in SVG

.NET runtime identifier graph for iOS

About

This simple tool brings 4 awesome components together.

  1. Graphviz for generating the SVG graph
  2. The NuGet.Packaging library for reading the runtime.json file
  3. The GiGraph library for generating the dot graph
  4. The CliWrap library for making calling the dot command line tool a breeze

Have a look at GiGraph and CliWrap repositories, they have perfect READMEs! 🤩

Usage

The dot command line tool must be installed, see the Graphviz download page to install it on your operating system.

  • Generate the full graph of .NET runtime identifiers from the latest runtime.json definition
dotnet run > rid.svg

⚠️ The produced graph will be huge and pretty hard to read. See below for how to generate only parts of the graph.

  • Generate a sub-graph for a given runtime identifier
dotnet run ios > rid-ios.svg
  • Generate multiple sub-graphs for given runtime identifiers
dotnet run win aot > rid-win-aot.svg
namespace RidGraph;
public class DotGraphPipeSource : PipeSource
{
private readonly DotGraph _graph;
public DotGraphPipeSource(DotGraph graph)
{
_graph = graph ?? throw new ArgumentNullException(nameof(graph));
}
public override Task CopyToAsync(Stream destination, CancellationToken cancellationToken = default)
{
using var streamWriter = new StreamWriter(destination, leaveOpen: true);
_graph.Build(streamWriter);
return Task.CompletedTask;
}
}
var compatibleRuntimes = args;
var errorConsole = AnsiConsole.Create(new AnsiConsoleSettings { Out = new AnsiConsoleOutput(Console.Error) });
try
{
var runtimeJsonUri = new Uri("https://raw.githubusercontent.com/dotnet/runtime/main/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json");
using var httpClient = new HttpClient();
var runtimeJsonStream = await httpClient.GetStreamAsync(runtimeJsonUri);
var runtimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(runtimeJsonStream);
var edges = from runtime in runtimeGraph.Runtimes.Values
from inheritedRuntime in runtime.InheritedRuntimes
where compatibleRuntimes.Length == 0 || compatibleRuntimes.Any(e => runtimeGraph.AreCompatible(inheritedRuntime, e))
select new DotEdge(runtime.RuntimeIdentifier, inheritedRuntime);
var filterDescription = compatibleRuntimes.Any() ? $" ({string.Join(" ", compatibleRuntimes)})" : "";
var linkStyle = new DotStyledFont(DotFontStyles.Underline, System.Drawing.Color.Blue);
var dotGraph = new DotGraph
{
Label = new DotHtmlBuilder().AppendStyledText(".NET RID Catalog", linkStyle).AppendText(filterDescription).Build(),
Hyperlink = { Url = "https://docs.microsoft.com/en-us/dotnet/core/rid-catalog" },
Layout = { Direction = DotLayoutDirection.BottomToTop },
};
dotGraph.Edges.AddRange(edges);
if (dotGraph.Edges.Count == 0)
{
throw new ApplicationException($"No graph was constructed. Make sure that the specified filter{filterDescription} matches at least one runtime identifier in the graph.");
}
var input = new RidGraph.DotGraphPipeSource(dotGraph);
var dot = Cli.Wrap("dot").WithArguments(new[] { "-Tsvg" }).WithStandardErrorPipe(PipeTarget.ToDelegate(line => errorConsole.WriteLine(line, new Style(Color.DarkOrange))));
var output = PipeTarget.ToDelegate(AnsiConsole.WriteLine);
await (input | dot | output).ExecuteAsync();
}
catch (Exception exception)
{
errorConsole.WriteException(exception);
}
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Using Include="CliWrap" />
<Using Include="GiGraph.Dot.Entities.Edges" />
<Using Include="GiGraph.Dot.Entities.Graphs" />
<Using Include="GiGraph.Dot.Entities.Html.Builder" />
<Using Include="GiGraph.Dot.Extensions" />
<Using Include="GiGraph.Dot.Types.Fonts" />
<Using Include="GiGraph.Dot.Types.Layout" />
<Using Include="NuGet.RuntimeModel" />
<Using Include="Spectre.Console" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.4.2" />
<PackageReference Include="GiGraph.Dot" Version="2.0.0" />
<PackageReference Include="NuGet.Packaging" Version="6.1.0" />
<PackageReference Include="Spectre.Console" Version="0.44.0" />
</ItemGroup>
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment