|
// 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();
}
}
}
|