Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support displaying package version and dependency in chart #38

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
91 changes: 60 additions & 31 deletions NuGet.Extensions/Commands/Graph.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using NuGet.Commands;
using NuGet.Common;
using NuGet.Extensions.Commands.GraphCommand;
using QuickGraph;
using QuickGraph.Serialization;
using QuickGraph.Algorithms;
using QuickGraph.Serialization;

namespace NuGet.Extensions.Commands
{
[Command("graph", "Provides a DGML graph of either a package and all its dependencies, or an entire feed.", MinArgs = 0)]
public class Graph : Command
{
private readonly List<string> _sources = new List<string>();
private AdjacencyGraph<string, Edge<string>> _graph;
private AdjacencyGraph<IPackageInfo, Edge<IPackageInfo>> _graph;
private IPackageInfoFactory _packageInfoFactory;

[Option("A list of sources to search")]
public ICollection<string> Source
Expand All @@ -40,18 +41,26 @@ public string Output
set { _output = value; }
}


[Option("With version details.")]
public bool WithVersion { get; set; }

[ImportingConstructor]
public Graph () {}

public override void ExecuteCommand()
{
var sw = new Stopwatch();
sw.Start();

var repository = GetRepository();
var packageSource = repository.GetPackages().Where(p => p.IsLatestVersion);
var packageSource = repository.GetPackages();
var packages = FilterPackageList(packageSource);

_graph = new AdjacencyGraph<string,Edge<string>>();
_packageInfoFactory = WithVersion
? (IPackageInfoFactory) new FullPackageInfoFactory()
: new SimplePackageInfoFactory();
_graph = new AdjacencyGraph<IPackageInfo, Edge<IPackageInfo>>();

foreach (var package in packages)
{
Expand All @@ -63,9 +72,17 @@ public override void ExecuteCommand()
RemoveLonersFromGraph();
}

var dgml = _graph.ToDirectedGraphML(_graph.GetVertexIdentity(),_graph.GetEdgeIdentity(),(n,d) => d.Label = n,(e,l) => l.Label = "");
var dgml = _graph.ToDirectedGraphML(_graph.GetVertexIdentity(),
_graph.GetEdgeIdentity(),
(n, d) =>
{
d.Label = n.ToString();
d.Description = n.Details;
},
(e, l) => l.Label = e.Target.TargetInfo);
if (File.Exists(Output))
File.Delete(Output);

dgml.WriteXml(Output);

var isDirectedAcyclicGraph = _graph.IsDirectedAcyclicGraph();
Expand All @@ -83,54 +100,66 @@ public override void ExecuteCommand()

private void RemoveLonersFromGraph()
{
//If we have a vertex that isnt used in an edge, and we are looking for a cleaner graph, then delete the vertex...
//If we have a vertex that isn't used in an edge, and we are looking for a cleaner graph, then delete the vertex...
var toDelete =
_graph.Vertices.Where(vertex => !_graph.Edges.Any(e => e.Source.Equals(vertex) || e.Target.Equals(vertex))).
ToList();
ToList();
toDelete.ForEach(v =>
{
Console.WriteLine("Removing loner: {0}", v);
_graph.RemoveVertex(v);
});
{
Console.WriteLine("Removing loner: {0}", v);
_graph.RemoveVertex(v);
});
}

private void RecursePackageDependencies(IPackage package)
{
Console.WriteLine("Adding package and dependencies: {0}", package.Id);
_graph.AddVertex(package.Id.ToLowerInvariant());
_graph.AddVertex(_packageInfoFactory.From(package));
//We are going to ignore the TargetFramework for these purposes...
foreach (var dependency in package.DependencySets.SelectMany(p => p.Dependencies))
foreach (PackageDependency dependency in package.DependencySets.SelectMany(p => p.Dependencies))
{
if (!_graph.Vertices.Contains(package.Id.ToLowerInvariant()))
RecursePackageDependencies((IPackage)dependency);
_graph.AddVerticesAndEdge(new Edge<string>(package.Id.ToLowerInvariant(), dependency.Id.ToLowerInvariant()));
_graph.AddVerticesAndEdge(new Edge<IPackageInfo>(_packageInfoFactory.From(package),
_packageInfoFactory.From(dependency)));
}
}


private IEnumerable<IPackage> FilterPackageList(IQueryable<IPackage> packageSource)
{
IQueryable<IPackage> packages;
if (Arguments.Count > 0 && !string.IsNullOrEmpty(Arguments[0]))
{
packages = packageSource.Where(p => p.Id.Equals(Arguments[0], StringComparison.OrdinalIgnoreCase));
}
else
{
packages = packageSource.OrderBy(p => p.Id);
}
return packages;
}
{
IQueryable<IPackage> packages;
if (Arguments.Count > 0 && !string.IsNullOrEmpty(Arguments[0]))
{
packages = packageSource.Where(p => p.Id.Equals(Arguments[0], StringComparison.OrdinalIgnoreCase));
}
else
{
packages = packageSource.OrderBy(p => p.Id);
}
return packages;
}

private void OutputElapsedTime(Stopwatch sw)
{
Console.WriteLine("Completed graph in {0} seconds", sw.Elapsed.TotalSeconds);
}

private IPackageRepository GetRepository()
{
FixSource();

var repository = AggregateRepositoryHelper.CreateAggregateRepositoryFromSources(RepositoryFactory, SourceProvider, Source);
repository.Logger = Console;
return repository;
}

private void FixSource()
{
// Allow to run without source or with "." as current folder
if (Source.Count == 0 || (Source.Count == 1 && Source.Contains(".")))
{
Source.Clear();
Source.Add(Environment.CurrentDirectory);
}
}
}
}
}
57 changes: 57 additions & 0 deletions NuGet.Extensions/Commands/GraphCommand/Comparers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;

namespace NuGet.Extensions.Commands.GraphCommand
{
public static class Comparers
{
internal static bool DependencyEquals(PackageDependency dep1, PackageDependency dep2)
{
return 0 == DependencyComparer(dep1, dep2);
}

internal static int DependencyComparer(PackageDependency dep1, PackageDependency dep2)
{
int cmp = StringComparer.OrdinalIgnoreCase.Compare(dep1.Id, dep2.Id);
if (cmp != 0)
{
return cmp;
}
return VersionComparison((VersionSpec)dep1.VersionSpec, (VersionSpec)dep2.VersionSpec);
}

private static int VersionComparison(VersionSpec ver1, VersionSpec ver2)
{
if (ver1 == null && ver2 == null)
{
return 0;
}
if (ver1 == null)
{
return -1;
}
if (ver2 == null)
{
return 1;
}


var cmp = VersionComparison(ver1.MinVersion, ver2.MinVersion);
if (cmp != 0)
{
return cmp;
}

return VersionComparison(ver1.MaxVersion, ver2.MaxVersion);
}

private static int VersionComparison(SemanticVersion ver1, SemanticVersion ver2)
{
if (ver1 == null && ver2 == null)
{
return 0;
}

return ver1 != null ? ver1.CompareTo(ver2) : 1;
}
}
}
65 changes: 65 additions & 0 deletions NuGet.Extensions/Commands/GraphCommand/FullPackageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace NuGet.Extensions.Commands.GraphCommand
{
public class FullPackageInfo : SimplePackageInfo
{
private readonly List<PackageDependency> _dependencyConstraints = new List<PackageDependency>();
private const string NotInstalledName = "<not installed>";

private string _fullName;

public FullPackageInfo(IPackage from) : base(from)
{
_dependencyConstraints.Add(new PackageDependency(Id));
_fullName = from.ToString();
}

public void Add(PackageDependency dependency)
{
if (!_dependencyConstraints.Any(dep => Comparers.DependencyEquals(dependency, dep)))
{
var dummy = _dependencyConstraints.SingleOrDefault(dep => dep.VersionSpec == null);
if (dummy != null)
{
_dependencyConstraints.Remove(dummy);
}

_dependencyConstraints.Add(dependency);
_dependencyConstraints.Sort(Comparers.DependencyComparer);
}
}

public FullPackageInfo(PackageDependency to) : base(to)
{
_dependencyConstraints.Add(to);
TargetInfo = to.ToString();
_fullName = NotInstalledName;
}

public void Update(IPackage installedPackage)
{
_fullName = installedPackage.ToString();
string dependencies = string.Join("\n", installedPackage.DependencySets.SelectMany(p => p.Dependencies));
Details = "Dependencies: " + (string.IsNullOrEmpty(dependencies) ? "none" : ("\n" + dependencies));
}

private bool IsInstalled()
{
return _fullName != NotInstalledName;
}

public override string ToString()
{
string text = (IsInstalled() ? _fullName : "")
+
(IsInstalled() && _dependencyConstraints.Count == 1
? ""
: ("\n" + string.Join("\n", _dependencyConstraints)));

return text;
}
}
}
43 changes: 43 additions & 0 deletions NuGet.Extensions/Commands/GraphCommand/FullPackageInfoFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Linq;
using QuickGraph.Algorithms.Search;

namespace NuGet.Extensions.Commands.GraphCommand
{
public class FullPackageInfoFactory : IPackageInfoFactory
{
private readonly List<FullPackageInfo> _packages = new List<FullPackageInfo>();

public IPackageInfo From(IPackage package)
{
FullPackageInfo me = _packages.FirstOrDefault(p => p.Is(package));
if (me == null)
{
me = new FullPackageInfo(package);
_packages.Add(me);
}
else
{
me.Update(package);
}

return me;
}

public IPackageInfo From(PackageDependency dependency)
{
FullPackageInfo refs = _packages.FirstOrDefault(p => p.Is(dependency));
if (refs == null)
{
refs = new FullPackageInfo(dependency);
_packages.Add(refs);
}
else
{
refs.Add(dependency);
}

return refs;
}
}
}
11 changes: 11 additions & 0 deletions NuGet.Extensions/Commands/GraphCommand/IPackageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace NuGet.Extensions.Commands.GraphCommand
{
public interface IPackageInfo
{
bool Is(PackageDependency dependency);
bool Is(IPackage package);

string TargetInfo { get; }
string Details { get; set; }
}
}
8 changes: 8 additions & 0 deletions NuGet.Extensions/Commands/GraphCommand/IPackageInfoFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace NuGet.Extensions.Commands.GraphCommand
{
public interface IPackageInfoFactory
{
IPackageInfo From(IPackage package);
IPackageInfo From(PackageDependency dependency);
}
}
Loading