Skip to content

Commit

Permalink
Merge pull request #165 from gbtb/load_assemblies_with_filter
Browse files Browse the repository at this point in the history
Added LoadAssembliesRecursively with custom load filter function support
  • Loading branch information
fgather authored Jul 31, 2022
2 parents b938367 + af59928 commit c26edb4
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 6 deletions.
35 changes: 29 additions & 6 deletions ArchUnitNET/Loader/ArchLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private ArchLoader LoadAssembly(string fileName, bool includeDependencies, bool
return this;
}

private void LoadModule(string fileName, string nameSpace, bool includeDependencies, bool recursive)
private void LoadModule(string fileName, string nameSpace, bool includeDependencies, bool recursive, FilterFunc filterFunc = null)
{
try
{
Expand All @@ -112,7 +112,7 @@ private void LoadModule(string fileName, string nameSpace, bool includeDependenc
{
if (includeDependencies && recursive)
{
AddReferencedAssembliesRecursively(assemblyReference, processedAssemblies, resolvedModules);
AddReferencedAssembliesRecursively(assemblyReference, processedAssemblies, resolvedModules, filterFunc);
}
else
{
Expand Down Expand Up @@ -148,7 +148,8 @@ private void LoadModule(string fileName, string nameSpace, bool includeDependenc
}

private void AddReferencedAssembliesRecursively(AssemblyNameReference currentAssemblyReference,
ICollection<AssemblyNameReference> processedAssemblies, List<ModuleDefinition> resolvedModules)
ICollection<AssemblyNameReference> processedAssemblies, List<ModuleDefinition> resolvedModules,
FilterFunc filterFunc)
{
if (processedAssemblies.Contains(currentAssemblyReference))
{
Expand All @@ -161,17 +162,39 @@ private void AddReferencedAssembliesRecursively(AssemblyNameReference currentAss
_assemblyResolver.AddLib(currentAssemblyReference);
var assemblyDefinition = _assemblyResolver.Resolve(currentAssemblyReference) ??
throw new AssemblyResolutionException(currentAssemblyReference);
_archBuilder.AddAssembly(assemblyDefinition, false);
resolvedModules.AddRange(assemblyDefinition.Modules);

var filterResult = filterFunc?.Invoke(assemblyDefinition);
if (filterResult?.LoadThisAssembly != false)
{
_archBuilder.AddAssembly(assemblyDefinition, false);
resolvedModules.AddRange(assemblyDefinition.Modules);
}

foreach (var reference in assemblyDefinition.Modules.SelectMany(m => m.AssemblyReferences))
{
AddReferencedAssembliesRecursively(reference, processedAssemblies, resolvedModules);
if (filterResult?.TraverseDependencies != false)
AddReferencedAssembliesRecursively(reference, processedAssemblies, resolvedModules, filterFunc);
}
}
catch (AssemblyResolutionException)
{
//Failed to resolve assembly, skip it
}
}

/// <summary>
/// Loads assemblies from dependency tree with user-defined filtration logic
/// </summary>
/// <param name="assemblies">Assemblies to start traversal from</param>
/// <param name="filterFunc">Delegate to control loading and traversal logic</param>
/// <returns></returns>
public ArchLoader LoadAssembliesRecursively(IEnumerable<Assembly> assemblies, FilterFunc filterFunc)
{
foreach (var assembly in assemblies)
{
LoadModule(assembly.Location, null, true, true, filterFunc);
}
return this;
}
}
}
53 changes: 53 additions & 0 deletions ArchUnitNET/Loader/FilterResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2019 Florian Gather <[email protected]>
// Copyright 2019 Fritz Brandhuber <[email protected]>
// Copyright 2020 Pavel Fischer <[email protected]>
//
// SPDX-License-Identifier: Apache-2.0
//

using Mono.Cecil;

namespace ArchUnitNET.Loader
{
/// <summary>
/// Type of delegate to control assemblies loading
/// </summary>
/// <param name="assemblyDefinition">Current assembly definition</param>
public delegate FilterResult FilterFunc(AssemblyDefinition assemblyDefinition);

/// <summary>
/// Filter function result options
/// </summary>
public struct FilterResult
{
/// <summary>
/// Load this assembly and traverse its dependencies
/// </summary>
public static FilterResult LoadAndContinue = new FilterResult(true, true);

/// <summary>
/// Do not load this assembly, but traverse its dependencies
/// </summary>
public static FilterResult SkipAndContinue = new FilterResult(true, false);

/// <summary>
/// Load this assembly and do not traverse its dependencies
/// </summary>
public static FilterResult LoadAndStop = new FilterResult(false, true);

/// <summary>
/// Do not load this assembly and do not traverse its dependencies
/// </summary>
public static FilterResult DontLoadAndStop = new FilterResult(false, false);

private FilterResult(bool traverseDependencies, bool loadThisAssembly)
{
TraverseDependencies = traverseDependencies;
LoadThisAssembly = loadThisAssembly;
}

internal bool TraverseDependencies { get; }

internal bool LoadThisAssembly { get; }
}
}
26 changes: 26 additions & 0 deletions ArchUnitNETTests/Loader/ArchLoaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,31 @@ public void LoadAssembliesIncludingRecursiveDependencies()

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

[Fact]
public void LoadAssembliesRecursivelyWithCustomFilter()
{
FilterFunc filterFunc = assembly => assembly.Name.Name.StartsWith("ArchUnit") ? FilterResult.LoadAndContinue : FilterResult.DontLoadAndStop;
var loader = new ArchLoader();
var architecture = loader.LoadAssembliesRecursively(new[] { typeof(BaseClass).Assembly }, filterFunc).Build();

Assert.Equal(3, architecture.Assemblies.Count());
}

[Fact]
public void LoadAssembliesRecursively_NestedDependencyOnly()
{
FilterFunc filterFunc = assembly =>
{
if (assembly.Name.Name == "ArchUnitNet")
return FilterResult.LoadAndStop;

return FilterResult.SkipAndContinue;
};
var loader = new ArchLoader();
var architecture = loader.LoadAssembliesRecursively(new[] { typeof(BaseClass).Assembly }, filterFunc).Build();

Assert.Equal(1, architecture.Assemblies.Count());
}
}
}

0 comments on commit c26edb4

Please sign in to comment.