Skip to content
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
94 changes: 67 additions & 27 deletions ArchUnitNET/Loader/ArchLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ public ArchLoader LoadAssemblies(params Assembly[] assemblies)
}

public ArchLoader LoadAssembliesIncludingDependencies(params Assembly[] assemblies)
{
return LoadAssembliesIncludingDependencies(assemblies, false);
}

public ArchLoader LoadAssembliesIncludingDependencies(IEnumerable<Assembly> assemblies, bool recursive)
{
var assemblySet = new HashSet<Assembly>(assemblies);
assemblySet.ForEach(assembly => LoadAssemblyIncludingDependencies(assembly));
assemblySet.ForEach(assembly => LoadAssemblyIncludingDependencies(assembly, recursive));
return this;
}

Expand All @@ -53,10 +58,11 @@ public ArchLoader LoadFilteredDirectory(string directory, string filter,

var result = this;
return assemblies.Aggregate(result,
(current, assembly) => current.LoadAssembly(assembly, false));
(current, assembly) => current.LoadAssembly(assembly, false, false));
}

public ArchLoader LoadFilteredDirectoryIncludingDependencies(string directory, string filter,
bool recursive = false,
SearchOption searchOption = TopDirectoryOnly)
{
var path = Path.GetFullPath(directory);
Expand All @@ -65,73 +71,107 @@ public ArchLoader LoadFilteredDirectoryIncludingDependencies(string directory, s

var result = this;
return assemblies.Aggregate(result,
(current, assembly) => current.LoadAssembly(assembly, true));
(current, assembly) => current.LoadAssembly(assembly, true, recursive));
}

public ArchLoader LoadNamespacesWithinAssembly(Assembly assembly, params string[] namespc)
{
var nameSpaces = new HashSet<string>(namespc);
nameSpaces.ForEach(nameSpace => { LoadModule(assembly.Location, nameSpace, false); });
nameSpaces.ForEach(nameSpace => { LoadModule(assembly.Location, nameSpace, false, false); });
return this;
}

public ArchLoader LoadAssembly(Assembly assembly)
{
return LoadAssembly(assembly.Location, false);
return LoadAssembly(assembly.Location, false, false);
}

public ArchLoader LoadAssemblyIncludingDependencies(Assembly assembly)
public ArchLoader LoadAssemblyIncludingDependencies(Assembly assembly, bool recursive = false)
{
return LoadAssembly(assembly.Location, true);
return LoadAssembly(assembly.Location, true, recursive);
}

private ArchLoader LoadAssembly(string fileName, bool includeDependencies)
private ArchLoader LoadAssembly(string fileName, bool includeDependencies, bool recursive)
{
LoadModule(fileName, null, includeDependencies);
LoadModule(fileName, null, includeDependencies, recursive);

return this;
}

private void LoadModule(string fileName, string nameSpace, bool includeDependencies)
private void LoadModule(string fileName, string nameSpace, bool includeDependencies, bool recursive)
{
try
{
var module = ModuleDefinition.ReadModule(fileName,
new ReaderParameters {AssemblyResolver = _assemblyResolver});
new ReaderParameters { AssemblyResolver = _assemblyResolver });
var processedAssemblies = new List<AssemblyNameReference> { module.Assembly.Name };
var resolvedModules = new List<ModuleDefinition>();
_assemblyResolver.AddLib(module.Assembly);
_archBuilder.AddAssembly(module.Assembly, false);
foreach (var assemblyReference in module.AssemblyReferences)
{
try
if (includeDependencies && recursive)
{
_assemblyResolver.AddLib(assemblyReference);
if (includeDependencies)
{
_archBuilder.AddAssembly(
_assemblyResolver.Resolve(assemblyReference) ??
throw new AssemblyResolutionException(assemblyReference), false);
}
AddReferencedAssembliesRecursively(assemblyReference, processedAssemblies, resolvedModules);
}
catch (AssemblyResolutionException)
else
{
//Failed to resolve assembly, skip it
try
{
processedAssemblies.Add(assemblyReference);
_assemblyResolver.AddLib(assemblyReference);
if (includeDependencies)
{
var assemblyDefinition = _assemblyResolver.Resolve(assemblyReference) ??
throw new AssemblyResolutionException(assemblyReference);
_archBuilder.AddAssembly(assemblyDefinition, false);
resolvedModules.AddRange(assemblyDefinition.Modules);
}
}
catch (AssemblyResolutionException)
{
//Failed to resolve assembly, skip it
}
}
}

_archBuilder.LoadTypesForModule(module, nameSpace);
if (includeDependencies)
foreach (var moduleDefinition in resolvedModules)
{
foreach (var moduleDefinition in module.AssemblyReferences.SelectMany(reference =>
_assemblyResolver.Resolve(reference)?.Modules))
{
_archBuilder.LoadTypesForModule(moduleDefinition, null);
}
_archBuilder.LoadTypesForModule(moduleDefinition, null);
}
}
catch (BadImageFormatException)
{
// invalid file format of DLL or executable, therefore ignored
}
}

private void AddReferencedAssembliesRecursively(AssemblyNameReference currentAssemblyReference,
ICollection<AssemblyNameReference> processedAssemblies, List<ModuleDefinition> resolvedModules)
{
if (processedAssemblies.Contains(currentAssemblyReference))
{
return;
}

processedAssemblies.Add(currentAssemblyReference);
try
{
_assemblyResolver.AddLib(currentAssemblyReference);
var assemblyDefinition = _assemblyResolver.Resolve(currentAssemblyReference) ??
throw new AssemblyResolutionException(currentAssemblyReference);
_archBuilder.AddAssembly(assemblyDefinition, false);
resolvedModules.AddRange(assemblyDefinition.Modules);
foreach (var reference in assemblyDefinition.Modules.SelectMany(m => m.AssemblyReferences))
{
AddReferencedAssembliesRecursively(reference, processedAssemblies, resolvedModules);
}
}
catch (AssemblyResolutionException)
{
//Failed to resolve assembly, skip it
}
}
}
}
12 changes: 12 additions & 0 deletions ArchUnitNETTests/Loader/ArchLoaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
// SPDX-License-Identifier: Apache-2.0

using System.Linq;
using ArchUnitNET.Loader;
using ArchUnitNETTests.Domain.Dependencies.Members;
using Xunit;
using static ArchUnitNETTests.StaticTestArchitectures;

Expand All @@ -22,5 +24,15 @@ public void LoadAssemblies()
Assert.NotEmpty(ArchUnitNETTestArchitectureWithDependencies.Assemblies);
Assert.NotEmpty(ArchUnitNETTestAssemblyArchitectureWithDependencies.Assemblies);
}

[Fact(Skip = "This takes very long.")]
public void LoadAssembliesIncludingRecursiveDependencies()
{
var archUnitNetTestArchitectureWithRecursiveDependencies =
new ArchLoader().LoadAssembliesIncludingDependencies(new[] { typeof(BaseClass).Assembly }, true)
.Build();

Assert.True(archUnitNetTestArchitectureWithRecursiveDependencies.Assemblies.Count() > 100);
}
}
}