From 084dcbf062f45ef27b0d0e93f73901902da37b44 Mon Sep 17 00:00:00 2001 From: Fraser Waters Date: Thu, 7 Sep 2017 17:10:39 +0100 Subject: [PATCH] Use custom cecil assembly resolver Fixes #642, until https://github.com/jbevain/cecil/pull/477 is merged and released and we can update our Mono.Cecil dependency to that version. This custom resolver takes into account Mono reference assemblies for mscorlib. This ensures we don't add spurious references to mscorlib 4.0. --- .../Generator.Rewrite.csproj | 1 + .../OpenTKAssemblyResolver.cs | 305 ++++++++++++++++++ src/Generator.Rewrite/Program.cs | 1 + 3 files changed, 307 insertions(+) create mode 100644 src/Generator.Rewrite/OpenTKAssemblyResolver.cs diff --git a/src/Generator.Rewrite/Generator.Rewrite.csproj b/src/Generator.Rewrite/Generator.Rewrite.csproj index 38c3389a..a726accc 100644 --- a/src/Generator.Rewrite/Generator.Rewrite.csproj +++ b/src/Generator.Rewrite/Generator.Rewrite.csproj @@ -59,6 +59,7 @@ + diff --git a/src/Generator.Rewrite/OpenTKAssemblyResolver.cs b/src/Generator.Rewrite/OpenTKAssemblyResolver.cs new file mode 100644 index 00000000..1bdd0a9d --- /dev/null +++ b/src/Generator.Rewrite/OpenTKAssemblyResolver.cs @@ -0,0 +1,305 @@ +using Mono.Cecil; +using System; +using System.Collections.Generic; +using System.IO; + +namespace OpenTK.Rewrite +{ + /// This is based on the Mono.Cecil BaseAssemblyResolver. But with + /// additions to GetCorlib but with some extra code to check for -api + /// directories which we're added by mono in the 4.4 release. Without this + /// we can end up adding assembly references to mscorlib 4.0 even though + /// OpenTK is compiled for 2.0. + internal sealed class OpenTKAssemblyResolver : IAssemblyResolver + { + static readonly bool on_mono = Type.GetType("Mono.Runtime") != null; + + readonly List directories; + + List gac_paths; + + public OpenTKAssemblyResolver() + { + directories = new List(2) { ".", "bin" }; + } + + AssemblyDefinition GetAssembly(string file, ReaderParameters parameters) + { + if (parameters.AssemblyResolver == null) + parameters.AssemblyResolver = this; + + return ModuleDefinition.ReadModule(file, parameters).Assembly; + } + + public AssemblyDefinition Resolve(AssemblyNameReference name) + { + return Resolve(name, new ReaderParameters()); + } + + public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) + { + var assembly = SearchDirectory(name, directories, parameters); + if (assembly != null) + return assembly; + + if (name.IsRetargetable) + { + // if the reference is retargetable, zero it + name = new AssemblyNameReference(name.Name, new Version(0, 0, 0, 0)) + { + PublicKeyToken = new byte[0], + }; + } + + var framework_dir = Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName); + var framework_dirs = on_mono + ? new[] { framework_dir, Path.Combine(framework_dir, "Facades") } + : new[] { framework_dir }; + + if (IsZero(name.Version)) + { + assembly = SearchDirectory(name, framework_dirs, parameters); + if (assembly != null) + return assembly; + } + + if (name.Name == "mscorlib") + { + assembly = GetCorlib(name, parameters); + if (assembly != null) + return assembly; + } + + assembly = GetAssemblyInGac(name, parameters); + if (assembly != null) + return assembly; + + assembly = SearchDirectory(name, framework_dirs, parameters); + if (assembly != null) + return assembly; + + throw new AssemblyResolutionException(name); + } + + AssemblyDefinition SearchDirectory(AssemblyNameReference name, IEnumerable directories, ReaderParameters parameters) + { + var extensions = name.IsWindowsRuntime ? new[] { ".winmd", ".dll" } : new[] { ".exe", ".dll" }; + foreach (var directory in directories) + { + foreach (var extension in extensions) + { + string file = Path.Combine(directory, name.Name + extension); + if (!File.Exists(file)) + continue; + try + { + var assembly = GetAssembly(file, parameters); + if (assembly.Name.Version == name.Version || IsZero(name.Version)) + { + return assembly; + } + } + catch (System.BadImageFormatException) + { + continue; + } + } + } + + return null; + } + + static bool IsZero(Version version) + { + return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0; + } + + AssemblyDefinition GetCorlib(AssemblyNameReference reference, ReaderParameters parameters) + { + var version = reference.Version; + var corlib = typeof(object).Assembly.GetName(); + + if (corlib.Version == version || IsZero(version)) + return GetAssembly(typeof(object).Module.FullyQualifiedName, parameters); + + var path = Directory.GetParent( + Directory.GetParent( + typeof(object).Module.FullyQualifiedName).FullName + ).FullName; + + if (on_mono) + { + if (version.Major == 1) + path = Path.Combine(path, "1.0"); + else if (version.Major == 2) + { + if (version.MajorRevision == 5) + path = Path.Combine(path, "2.1"); + else + path = Path.Combine(path, "2.0"); + } + else if (version.Major == 4) + path = Path.Combine(path, "4.0"); + else + throw new NotSupportedException("Version not supported: " + version); + } + else + { + switch (version.Major) + { + case 1: + if (version.MajorRevision == 3300) + path = Path.Combine(path, "v1.0.3705"); + else + path = Path.Combine(path, "v1.0.5000.0"); + break; + case 2: + path = Path.Combine(path, "v2.0.50727"); + break; + case 4: + path = Path.Combine(path, "v4.0.30319"); + break; + default: + throw new NotSupportedException("Version not supported: " + version); + } + } + + var file = Path.Combine(path, "mscorlib.dll"); + if (File.Exists(file)) + return GetAssembly(file, parameters); + else if (on_mono && Directory.Exists(path + "-api")) + { + path = path + "-api"; + file = Path.Combine(path, "mscorlib.dll"); + if (File.Exists(file)) + return GetAssembly(file, parameters); + } + + return null; + } + + static List GetGacPaths() + { + if (on_mono) + return GetDefaultMonoGacPaths(); + + var paths = new List(2); + var windir = Environment.GetEnvironmentVariable("WINDIR"); + if (windir == null) + return paths; + + paths.Add(Path.Combine(windir, "assembly")); + paths.Add(Path.Combine(windir, Path.Combine("Microsoft.NET", "assembly"))); + return paths; + } + + static List GetDefaultMonoGacPaths() + { + var paths = new List(1); + var gac = GetCurrentMonoGac(); + if (gac != null) + paths.Add(gac); + + var gac_paths_env = Environment.GetEnvironmentVariable("MONO_GAC_PREFIX"); + if (string.IsNullOrEmpty(gac_paths_env)) + return paths; + + var prefixes = gac_paths_env.Split(Path.PathSeparator); + foreach (var prefix in prefixes) + { + if (string.IsNullOrEmpty(prefix)) + continue; + + var gac_path = Path.Combine(Path.Combine(Path.Combine(prefix, "lib"), "mono"), "gac"); + if (Directory.Exists(gac_path) && !paths.Contains(gac)) + paths.Add(gac_path); + } + + return paths; + } + + static string GetCurrentMonoGac() + { + return Path.Combine( + Directory.GetParent( + Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName)).FullName, + "gac"); + } + + AssemblyDefinition GetAssemblyInGac(AssemblyNameReference reference, ReaderParameters parameters) + { + if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0) + return null; + + if (gac_paths == null) + gac_paths = GetGacPaths(); + + if (on_mono) + return GetAssemblyInMonoGac(reference, parameters); + + return GetAssemblyInNetGac(reference, parameters); + } + + AssemblyDefinition GetAssemblyInMonoGac(AssemblyNameReference reference, ReaderParameters parameters) + { + for (int i = 0; i < gac_paths.Count; i++) + { + var gac_path = gac_paths[i]; + var file = GetAssemblyFile(reference, string.Empty, gac_path); + if (File.Exists(file)) + { + var assembly = GetAssembly(file, parameters); + if (assembly.Name.Version == reference.Version || IsZero(reference.Version)) + { + return assembly; + } + } + } + + return null; + } + + AssemblyDefinition GetAssemblyInNetGac(AssemblyNameReference reference, ReaderParameters parameters) + { + var gacs = new[] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" }; + var prefixes = new[] { string.Empty, "v4.0_" }; + + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < gacs.Length; j++) + { + var gac = Path.Combine(gac_paths[i], gacs[j]); + var file = GetAssemblyFile(reference, prefixes[i], gac); + if (Directory.Exists(gac) && File.Exists(file)) + { + var assembly = GetAssembly(file, parameters); + if (assembly.Name.Version == reference.Version || IsZero(reference.Version)) + { + return assembly; + } + } + } + } + + return null; + } + + static string GetAssemblyFile(AssemblyNameReference reference, string prefix, string gac) + { + var gac_folder = new System.Text.StringBuilder() + .Append(prefix) + .Append(reference.Version) + .Append("__"); + + for (int i = 0; i < reference.PublicKeyToken.Length; i++) + gac_folder.Append(reference.PublicKeyToken[i].ToString("x2")); + + return Path.Combine( + Path.Combine( + Path.Combine(gac, reference.Name), gac_folder.ToString()), + reference.Name + ".dll"); + } + + void IDisposable.Dispose() { } + } +} diff --git a/src/Generator.Rewrite/Program.cs b/src/Generator.Rewrite/Program.cs index c6620889..6c653348 100644 --- a/src/Generator.Rewrite/Program.cs +++ b/src/Generator.Rewrite/Program.cs @@ -72,6 +72,7 @@ namespace OpenTK.Rewrite var read_params = new ReaderParameters(); var write_params = new WriterParameters(); + read_params.AssemblyResolver = new OpenTKAssemblyResolver(); read_params.ReadSymbols = true; read_params.ReadWrite = true; write_params.WriteSymbols = true;