diff --git a/T4.Build/SdkAssemblyLoadContext.cs b/T4.Build/SdkAssemblyLoadContext.cs new file mode 100644 index 0000000..7261949 --- /dev/null +++ b/T4.Build/SdkAssemblyLoadContext.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; +using System.Reflection; +using System.Runtime.Loader; +using Mono.TextTemplating.CodeCompilation; + +namespace T4.Build +{ + class SdkAssemblyLoadContext : AssemblyLoadContext + { + readonly string runtimeDir; + readonly string roslynDir; + + public SdkAssemblyLoadContext() + { + runtimeDir = Path.GetDirectoryName(typeof(object).Assembly.Location); + + var dotnetRoot = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(runtimeDir))); + string MakeRoslynPath(string d) => Path.Combine(d, "Roslyn", "bincore"); + var sdkDir = FindHighestVersionedDirectory(Path.Combine(dotnetRoot, "sdk"), d => Directory.Exists(MakeRoslynPath(d))); + roslynDir = MakeRoslynPath(sdkDir); + } + + static string FindHighestVersionedDirectory(string parentFolder, Func validate) + { + string bestMatch = null; + SemVersion bestVersion = SemVersion.Zero; + foreach (var dir in Directory.EnumerateDirectories(parentFolder)) + { + var name = Path.GetFileName(dir); + if (SemVersion.TryParse(name, out var version) && version.Major >= 0) + { + if (version > bestVersion && (validate == null || validate(dir))) + { + bestVersion = version; + bestMatch = dir; + } + } + } + return bestMatch; + } + + protected override Assembly Load(AssemblyName assemblyName) + { + switch (assemblyName.Name) + { + case "Microsoft.CodeAnalysis": + case "Microsoft.CodeAnalysis.CSharp": + return LoadFromAssemblyPath(Path.Combine(roslynDir, $"{assemblyName.Name}.dll")); + case "System.Collections.Immutable": + case "System.Reflection.Metadata": + return LoadFromAssemblyPath(Path.Combine(runtimeDir, $"{assemblyName.Name}.dll")); + } + + return null; + } + } +} \ No newline at end of file diff --git a/T4.Build/T4.Build.nuspec b/T4.Build/T4.Build.nuspec index 8853a1a..1e7c141 100644 --- a/T4.Build/T4.Build.nuspec +++ b/T4.Build/T4.Build.nuspec @@ -11,7 +11,7 @@ https://licenses.nuget.org/Apache-2.0 - + diff --git a/T4.Build/TemplatingEngineExtension.cs b/T4.Build/TemplatingEngineExtension.cs new file mode 100644 index 0000000..cec906a --- /dev/null +++ b/T4.Build/TemplatingEngineExtension.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; +using System.Reflection; +using Mono.TextTemplating; + +namespace T4.Build +{ + public static class RoslynTemplatingEngineExtensions + { + private static readonly object sync = new object(); + private static MethodInfo useInProcessCompilerMethod; + + private static MethodInfo UseInProcessCompilerMethod + { + get + { + lock (sync) + { + if (useInProcessCompilerMethod == null) + { + var context = new SdkAssemblyLoadContext(); + var assembly = context.LoadFromAssemblyPath(Path.Combine(Path.GetDirectoryName(typeof(RoslynTemplatingEngineExtensions).Assembly.Location), "Mono.TextTemplating.Roslyn.dll")); + var extensions = assembly.GetType("Mono.TextTemplating.RoslynTemplatingEngineExtensions"); + useInProcessCompilerMethod = extensions.GetMethod("UseInProcessCompiler", new Type[] { typeof(TemplatingEngine) }); + } + } + + return useInProcessCompilerMethod; + } + } + + public static void UseInProcessCompiler(this TemplatingEngine engine) + { + UseInProcessCompilerMethod.Invoke(null, new object[] { engine }); + } + } +} \ No newline at end of file