Skip to content

Commit

Permalink
Merge pull request #1424 from OneThatWalks/master
Browse files Browse the repository at this point in the history
Temporary workaround for runtime and local assembly loading
  • Loading branch information
jzabroski committed Feb 19, 2021
2 parents 07ace7e + 29b98a2 commit 1680e1f
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/FluentMigrator.DotNet.Cli/Commands/MigrationCommand.cs
Expand Up @@ -58,5 +58,8 @@ public class MigrationCommand : BaseCommand

[Option("--include-untagged-maintenances", Description = "Include untagged maintenances.")]
public bool IncludeUntaggedMaintenances { get; }

[Option("--allowDirtyAssemblies", Description = "Allows dirty assemblies.")]
public bool AllowDirtyAssemblies { get; }
}
}
80 changes: 80 additions & 0 deletions src/FluentMigrator.DotNet.Cli/Program.cs
Expand Up @@ -16,6 +16,13 @@
//
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

using FluentMigrator.DotNet.Cli.Commands;

using McMaster.Extensions.CommandLineUtils;
Expand All @@ -32,7 +39,80 @@ static Program()

public static int Main(string[] args)
{
if (args.Contains("--allowDirtyAssemblies"))
{
using (DirtyAssemblyResolveHelper.Create())
{
return CommandLineApplication.Execute<Root>(args);
}
}
return CommandLineApplication.Execute<Root>(args);
}
}

/// <summary>
/// Temporary workaround for
/// https://github.com/fluentmigrator/fluentmigrator/issues/1406
///
/// taken mostly from
/// https://github.com/dotnet/BenchmarkDotNet/blob/852bb8cd9c2ac2530866dc53723c5f2ce3d411fa/src/BenchmarkDotNet/Helpers/DirtyAssemblyResolveHelper.cs#L18
///
/// Sometimes NuGet/VS/other tool does not generate the right assembly binding redirects
/// or just for any other magical reasons
/// our users get FileNotFoundException when trying to run their benchmarks
///
/// We want our users to be happy and we try to help the .NET framework when it fails to load an assembly
///
/// It's not recommended to copy this code OR reuse it anywhere. It's an UGLY WORKAROUND.
///
/// If one day we can remove it, the person doing that should celebrate!!
/// </summary>
internal class DirtyAssemblyResolveHelper : IDisposable
{
internal static IDisposable Create() => new DirtyAssemblyResolveHelper();

private DirtyAssemblyResolveHelper() => AppDomain.CurrentDomain.AssemblyResolve += HelpTheFrameworkToResolveTheAssembly;

public void Dispose() => AppDomain.CurrentDomain.AssemblyResolve -= HelpTheFrameworkToResolveTheAssembly;

/// <summary>
/// according to https://msdn.microsoft.com/en-us/library/ff527268(v=vs.110).aspx
/// "the handler is invoked whenever the runtime fails to bind to an assembly by name."
/// </summary>
/// <returns>not null when we find it manually, null when can't help</returns>
private Assembly HelpTheFrameworkToResolveTheAssembly(object sender, ResolveEventArgs args)
{
var fullName = new AssemblyName(args.Name);
string simpleName = fullName.Name;

// Get assemblies from runtime environment
string[] runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), $"{simpleName}.dll");

// Create the list of assembly paths consisting of runtime assemblies
var paths = new List<string>(runtimeAssemblies);

// Since we search by simple name, there should hopefully be one file
string guessedPath = paths.FirstOrDefault();

// If there wasn't a runtime file check the app directory for the file
if (guessedPath == null)
{
guessedPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{simpleName}.dll");
}

if (!File.Exists(guessedPath))
return null; // we can't help, and we also don't call Assembly.Load which if fails comes back here, creates endless loop and causes StackOverflow

// the file is right there, but has most probably different version and there is no assembly redirect
// so we just load it and ignore the version mismatch
// we can at least try because benchmarks are not executed in the Host process,
// so even if we load some bad version of the assembly
// we might still produce the right exe with proper references

// we warn the user about that, in case some Super User want to be aware of that
Console.WriteLine($"// Wrong assembly binding redirects for {simpleName}, loading it from disk anyway. {guessedPath}");

return Assembly.LoadFrom(guessedPath);
}
}
}

0 comments on commit 1680e1f

Please sign in to comment.