Skip to content

Commit c26edb4

Browse files
authored
Merge pull request #165 from gbtb/load_assemblies_with_filter
Added LoadAssembliesRecursively with custom load filter function support
2 parents b938367 + af59928 commit c26edb4

File tree

3 files changed

+108
-6
lines changed

3 files changed

+108
-6
lines changed

ArchUnitNET/Loader/ArchLoader.cs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private ArchLoader LoadAssembly(string fileName, bool includeDependencies, bool
9898
return this;
9999
}
100100

101-
private void LoadModule(string fileName, string nameSpace, bool includeDependencies, bool recursive)
101+
private void LoadModule(string fileName, string nameSpace, bool includeDependencies, bool recursive, FilterFunc filterFunc = null)
102102
{
103103
try
104104
{
@@ -112,7 +112,7 @@ private void LoadModule(string fileName, string nameSpace, bool includeDependenc
112112
{
113113
if (includeDependencies && recursive)
114114
{
115-
AddReferencedAssembliesRecursively(assemblyReference, processedAssemblies, resolvedModules);
115+
AddReferencedAssembliesRecursively(assemblyReference, processedAssemblies, resolvedModules, filterFunc);
116116
}
117117
else
118118
{
@@ -148,7 +148,8 @@ private void LoadModule(string fileName, string nameSpace, bool includeDependenc
148148
}
149149

150150
private void AddReferencedAssembliesRecursively(AssemblyNameReference currentAssemblyReference,
151-
ICollection<AssemblyNameReference> processedAssemblies, List<ModuleDefinition> resolvedModules)
151+
ICollection<AssemblyNameReference> processedAssemblies, List<ModuleDefinition> resolvedModules,
152+
FilterFunc filterFunc)
152153
{
153154
if (processedAssemblies.Contains(currentAssemblyReference))
154155
{
@@ -161,17 +162,39 @@ private void AddReferencedAssembliesRecursively(AssemblyNameReference currentAss
161162
_assemblyResolver.AddLib(currentAssemblyReference);
162163
var assemblyDefinition = _assemblyResolver.Resolve(currentAssemblyReference) ??
163164
throw new AssemblyResolutionException(currentAssemblyReference);
164-
_archBuilder.AddAssembly(assemblyDefinition, false);
165-
resolvedModules.AddRange(assemblyDefinition.Modules);
165+
166+
var filterResult = filterFunc?.Invoke(assemblyDefinition);
167+
if (filterResult?.LoadThisAssembly != false)
168+
{
169+
_archBuilder.AddAssembly(assemblyDefinition, false);
170+
resolvedModules.AddRange(assemblyDefinition.Modules);
171+
}
172+
166173
foreach (var reference in assemblyDefinition.Modules.SelectMany(m => m.AssemblyReferences))
167174
{
168-
AddReferencedAssembliesRecursively(reference, processedAssemblies, resolvedModules);
175+
if (filterResult?.TraverseDependencies != false)
176+
AddReferencedAssembliesRecursively(reference, processedAssemblies, resolvedModules, filterFunc);
169177
}
170178
}
171179
catch (AssemblyResolutionException)
172180
{
173181
//Failed to resolve assembly, skip it
174182
}
175183
}
184+
185+
/// <summary>
186+
/// Loads assemblies from dependency tree with user-defined filtration logic
187+
/// </summary>
188+
/// <param name="assemblies">Assemblies to start traversal from</param>
189+
/// <param name="filterFunc">Delegate to control loading and traversal logic</param>
190+
/// <returns></returns>
191+
public ArchLoader LoadAssembliesRecursively(IEnumerable<Assembly> assemblies, FilterFunc filterFunc)
192+
{
193+
foreach (var assembly in assemblies)
194+
{
195+
LoadModule(assembly.Location, null, true, true, filterFunc);
196+
}
197+
return this;
198+
}
176199
}
177200
}

ArchUnitNET/Loader/FilterResult.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2019 Florian Gather <[email protected]>
2+
// Copyright 2019 Fritz Brandhuber <[email protected]>
3+
// Copyright 2020 Pavel Fischer <[email protected]>
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
using Mono.Cecil;
9+
10+
namespace ArchUnitNET.Loader
11+
{
12+
/// <summary>
13+
/// Type of delegate to control assemblies loading
14+
/// </summary>
15+
/// <param name="assemblyDefinition">Current assembly definition</param>
16+
public delegate FilterResult FilterFunc(AssemblyDefinition assemblyDefinition);
17+
18+
/// <summary>
19+
/// Filter function result options
20+
/// </summary>
21+
public struct FilterResult
22+
{
23+
/// <summary>
24+
/// Load this assembly and traverse its dependencies
25+
/// </summary>
26+
public static FilterResult LoadAndContinue = new FilterResult(true, true);
27+
28+
/// <summary>
29+
/// Do not load this assembly, but traverse its dependencies
30+
/// </summary>
31+
public static FilterResult SkipAndContinue = new FilterResult(true, false);
32+
33+
/// <summary>
34+
/// Load this assembly and do not traverse its dependencies
35+
/// </summary>
36+
public static FilterResult LoadAndStop = new FilterResult(false, true);
37+
38+
/// <summary>
39+
/// Do not load this assembly and do not traverse its dependencies
40+
/// </summary>
41+
public static FilterResult DontLoadAndStop = new FilterResult(false, false);
42+
43+
private FilterResult(bool traverseDependencies, bool loadThisAssembly)
44+
{
45+
TraverseDependencies = traverseDependencies;
46+
LoadThisAssembly = loadThisAssembly;
47+
}
48+
49+
internal bool TraverseDependencies { get; }
50+
51+
internal bool LoadThisAssembly { get; }
52+
}
53+
}

ArchUnitNETTests/Loader/ArchLoaderTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,31 @@ public void LoadAssembliesIncludingRecursiveDependencies()
3434

3535
Assert.True(archUnitNetTestArchitectureWithRecursiveDependencies.Assemblies.Count() > 100);
3636
}
37+
38+
[Fact]
39+
public void LoadAssembliesRecursivelyWithCustomFilter()
40+
{
41+
FilterFunc filterFunc = assembly => assembly.Name.Name.StartsWith("ArchUnit") ? FilterResult.LoadAndContinue : FilterResult.DontLoadAndStop;
42+
var loader = new ArchLoader();
43+
var architecture = loader.LoadAssembliesRecursively(new[] { typeof(BaseClass).Assembly }, filterFunc).Build();
44+
45+
Assert.Equal(3, architecture.Assemblies.Count());
46+
}
47+
48+
[Fact]
49+
public void LoadAssembliesRecursively_NestedDependencyOnly()
50+
{
51+
FilterFunc filterFunc = assembly =>
52+
{
53+
if (assembly.Name.Name == "ArchUnitNet")
54+
return FilterResult.LoadAndStop;
55+
56+
return FilterResult.SkipAndContinue;
57+
};
58+
var loader = new ArchLoader();
59+
var architecture = loader.LoadAssembliesRecursively(new[] { typeof(BaseClass).Assembly }, filterFunc).Build();
60+
61+
Assert.Equal(1, architecture.Assemblies.Count());
62+
}
3763
}
3864
}

0 commit comments

Comments
 (0)