From 90cf3441fe7847da373c48bb57b8060cd48d132e Mon Sep 17 00:00:00 2001 From: xerootg <4009802+xerootg@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:55:52 -0700 Subject: [PATCH 1/2] Pipe the compilation context through to the RuntimeInfo class so the csc used by MSBuild can be used --- .../Mono.TextTemplating.Build.Tests.csproj | 2 +- .../BuildSpecificCodeCompilationContext.cs | 20 +++++ .../Mono.TextTemplating.Build.csproj | 2 +- .../T4.BuildTools.targets | 2 + Mono.TextTemplating.Build/TextTransform.cs | 15 +++- .../TextTransformProcessor.cs | 5 +- Mono.TextTemplating.Tests/ProcessingTests.cs | 4 +- .../CSharpLangVersionHelper.cs | 31 +++++++ .../DefaultCodeCompilationContext.cs | 12 +++ .../ICodeCompilationContext.cs | 15 ++++ .../RuntimeInfo.cs | 84 ++++++++++++------- .../Mono.TextTemplating/TemplateGenerator.cs | 30 ++++++- .../Mono.TextTemplating/TemplatingEngine.cs | 31 ++++--- 13 files changed, 204 insertions(+), 49 deletions(-) create mode 100644 Mono.TextTemplating.Build/BuildSpecificCodeCompilationContext.cs create mode 100644 Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/DefaultCodeCompilationContext.cs create mode 100644 Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ICodeCompilationContext.cs diff --git a/Mono.TextTemplating.Build.Tests/Mono.TextTemplating.Build.Tests.csproj b/Mono.TextTemplating.Build.Tests/Mono.TextTemplating.Build.Tests.csproj index 9f81497..d309a71 100644 --- a/Mono.TextTemplating.Build.Tests/Mono.TextTemplating.Build.Tests.csproj +++ b/Mono.TextTemplating.Build.Tests/Mono.TextTemplating.Build.Tests.csproj @@ -21,7 +21,7 @@ and loads first, thereby breaking loading of MSBuild assemblies. Force-upgrade it. --> - + diff --git a/Mono.TextTemplating.Build/BuildSpecificCodeCompilationContext.cs b/Mono.TextTemplating.Build/BuildSpecificCodeCompilationContext.cs new file mode 100644 index 0000000..e7d1042 --- /dev/null +++ b/Mono.TextTemplating.Build/BuildSpecificCodeCompilationContext.cs @@ -0,0 +1,20 @@ +using Mono.TextTemplating.CodeCompilation; + +namespace Mono.TextTemplating.Build +{ + public sealed class BuildSpecificCodeCompilationContext : ICodeCompilationContext + { + + readonly string _compilerSearchPath; + readonly string _netCoreRoot; + public BuildSpecificCodeCompilationContext (string compilerSearchPath, string NetCoreRoot) + { + _compilerSearchPath = compilerSearchPath; + _netCoreRoot = NetCoreRoot; + } + + public string CompilerSearchPath => _compilerSearchPath; + + public string NetCoreRoot => _netCoreRoot; + } +} \ No newline at end of file diff --git a/Mono.TextTemplating.Build/Mono.TextTemplating.Build.csproj b/Mono.TextTemplating.Build/Mono.TextTemplating.Build.csproj index 81b90d2..4c81069 100644 --- a/Mono.TextTemplating.Build/Mono.TextTemplating.Build.csproj +++ b/Mono.TextTemplating.Build/Mono.TextTemplating.Build.csproj @@ -34,7 +34,7 @@ - + diff --git a/Mono.TextTemplating.Build/T4.BuildTools.targets b/Mono.TextTemplating.Build/T4.BuildTools.targets index 3839b1b..1301e18 100644 --- a/Mono.TextTemplating.Build/T4.BuildTools.targets +++ b/Mono.TextTemplating.Build/T4.BuildTools.targets @@ -84,6 +84,8 @@ IncludePaths="@(T4IncludePath)" ParameterValues="@(T4Argument)" ReferencePaths="@(T4ReferencePath)" + RoslynTargetsPath="$(RoslynTargetsPath)" + NetCoreRoot="$(NetCoreRoot)" TransformTemplates="@(T4Transform)" PreprocessTemplates="@(T4Preprocess)" AssemblyReferences="@(T4AssemblyReference)" diff --git a/Mono.TextTemplating.Build/TextTransform.cs b/Mono.TextTemplating.Build/TextTransform.cs index 79a4e02..ed7958f 100644 --- a/Mono.TextTemplating.Build/TextTransform.cs +++ b/Mono.TextTemplating.Build/TextTransform.cs @@ -10,6 +10,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Mono.TextTemplating.CodeCompilation; namespace Mono.TextTemplating.Build { @@ -37,6 +38,16 @@ public TextTransform () : base (Messages.ResourceManager) { } public string PreprocessTargetRuntimeIdentifier { get; set; } + /// + /// The path to roslyn which will be used for this build. + /// + public string RoslynTargetsPath { get; set; } + + /// + /// The root directory of the .NET Core installation. + /// + public string NetCoreRoot { get; set; } + [Required] public string IntermediateDirectory { get; set; } @@ -130,7 +141,9 @@ public override bool Execute () } } - TextTransformProcessor.Process (Log, previousBuildState, buildState, PreprocessOnly); + ICodeCompilationContext context = RoslynTargetsPath != null ? new BuildSpecificCodeCompilationContext(RoslynTargetsPath, NetCoreRoot) : new DefaultCodeCompilationContext (); + + TextTransformProcessor.Process (Log, previousBuildState, buildState, PreprocessOnly, context); if (buildState.TransformTemplates != null) { TransformTemplateOutput = new ITaskItem[buildState.TransformTemplates.Count]; diff --git a/Mono.TextTemplating.Build/TextTransformProcessor.cs b/Mono.TextTemplating.Build/TextTransformProcessor.cs index 3854cd2..2c68126 100644 --- a/Mono.TextTemplating.Build/TextTransformProcessor.cs +++ b/Mono.TextTemplating.Build/TextTransformProcessor.cs @@ -12,12 +12,13 @@ using Microsoft.Build.Utilities; using Microsoft.VisualStudio.TextTemplating; +using Mono.TextTemplating.CodeCompilation; namespace Mono.TextTemplating.Build { static class TextTransformProcessor { - public static bool Process (TaskLoggingHelper taskLog, TemplateBuildState previousBuildState, TemplateBuildState buildState, bool preprocessOnly) + public static bool Process (TaskLoggingHelper taskLog, TemplateBuildState previousBuildState, TemplateBuildState buildState, bool preprocessOnly, ICodeCompilationContext context) { (var transforms, var preprocessed) = buildState.GetStaleAndNewTemplates (previousBuildState, preprocessOnly, new WriteTimeCache ().GetWriteTime, taskLog); @@ -50,7 +51,7 @@ public static bool Process (TaskLoggingHelper taskLog, TemplateBuildState previo } string outputContent; - (outputFile, outputContent) = generator.ProcessTemplateAsync (pt, inputFile, inputContent, outputFile, settings).Result; + (outputFile, outputContent) = generator.ProcessTemplateAsync (pt, inputFile, inputContent, outputFile, settings, context).Result; if (generator.Errors.HasErrors) { return; diff --git a/Mono.TextTemplating.Tests/ProcessingTests.cs b/Mono.TextTemplating.Tests/ProcessingTests.cs index 2cb8b75..223c41a 100644 --- a/Mono.TextTemplating.Tests/ProcessingTests.cs +++ b/Mono.TextTemplating.Tests/ProcessingTests.cs @@ -29,7 +29,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; - +using Mono.TextTemplating.CodeCompilation; using Xunit; namespace Mono.TextTemplating.Tests @@ -150,7 +150,7 @@ public async Task SetOutputExtension () var gen = new TemplateGenerator (); var pt = gen.ParseTemplate (inputFile, inputContent); TemplateSettings settings = TemplatingEngine.GetSettings (gen, pt); - (outputName, _) = await gen.ProcessTemplateAsync (pt, inputFile, inputContent, outputName, settings); + (outputName, _) = await gen.ProcessTemplateAsync (pt, inputFile, inputContent, outputName, settings, new DefaultCodeCompilationContext()); Assert.Equal ("hello.cfg", outputName); } diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/CSharpLangVersionHelper.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/CSharpLangVersionHelper.cs index 4235e06..da96513 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/CSharpLangVersionHelper.cs +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/CSharpLangVersionHelper.cs @@ -45,6 +45,37 @@ public static bool IsLangVersionArg (string arg) => return $"-langversion:{ToString (runtime.RuntimeLangVersion)}"; } + public static CSharpLangVersion ParseLangVersionOutput(string stdOut){ + // first remove any lines which are not numbers, and handle the default of something similar to "13.0 (default)" + var lines = stdOut.Split(new[] { Environment.NewLine, " " }, StringSplitOptions.RemoveEmptyEntries); + // now find any numbers remaining - these are the versions and 1-5 are ints and greater than 6.0 are floats + var version = lines.Where(l => float.TryParse(l, out _)) + .Select(float.Parse) + .ToArray(); + + // if we have any numbers, return the highest one + if(version.Length > 0){ + return version.Max() switch { + 1 => CSharpLangVersion.v5_0, + 2 => CSharpLangVersion.v6_0, + 3 => CSharpLangVersion.v7_0, + 4 => CSharpLangVersion.v7_1, + 5 => CSharpLangVersion.v7_2, + 6 => CSharpLangVersion.v7_3, + 7 => CSharpLangVersion.v8_0, + 8 => CSharpLangVersion.v9_0, + 9 => CSharpLangVersion.v10_0, + 10 => CSharpLangVersion.v11_0, + 11 => CSharpLangVersion.v12_0, + 12 => CSharpLangVersion.v13_0, + _ => CSharpLangVersion.Latest + }; + } + + // we didn't find any numbers, so return the latest version + return CSharpLangVersion.Latest; + } + //https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history public static CSharpLangVersion FromNetCoreSdkVersion (SemVersion sdkVersion) => sdkVersion switch { diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/DefaultCodeCompilationContext.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/DefaultCodeCompilationContext.cs new file mode 100644 index 0000000..4f645c1 --- /dev/null +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/DefaultCodeCompilationContext.cs @@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Mono.TextTemplating.CodeCompilation +{ + public class DefaultCodeCompilationContext : ICodeCompilationContext + { + public string CompilerSearchPath { get; } = Path.GetDirectoryName (typeof (object).Assembly.Location); + + public string NetCoreRoot { get; } = Environment.GetEnvironmentVariable ("DOTNET_ROOT"); + } +} \ No newline at end of file diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ICodeCompilationContext.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ICodeCompilationContext.cs new file mode 100644 index 0000000..4a1b531 --- /dev/null +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ICodeCompilationContext.cs @@ -0,0 +1,15 @@ +namespace Mono.TextTemplating.CodeCompilation +{ + public interface ICodeCompilationContext + { + /// + /// The directory to search for the compiler in. + /// + string CompilerSearchPath { get; } + + /// + /// The root directory of the .NET Core installation. + /// + string NetCoreRoot { get; } + } +} \ No newline at end of file diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs index 3a00fac..d7e1594 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs @@ -85,7 +85,9 @@ class RuntimeInfo public string RefAssembliesDir { get; } public string RuntimeFacadesDir { get; } - public static RuntimeInfo GetRuntime () + public static RuntimeInfo GetRuntime () => GetRuntime (new DefaultCodeCompilationContext ()); + + public static RuntimeInfo GetRuntime (ICodeCompilationContext context) { if (Type.GetType ("Mono.Runtime") != null) { @@ -93,11 +95,11 @@ public static RuntimeInfo GetRuntime () } else if (RuntimeInformation.FrameworkDescription.StartsWith (".NET Framework", StringComparison.OrdinalIgnoreCase)) { - return GetNetFrameworkRuntime (); + return GetNetFrameworkRuntime (context); } else { - return GetDotNetCoreSdk (); + return GetDotNetCoreSdk (context); } } @@ -123,39 +125,67 @@ static RuntimeInfo GetMonoRuntime () ); } - static RuntimeInfo GetNetFrameworkRuntime () + static RuntimeInfo GetNetFrameworkRuntime (ICodeCompilationContext context) { - var runtimeDir = Path.GetDirectoryName (typeof (object).Assembly.Location); - var csc = Path.Combine (runtimeDir, "csc.exe"); + var csc = Path.Combine (context.CompilerSearchPath, "csc.exe"); if (!File.Exists (csc)) { return FromError (RuntimeKind.NetFramework, "Could not find csc in host .NET Framework installation"); } - return new RuntimeInfo ( - RuntimeKind.NetFramework, - runtimeDir: runtimeDir, - // we don't really care about the version if it's not .net core - runtimeVersion: new Version ("4.7.2"), - refAssembliesDir: null, - runtimeFacadesDir: runtimeDir, - cscPath: csc, - cscMaxLangVersion: CSharpLangVersion.v5_0, - runtimeLangVersion: CSharpLangVersion.v5_0 - ); + + // now, determine the csc max lang version, just run csc -langversion:? and parse the output + var psi = new System.Diagnostics.ProcessStartInfo (csc, "-langversion:?") { + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + psi.RedirectStandardOutput = true; + using (var p = System.Diagnostics.Process.Start (psi)) { + p.WaitForExit (); + var output = p.StandardOutput.ReadToEnd (); + if (output.Contains ("error CS2008")) { + // then we have a basic csc that doesn't support -langversion, probably net4 + return new RuntimeInfo ( + RuntimeKind.NetFramework, + runtimeDir: context.CompilerSearchPath, + // we don't really care about the version if it's not .net core + runtimeVersion: new Version ("4.7.2"), + refAssembliesDir: null, + runtimeFacadesDir: context.CompilerSearchPath, + cscPath: csc, + cscMaxLangVersion: CSharpLangVersion.v5_0, + runtimeLangVersion: CSharpLangVersion.v5_0 + ); + } + + var maxLangVersion = CSharpLangVersionHelper.ParseLangVersionOutput (output); + + // now that we know the max lang version, we need to find the runtime dir, which if it's msbuild, it's gonna be one dir up from CompilerSearchPath + // C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\Roslyn -> C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin + + return new RuntimeInfo ( + RuntimeKind.NetFramework, + runtimeDir: Directory.GetParent (context.CompilerSearchPath).FullName, + // we don't really care about the version if it's not .net core + runtimeVersion: new Version ("4.7.2"), + refAssembliesDir: null, + runtimeFacadesDir: context.CompilerSearchPath, + cscPath: csc, + cscMaxLangVersion: maxLangVersion, + runtimeLangVersion: maxLangVersion + ); + } } - static RuntimeInfo GetDotNetCoreSdk () + static RuntimeInfo GetDotNetCoreSdk (ICodeCompilationContext context) { static bool DotnetRootIsValid (string root) => !string.IsNullOrEmpty (root) && (File.Exists (Path.Combine (root, "dotnet")) || File.Exists (Path.Combine (root, "dotnet.exe"))); - // the runtime dir is used when probing for DOTNET_ROOT - // and as a fallback in case we cannot locate reference assemblies - var runtimeDir = Path.GetDirectoryName (typeof (object).Assembly.Location); - - var dotnetRoot = Environment.GetEnvironmentVariable ("DOTNET_ROOT"); + var dotnetRoot = context.NetCoreRoot; if (!DotnetRootIsValid (dotnetRoot)) { // this will work if runtimeDir is $DOTNET_ROOT/shared/Microsoft.NETCore.App/5.0.0 - dotnetRoot = Path.GetDirectoryName (Path.GetDirectoryName (Path.GetDirectoryName (runtimeDir))); + dotnetRoot = Path.GetDirectoryName (Path.GetDirectoryName (Path.GetDirectoryName (context.CompilerSearchPath))); if (!DotnetRootIsValid (dotnetRoot)) { return FromError (RuntimeKind.NetCore, "Could not locate .NET root directory from running app. It can be set explicitly via the `DOTNET_ROOT` environment variable."); @@ -168,7 +198,7 @@ static RuntimeInfo GetDotNetCoreSdk () if (hostVersion.Major == 4) { // this will work if runtimeDir is $DOTNET_ROOT/shared/Microsoft.NETCore.App/5.0.0 - var versionPathComponent = Path.GetFileName (runtimeDir); + var versionPathComponent = Path.GetFileName (context.CompilerSearchPath); if (SemVersion.TryParse (versionPathComponent, out var hostSemVersion)) { hostVersion = new Version (hostSemVersion.Major, hostSemVersion.Minor, hostSemVersion.Patch); } @@ -193,11 +223,9 @@ static RuntimeInfo GetDotNetCoreSdk () out _ ); - - return new RuntimeInfo ( RuntimeKind.NetCore, - runtimeDir: runtimeDir, + runtimeDir: context.CompilerSearchPath, runtimeVersion: hostVersion, refAssembliesDir: refAssembliesDir, runtimeFacadesDir: null, diff --git a/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs b/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs index 9cd4839..33ff3e4 100644 --- a/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs +++ b/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs @@ -33,6 +33,7 @@ using Microsoft.VisualStudio.TextTemplating; using System.Threading; using System.Threading.Tasks; +using Mono.TextTemplating.CodeCompilation; namespace Mono.TextTemplating { @@ -119,7 +120,22 @@ void InitializeForRun (string inputFileName = null, string outputFileName = null public bool ProcessTemplate (string inputFile, string outputFile) => ProcessTemplateAsync (inputFile, outputFile, CancellationToken.None).Result; - public async Task ProcessTemplateAsync (string inputFile, string outputFile, CancellationToken token = default) + public async Task ProcessTemplateAsync (string inputFile, string outputFile) + => await ProcessTemplateAsync (inputFile, outputFile, CancellationToken.None).ConfigureAwait (false); + + public async Task ProcessTemplateAsync (string inputFile, string outputFile, CancellationToken token) + => await ProcessTemplateAsync (inputFile, outputFile, new DefaultCodeCompilationContext(), token).ConfigureAwait (false); + + internal async Task<(string outputFile, string outputContent)> ProcessTemplateAsync (ParsedTemplate pt, string inputFile, string inputContent, string outputFile, TemplateSettings settings) + { + InitializeForRun (inputFileName: inputFile, outputFileName: outputFile); + + var outputContent = await Engine.ProcessTemplateAsync (pt, inputContent, settings, this, new DefaultCodeCompilationContext()).ConfigureAwait (false); + + return (OutputFile, outputContent); + } + + public async Task ProcessTemplateAsync (string inputFile, string outputFile, ICodeCompilationContext context, CancellationToken token) { if (string.IsNullOrEmpty (inputFile)) throw new ArgumentNullException (nameof (inputFile)); @@ -263,10 +279,20 @@ public string PreprocessTemplate ( string outputFileName, TemplateSettings settings, CancellationToken token = default) + => await ProcessTemplateAsync (pt, inputFileName, inputContent, outputFileName, settings, new DefaultCodeCompilationContext (), token).ConfigureAwait (false); + + public async Task<(string fileName, string content)> ProcessTemplateAsync ( + ParsedTemplate pt, + string inputFileName, + string inputContent, + string outputFileName, + TemplateSettings settings, + ICodeCompilationContext context, + CancellationToken token = default) { InitializeForRun (inputFileName, outputFileName); - var outputContent = await Engine.ProcessTemplateAsync (pt, inputContent, settings, this, token).ConfigureAwait (false); + var outputContent = await Engine.ProcessTemplateAsync (pt, inputContent, settings, this, context, token).ConfigureAwait (false); return (OutputFile, outputContent); } diff --git a/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs b/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs index 7a00f75..e448f04 100644 --- a/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs +++ b/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs @@ -59,10 +59,10 @@ internal void SetCompilerFunc (Func cr createCompilerFunc = createCompiler; } - CodeCompilation.CodeCompiler GetOrCreateCompiler () + CodeCompilation.CodeCompiler GetOrCreateCompiler (ICodeCompilationContext context) { if (cachedCompiler == null) { - var runtime = RuntimeInfo.GetRuntime (); + var runtime = RuntimeInfo.GetRuntime (context); if (runtime.Error != null) { throw new TemplatingEngineException (runtime.Error); } @@ -79,13 +79,13 @@ public string ProcessTemplate (string content, ITextTemplatingEngineHost host) public async Task ProcessTemplateAsync (string content, ITextTemplatingEngineHost host, CancellationToken token = default) { - using var tpl = await CompileTemplateAsync (content, host, token).ConfigureAwait (false); + using var tpl = await CompileTemplateAsync (content, host, new DefaultCodeCompilationContext(), token).ConfigureAwait (false); return tpl?.Process (); } - public async Task ProcessTemplateAsync (ParsedTemplate pt, string content, TemplateSettings settings, ITextTemplatingEngineHost host, CancellationToken token = default) + public async Task ProcessTemplateAsync (ParsedTemplate pt, string content, TemplateSettings settings, ITextTemplatingEngineHost host, ICodeCompilationContext context, CancellationToken token = default) { - var tpl = await CompileTemplateAsync (pt, content, host, settings, token).ConfigureAwait (false); + var tpl = await CompileTemplateAsync (pt, content, host, settings, context, token).ConfigureAwait (false); using (tpl?.template) { return tpl?.template.Process (); } @@ -189,9 +189,12 @@ internal static string PreprocessTemplateInternal (ParsedTemplate pt, string con [Obsolete("Use CompileTemplateAsync")] public CompiledTemplate CompileTemplate (string content, ITextTemplatingEngineHost host) - => CompileTemplateAsync (content, host, CancellationToken.None).Result; + => CompileTemplateAsync (content, host, new DefaultCodeCompilationContext(), CancellationToken.None).Result; public async Task CompileTemplateAsync (string content, ITextTemplatingEngineHost host, CancellationToken token) + => await CompileTemplateAsync (content, host, new DefaultCodeCompilationContext(), token).ConfigureAwait (false); + + public async Task CompileTemplateAsync (string content, ITextTemplatingEngineHost host, ICodeCompilationContext context, CancellationToken token) { if (content == null) throw new ArgumentNullException (nameof (content)); @@ -204,7 +207,7 @@ public async Task CompileTemplateAsync (string content, ITextT return null; } - var tpl = await CompileTemplateInternal (pt, content, host, null, token).ConfigureAwait (false); + var tpl = await CompileTemplateInternal (pt, content, host, null, context, token).ConfigureAwait (false); return tpl?.template; } @@ -224,7 +227,7 @@ public CompiledTemplate CompileTemplate ( out string[] references, TemplateSettings settings = null) { - var result = CompileTemplateAsync (pt, content, host, settings, CancellationToken.None).Result; + var result = CompileTemplateAsync (pt, content, host, settings, new DefaultCodeCompilationContext(), CancellationToken.None).Result; references = result?.references; return result?.template; } @@ -234,14 +237,17 @@ public CompiledTemplate CompileTemplate ( string content, ITextTemplatingEngineHost host, TemplateSettings settings = null, + ICodeCompilationContext context = null, CancellationToken token = default) { if (pt == null) throw new ArgumentNullException (nameof (pt)); if (host == null) throw new ArgumentNullException (nameof (host)); + if (content == null) + context = new DefaultCodeCompilationContext(); - return CompileTemplateInternal (pt, content, host, settings, token); + return CompileTemplateInternal (pt, content, host, settings,context, token); } async Task<(CompiledTemplate template, string[] references)?> CompileTemplateInternal ( @@ -249,6 +255,7 @@ public CompiledTemplate CompileTemplate ( string content, ITextTemplatingEngineHost host, TemplateSettings settings, + ICodeCompilationContext context, CancellationToken token ) { @@ -274,7 +281,7 @@ CancellationToken token return null; } - (var results, var assembly) = await CompileCode (references, settings, ccu, token).ConfigureAwait (false); + (var results, var assembly) = await CompileCode (references, settings, ccu, context, token).ConfigureAwait (false); if (results.Errors.HasErrors) { host.LogErrors (pt.Errors); host.LogErrors (results.Errors); @@ -288,7 +295,7 @@ CancellationToken token return (compiledTemplate, references); } - async Task<(CompilerResults, CompiledAssemblyData)> CompileCode (IEnumerable references, TemplateSettings settings, CodeCompileUnit ccu, CancellationToken token) + async Task<(CompilerResults, CompiledAssemblyData)> CompileCode (IEnumerable references, TemplateSettings settings, CodeCompileUnit ccu, ICodeCompilationContext context, CancellationToken token) { string sourceText; var genOptions = new CodeGeneratorOptions (); @@ -300,7 +307,7 @@ CancellationToken token CompiledAssemblyData compiledAssembly = null; // this may throw, so do it before writing source files - var compiler = GetOrCreateCompiler (); + var compiler = GetOrCreateCompiler (context); // GetTempFileName guarantees that the returned file name is unique, but // there are no equivalent for directories, so we create a directory From e51c7a09493ecc6bee265b3dbdded5c47982c726 Mon Sep 17 00:00:00 2001 From: xerootg <4009802+xerootg@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:18:07 -0700 Subject: [PATCH 2/2] revert dotnetsdk resolution --- .../BuildSpecificCodeCompilationContext.cs | 6 +----- Mono.TextTemplating.Build/T4.BuildTools.targets | 1 - Mono.TextTemplating.Build/TextTransform.cs | 7 +------ .../DefaultCodeCompilationContext.cs | 1 - .../ICodeCompilationContext.cs | 5 ----- .../RuntimeInfo.cs | 16 ++++++++++------ 6 files changed, 12 insertions(+), 24 deletions(-) diff --git a/Mono.TextTemplating.Build/BuildSpecificCodeCompilationContext.cs b/Mono.TextTemplating.Build/BuildSpecificCodeCompilationContext.cs index e7d1042..d73d5fd 100644 --- a/Mono.TextTemplating.Build/BuildSpecificCodeCompilationContext.cs +++ b/Mono.TextTemplating.Build/BuildSpecificCodeCompilationContext.cs @@ -6,15 +6,11 @@ public sealed class BuildSpecificCodeCompilationContext : ICodeCompilationContex { readonly string _compilerSearchPath; - readonly string _netCoreRoot; - public BuildSpecificCodeCompilationContext (string compilerSearchPath, string NetCoreRoot) + public BuildSpecificCodeCompilationContext (string compilerSearchPath) { _compilerSearchPath = compilerSearchPath; - _netCoreRoot = NetCoreRoot; } public string CompilerSearchPath => _compilerSearchPath; - - public string NetCoreRoot => _netCoreRoot; } } \ No newline at end of file diff --git a/Mono.TextTemplating.Build/T4.BuildTools.targets b/Mono.TextTemplating.Build/T4.BuildTools.targets index 1301e18..b6bcfc9 100644 --- a/Mono.TextTemplating.Build/T4.BuildTools.targets +++ b/Mono.TextTemplating.Build/T4.BuildTools.targets @@ -85,7 +85,6 @@ ParameterValues="@(T4Argument)" ReferencePaths="@(T4ReferencePath)" RoslynTargetsPath="$(RoslynTargetsPath)" - NetCoreRoot="$(NetCoreRoot)" TransformTemplates="@(T4Transform)" PreprocessTemplates="@(T4Preprocess)" AssemblyReferences="@(T4AssemblyReference)" diff --git a/Mono.TextTemplating.Build/TextTransform.cs b/Mono.TextTemplating.Build/TextTransform.cs index ed7958f..4afd1a3 100644 --- a/Mono.TextTemplating.Build/TextTransform.cs +++ b/Mono.TextTemplating.Build/TextTransform.cs @@ -43,11 +43,6 @@ public TextTransform () : base (Messages.ResourceManager) { } /// public string RoslynTargetsPath { get; set; } - /// - /// The root directory of the .NET Core installation. - /// - public string NetCoreRoot { get; set; } - [Required] public string IntermediateDirectory { get; set; } @@ -141,7 +136,7 @@ public override bool Execute () } } - ICodeCompilationContext context = RoslynTargetsPath != null ? new BuildSpecificCodeCompilationContext(RoslynTargetsPath, NetCoreRoot) : new DefaultCodeCompilationContext (); + ICodeCompilationContext context = RoslynTargetsPath != null ? new BuildSpecificCodeCompilationContext(RoslynTargetsPath) : new DefaultCodeCompilationContext (); TextTransformProcessor.Process (Log, previousBuildState, buildState, PreprocessOnly, context); diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/DefaultCodeCompilationContext.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/DefaultCodeCompilationContext.cs index 4f645c1..07c1ae0 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/DefaultCodeCompilationContext.cs +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/DefaultCodeCompilationContext.cs @@ -7,6 +7,5 @@ public class DefaultCodeCompilationContext : ICodeCompilationContext { public string CompilerSearchPath { get; } = Path.GetDirectoryName (typeof (object).Assembly.Location); - public string NetCoreRoot { get; } = Environment.GetEnvironmentVariable ("DOTNET_ROOT"); } } \ No newline at end of file diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ICodeCompilationContext.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ICodeCompilationContext.cs index 4a1b531..116db07 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ICodeCompilationContext.cs +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ICodeCompilationContext.cs @@ -6,10 +6,5 @@ public interface ICodeCompilationContext /// The directory to search for the compiler in. /// string CompilerSearchPath { get; } - - /// - /// The root directory of the .NET Core installation. - /// - string NetCoreRoot { get; } } } \ No newline at end of file diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs index d7e1594..9307950 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs @@ -99,7 +99,7 @@ public static RuntimeInfo GetRuntime (ICodeCompilationContext context) } else { - return GetDotNetCoreSdk (context); + return GetDotNetCoreSdk (); } } @@ -177,15 +177,19 @@ static RuntimeInfo GetNetFrameworkRuntime (ICodeCompilationContext context) } } - static RuntimeInfo GetDotNetCoreSdk (ICodeCompilationContext context) + static RuntimeInfo GetDotNetCoreSdk () { static bool DotnetRootIsValid (string root) => !string.IsNullOrEmpty (root) && (File.Exists (Path.Combine (root, "dotnet")) || File.Exists (Path.Combine (root, "dotnet.exe"))); - var dotnetRoot = context.NetCoreRoot; + // the runtime dir is used when probing for DOTNET_ROOT + // and as a fallback in case we cannot locate reference assemblies + var runtimeDir = Path.GetDirectoryName (typeof (object).Assembly.Location); + + var dotnetRoot = Environment.GetEnvironmentVariable ("DOTNET_ROOT"); if (!DotnetRootIsValid (dotnetRoot)) { // this will work if runtimeDir is $DOTNET_ROOT/shared/Microsoft.NETCore.App/5.0.0 - dotnetRoot = Path.GetDirectoryName (Path.GetDirectoryName (Path.GetDirectoryName (context.CompilerSearchPath))); + dotnetRoot = Path.GetDirectoryName (Path.GetDirectoryName (Path.GetDirectoryName (runtimeDir))); if (!DotnetRootIsValid (dotnetRoot)) { return FromError (RuntimeKind.NetCore, "Could not locate .NET root directory from running app. It can be set explicitly via the `DOTNET_ROOT` environment variable."); @@ -198,7 +202,7 @@ static RuntimeInfo GetDotNetCoreSdk (ICodeCompilationContext context) if (hostVersion.Major == 4) { // this will work if runtimeDir is $DOTNET_ROOT/shared/Microsoft.NETCore.App/5.0.0 - var versionPathComponent = Path.GetFileName (context.CompilerSearchPath); + var versionPathComponent = Path.GetFileName (runtimeDir); if (SemVersion.TryParse (versionPathComponent, out var hostSemVersion)) { hostVersion = new Version (hostSemVersion.Major, hostSemVersion.Minor, hostSemVersion.Patch); } @@ -225,7 +229,7 @@ out _ return new RuntimeInfo ( RuntimeKind.NetCore, - runtimeDir: context.CompilerSearchPath, + runtimeDir: runtimeDir, runtimeVersion: hostVersion, refAssembliesDir: refAssembliesDir, runtimeFacadesDir: null,