Skip to content
This repository has been archived by the owner on Dec 18, 2017. It is now read-only.

#1513: Print source of assemblies dependencies in dnu list --assembly #1611

Merged
merged 11 commits into from
Apr 16, 2015
82 changes: 69 additions & 13 deletions src/Microsoft.Framework.PackageManager/List/AssemblyWalker.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand All @@ -18,38 +19,74 @@ public class AssemblyWalker
private readonly FrameworkName _framework;
private readonly ApplicationHostContext _hostContext;
private readonly string _runtimeFolder;
private readonly bool _showDetails;
private readonly Reports _reports;

private HashSet<string> _assemblyFilePaths;
private Dictionary<string, HashSet<string>> _dependencyAssemblySources;
private Dictionary<string, HashSet<string>> _dependencyPackageSources;

public AssemblyWalker(
FrameworkName framework,
ApplicationHostContext hostContext,
string runtimeFolder)
string runtimeFolder,
bool showDetails,
Reports reports)
{
_framework = framework;
_hostContext = hostContext;
_runtimeFolder = runtimeFolder;
_showDetails = showDetails;
_reports = reports;
}

public ISet<string> Walk(IGraphNode<LibraryDescription> root)
public void Walk(IGraphNode<LibraryDescription> root)
{
var assemblies = new HashSet<string>();
_assemblyFilePaths = new HashSet<string>(StringComparer.Ordinal);
_dependencyAssemblySources = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);
_dependencyPackageSources = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);

var libraries = new HashSet<LibraryDescription>();
root.DepthFirstPreOrderWalk(visitNode: (node, _) => VisitLibrary(node, _, libraries, assemblies));
root.DepthFirstPreOrderWalk(visitNode: (node, _) => VisitLibrary(node, _, libraries));

_reports.Information.WriteLine("\n[Target framework {0} ({1})]\n",
_framework.ToString(), VersionUtility.GetShortFrameworkName(_framework));

return assemblies;
foreach (var assemblyFilePath in _assemblyFilePaths.OrderBy(assemblyName => assemblyName))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StringComparer.Ordinal

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will skip this one. The order is for presentation only.

{
_reports.Information.WriteLine(assemblyFilePath);
if (_showDetails)
{
var assemblyName = Path.GetFileNameWithoutExtension(assemblyFilePath);

HashSet<string> packagesSources;
if (_dependencyPackageSources.TryGetValue(assemblyName, out packagesSources) && packagesSources.Any())
{
_reports.Information.WriteLine(" from package: {0}", string.Join(", ", packagesSources));
}

HashSet<string> assemblySources;
if (_dependencyAssemblySources.TryGetValue(assemblyName, out assemblySources) && assemblySources.Any())
{
_reports.Information.WriteLine(" from assembly: {0}", string.Join(", ", assemblySources));
}
}
}
}

private bool VisitLibrary(IGraphNode<LibraryDescription> node,
IEnumerable<IGraphNode<LibraryDescription>> ancestors,
ISet<LibraryDescription> visitedLibraries,
ISet<string> assemblies)
ISet<LibraryDescription> visitedLibraries)
{
if (visitedLibraries.Add(node.Item))
{
foreach (var loadableAssembly in node.Item.LoadableAssemblies)
{
AddDependencySource(_dependencyPackageSources, loadableAssembly, node.Item.Identity.Name);

DepthFirstGraphTraversal.PreOrderWalk(
root: loadableAssembly,
visitNode: (assemblyNode, assemblyAncestors) => VisitAssembly(assemblyNode, assemblyAncestors, assemblies),
visitNode: VisitAssembly,
getChildren: GetAssemblyDependencies);
}

Expand All @@ -65,31 +102,38 @@ private IEnumerable<string> GetAssemblyDependencies(string assemblyName)
if (filepath != null && File.Exists(filepath))
{
var assemblyInfo = new AssemblyInformation(filepath, processorArchitecture: null);
return assemblyInfo.GetDependencies();
var dependencies = assemblyInfo.GetDependencies();

foreach (var dependency in dependencies)
{
AddDependencySource(_dependencyAssemblySources, dependency, assemblyName);
}

return dependencies;
}
else
{
return Enumerable.Empty<string>();
}
}

private bool VisitAssembly(string assembly, IEnumerable<string> ancestors, ISet<string> assemblies)
private bool VisitAssembly(string assemblyName, IEnumerable<string> ancestors)
{
// determine if keep walking down the path
if (ancestors.Any(a => a == assembly))
if (ancestors.Any(a => string.Equals(a, assemblyName, StringComparison.Ordinal)))
{
// break the reference loop
return false;
}

var filepath = ResolveAssemblyFilePath(assembly);
var filepath = ResolveAssemblyFilePath(assemblyName);
if (filepath == null)
{
return false;
}

filepath = Path.GetFullPath(filepath);
assemblies.Add(filepath);
_assemblyFilePaths.Add(filepath);

return true;
}
Expand All @@ -115,5 +159,17 @@ private string ResolveAssemblyFilePath(string assemblyName)

return null;
}

private static void AddDependencySource(Dictionary<string, HashSet<string>> dependencySources, string dependency, string source)
{
HashSet<string> sources;
if (!dependencySources.TryGetValue(dependency, out sources))
{
sources = new HashSet<string>();
dependencySources.Add(dependency, sources);
}

sources.Add(source);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,22 @@ public bool Execute()
return true;
}

// 2. Walk the local dependencies
var assemblyWalker = new AssemblyWalker(_framework, _hostContext, _options.RuntimeFolder);
var assemblies = assemblyWalker.Walk(root);

foreach (var assemblyName in assemblies.OrderBy(assemblyName => assemblyName))
{
_options.Reports.Information.WriteLine(assemblyName);
}
// 2. Walk the local dependencies and print the assemblies list
var assemblyWalker = new AssemblyWalker(_framework,
_hostContext,
_options.RuntimeFolder,
_options.Details,
_options.Reports);
assemblyWalker.Walk(root);

return true;
}

private void Render(IGraphNode<LibraryDescription> root)
{
var renderer = new LibraryDependencyFlatRenderer(_options.HideDependents, _options.ResultsFilter, _options.Project.Dependencies.Select(dep => dep.LibraryRange.Name));
var renderer = new LibraryDependencyFlatRenderer(_options.Details,
_options.ResultsFilter,
_options.Project.Dependencies.Select(dep => dep.LibraryRange.Name));
var content = renderer.GetRenderContent(root);

if (content.Any())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public DependencyListOptions(Reports reports, CommandArgument path)

public IEnumerable<string> TargetFrameworks { get; set; }

public bool HideDependents { get; set; }
public bool Details { get; set; }

public string ResultsFilter { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ namespace Microsoft.Framework.PackageManager.List
{
public class LibraryDependencyFlatRenderer
{
private readonly bool _hideDependent;
private readonly bool _showDetails;
private readonly string _filterPattern;
private readonly HashSet<string> _listedProjects;

public LibraryDependencyFlatRenderer(bool hideDependent, string filterPattern, IEnumerable<string> listedProjects)
public LibraryDependencyFlatRenderer(bool showDetails, string filterPattern, IEnumerable<string> listedProjects)
{
_hideDependent = hideDependent;
_showDetails = showDetails;
_filterPattern = filterPattern;
_listedProjects = new HashSet<string>(listedProjects);
}
Expand Down Expand Up @@ -87,7 +87,7 @@ private void RenderLibraries(IEnumerable<LibraryDescription> descriptions,
results.Add(string.Format("{0} - Unresolved", libDisplay).Red().Bold());
}

if (!_hideDependent)
if (_showDetails)
{
var dependents = string.Join(", ", dependenciesMap[description].Select(dep => dep.Identity.ToString()).OrderBy(name => name));
results.Add(string.Format(" -> {0}", dependents));
Expand Down
10 changes: 4 additions & 6 deletions src/Microsoft.Framework.PackageManager/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
using System.Net;
using System.Reflection;
using System.Threading;
using Microsoft.Framework.PackageManager.Packages;
using Microsoft.Framework.PackageManager.List;
using Microsoft.Framework.PackageManager.Packages;
using Microsoft.Framework.PackageManager.Publish;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Runtime.Common.CommandLine;
using System.Linq;
using System.Diagnostics;

namespace Microsoft.Framework.PackageManager
{
Expand Down Expand Up @@ -370,8 +368,8 @@ public int Main(string[] args)
var runtimeFolder = c.Option("--runtime <PATH>",
"The folder containing all available framework assemblies",
CommandOptionType.SingleValue);
var hideDependents = c.Option("--hide-dependents",
"Hide the immediate dependents of libraries referenced in the project",
var details = c.Option("--details",
"Show the details of how each dependency is introduced",
CommandOptionType.NoValue);
var resultsFilter = c.Option("--filter <PATTERN>",
"Filter the libraries referenced by the project base on their names. The matching pattern supports * and ?",
Expand All @@ -386,7 +384,7 @@ public int Main(string[] args)
TargetFrameworks = frameworks.Values,
ShowAssemblies = showAssemblies.HasValue(),
RuntimeFolder = runtimeFolder.Value(),
HideDependents = hideDependents.HasValue(),
Details = details.HasValue(),
ResultsFilter = resultsFilter.Value()
};
Expand Down
23 changes: 19 additions & 4 deletions test/Microsoft.Framework.CommonTestUtils/DnuTestUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ namespace Microsoft.Framework.PackageManager
{
public static class DnuTestUtils
{
public static int ExecDnu(string runtimeHomePath, string subcommand, string arguments,
IDictionary<string, string> environment = null, string workingDir = null)
public static int ExecDnu(string runtimeHomePath,
string subcommand,
string arguments,
out string stdOut,
out string stdErr,
IDictionary<string, string> environment = null,
string workingDir = null)
{
var runtimeRoot = Directory.EnumerateDirectories(Path.Combine(runtimeHomePath, "runtimes"), Constants.RuntimeNamePrefix + "*").First();
string program, commandLine;
var runtimeRoot = Directory.EnumerateDirectories(Path.Combine(runtimeHomePath, "runtimes"), Constants.RuntimeNamePrefix + "*").First();
if (PlatformHelper.IsMono)
{
program = Path.Combine(runtimeRoot, "bin", "dnu");
Expand All @@ -28,9 +33,19 @@ public static int ExecDnu(string runtimeHomePath, string subcommand, string argu
commandLine = string.Format("/C {0} {1} {2}", dnuCmdPath, subcommand, arguments);
}

string stdOut, stdErr;
var exitCode = TestUtils.Exec(program, commandLine, out stdOut, out stdErr, environment, workingDir);

return exitCode;
}

public static int ExecDnu(string runtimeHomePath,
string subcommand,
string arguments,
IDictionary<string, string> environment = null,
string workingDir = null)
{
string stdOut, stdErr;
return ExecDnu(runtimeHomePath, subcommand, arguments, out stdOut, out stdErr, environment, workingDir);
}
}
}
1 change: 0 additions & 1 deletion test/Microsoft.Framework.CommonTestUtils/TestUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ public static string GetTestArtifactsFolder()
return Path.Combine(kRuntimeRoot, "artifacts", "test");
}


public static DisposableDir GetRuntimeHomeDir(string flavor, string os, string architecture)
{
// The build script creates an unzipped image that can be reused
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Framework.CommonTestUtils;

namespace Microsoft.Framework.PackageManager.FunctionalTests
{
public class DnuListTestContext : IDisposable
{
private readonly IDictionary<Tuple<string, string, string>, DisposableDir> _runtimeHomeDirs =
new Dictionary<Tuple<string, string, string>, DisposableDir>();

private readonly DisposableDir _contextDir;

public DnuListTestContext()
{
_contextDir = TestUtils.CreateTempDir();
PackageSource = Path.Combine(_contextDir.DirPath, "packages");
Directory.CreateDirectory(PackageSource);

CreateNewPackage("alpha", "0.1.0");
}

public string GetRuntimeHome(string flavor, string os, string architecture)
{
DisposableDir result;
if (!_runtimeHomeDirs.TryGetValue(Tuple.Create(flavor, os, architecture), out result))
{
result = TestUtils.GetRuntimeHomeDir(flavor, os, architecture);
_runtimeHomeDirs.Add(Tuple.Create(flavor, os, architecture), result);
}

return result.DirPath;
}

public string PackageSource { get; }

private void CreateNewPackage(string name, string version)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it should be separate from this class, a static method somewhere so that other tests can use it. Right now this class is specific to DnuList.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Next time.

{
var runtimeForPacking = TestUtils.GetClrRuntimeComponents().FirstOrDefault();
if (runtimeForPacking == null)
{
throw new InvalidOperationException("Can't find a CLR runtime to pack test packages.");
}

var runtimeHomePath = GetRuntimeHome((string)runtimeForPacking[0],
(string)runtimeForPacking[1],
(string)runtimeForPacking[2]);

using (var tempdir = TestUtils.CreateTempDir())
{
var dir = new DirectoryInfo(tempdir);
var projectDir = dir.CreateSubdirectory(name);
var outputDir = dir.CreateSubdirectory("output");
var projectJson = Path.Combine(projectDir.FullName, "project.json");

File.WriteAllText(projectJson, "{\"version\": \"" + version + "\"}");
DnuTestUtils.ExecDnu(runtimeHomePath, "pack", projectJson + " --out " + outputDir.FullName, environment: null, workingDir: null);

var packageName = string.Format("{0}.{1}.nupkg", name, version);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var packageName = $"{name}.{version}.nupkg";

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 will do

var packageFile = Path.Combine(outputDir.FullName, "Debug", packageName);

File.Copy(packageFile, Path.Combine(PackageSource, packageName), overwrite: true);
}
}

public void Dispose()
{
foreach (var each in _runtimeHomeDirs.Values)
{
each.Dispose();
}

_contextDir.Dispose();
}
}
}
Loading