Sharpmake/DependencyTracker.cs (180 lines of code) (raw):
// Copyright (c) Ubisoft. All Rights Reserved.
// Licensed under the Apache 2.0 License. See LICENSE.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Sharpmake
{
internal class DependencyTracker
{
public static DependencyTracker Instance { get; private set; } = new DependencyTracker();
public static bool ShowDependenciesFromExtern = true;
public static bool ShowDependenciesToExtern = true;
public static bool GraphWriteLegend = true;
public const string ColorExe = "lightcyan";
public const string ColorDll = "pink";
public const string ColorLib = "lemonchiffon";
public const string ColorExternExe = ColorExe + "3";
public const string ColorExternDll = ColorDll + "3";
public const string ColorExternLib = ColorLib + "3";
public const string ColorUnkownOutputType = "white";
public const string ColorPublicDep = "green";
public const string ColorPrivateDep = "red";
public const string ShapeProject = "egg";
public const string ShapeExtern = "diamond";
public const string ShapeUnknown = "octagon";
public const int WidthExternDep = 1;
public const int WidthDep = 2;
public DependencyTracker()
{
_projects = new Dictionary<string, TrackedProject>();
}
public static void ResetSingleton()
{
Instance = new DependencyTracker();
}
public void UpdateConfiguration(Project project, Project.Configuration config)
{
lock (this)
{
foreach (KeyValuePair<string, TrackedProject> p in _projects)
{
if (p.Key == project.ToString())
{
p.Value.AddConfig(config);
return;
}
}
_projects.Add(project.ToString(), new TrackedProject(project, config));
}
}
public void AddDependency(
DependencyType dependencyType,
Project projectFrom,
Project.Configuration configFrom,
IEnumerable<KeyValuePair<Type, ITarget>> dependencies,
IDictionary<ValueTuple<Type, ITarget>, DependencySetting> dependenciesSetting
)
{
lock (this)
{
foreach (KeyValuePair<Type, ITarget> pair in dependencies)
{
TrackedConfiguration confFrom = FindConfiguration(projectFrom, configFrom);
TrackedConfiguration confTo = FindConfiguration(pair.Key, pair.Value);
var key = new ValueTuple<Type, ITarget>(pair.Key, pair.Value);
DependencySetting dependencySetting;
if (!dependenciesSetting.TryGetValue(key, out dependencySetting))
dependencySetting = DependencySetting.Default;
confFrom.AddDependency(confTo, dependencyType, dependencySetting);
}
}
}
public void DumpGraphs(IDictionary<Type, GenerationOutput> outputs)
{
lock (this)
{
var dependencyOutput = outputs.GetValueOrAdd(typeof(DependencyTracker), new GenerationOutput());
foreach (KeyValuePair<string, TrackedProject> pair in _projects)
{
TrackedProject p = pair.Value;
foreach (KeyValuePair<string, TrackedConfiguration> confPair in p.Configurations)
{
TrackedConfiguration c = confPair.Value;
if (!c.DumpDependencyGraph())
continue;
string fileName = @"Dependencies ("
+ c.GetDisplayedName(false)
+ ", "
+ c.GetConfigName()
+ (DependencyTracker.ShowDependenciesFromExtern ? "" : " [excl from Extern]")
+ (DependencyTracker.ShowDependenciesToExtern ? "" : " [excl to Extern]")
+ ").gv";
// Open the stream and read it back.
MemoryStream memoryStream = new MemoryStream();
StreamWriter writer = new StreamWriter(memoryStream);
writer.WriteLine("digraph g");
writer.WriteLine("{");
writer.WriteLine("graph [rankdir = \"TD\" bgcolor = \"lightblue:black\" style=\"filled\" gradientangle = 270 splines=true];");
WriteGraph(writer, c, "extStruct", false, false, () =>
{
foreach (KeyValuePair<string, TrackedProject> proj in _projects)
proj.Value.ResetVisit();
}
);
if (GraphWriteLegend)
WriteLegend(writer);
writer.WriteLine("}");
writer.Flush();
var outputFileInfo = new FileInfo(fileName);
bool written = Builder.Instance.Context.WriteGeneratedFile(typeof(DependencyTracker), outputFileInfo, memoryStream);
if (written)
dependencyOutput.Generated.Add(outputFileInfo.FullName);
else
dependencyOutput.Skipped.Add(outputFileInfo.FullName);
}
}
}
}
private TrackedConfiguration FindConfiguration(Project project, Project.Configuration config)
{
TrackedProject p = _projects[project.ToString()];
return p.FindConfiguration(config);
}
private TrackedConfiguration FindConfiguration(Type project, ITarget target)
{
TrackedProject p = _projects[project.ToString()];
return p.FindConfiguration(target);
}
private delegate void ResetVisitDelegate();
private void WriteGraph(StreamWriter stream, TrackedConfiguration root, string externStructName, bool verboseEdges, bool verboseNodes, ResetVisitDelegate ResetVisit)
{
try
{
ResetVisit();
root.WriteNodeDescription(stream, false, verboseNodes, "");
ResetVisit();
root.WriteNodeDescription(stream, true, verboseNodes, "");
stream.WriteLine();
ResetVisit();
root.WriteDependencies(stream, verboseEdges, "");
stream.WriteLine();
}
catch (Exception e)
{
System.Diagnostics.Trace.WriteLine(e);
}
}
private void WriteLegend(StreamWriter stream)
{
stream.WriteLine("subgraph cluster_l{");
stream.WriteLine("\tgraph[style = \"rounded, filled\", fillcolor = lightyellow];");
List<TrackedProject> legendProjects = new List<TrackedProject>();
List<TrackedProject> legendExtProjects = new List<TrackedProject>();
legendProjects.Add(new TrackedProject("Executable", false, Project.Configuration.OutputType.Exe));
legendProjects.Add(new TrackedProject("Static_Library1", false, Project.Configuration.OutputType.Lib));
legendProjects.Add(new TrackedProject("Dll", false, Project.Configuration.OutputType.Dll));
legendProjects.Add(new TrackedProject("Static_Library2", false, Project.Configuration.OutputType.Lib));
legendExtProjects.Add(new TrackedProject("Extern_Library1", true, Project.Configuration.OutputType.Lib));
legendExtProjects.Add(new TrackedProject("Extern_Dll", true, Project.Configuration.OutputType.Dll));
legendExtProjects.Add(new TrackedProject("Extern_Library2", true, Project.Configuration.OutputType.Lib));
legendProjects[0].Configurations.First().Value.AddDependency(legendProjects[1].Configurations.First().Value, DependencyType.Public);
legendProjects[1].Configurations.First().Value.AddDependency(legendProjects[2].Configurations.First().Value, DependencyType.Public);
legendProjects[2].Configurations.First().Value.AddDependency(legendProjects[3].Configurations.First().Value, DependencyType.Private);
legendProjects[1].Configurations.First().Value.AddDependency(legendExtProjects[0].Configurations.First().Value, DependencyType.Public);
legendProjects[2].Configurations.First().Value.AddDependency(legendExtProjects[2].Configurations.First().Value, DependencyType.Public);
legendProjects[3].Configurations.First().Value.AddDependency(legendExtProjects[1].Configurations.First().Value, DependencyType.Private);
WriteGraph(stream, legendProjects[0].Configurations.First().Value, "ExternLegend", true, false,
() =>
{
foreach (TrackedProject p in legendProjects)
p.ResetVisit();
foreach (TrackedProject p in legendExtProjects)
p.ResetVisit();
}
);
stream.WriteLine("\t}");
}
private readonly Dictionary<string, TrackedProject> _projects;
}
}