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

feat(binding-redirect): add option to allow binding redirects via provided configuration file (#54) #55

Merged
merged 3 commits into from
Sep 26, 2018
Merged
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
17 changes: 13 additions & 4 deletions AsmSpy.CommandLine/ConsoleVisualizer.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
using System;
using AsmSpy.Core;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using AsmSpy.Core;

namespace AsmSpy.CommandLine
{
public class ConsoleVisualizer : IDependencyVisualizer
{
private const ConsoleColor AssemblyNotFoundColor = ConsoleColor.Red;
private const ConsoleColor AssemblyLocalColor = ConsoleColor.Green;
private const ConsoleColor AssemblyLocalRedirectedColor = ConsoleColor.DarkGreen;
private const ConsoleColor AssemblyGlobalAssemblyCacheColor = ConsoleColor.Yellow;
private const ConsoleColor AssemblyUnknownColor = ConsoleColor.Magenta;

Expand Down Expand Up @@ -53,7 +55,7 @@ public virtual void Visualize()
Console.WriteLine(AsmSpy_CommandLine.Detailing_only_conflicting_assembly_references);
}

var assemblyGroups = _analyzerResult.Assemblies.Values.GroupBy(x => x.AssemblyName);
var assemblyGroups = _analyzerResult.Assemblies.Values.GroupBy(x => x.RedirectedAssemblyName);

foreach (var assemblyGroup in assemblyGroups.OrderBy(i => i.Key.Name))
{
Expand Down Expand Up @@ -118,7 +120,14 @@ protected virtual ConsoleColor GetMainNameColor(IList<AssemblyReferenceInfo> ass
}
else
{
mainNameColor = AssemblyLocalColor;
if (assemblyReferenceInfoList.All(x => x.AssemblyName.FullName == x.RedirectedAssemblyName.FullName))
{
mainNameColor = AssemblyLocalColor;
}
else
{
mainNameColor = AssemblyLocalRedirectedColor;
}
}
return mainNameColor;
}
Expand Down
250 changes: 140 additions & 110 deletions AsmSpy.CommandLine/Program.cs
Original file line number Diff line number Diff line change
@@ -1,110 +1,140 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using AsmSpy.Core;
using Microsoft.Extensions.CommandLineUtils;

namespace AsmSpy.CommandLine
{
public static class Program
{
public static void Main(string[] args)
{
var commandLineApplication = new CommandLineApplication(throwOnUnexpectedArg: true);
var directoryOrFile = commandLineApplication.Argument("directoryOrFile", "The directory to search for assemblies or file path to a single assembly");
var dgmlExport = commandLineApplication.Option("-dg|--dgml <filename>", "Export to a dgml file", CommandOptionType.SingleValue);
var nonsystem = commandLineApplication.Option("-n|--nonsystem", "Ignore 'System' assemblies", CommandOptionType.NoValue);
var all = commandLineApplication.Option("-a|--all", "List all assemblies and references.", CommandOptionType.NoValue);
var noconsole = commandLineApplication.Option("-nc|--noconsole", "Do not show references on console.", CommandOptionType.NoValue);
var silent = commandLineApplication.Option("-s|--silent", "Do not show any message, only warnings and errors will be shown.", CommandOptionType.NoValue);
var bindingRedirect = commandLineApplication.Option("-b|--bindingredirect", "Create binding-redirects", CommandOptionType.NoValue);
var referencedStartsWith = commandLineApplication.Option("-rsw|--referencedstartswith", "Referenced Assembly should start with <string>. Will only analyze assemblies if their referenced assemblies starts with the given value.", CommandOptionType.SingleValue);
var includeSubDirectories = commandLineApplication.Option("-i|--includesub", "Include subdirectories in search", CommandOptionType.NoValue);

commandLineApplication.HelpOption("-? | -h | --help");
commandLineApplication.OnExecute(() =>
{
var consoleLogger = new ConsoleLogger(!silent.HasValue());

var directoryOrFilePath = directoryOrFile.Value;
var directoryPath = directoryOrFile.Value;

if (!File.Exists(directoryOrFilePath) && !Directory.Exists(directoryOrFilePath))
{
consoleLogger.LogMessage(string.Format(CultureInfo.InvariantCulture, "Directory or file: '{0}' does not exist.", directoryOrFilePath));
return -1;
}

var isFilePathProvided = false;
var fileName = "";
if (File.Exists(directoryOrFilePath))
{
isFilePathProvided = true;
fileName = Path.GetFileName(directoryOrFilePath);
directoryPath = Path.GetDirectoryName(directoryOrFilePath);
}

var onlyConflicts = !all.HasValue();
var skipSystem = nonsystem.HasValue();
var searchPattern = includeSubDirectories.HasValue() ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;

var directoryInfo = new DirectoryInfo(directoryPath);

List<FileInfo> fileList;
if (isFilePathProvided)
{
fileList = directoryInfo.GetFiles(fileName, SearchOption.TopDirectoryOnly).ToList();
consoleLogger.LogMessage(string.Format(CultureInfo.InvariantCulture, "Check assemblies referenced in: {0}", directoryOrFilePath));
}
else
{
fileList = directoryInfo.GetFiles("*.dll", searchPattern).Concat(directoryInfo.GetFiles("*.exe", searchPattern)).ToList();
consoleLogger.LogMessage(string.Format(CultureInfo.InvariantCulture, "Check assemblies in: {0}", directoryInfo));
}

IDependencyAnalyzer analyzer = new DependencyAnalyzer(fileList);

var result = analyzer.Analyze(consoleLogger);

if (!noconsole.HasValue())
{
IDependencyVisualizer visualizer = new ConsoleVisualizer(result) { SkipSystem = skipSystem, OnlyConflicts = onlyConflicts, ReferencedStartsWith = referencedStartsWith.HasValue() ? referencedStartsWith.Value() : string.Empty };
visualizer.Visualize();
}

if (dgmlExport.HasValue())
{
IDependencyVisualizer export = new DgmlExport(result, string.IsNullOrWhiteSpace(dgmlExport.Value()) ? Path.Combine(directoryInfo.FullName, "references.dgml") : dgmlExport.Value(), consoleLogger) { SkipSystem = skipSystem };
export.Visualize();
}

if (bindingRedirect.HasValue())
{
// binding redirect export explicitly doesn't respect SkipSystem
IDependencyVisualizer bindingRedirects = new BindingRedirectExport(result, string.IsNullOrWhiteSpace(dgmlExport.Value()) ? Path.Combine(directoryInfo.FullName, "bindingRedirects.xml") : dgmlExport.Value(), consoleLogger);
bindingRedirects.Visualize();
}

return 0;
});
try
{
if (args == null || args.Length == 0)
{
commandLineApplication.ShowHelp();
}
else
{
commandLineApplication.Execute(args);
}
}
catch (CommandParsingException cpe)
{
Console.WriteLine(cpe.Message);
commandLineApplication.ShowHelp();
}
}
}
}
using AsmSpy.Core;

using Microsoft.Extensions.CommandLineUtils;

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;

namespace AsmSpy.CommandLine
{
public static class Program
{
public static int Main(string[] args)
{
var commandLineApplication = new CommandLineApplication(throwOnUnexpectedArg: true);
var directoryOrFile = commandLineApplication.Argument("directoryOrFile", "The directory to search for assemblies or file path to a single assembly");
var dgmlExport = commandLineApplication.Option("-dg|--dgml <filename>", "Export to a dgml file", CommandOptionType.SingleValue);
var nonsystem = commandLineApplication.Option("-n|--nonsystem", "Ignore 'System' assemblies", CommandOptionType.NoValue);
var all = commandLineApplication.Option("-a|--all", "List all assemblies and references.", CommandOptionType.NoValue);
var noconsole = commandLineApplication.Option("-nc|--noconsole", "Do not show references on console.", CommandOptionType.NoValue);
var silent = commandLineApplication.Option("-s|--silent", "Do not show any message, only warnings and errors will be shown.", CommandOptionType.NoValue);
var bindingRedirect = commandLineApplication.Option("-b|--bindingredirect", "Create binding-redirects", CommandOptionType.NoValue);
var referencedStartsWith = commandLineApplication.Option("-rsw|--referencedstartswith", "Referenced Assembly should start with <string>. Will only analyze assemblies if their referenced assemblies starts with the given value.", CommandOptionType.SingleValue);
var includeSubDirectories = commandLineApplication.Option("-i|--includesub", "Include subdirectories in search", CommandOptionType.NoValue);
var configurationFile = commandLineApplication.Option("-c|--configurationFile", "Use the binding redirects of the given configuration file (Web.config or App.config)", CommandOptionType.SingleValue);
var failOnMissing = commandLineApplication.Option("-f|--failOnMissing", "Whether to exit with an error code when AsmSpy detected Assemblies which could not be found", CommandOptionType.NoValue);

commandLineApplication.HelpOption("-? | -h | --help");
commandLineApplication.OnExecute(() =>
{
var consoleLogger = new ConsoleLogger(!silent.HasValue());

var directoryOrFilePath = directoryOrFile.Value;
var directoryPath = directoryOrFile.Value;

if (!File.Exists(directoryOrFilePath) && !Directory.Exists(directoryOrFilePath))
{
consoleLogger.LogMessage(string.Format(CultureInfo.InvariantCulture, "Directory or file: '{0}' does not exist.", directoryOrFilePath));
return -1;
}

var configurationFilePath = configurationFile.Value();
if (!string.IsNullOrEmpty(configurationFilePath) && !File.Exists(configurationFilePath))
{
consoleLogger.LogMessage(string.Format(CultureInfo.InvariantCulture, "Directory or file: '{0}' does not exist.", configurationFilePath));
return -1;
}

var isFilePathProvided = false;
var fileName = "";
if (File.Exists(directoryOrFilePath))
{
isFilePathProvided = true;
fileName = Path.GetFileName(directoryOrFilePath);
directoryPath = Path.GetDirectoryName(directoryOrFilePath);
}

var onlyConflicts = !all.HasValue();
var skipSystem = nonsystem.HasValue();
var searchPattern = includeSubDirectories.HasValue() ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;

var directoryInfo = new DirectoryInfo(directoryPath);

List<FileInfo> fileList;
if (isFilePathProvided)
{
fileList = directoryInfo.GetFiles(fileName, SearchOption.TopDirectoryOnly).ToList();
consoleLogger.LogMessage(string.Format(CultureInfo.InvariantCulture, "Check assemblies referenced in: {0}", directoryOrFilePath));
}
else
{
fileList = directoryInfo.GetFiles("*.dll", searchPattern).Concat(directoryInfo.GetFiles("*.exe", searchPattern)).ToList();
consoleLogger.LogMessage(string.Format(CultureInfo.InvariantCulture, "Check assemblies in: {0}", directoryInfo));
}

AppDomain appDomainWithBindingRedirects = null;
try
{
var domaininfo = new AppDomainSetup
{
ConfigurationFile = configurationFilePath
};
appDomainWithBindingRedirects = AppDomain.CreateDomain("AppDomainWithBindingRedirects", null, domaininfo);
}
catch (Exception ex)
{
consoleLogger.LogError($"Failed creating AppDomain from configuration file with message {ex.Message}");
return -1;
}

IDependencyAnalyzer analyzer = new DependencyAnalyzer(fileList, appDomainWithBindingRedirects);

var result = analyzer.Analyze(consoleLogger);

if (!noconsole.HasValue())
{
IDependencyVisualizer visualizer = new ConsoleVisualizer(result) { SkipSystem = skipSystem, OnlyConflicts = onlyConflicts, ReferencedStartsWith = referencedStartsWith.HasValue() ? referencedStartsWith.Value() : string.Empty };
visualizer.Visualize();
}

if (dgmlExport.HasValue())
{
IDependencyVisualizer export = new DgmlExport(result, string.IsNullOrWhiteSpace(dgmlExport.Value()) ? Path.Combine(directoryInfo.FullName, "references.dgml") : dgmlExport.Value(), consoleLogger) { SkipSystem = skipSystem };
export.Visualize();
}

if (bindingRedirect.HasValue())
{
// binding redirect export explicitly doesn't respect SkipSystem
IDependencyVisualizer bindingRedirects = new BindingRedirectExport(result, string.IsNullOrWhiteSpace(dgmlExport.Value()) ? Path.Combine(directoryInfo.FullName, "bindingRedirects.xml") : dgmlExport.Value(), consoleLogger);
bindingRedirects.Visualize();
}

if (failOnMissing.HasValue() && result.HasMissingAssemblies)
{
return -1;
}
return 0;
});
try
{
if (args == null || args.Length == 0)
{
commandLineApplication.ShowHelp();
return 0;
}

return commandLineApplication.Execute(args);
}
catch (CommandParsingException cpe)
{
Console.WriteLine(cpe.Message);
commandLineApplication.ShowHelp();
return 0;
}
}
}
}
8 changes: 4 additions & 4 deletions AsmSpy.CommandLine/packages.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILMerge" version="2.14.1208" targetFramework="net462" />
<package id="Microsoft.Extensions.CommandLineUtils" version="1.1.0" targetFramework="net452" />
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILMerge" version="2.14.1208" targetFramework="net462" />
<package id="Microsoft.Extensions.CommandLineUtils" version="1.1.0" targetFramework="net462" />
</packages>
5 changes: 4 additions & 1 deletion AsmSpy.Core/AssemblyReferenceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace AsmSpy.Core
{
public class AssemblyReferenceInfo : IAssemblyReferenceInfo
{

#region Fields

private readonly HashSet<IAssemblyReferenceInfo> _references = new HashSet<IAssemblyReferenceInfo>();
Expand All @@ -18,6 +19,7 @@ public class AssemblyReferenceInfo : IAssemblyReferenceInfo
public virtual Assembly ReflectionOnlyAssembly { get; set; }
public virtual AssemblySource AssemblySource { get; set; }
public virtual AssemblyName AssemblyName { get; }
public virtual AssemblyName RedirectedAssemblyName { get; }
public virtual ICollection<IAssemblyReferenceInfo> ReferencedBy => _referencedBy.ToArray();
public virtual ICollection<IAssemblyReferenceInfo> References => _references.ToArray();
public bool IsSystem => AssemblyInformationProvider.IsSystemAssembly(AssemblyName);
Expand All @@ -26,9 +28,10 @@ public class AssemblyReferenceInfo : IAssemblyReferenceInfo

#region Constructor

public AssemblyReferenceInfo(AssemblyName assemblyName)
public AssemblyReferenceInfo(AssemblyName assemblyName, AssemblyName redirectedAssemblyName)
{
AssemblyName = assemblyName;
RedirectedAssemblyName = redirectedAssemblyName;
}

#endregion
Expand Down
20 changes: 13 additions & 7 deletions AsmSpy.Core/DependencyAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using System;
using AsmSpy.Core.Native;

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using AsmSpy.Core.Native;

namespace AsmSpy.Core
{
Expand All @@ -14,25 +15,30 @@ public class DependencyAnalyzer : IDependencyAnalyzer

public virtual IEnumerable<FileInfo> Files { get; }

protected virtual AppDomain AppDomainWithBindingRedirects { get; }

#endregion

#region Analyze Support

public DependencyAnalyzer(IEnumerable<FileInfo> files)
public DependencyAnalyzer(IEnumerable<FileInfo> files, AppDomain appDomainWithBindingRedirects = null)
{
Files = files;
AppDomainWithBindingRedirects = appDomainWithBindingRedirects;
}

private static AssemblyReferenceInfo GetAssemblyReferenceInfo(IDictionary<string, AssemblyReferenceInfo> assemblies, AssemblyName assemblyName)
private AssemblyReferenceInfo GetAssemblyReferenceInfo(IDictionary<string, AssemblyReferenceInfo> assemblies, AssemblyName assemblyName)
{
var assemblyFullName = AppDomainWithBindingRedirects != null ? AppDomainWithBindingRedirects.ApplyPolicy(assemblyName.FullName) : assemblyName.FullName;

AssemblyReferenceInfo assemblyReferenceInfo;
if (assemblies.TryGetValue(assemblyName.FullName, out assemblyReferenceInfo))
if (assemblies.TryGetValue(assemblyFullName, out assemblyReferenceInfo))
{
return assemblyReferenceInfo;
}

assemblyReferenceInfo = new AssemblyReferenceInfo(assemblyName);
assemblies.Add(assemblyName.FullName, assemblyReferenceInfo);
assemblyReferenceInfo = new AssemblyReferenceInfo(assemblyName, new AssemblyName(assemblyFullName));
assemblies.Add(assemblyFullName, assemblyReferenceInfo);
return assemblyReferenceInfo;
}

Expand Down
Loading