Skip to content

Commit 0fb3ea8

Browse files
authored
Merge pull request #128 from brandhuf/find-assembly-dependencies-recursive
add all referenced assemblies recursively
2 parents 2ae80b0 + 17430e6 commit 0fb3ea8

File tree

2 files changed

+79
-27
lines changed

2 files changed

+79
-27
lines changed

ArchUnitNET/Loader/ArchLoader.cs

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,14 @@ public ArchLoader LoadAssemblies(params Assembly[] assemblies)
3838
}
3939

4040
public ArchLoader LoadAssembliesIncludingDependencies(params Assembly[] assemblies)
41+
{
42+
return LoadAssembliesIncludingDependencies(assemblies, false);
43+
}
44+
45+
public ArchLoader LoadAssembliesIncludingDependencies(IEnumerable<Assembly> assemblies, bool recursive)
4146
{
4247
var assemblySet = new HashSet<Assembly>(assemblies);
43-
assemblySet.ForEach(assembly => LoadAssemblyIncludingDependencies(assembly));
48+
assemblySet.ForEach(assembly => LoadAssemblyIncludingDependencies(assembly, recursive));
4449
return this;
4550
}
4651

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

5459
var result = this;
5560
return assemblies.Aggregate(result,
56-
(current, assembly) => current.LoadAssembly(assembly, false));
61+
(current, assembly) => current.LoadAssembly(assembly, false, false));
5762
}
5863

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

6672
var result = this;
6773
return assemblies.Aggregate(result,
68-
(current, assembly) => current.LoadAssembly(assembly, true));
74+
(current, assembly) => current.LoadAssembly(assembly, true, recursive));
6975
}
7076

7177
public ArchLoader LoadNamespacesWithinAssembly(Assembly assembly, params string[] namespc)
7278
{
7379
var nameSpaces = new HashSet<string>(namespc);
74-
nameSpaces.ForEach(nameSpace => { LoadModule(assembly.Location, nameSpace, false); });
80+
nameSpaces.ForEach(nameSpace => { LoadModule(assembly.Location, nameSpace, false, false); });
7581
return this;
7682
}
7783

7884
public ArchLoader LoadAssembly(Assembly assembly)
7985
{
80-
return LoadAssembly(assembly.Location, false);
86+
return LoadAssembly(assembly.Location, false, false);
8187
}
8288

83-
public ArchLoader LoadAssemblyIncludingDependencies(Assembly assembly)
89+
public ArchLoader LoadAssemblyIncludingDependencies(Assembly assembly, bool recursive = false)
8490
{
85-
return LoadAssembly(assembly.Location, true);
91+
return LoadAssembly(assembly.Location, true, recursive);
8692
}
8793

88-
private ArchLoader LoadAssembly(string fileName, bool includeDependencies)
94+
private ArchLoader LoadAssembly(string fileName, bool includeDependencies, bool recursive)
8995
{
90-
LoadModule(fileName, null, includeDependencies);
96+
LoadModule(fileName, null, includeDependencies, recursive);
9197

9298
return this;
9399
}
94100

95-
private void LoadModule(string fileName, string nameSpace, bool includeDependencies)
101+
private void LoadModule(string fileName, string nameSpace, bool includeDependencies, bool recursive)
96102
{
97103
try
98104
{
99105
var module = ModuleDefinition.ReadModule(fileName,
100-
new ReaderParameters {AssemblyResolver = _assemblyResolver});
106+
new ReaderParameters { AssemblyResolver = _assemblyResolver });
107+
var processedAssemblies = new List<AssemblyNameReference> { module.Assembly.Name };
108+
var resolvedModules = new List<ModuleDefinition>();
101109
_assemblyResolver.AddLib(module.Assembly);
102110
_archBuilder.AddAssembly(module.Assembly, false);
103111
foreach (var assemblyReference in module.AssemblyReferences)
104112
{
105-
try
113+
if (includeDependencies && recursive)
106114
{
107-
_assemblyResolver.AddLib(assemblyReference);
108-
if (includeDependencies)
109-
{
110-
_archBuilder.AddAssembly(
111-
_assemblyResolver.Resolve(assemblyReference) ??
112-
throw new AssemblyResolutionException(assemblyReference), false);
113-
}
115+
AddReferencedAssembliesRecursively(assemblyReference, processedAssemblies, resolvedModules);
114116
}
115-
catch (AssemblyResolutionException)
117+
else
116118
{
117-
//Failed to resolve assembly, skip it
119+
try
120+
{
121+
processedAssemblies.Add(assemblyReference);
122+
_assemblyResolver.AddLib(assemblyReference);
123+
if (includeDependencies)
124+
{
125+
var assemblyDefinition = _assemblyResolver.Resolve(assemblyReference) ??
126+
throw new AssemblyResolutionException(assemblyReference);
127+
_archBuilder.AddAssembly(assemblyDefinition, false);
128+
resolvedModules.AddRange(assemblyDefinition.Modules);
129+
}
130+
}
131+
catch (AssemblyResolutionException)
132+
{
133+
//Failed to resolve assembly, skip it
134+
}
118135
}
119136
}
120137

121138
_archBuilder.LoadTypesForModule(module, nameSpace);
122-
if (includeDependencies)
139+
foreach (var moduleDefinition in resolvedModules)
123140
{
124-
foreach (var moduleDefinition in module.AssemblyReferences.SelectMany(reference =>
125-
_assemblyResolver.Resolve(reference)?.Modules))
126-
{
127-
_archBuilder.LoadTypesForModule(moduleDefinition, null);
128-
}
141+
_archBuilder.LoadTypesForModule(moduleDefinition, null);
129142
}
130143
}
131144
catch (BadImageFormatException)
132145
{
133146
// invalid file format of DLL or executable, therefore ignored
134147
}
135148
}
149+
150+
private void AddReferencedAssembliesRecursively(AssemblyNameReference currentAssemblyReference,
151+
ICollection<AssemblyNameReference> processedAssemblies, List<ModuleDefinition> resolvedModules)
152+
{
153+
if (processedAssemblies.Contains(currentAssemblyReference))
154+
{
155+
return;
156+
}
157+
158+
processedAssemblies.Add(currentAssemblyReference);
159+
try
160+
{
161+
_assemblyResolver.AddLib(currentAssemblyReference);
162+
var assemblyDefinition = _assemblyResolver.Resolve(currentAssemblyReference) ??
163+
throw new AssemblyResolutionException(currentAssemblyReference);
164+
_archBuilder.AddAssembly(assemblyDefinition, false);
165+
resolvedModules.AddRange(assemblyDefinition.Modules);
166+
foreach (var reference in assemblyDefinition.Modules.SelectMany(m => m.AssemblyReferences))
167+
{
168+
AddReferencedAssembliesRecursively(reference, processedAssemblies, resolvedModules);
169+
}
170+
}
171+
catch (AssemblyResolutionException)
172+
{
173+
//Failed to resolve assembly, skip it
174+
}
175+
}
136176
}
137177
}

ArchUnitNETTests/Loader/ArchLoaderTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// SPDX-License-Identifier: Apache-2.0
66

77
using System.Linq;
8+
using ArchUnitNET.Loader;
9+
using ArchUnitNETTests.Domain.Dependencies.Members;
810
using Xunit;
911
using static ArchUnitNETTests.StaticTestArchitectures;
1012

@@ -22,5 +24,15 @@ public void LoadAssemblies()
2224
Assert.NotEmpty(ArchUnitNETTestArchitectureWithDependencies.Assemblies);
2325
Assert.NotEmpty(ArchUnitNETTestAssemblyArchitectureWithDependencies.Assemblies);
2426
}
27+
28+
[Fact(Skip = "This takes very long.")]
29+
public void LoadAssembliesIncludingRecursiveDependencies()
30+
{
31+
var archUnitNetTestArchitectureWithRecursiveDependencies =
32+
new ArchLoader().LoadAssembliesIncludingDependencies(new[] { typeof(BaseClass).Assembly }, true)
33+
.Build();
34+
35+
Assert.True(archUnitNetTestArchitectureWithRecursiveDependencies.Assemblies.Count() > 100);
36+
}
2537
}
2638
}

0 commit comments

Comments
 (0)