using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; namespace Umbraco.Core.Composing { /// /// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively. /// /// /// Borrowed and modified from https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs /// internal class ReferenceResolver { private readonly HashSet _umbracoAssemblies; private readonly IReadOnlyList _assemblyItems; private readonly Dictionary _classifications; private readonly List _lookup = new List(); public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList assemblyItems) { _umbracoAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); _assemblyItems = assemblyItems; _classifications = new Dictionary(); foreach (var item in assemblyItems) { _lookup.Add(item); } } public IEnumerable ResolveAssemblies() { var applicationParts = new List(); foreach (var item in _assemblyItems) { var classification = Resolve(item); if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco) { applicationParts.Add(item); } } return applicationParts; } private Classification Resolve(Assembly assemblyItem) { if (_classifications.TryGetValue(assemblyItem, out var classification)) { return classification; } // Initialize the dictionary with a value to short-circuit recursive references. classification = Classification.Unknown; _classifications[assemblyItem] = classification; if (_umbracoAssemblies.Contains(assemblyItem.GetName().Name)) { classification = Classification.IsUmbraco; } else { classification = Classification.DoesNotReferenceUmbraco; foreach (var reference in GetReferences(assemblyItem)) { // recurse var referenceClassification = Resolve(reference); if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco) { classification = Classification.ReferencesUmbraco; break; } } } Debug.Assert(classification != Classification.Unknown); _classifications[assemblyItem] = classification; return classification; } protected virtual IEnumerable GetReferences(Assembly assembly) { foreach (var referenceName in assembly.GetReferencedAssemblies()) { // don't include if this is excluded if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f))) continue; var reference = Assembly.Load(referenceName); if (!_lookup.Contains(reference)) { // A dependency references an item that isn't referenced by this project. // We'll construct an item for so that we can calculate the classification based on it's name. _lookup.Add(reference); yield return reference; } } } protected enum Classification { Unknown, DoesNotReferenceUmbraco, ReferencesUmbraco, IsUmbraco, } } }