|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Xml;
namespace Mono.Linker
{
/// <summary>
/// Class which implements IDependencyRecorder and writes the dependencies into an DGML file.
/// </summary>
public class DgmlDependencyRecorder : IDependencyRecorder, IDisposable
{
public const string DefaultDependenciesFileName = "linker-dependencies.dgml";
public Dictionary<string, int> nodeList = new ();
public HashSet<(string dependent, string dependee, string reason)> linkList = new (); // first element is source, second is target (dependent --> dependee), third is reason
private readonly LinkContext context;
private XmlWriter? writer;
private Stream? stream;
public DgmlDependencyRecorder (LinkContext context, string? fileName = null)
{
this.context = context;
XmlWriterSettings settings = new XmlWriterSettings {
Indent = true,
IndentChars = " "
};
fileName ??= DefaultDependenciesFileName;
if (string.IsNullOrEmpty (Path.GetDirectoryName (fileName)) && !string.IsNullOrEmpty (context.OutputDirectory)) {
fileName = Path.Combine (context.OutputDirectory, fileName);
Directory.CreateDirectory (context.OutputDirectory);
}
var depsFile = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
stream = depsFile;
writer = XmlWriter.Create (stream, settings);
writer.WriteStartDocument ();
writer.WriteStartElement ("DirectedGraph", "http://schemas.microsoft.com/vs/2009/dgml");
}
public void FinishRecording ()
{
Debug.Assert (writer != null);
writer.WriteStartElement ("Nodes");
{
foreach (var pair in nodeList) {
writer.WriteStartElement ("Node");
writer.WriteAttributeString ("Id", pair.Value.ToString ());
writer.WriteAttributeString ("Label", pair.Key);
writer.WriteEndElement ();
}
}
writer.WriteEndElement ();
writer.WriteStartElement ("Links");
{
foreach (var tup in linkList) {
writer.WriteStartElement ("Link");
writer.WriteAttributeString ("Source", nodeList[tup.dependent].ToString ());
writer.WriteAttributeString ("Target", nodeList[tup.dependee].ToString ());
writer.WriteAttributeString ("Reason", tup.reason);
writer.WriteEndElement ();
}
}
writer.WriteEndElement ();
writer.WriteStartElement ("Properties");
{
writer.WriteStartElement ("Property");
writer.WriteAttributeString ("Id", "Label");
writer.WriteAttributeString ("Label", "Label");
writer.WriteAttributeString ("DataType", "String");
writer.WriteEndElement ();
writer.WriteStartElement ("Property");
writer.WriteAttributeString ("Id", "Reason");
writer.WriteAttributeString ("Label", "Reason");
writer.WriteAttributeString ("DataType", "String");
writer.WriteEndElement ();
}
writer.WriteEndElement ();
writer.WriteEndElement ();
writer.WriteEndDocument ();
writer.Flush ();
}
public void Dispose ()
{
if (writer == null)
return;
writer.Dispose ();
stream?.Dispose ();
writer = null;
stream = null;
}
public void RecordDependency (object target, in DependencyInfo reason, bool marked)
{
if (writer == null)
throw new InvalidOperationException ();
if (reason.Kind == DependencyKind.Unspecified)
return;
// For now, just report a dependency from source to target without noting the DependencyKind.
RecordDependency (reason.Source, target, reason.Kind);
}
public void RecordDependency (object? source, object target, object? reason)
{
if (writer == null)
throw new InvalidOperationException ();
if (!DependencyRecorderHelper.ShouldRecord (context, source, target))
return;
string dependent = DependencyRecorderHelper.TokenString (context, source);
string dependee = DependencyRecorderHelper.TokenString (context, target);
// figure out why nodes are sometimes null, are we missing some information in the graph?
if (!nodeList.ContainsKey (dependent)) AddNode (dependent);
if (!nodeList.ContainsKey (dependee)) AddNode (dependee);
if (source != target) {
AddLink (dependent, dependee, reason);
}
}
private int _nodeIndex;
void AddNode (string node)
{
nodeList.Add (node, _nodeIndex);
_nodeIndex++;
}
void AddLink (string source, string target, object? kind)
{
linkList.Add ((source, target, DependencyRecorderHelper.TokenString (context, kind)));
}
public void RecordDependency (object source, object target, bool marked)
{
throw new NotImplementedException ();
}
}
}
|