private static void Run(InputParameters inputParameters) { Logger.Information("Run started."); Logger.Information(Separator); var graph = new AssemblyDependencyGraph(); var baseDirectory = string.IsNullOrEmpty(inputParameters.BaseDirectory) ? new DirectoryInfo(Environment.CurrentDirectory) : new DirectoryInfo(inputParameters.BaseDirectory); if (!baseDirectory.Exists) { throw new DirectoryNotFoundException($"The base directory '{baseDirectory.FullName}' was not found."); } // register assembly sources graph.EnsureNodeWithAssemblySourceOrPatterns(inputParameters.Assemblies, baseDirectory); // register main assembly graph.EnsureNodeWithAssemblySource(inputParameters.MainAssembly, baseDirectory); // register additional dependencies foreach (var additionalDependency in inputParameters.AdditionalDependencies ?? Enumerable.Empty <InputParameters.AdditionalDependency>()) { var dependant = graph.EnsureNodeWithAssemblySource(additionalDependency.Dependant, baseDirectory); var dependencies = graph.EnsureNodeWithAssemblySourceOrPatterns(additionalDependency.Dependencies, baseDirectory); foreach (var dependency in dependencies) { graph.RegisterDependency(dependant, dependency); } } // load assemblies from files foreach (var node in graph.GetNodesToLoadFromFile()) { graph.LoadNodeFromFile(node); } // load assemblies from assembly names foreach (var node in graph.GetNodesToLoadFromName()) { graph.LoadNodeFromName(node); } Logger.Information(Separator); Logger.Information("Entire graph processed."); // locate the main assembly var mainNode = graph.EnsureNodeWithAssemblySource(inputParameters.MainAssembly, baseDirectory); // write report WriteReport(mainNode, graph); Logger.Information(Separator); Logger.Information("Run finished."); }
private static void WriteMainNodeDependants(AssemblyDependencyNode mainNode, AssemblyDependencyGraph graph) { var mainNodeDependants = graph.GetDirectDependants(mainNode).OrderBy(x => x.Identity).ToList(); if (mainNodeDependants.Any()) { Logger.Information(Separator); Logger.Warning("WARNING: Detected that there are still some assemblies that depend on the main assembly. Try to avoid this scenario. Different binding redirects might be needed for them."); foreach (var node in mainNodeDependants) { Logger.Information("{AssemblyIdentity}", node.Identity); } } }
private static void WriteDirectDependencies(AssemblyDependencyNode mainNode, AssemblyDependencyGraph graph) { Logger.Information(Separator); Logger.Information("Direct dependencies:"); Logger.Information(""); foreach (var dependencyGroup in graph.GetDirectDependencies(mainNode).GroupBy(x => x.Identity.Unversioned).OrderBy(x => x.Key)) { Logger.Information("{AssemblyNameWithVersion}", GetSimpleName(dependencyGroup.Key, dependencyGroup.ToList())); foreach (var node in dependencyGroup) { Logger.Information("-- {AssemblyIdentity}", node.Identity); } Logger.Information(""); } }
private static void WriteOtherLeafNodes(AssemblyDependencyNode mainNode, AssemblyDependencyGraph graph, List <AssemblyDependencyNode> nodes) { var otherLeafNodes = nodes.Where(x => x != mainNode && !graph.GetDirectDependantsByGroup(x.Identity.Unversioned).Any()).ToList(); if (otherLeafNodes.Any()) { Logger.Information(Separator); Logger.Information("Other assemblies that don't have any dependants:"); foreach (var node in otherLeafNodes) { Logger.Warning("{AssemblyName}", node.Name?.FullName ?? node.File.Name); } Logger.Information("If these assemblies relate to some assembly dynamically, you can add them as an additional dependency on input."); Logger.Information("If these are not loaded dynamically, they might also redundant."); } }
private static void WriteReport(AssemblyDependencyNode mainNode, AssemblyDependencyGraph graph) { Logger.Information(Separator); Logger.Information("Overview for [{AssemblyName}]", mainNode.Name?.ToString() ?? mainNode.File.Name); WriteDirectDependencies(mainNode, graph); WriteIndirectDependencies(mainNode, graph); WriteAllDependencies(mainNode, graph); var nodes = graph.GetAllNodes() .OrderBy(x => x.Identity) .ToList(); WriteAssembliesNotLoaded(nodes, graph); WriteAssembliesRedirectedUponLoad(nodes); WriteMainNodeDependants(mainNode, graph); WriteOtherLeafNodes(mainNode, graph, nodes); var allMainDependenciesIncludingEntireGroup = graph.GetAllDependenciesIncludingEntireGroup(mainNode); WriteNodesOutsideMainDependencyTree(mainNode, nodes, allMainDependenciesIncludingEntireGroup, graph); WriteBindingRedirects(allMainDependenciesIncludingEntireGroup, graph); }
private static void WriteAssembliesNotLoaded(List <AssemblyDependencyNode> nodes, AssemblyDependencyGraph graph) { var nodesByGroup = nodes.ToLookup(x => x.Identity.Unversioned); var nodesNotLoaded = nodes.Where(node => !node.Loaded).ToList(); if (nodesNotLoaded.Any()) { Logger.Information(Separator); Logger.Information("Assemblies that couldn't be loaded:"); foreach (var node in nodesNotLoaded) { Logger.Information(Separator); if (node.Name != null) { Logger.Information("{AssemblyIdentity}", node.Identity); } else { Logger.Information("{File}", node.File.FullName); } Logger.Information(""); if (node.LoadedFromName == AssemblyLoadStatus.Failed) { Logger.Information("Couldn't load from assembly name. Exception message:"); Logger.Information(node.LoadedFromNameError.Message); } else if (node.LoadedFromFile == AssemblyLoadStatus.Failed) { Logger.Information("Couldn't load from file. Exception message:"); Logger.Information(node.LoadedFromFileError.Message); } else { throw new InvalidOperationException("Assembly not attempted to be loaded, something went wrong."); } var directDependants = graph.GetDirectDependants(node).ToList(); if (directDependants.Any()) { Logger.Information(""); Logger.Information("Nodes that depend on this assembly directly:"); foreach (var dependant in directDependants) { Logger.Information("{AssemblyIdentity}", dependant.Identity); } var indirectDependants = graph.GetIndirectDependants(node).ToList(); if (indirectDependants.Any()) { Logger.Information(""); Logger.Information("Nodes that depend on this assembly indirectly:"); foreach (var dependant in indirectDependants) { Logger.Information("{AssemblyIdentity}", dependant.Identity); } } } else { Logger.Information(""); Logger.Information("No nodes that depend on this were found."); } Logger.Information(""); Logger.Information("If this is an important reference, consider fixing this reference. Also make sure that you didn't omit it from the inputs."); var otherNodesInGroup = nodesByGroup[node.Identity.Unversioned].Where(x => x != node && x.Loaded).ToList(); if (otherNodesInGroup.Any()) { Logger.Information(""); Logger.Information("Other versions of this assembly were loaded:"); foreach (var otherVersionNode in otherNodesInGroup.OrderBy(x => x.Identity.Version)) { Logger.Information("{Version}", otherVersionNode.Identity.Version); } if (otherNodesInGroup.All(x => x.Identity.Version < node.Identity.Version)) { Logger.Warning("WARNING: This is the highest version of the assembly, yet it wasn't loaded. A downgrading binding redirect might be needed."); } } } } }
private static void WriteNodesOutsideMainDependencyTree(AssemblyDependencyNode mainNode, List <AssemblyDependencyNode> nodes, IList <AssemblyDependencyNode> allMainDependencies, AssemblyDependencyGraph graph) { var nodesOutsideOfDependencyTree = nodes .Where(x => x != mainNode && !allMainDependencies.Contains(x)) .Where(x => x.Loaded) .ToList(); if (nodesOutsideOfDependencyTree.Any()) { Logger.Information(Separator); Logger.Information("All assemblies that aren't in any way connected to the main, but were loaded:"); foreach (var node in nodesOutsideOfDependencyTree) { Logger.Information(Separator); Logger.Warning("{AssemblyIdentity}", node.Identity); var directDependants = graph.GetDirectDependants(node).ToList(); if (directDependants.Any()) { Logger.Information(""); Logger.Information("Nodes that depend on this assembly directly:"); foreach (var dependant in directDependants) { Logger.Information("{AssemblyIdentity}", dependant.Identity); } var indirectDependants = graph.GetIndirectDependants(node).ToList(); if (indirectDependants.Any()) { Logger.Information(""); Logger.Information("Nodes that depend on this assembly indirectly:"); foreach (var dependant in indirectDependants) { Logger.Information("{AssemblyIdentity}", dependant.Identity); } } } else { Logger.Information(""); Logger.Information("No nodes that depend on this were found."); } } } }
private static void WriteBindingRedirects(IList <AssemblyDependencyNode> allMainDependencies, AssemblyDependencyGraph graph) { var dependenciesGrouped = allMainDependencies .GroupBy(x => x.Identity.Unversioned) .Where(x => x.Count() > 1 && x.Any(y => y.Loaded)) .OrderBy(x => x.Key) .ToList(); if (dependenciesGrouped.Any()) { Logger.Information(Separator); Logger.Information("Recommended binding redirects:"); var dependentAssemblyTriples = new List <(AssemblyUnversionedIdentity Key, Version highestVersion, Version highestVersionLoaded)>(); foreach (var group in dependenciesGrouped) { Logger.Information(Separator); Logger.Information("{AssemblyName}", group.Key.ToString()); Logger.Information("--- Versions ---"); foreach (var node in group.OrderBy(x => x.Identity.Version)) { Logger.Information(""); if (node.Loaded) { Logger.Information("-- {Version} [Location=\"{Location}\"]", node.Identity.Version, node.File.FullName); } else { Logger.Warning("-- {Version} [Not Found]", node.Identity.Version); } var directDependants = graph.GetDirectDependants(node).ToList(); if (directDependants.Any()) { Logger.Information(""); Logger.Information("Nodes that depend on this version directly:"); foreach (var dependant in directDependants) { Logger.Information("{AssemblyIdentity}", dependant.Identity); } var indirectDependants = graph.GetIndirectDependants(node).ToList(); if (indirectDependants.Any()) { Logger.Information(""); Logger.Information("Nodes that depend on this version indirectly:"); foreach (var dependant in indirectDependants) { Logger.Information("{AssemblyIdentity}", dependant.Identity); } } } else { Logger.Information(""); Logger.Warning("No nodes that depend on this version were found."); } } var highestVersion = group.Max(x => x.Identity.Version); var highestVersionLoaded = group.Where(x => x.Loaded).Max(x => x.Identity.Version); var dependentAssemblyTriple = (group.Key, highestVersion, highestVersionLoaded); var element = GetAssemblyBindingXElement(dependentAssemblyTriple); Logger.Information(""); Logger.Information("--- Recommended redirect ---\n{Element}", element); if (highestVersionLoaded < highestVersion) { Logger.Warning("WARNING: Recommending a downgrading redirect."); } dependentAssemblyTriples.Add(dependentAssemblyTriple); } if (dependenciesGrouped.Count > 1) { Logger.Information(Separator); Logger.Information("Collected binding redirects:"); var element = GetAssemblyBindingXElement(dependentAssemblyTriples.ToArray()); Logger.Information("\n{Element}", element); } } else { Logger.Information(Separator); Logger.Information("No recommended binding redirects."); } }