diff --git a/Mono.TextTemplating.Roslyn/Mono.TextTemplating.Roslyn.csproj b/Mono.TextTemplating.Roslyn/Mono.TextTemplating.Roslyn.csproj index 40f813a..892551f 100644 --- a/Mono.TextTemplating.Roslyn/Mono.TextTemplating.Roslyn.csproj +++ b/Mono.TextTemplating.Roslyn/Mono.TextTemplating.Roslyn.csproj @@ -27,15 +27,23 @@ To enable the in-process C# compiler, use the TemplatingEngine.UseInProcessCompi + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + + + + + diff --git a/Mono.TextTemplating.Tests/DummyHost.cs b/Mono.TextTemplating.Tests/DummyHost.cs index 86da60f..93358d8 100644 --- a/Mono.TextTemplating.Tests/DummyHost.cs +++ b/Mono.TextTemplating.Tests/DummyHost.cs @@ -27,7 +27,7 @@ using System; using System.Collections.Generic; using System.CodeDom.Compiler; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; namespace Mono.TextTemplating.Tests { @@ -37,11 +37,11 @@ public class DummyHost : ITextTemplatingEngineHost public readonly Dictionary Locations = new Dictionary (); public readonly Dictionary Contents = new Dictionary (); public readonly Dictionary HostOptions = new Dictionary (); - List standardAssemblyReferences = new List (); + List standardAssemblyReferences = new List (new[] { typeof(TemplatingEngine).Assembly.Location }); List standardImports = new List (); public readonly CompilerErrorCollection Errors = new CompilerErrorCollection (); public readonly Dictionary DirectiveProcessors = new Dictionary (); - + public readonly Dictionary Parameters = new Dictionary (); public virtual object GetHostOption (string optionName) { object o; @@ -68,7 +68,7 @@ public virtual AppDomain ProvideTemplatingAppDomain (string content) public virtual string ResolveAssemblyReference (string assemblyReference) { - throw new System.NotImplementedException(); + return assemblyReference; } public virtual Type ResolveDirectiveProcessor (string processorName) @@ -80,22 +80,24 @@ public virtual Type ResolveDirectiveProcessor (string processorName) public virtual string ResolveParameterValue (string directiveId, string processorName, string parameterName) { - throw new System.NotImplementedException(); + return Parameters[parameterName]; } public virtual string ResolvePath (string path) { throw new System.NotImplementedException(); } - + + string extension; + public virtual void SetFileExtension (string extension) { - throw new System.NotImplementedException(); + this.extension = extension; } public virtual void SetOutputEncoding (System.Text.Encoding encoding, bool fromOutputDirective) { - throw new System.NotImplementedException(); + } public virtual IList StandardAssemblyReferences { diff --git a/Mono.TextTemplating.Tests/GenerationTests.cs b/Mono.TextTemplating.Tests/GenerationTests.cs index fb5038a..dc76f6d 100644 --- a/Mono.TextTemplating.Tests/GenerationTests.cs +++ b/Mono.TextTemplating.Tests/GenerationTests.cs @@ -28,9 +28,10 @@ using System.Collections.Generic; using System.IO; using NUnit.Framework; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; using System.Linq; using System.CodeDom.Compiler; +using System.Reflection; namespace Mono.TextTemplating.Tests { @@ -48,6 +49,60 @@ public void TemplateGeneratorTest () Assert.IsNull (gen.Errors.OfType ().FirstOrDefault (), "ProcessTemplate"); } + [Test] + public void GenerateStaticPropertyForParameter () + { + var engine = new TemplatingEngine (); + + var host = new DummyHost (); + + var output = engine.PreprocessTemplate (T4ParameterSample, host, "ParameterTestClass", "Testing", out string language, out string[] references); + + foreach (CompilerError error in host.Errors) { + Console.Error.WriteLine (error.ErrorText); + } + + Assert.IsTrue (output.Contains ("public static string TestParameter")); + + Console.Out.WriteLine (output); + } + + [Test] + [TestCase("some nonsense value")] + public void GenerateStaticPropertyForParameterCanInitilialize (string value) + { + var engine = new TemplatingEngine (); + + var host = new DummyHost () { + TemplateFile = "test.tt" + }; + + host.Parameters.Add ("TestParameter", value); + + + var tt = engine.CompileTemplate (T4ParameterSample, host); + + foreach (CompilerError error in host.Errors) { + Console.Error.WriteLine (error.ErrorText); + } + + Type ttType = tt.textTransformation?.GetType (); + + Assert.IsNotNull (ttType); + + var initMethod = ttType.GetMethod ("Initialize"); + var testAssignment = ttType.GetMethod ("TestAssignment"); + var parameter = ttType.GetProperty ("TestParameter", BindingFlags.Public | BindingFlags.Static); + + initMethod.Invoke (tt.textTransformation, null); + + if (testAssignment.Invoke(tt.textTransformation, null) is bool success) { + Assert.IsTrue (success); + } + + Assert.AreEqual (value, parameter.GetValue (null)); + } + [Test] public void ImportReferencesTest () { @@ -105,6 +160,8 @@ public void GenerateWindowsNewlines () Generate (WinInput, WinOutput, "\r\n"); } + + [Test] public void DefaultLanguage () { @@ -121,7 +178,7 @@ public void DefaultLanguage () void Generate (string input, string expectedOutput, string newline) { var host = new DummyHost (); - string nameSpaceName = "Microsoft.VisualStudio.TextTemplating4f504ca0"; + string nameSpaceName = "Mono.VisualStudio.TextTemplating4f504ca0"; string code = GenerateCode (host, input, nameSpaceName, newline); Assert.AreEqual (0, host.Errors.Count); @@ -164,14 +221,28 @@ string GenerateCode (ITextTemplatingEngineHost host, string content, string name #endregion + #region input strings + public const string T4ParameterSample = +@"<#@ template hostspecific=""true"" language=""C#"" #> +<#@ output extension="".cs"" #> +<#@ parameter type=""System.String"" name=""TestParameter"" #> +<#@ import namespace=""System"" #> +using System; +<#+ +public bool TestAssignment() { + return !string.IsNullOrEmpty(TestParameter); +} +#>"; + #endregion + #region Expected output strings public static string OutputSample1 = @" -namespace Microsoft.VisualStudio.TextTemplating4f504ca0 { +namespace Mono.VisualStudio.TextTemplating4f504ca0 { - public partial class GeneratedTextTransformation : global::Microsoft.VisualStudio.TextTemplating.TextTransformation { + public partial class GeneratedTextTransformation : global::Mono.VisualStudio.TextTemplating.TextTransformation { #line 9 """" @@ -205,7 +276,7 @@ public override string TransformText() { #line hidden #line 7 """" - this.Write(global::Microsoft.VisualStudio.TextTemplating.ToStringHelper.ToStringWithCulture( bar )); + this.Write(global::Mono.VisualStudio.TextTemplating.ToStringHelper.ToStringWithCulture( bar )); #line default #line hidden diff --git a/Mono.TextTemplating.Tests/Mono.TextTemplating.Tests.csproj b/Mono.TextTemplating.Tests/Mono.TextTemplating.Tests.csproj index 7a0d11b..7998921 100644 --- a/Mono.TextTemplating.Tests/Mono.TextTemplating.Tests.csproj +++ b/Mono.TextTemplating.Tests/Mono.TextTemplating.Tests.csproj @@ -8,8 +8,15 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - + + + + diff --git a/Mono.TextTemplating.Tests/TemplateEnginePreprocessTemplateTests.cs b/Mono.TextTemplating.Tests/TemplateEnginePreprocessTemplateTests.cs index 86b170d..fef001d 100644 --- a/Mono.TextTemplating.Tests/TemplateEnginePreprocessTemplateTests.cs +++ b/Mono.TextTemplating.Tests/TemplateEnginePreprocessTemplateTests.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.IO; using NUnit.Framework; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; namespace Mono.TextTemplating.Tests { diff --git a/Mono.TextTemplating.Tests/TextTemplatingSessionTests.cs b/Mono.TextTemplating.Tests/TextTemplatingSessionTests.cs index 1c7c5a8..405e953 100644 --- a/Mono.TextTemplating.Tests/TextTemplatingSessionTests.cs +++ b/Mono.TextTemplating.Tests/TextTemplatingSessionTests.cs @@ -1,4 +1,4 @@ -// +// // TextTemplatingSessionTests.cs // // Author: @@ -26,7 +26,7 @@ using System; using System.Reflection; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; using NUnit.Framework; namespace Mono.TextTemplating.Tests diff --git a/Mono.TextTemplating.sln b/Mono.TextTemplating.sln index d6a1007..847a0df 100644 --- a/Mono.TextTemplating.sln +++ b/Mono.TextTemplating.sln @@ -1,11 +1,12 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.TextTemplating", "Mono.TextTemplating\Mono.TextTemplating.csproj", "{A2364D6A-00EF-417C-80A6-815726C70032}" +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30320.27 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.TextTemplating", "Mono.TextTemplating\Mono.TextTemplating.csproj", "{A2364D6A-00EF-417C-80A6-815726C70032}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.TextTemplating.Tests", "Mono.TextTemplating.Tests\Mono.TextTemplating.Tests.csproj", "{CB590106-8331-4CBE-8131-B154E7BF79E1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.TextTemplating.Tests", "Mono.TextTemplating.Tests\Mono.TextTemplating.Tests.csproj", "{CB590106-8331-4CBE-8131-B154E7BF79E1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextTransform", "TextTransform\TextTransform.csproj", "{D1D35409-C814-47F6-B038-B9B5BF0FE490}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TextTransform", "TextTransform\TextTransform.csproj", "{D1D35409-C814-47F6-B038-B9B5BF0FE490}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EEE624A1-0ED3-4D57-96B4-5D1B22E72697}" ProjectSection(SolutionItems) = preProject @@ -13,11 +14,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Build.props = Directory.Build.props EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-t4", "dotnet-t4\dotnet-t4.csproj", "{6AA924D8-7119-4593-9B56-11D17AC3578E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-t4", "dotnet-t4\dotnet-t4.csproj", "{6AA924D8-7119-4593-9B56-11D17AC3578E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-t4-project-tool", "dotnet-t4-project-tool\dotnet-t4-project-tool.csproj", "{114B7AEF-61DA-453A-9A84-6DDEA13460B7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-t4-project-tool", "dotnet-t4-project-tool\dotnet-t4-project-tool.csproj", "{114B7AEF-61DA-453A-9A84-6DDEA13460B7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.TextTemplating.Roslyn", "Mono.TextTemplating.Roslyn\Mono.TextTemplating.Roslyn.csproj", "{8DB780A9-27C1-44B4-860F-50C4419FD349}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.TextTemplating.Roslyn", "Mono.TextTemplating.Roslyn\Mono.TextTemplating.Roslyn.csproj", "{8DB780A9-27C1-44B4-860F-50C4419FD349}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/Engine.cs b/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/Engine.cs index fd78b49..6af95ff 100644 --- a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/Engine.cs +++ b/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/Engine.cs @@ -26,11 +26,13 @@ using System; using Mono.TextTemplating; +using Mono.VisualStudio.TextTemplating; namespace Microsoft.VisualStudio.TextTemplating { - [Obsolete ("Use Mono.TextTemplating.TemplatingEngine directly")] - public class Engine : ITextTemplatingEngine + [Obsolete ("Use Mono.TextTemplating.TemplatingEngine directly", true)] + public class Engine + : ITextTemplatingEngine { TemplatingEngine engine = new TemplatingEngine (); @@ -44,7 +46,7 @@ public string PreprocessTemplate (string content, ITextTemplatingEngineHost host { return engine.PreprocessTemplate (content, host, className, classNamespace, out language, out references); } - + public const string CacheAssembliesOptionString = "CacheAssemblies"; } } diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/AssemblyResolver.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/AssemblyResolver.cs index 5e3145f..19648d2 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/AssemblyResolver.cs +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/AssemblyResolver.cs @@ -42,7 +42,7 @@ IEnumerable GetImplicitReferences () { yield return "mscorlib.dll"; yield return "netstandard.dll"; - + if (runtime.Kind == RuntimeKind.NetCore) { yield return "System.Runtime.dll"; //because we're referencing the impl not the ref asms, we end up diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/CscCodeCompiler.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/CscCodeCompiler.cs index 52c1b2f..f2809c4 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/CscCodeCompiler.cs +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/CscCodeCompiler.cs @@ -32,6 +32,7 @@ using System.Threading.Tasks; using System.Linq; using System.Text; +using System.Globalization; namespace Mono.TextTemplating.CodeCompilation { @@ -115,85 +116,96 @@ public override async Task CompileFile (CodeCompilerArgument UseShellExecute = false }; - if (log != null) { - log.WriteLine ($"{psi.FileName} {psi.Arguments}"); - } - if (runtime.Kind == RuntimeKind.NetCore) { psi.Arguments = $"\"{psi.FileName}\" {psi.Arguments}"; psi.FileName = Path.GetFullPath (Path.Combine (runtime.RuntimeDir, "..", "..", "..", "dotnet")); } - var stdout = new StringWriter (); - var stderr = new StringWriter (); - - TextWriter outWriter = stderr, errWriter = stderr; - if (log != null) { - outWriter = new SplitOutputWriter (log, outWriter); - errWriter = new SplitOutputWriter (log, errWriter); + if (log != null) + { + log.WriteLine($"{psi.FileName} {psi.Arguments}"); + log.WriteLine ("-------------------------------------------------------------------------------"); } - var process = ProcessUtils.StartProcess (psi, outWriter, errWriter, token); + using (var stdout = new StringWriter (new StringBuilder(), CultureInfo.CurrentCulture)) + using (var stderr = new StringWriter (new StringBuilder (), CultureInfo.CurrentCulture)) + using (TextWriter outWriter = log != null ? new SplitOutputWriter (log, stderr) : (TextWriter)stderr) + using (TextWriter errWriter = log != null ? new SplitOutputWriter (log, stderr) : (TextWriter)stderr) { - var result = await process; + var process = ProcessUtils.StartProcess (psi, outWriter, errWriter, token); - var outputList = new List (); - var errors = new List (); + int result = -1; - void ConsumeOutput (string s) - { - using (var sw = new StringReader (s)) { - string line; - while ((line = sw.ReadLine ()) != null) { - outputList.Add (line); - var err = MSBuildErrorParser.TryParseLine (line); - if (err != null) { - errors.Add (err); + if (!token.IsCancellationRequested) { + result = await process.ConfigureAwait (false); + } + + var outputList = new List (); + var errors = new List (); + + void ConsumeOutput (string s) + { + using (var sw = new StringReader (s)) { + string line; + while ((line = sw.ReadLine ()) != null) { + outputList.Add (line); + var err = MSBuildErrorParser.TryParseLine (line); + if (err != null) { + errors.Add (err); + } } } } - } - ConsumeOutput (stdout.ToString ()); - ConsumeOutput (stderr.ToString ()); + ConsumeOutput (stdout.ToString ()); + ConsumeOutput (stderr.ToString ()); - if (log != null) { - log.WriteLine ($"{psi.FileName} {psi.Arguments}"); - } + if (log != null) { + log.WriteLine (); + log.WriteLine (); + log.WriteLine ($"{psi.FileName} {psi.Arguments}"); + } - return new CodeCompilerResult { - Success = result == 0, - Errors = errors, - ExitCode = result, - Output = outputList, - ResponseFile = rspPath - }; + return new CodeCompilerResult { + Success = result == 0, + Errors = errors, + ExitCode = result, + Output = outputList, + ResponseFile = rspPath + }; + } } //we know that ProcessUtils.StartProcess only uses WriteLine and Write(string) class SplitOutputWriter : TextWriter { - readonly TextWriter a; - readonly TextWriter b; + readonly TextWriter logWriter; + readonly TextWriter errorWriter; public SplitOutputWriter (TextWriter a, TextWriter b) { - this.a = a; - this.b = b; + this.logWriter = a; + this.errorWriter = b; } public override Encoding Encoding => Encoding.UTF8; public override void WriteLine () { - a.WriteLine (); - b.WriteLine (); + logWriter.WriteLine (); + errorWriter.WriteLine (); + } + + public override void WriteLine (string value) + { + logWriter.WriteLine (value); + errorWriter.WriteLine (value); } public override void Write (string value) { - a.Write (value); - b.Write (value); + logWriter.Write (value); + errorWriter.Write (value); } } } diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ProcessUtils.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ProcessUtils.cs index b285b76..00ad60e 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ProcessUtils.cs +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/ProcessUtils.cs @@ -57,9 +57,11 @@ public static Task StartProcess (ProcessStartInfo psi, TextWriter stdout, T if (!p.HasExited) { p.Kill (); } - } catch (InvalidOperationException ex) { - if (ex.Message.IndexOf ("already exited", StringComparison.Ordinal) < 0) + } + catch (InvalidOperationException ex) { + if (ex.Message.IndexOf ("already exited", StringComparison.Ordinal) < 0) { throw; + } } }); } @@ -75,16 +77,19 @@ public static Task StartProcess (ProcessStartInfo psi, TextWriter stdout, T try { if (e.Data == null) { outputDone = true; - if (exitDone && errorDone) + if (exitDone && errorDone) { tcs.TrySetResult (p.ExitCode); + } return; } - if (stdOutInitialized) + if (stdOutInitialized) { stdout.WriteLine (); + } stdout.Write (e.Data); stdOutInitialized = true; - } catch (Exception ex) { + } + catch (Exception ex) { tcs.TrySetException (ex); } }; @@ -99,16 +104,19 @@ public static Task StartProcess (ProcessStartInfo psi, TextWriter stdout, T try { if (e.Data == null) { errorDone = true; - if (exitDone && outputDone) + if (exitDone && outputDone) { tcs.TrySetResult (p.ExitCode); + } return; } - if (stdErrInitialized) + if (stdErrInitialized) { stderr.WriteLine (); + } stderr.Write (e.Data); stdErrInitialized = true; - } catch (Exception ex) { + } + catch (Exception ex) { tcs.TrySetException (ex); } }; @@ -119,8 +127,9 @@ public static Task StartProcess (ProcessStartInfo psi, TextWriter stdout, T p.Exited += (sender, e) => { exitDone = true; - if (errorDone && outputDone) + if (errorDone && outputDone) { tcs.TrySetResult (p.ExitCode); + } }; return tcs.Task; diff --git a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs index d64b5d6..d2ab001 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs +++ b/Mono.TextTemplating/Mono.TextTemplating.CodeCompilation/RuntimeInfo.cs @@ -30,11 +30,16 @@ namespace Mono.TextTemplating.CodeCompilation { - enum RuntimeKind + public enum RuntimeKind { +#if !NET5 + Default = 0, NetCore, NetFramework, Mono +#else + Net +#endif } class RuntimeInfo @@ -49,18 +54,18 @@ class RuntimeInfo public string CscPath { get; private set; } public bool IsValid => Error == null; - public static RuntimeInfo GetRuntime () + public static RuntimeInfo GetRuntime (RuntimeKind kind = RuntimeKind.Default) { var monoFx = GetMonoRuntime (); - if (monoFx.IsValid) { + if (monoFx.IsValid && (monoFx.Kind == kind || kind == RuntimeKind.Default)) { return monoFx; } var netFx = GetNetFrameworkRuntime (); - if (netFx.IsValid) { + if (netFx.IsValid && (netFx.Kind == kind || kind == RuntimeKind.Default)) { return netFx; } var coreFx = GetDotNetCoreRuntime (); - if (coreFx.IsValid) { + if (coreFx.IsValid && (coreFx.Kind == kind || kind == RuntimeKind.Default)) { return coreFx; } return FromError (RuntimeKind.Mono, "Could not find any valid runtime" ); @@ -97,6 +102,27 @@ public static RuntimeInfo GetNetFrameworkRuntime () }; } + public static RuntimeInfo GetDotNetCoreRuntime () + { + var dotnetRoot = FindDotNetRoot (); + if (dotnetRoot == null) { + return FromError (RuntimeKind.NetCore, "Could not find .NET Core installation"); + } + + string MakeCscPath (string d) => Path.Combine (d, "Roslyn", "bincore", "csc.dll"); + var sdkDir = FindHighestVersionedDirectory (Path.Combine (dotnetRoot, "sdk"), d => File.Exists (MakeCscPath (d))); + if (sdkDir == null) { + return FromError (RuntimeKind.NetCore, "Could not find csc.dll in any .NET Core SDK"); + } + + var runtimeDir = FindHighestVersionedDirectory (Path.Combine (dotnetRoot, "shared", "Microsoft.NETCore.App"), d => File.Exists (Path.Combine (d, "System.Runtime.dll"))); + if (runtimeDir == null) { + return FromError (RuntimeKind.NetCore, "Could not find System.Runtime.dll in any .NET shared runtime"); + } + + return new RuntimeInfo (RuntimeKind.NetCore) { RuntimeDir = runtimeDir, CscPath = MakeCscPath (sdkDir) }; + } + static string FindDotNetRoot () { string dotnetRoot; @@ -144,25 +170,6 @@ static string FindHighestVersionedDirectory (string parentFolder, Func Path.Combine (d, "Roslyn", "bincore", "csc.dll"); - var sdkDir = FindHighestVersionedDirectory (Path.Combine (dotnetRoot, "sdk"), d => File.Exists (MakeCscPath (d))); - if (sdkDir == null) { - return FromError (RuntimeKind.NetCore, "Could not find csc.dll in any .NET Core SDK" ); - } - - var runtimeDir = FindHighestVersionedDirectory (Path.Combine (dotnetRoot, "shared", "Microsoft.NETCore.App"), d => File.Exists (Path.Combine (d, "System.Runtime.dll"))); - if (runtimeDir == null) { - return FromError (RuntimeKind.NetCore, "Could not find System.Runtime.dll in any .NET shared runtime" ); - } - - return new RuntimeInfo (RuntimeKind.NetCore) { RuntimeDir = runtimeDir, CscPath = MakeCscPath (sdkDir) }; - } + } } diff --git a/Mono.TextTemplating/Mono.TextTemplating.csproj b/Mono.TextTemplating/Mono.TextTemplating.csproj index 5034720..5401e2f 100644 --- a/Mono.TextTemplating/Mono.TextTemplating.csproj +++ b/Mono.TextTemplating/Mono.TextTemplating.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;net45;net35 true @@ -25,10 +25,44 @@ This package allows embedding the T4 engine in an application. + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + + + + + + 4.3.0 + + + 4.3.0 + + + + + + VsTemplatingErrorResources.resx + True + True + + + + + + VsTemplatingErrorResources.Designer.cs + ResXFileCodeGenerator + + + + + + 4.3.0 + \ No newline at end of file diff --git a/Mono.TextTemplating/Mono.TextTemplating/CompiledTemplate.cs b/Mono.TextTemplating/Mono.TextTemplating/CompiledTemplate.cs index 3760f9f..6ebd294 100644 --- a/Mono.TextTemplating/Mono.TextTemplating/CompiledTemplate.cs +++ b/Mono.TextTemplating/Mono.TextTemplating/CompiledTemplate.cs @@ -26,7 +26,7 @@ using System; using System.Reflection; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; using System.CodeDom.Compiler; using System.Globalization; using System.Collections.Generic; @@ -40,7 +40,7 @@ public sealed class CompiledTemplate : IDisposable { ITextTemplatingEngineHost host; - object textTransformation; + internal object textTransformation; readonly CultureInfo culture; readonly string [] assemblyFiles; @@ -54,12 +54,19 @@ public CompiledTemplate (ITextTemplatingEngineHost host, CompilerResults results Load (results, fullName); } + /// + /// Get the compiled assembly + /// + public Assembly Assembly { get; private set; } + void Load (CompilerResults results, string fullName) { //results.CompiledAssembly doesn't work on .NET core, it throws a cryptic internal error //use Assembly.LoadFile instead - var assembly = System.Reflection.Assembly.LoadFile (results.PathToAssembly); - Type transformType = assembly.GetType (fullName); + //for debugging we need the assembly + Assembly = System.Reflection.Assembly.LoadFile (results.PathToAssembly); + + Type transformType = Assembly.GetType (fullName); //MS Templating Engine does not look on the type itself, //it checks only that required methods are exists in the compiled type textTransformation = Activator.CreateInstance (transformType); @@ -82,7 +89,13 @@ void Load (CompilerResults results, string fullName) } } - public string Process () + + public string Process() + { + return Process (textTransformation); + } + + public string Process (object textTransformation) { var ttType = textTransformation.GetType (); diff --git a/Mono.TextTemplating/Mono.TextTemplating/DebugTextTemplateEngine.cs b/Mono.TextTemplating/Mono.TextTemplating/DebugTextTemplateEngine.cs new file mode 100644 index 0000000..235b68b --- /dev/null +++ b/Mono.TextTemplating/Mono.TextTemplating/DebugTextTemplateEngine.cs @@ -0,0 +1,145 @@ +using System; +using Mono.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating.VSHost; + +namespace Mono.TextTemplating +{ + using System.Globalization; + using System.IO; + using System.Reflection; + using System.Runtime.Serialization; + using System.Threading; + + public partial class TemplatingEngine + : IProcessTextTemplatingEngine + { + public IProcessTransformationRun PrepareTransformationRun (string content, ITextTemplatingEngineHost host, IProcessTransformationRunFactory runFactory, bool debugging = false) + { + if (content == null) { + throw new ArgumentNullException (nameof(content)); + } + if (host == null) { + throw new ArgumentNullException (nameof (host)); + } + if (runFactory == null) { + throw new ArgumentNullException (nameof (runFactory)); + } + + if (host is ITextTemplatingSessionHost sessionHost) { + if (sessionHost.Session == null) { + sessionHost.Session = sessionHost.CreateSession (); + } + } + + ParsedTemplate pt = ParsedTemplate.FromText (content, host); + + IProcessTransformationRun run = null; + + try { + if (pt.Errors.HasErrors) { + return null; + } + TemplateSettings settings = GetSettings (host, pt); + + settings.Debug = debugging; + + run = CompileAndPrepareRun (pt, content, host, runFactory, settings); + } catch(Exception ex) { + if (IsCriticalException(ex)) { + throw; + } + pt.LogError (string.Format(CultureInfo.CurrentCulture, VsTemplatingErrorResources.ExceptionProcessingTemplate, ex), new Location (host.TemplateFile, -1, -1)); + } + finally { + host.LogErrors (pt.Errors); + } + + return run; + } + + protected virtual IProcessTransformationRun CompileAndPrepareRun (ParsedTemplate pt, string content, ITextTemplatingEngineHost host, IProcessTransformationRunFactory runFactory, TemplateSettings settings) + { + TransformationRunner runner = null; + bool success = false; + + if (pt == null) { + throw new ArgumentNullException (nameof (pt)); + } + + if (host == null) { + throw new ArgumentNullException (nameof (host)); + } + + if (runFactory == null) { + throw new ArgumentNullException (nameof (runFactory)); + } + + if (settings == null) { + throw new ArgumentNullException (nameof (settings)); + } + + Assembly ResolveReferencedAssemblies (object sender, ResolveEventArgs args) + { + AssemblyName asmName = new AssemblyName (args.Name); + foreach (var asmFile in settings.Assemblies) { + if (asmName.Name == Path.GetFileNameWithoutExtension (asmFile)) + return Assembly.LoadFrom (asmFile); + } + + var path = host.ResolveAssemblyReference (asmName.Name); + + if (File.Exists (path)) { + return Assembly.LoadFrom (path); + } + + return null; + } + + try { + try { + if (runFactory.CreateTransformationRun (typeof (TransformationRunner), pt, new ResolveEventHandler(ResolveReferencedAssemblies)) is TransformationRunner theRunner) { + runner = theRunner; + } + } + catch (Exception ex) { + if (IsCriticalException (ex)) { + throw; + } + } + if (runner != null && !runner.Errors.HasErrors) { + ProcessReferences (host, pt, settings); + if (!pt.Errors.HasErrors) { + //with app domains dissappearing we may not be able to pre load anything + //runner.PreLoadAssemblies (settings.Assemblies); + try { + success = runner.PrepareTransformation (pt, content, settings.HostSpecific ? host : null, settings); + } + catch (SerializationException) { + pt.LogError (VsTemplatingErrorResources.SessionHostMarshalError, new Location (host.TemplateFile, -1, -1)); + throw; + } + } + } + } + catch(Exception ex) { + if (IsCriticalException (ex)) { + throw; + } + pt.LogError (ex.ToString (), new Location (host.TemplateFile, -1, -1)); + } + finally { + if (runner != null) { + pt.Errors.AddRange (runner.Errors); + runner.ClearErrors (); + } + } + + return success ? runner : null; + } + + public static bool IsCriticalException(Exception e) + { + return ((e is StackOverflowException) || ((e is OutOfMemoryException) || ((e is ThreadAbortException) || ((e.InnerException != null) && IsCriticalException (e.InnerException))))); + } + } +} diff --git a/Mono.TextTemplating/Mono.TextTemplating/ParsedTemplate.cs b/Mono.TextTemplating/Mono.TextTemplating/ParsedTemplate.cs index 8248385..062662b 100644 --- a/Mono.TextTemplating/Mono.TextTemplating/ParsedTemplate.cs +++ b/Mono.TextTemplating/Mono.TextTemplating/ParsedTemplate.cs @@ -28,7 +28,7 @@ using System.CodeDom.Compiler; using System.Collections.Generic; using System.IO; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; namespace Mono.TextTemplating { @@ -66,6 +66,10 @@ public IEnumerable Content { public static ParsedTemplate FromText (string content, ITextTemplatingEngineHost host) { + if (host == null) { + throw new ArgumentNullException (nameof (host)); + } + var template = new ParsedTemplate (host.TemplateFile); try { template.Parse (host, new Tokeniser (host.TemplateFile, content)); @@ -270,6 +274,9 @@ public Location (string fileName, int line, int column) : this() Column = column; Line = line; } + + public Location (string fileName) + : this (fileName, -1, -1) { } public int Line { get; private set; } public int Column { get; private set; } @@ -289,5 +296,25 @@ public bool Equals (Location other) { return other.Line == Line && other.Column == Column && other.FileName == FileName; } + + public override bool Equals (object obj) + { + return obj is Location && Equals ((Location)obj); + } + + public static bool operator == (Location left, Location right) + { + return left.Equals (right); + } + + public static bool operator != (Location left, Location right) + { + return !(left == right); + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } } } diff --git a/Mono.TextTemplating/Mono.TextTemplating/StringUtil.cs b/Mono.TextTemplating/Mono.TextTemplating/StringUtil.cs index bfcadb5..b98c412 100644 --- a/Mono.TextTemplating/Mono.TextTemplating/StringUtil.cs +++ b/Mono.TextTemplating/Mono.TextTemplating/StringUtil.cs @@ -1,4 +1,6 @@ using System; +using System.ComponentModel; +using System.Reflection; namespace Mono.TextTemplating { @@ -14,5 +16,33 @@ public static Boolean IsNullOrWhiteSpace (String value) return true; } + + public static TType GetValue(this PropertyInfo property, object @object, object[] index) + { + if (property.GetValue(@object, index) is TType success) { + return success; + } + return default; + } + + + public static bool TryParse (this string value, out TEnum @enum) + where TEnum: struct + { +#if NET35 + if (Enum.Parse (typeof (TEnum), value) is TEnum success) { + @enum = success; + + return true; + } + + @enum = default; + + return false; +#else + return Enum.TryParse (value, out @enum); +#endif + } + } } diff --git a/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs b/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs index 7ead97e..4c24063 100644 --- a/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs +++ b/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs @@ -30,7 +30,8 @@ using System.IO; using System.Reflection; using System.Text; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; +using System.Runtime.Serialization; namespace Mono.TextTemplating { @@ -508,5 +509,10 @@ public virtual IEnumerable GetAdditionalDirectiveProcessors { yield break; } + + public void GetObjectData (SerializationInfo info, StreamingContext context) + { + throw new NotImplementedException (); + } } } diff --git a/Mono.TextTemplating/Mono.TextTemplating/TemplateSettings.cs b/Mono.TextTemplating/Mono.TextTemplating/TemplateSettings.cs index cd3f318..7aa9f3f 100644 --- a/Mono.TextTemplating/Mono.TextTemplating/TemplateSettings.cs +++ b/Mono.TextTemplating/Mono.TextTemplating/TemplateSettings.cs @@ -27,8 +27,13 @@ using System; using System.Text; using System.Collections.Generic; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; using System.IO; +using System.Reflection; +using System.Threading; +#if !NET35 +using Mono.TextTemplating.CodeCompilation; +#endif namespace Mono.TextTemplating { @@ -45,6 +50,11 @@ public TemplateSettings () public bool HostSpecific { get; set; } public bool HostPropertyOnBase { get; set; } public bool Debug { get; set; } + public bool CachedTemplates { get; set; } +#if !NET35 + public CancellationToken CancellationToken { get; set; } + public RuntimeKind RuntimeKind { get; set; } +#endif public TextWriter Log { get; set; } public string Inherits { get; set; } public string Name { get; set; } @@ -61,10 +71,11 @@ public TemplateSettings () public Dictionary DirectiveProcessors { get; private set; } public bool IncludePreprocessingHelpers { get; set; } public bool IsPreprocessed { get; set; } - public bool RelativeLinePragmas { get; set; } + public bool UseRelativeLinePragmas { get; set; } public bool NoLinePragmas { get; set; } public bool InternalVisibility { get; set; } public Type HostType { get; set; } + public string GetFullName () => string.IsNullOrEmpty (Namespace) ? Name : Namespace + "." + Name; } diff --git a/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs b/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs index 781b4c7..960166b 100644 --- a/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs +++ b/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs @@ -34,14 +34,15 @@ using System.Text; using System.Threading; using Microsoft.CSharp; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; +using System.Globalization; #if FEATURE_ROSLYN using Mono.TextTemplating.CodeCompilation; #endif namespace Mono.TextTemplating { - public class TemplatingEngine : + public partial class TemplatingEngine : #if FEATURE_APPDOMAINS MarshalByRefObject, #endif @@ -60,10 +61,10 @@ internal void SetCompilerFunc (Func cr createCompilerFunc = createCompiler; } - CodeCompilation.CodeCompiler GetOrCreateCompiler () + CodeCompilation.CodeCompiler GetOrCreateCompiler (RuntimeKind kind = RuntimeKind.Default) { if (cachedCompiler == null) { - var runtime = RuntimeInfo.GetRuntime (); + var runtime = RuntimeInfo.GetRuntime (kind); if (runtime.Error != null) { throw new Exception (runtime.Error); } @@ -238,7 +239,7 @@ CompilerResults CompileCode (IEnumerable references, TemplateSettings se } // this may throw, so do it before writing source files - var compiler = GetOrCreateCompiler (); + var compiler = GetOrCreateCompiler (settings.RuntimeKind); var tempFolder = Path.GetTempFileName (); File.Delete (tempFolder); @@ -259,7 +260,7 @@ CompilerResults CompileCode (IEnumerable references, TemplateSettings se args.OutputPath = Path.Combine (tempFolder, settings.Name + ".dll"); args.TempDirectory = tempFolder; - var result = compiler.CompileFile (args, settings.Log, CancellationToken.None).Result; + var result = compiler.CompileFile (args, settings.Log, settings.CancellationToken).Result; var r = new CompilerResults (new TempFileCollection ()); r.TempFiles.AddFile (sourceFilename, false); @@ -283,7 +284,12 @@ CompilerResults CompileCode (IEnumerable references, TemplateSettings se } if (!args.Debug) { - r.TempFiles.Delete (); + if (r.TempFiles is IDisposable disposable) { + disposable.Dispose (); + } + } + else { + r.TempFiles.KeepFiles = args.Debug; } return r; @@ -311,8 +317,18 @@ static CompilerResults CompileCode (IEnumerable references, TemplateSett } #endif - static string [] ProcessReferences (ITextTemplatingEngineHost host, ParsedTemplate pt, TemplateSettings settings) + protected static string[] ProcessReferences (ITextTemplatingEngineHost host, ParsedTemplate pt, TemplateSettings settings) { + if (host == null) { + throw new ArgumentNullException (nameof (host)); + } + if (pt == null) { + throw new ArgumentNullException (nameof (pt)); + } + if (settings == null) { + throw new ArgumentNullException (nameof (settings)); + } + var resolved = new Dictionary (); foreach (string assem in settings.Assemblies.Union (host.StandardAssemblyReferences)) { @@ -335,12 +351,21 @@ static string [] ProcessReferences (ITextTemplatingEngineHost host, ParsedTempla public static TemplateSettings GetSettings (ITextTemplatingEngineHost host, ParsedTemplate pt) { + if (host == null) { + throw new ArgumentNullException (nameof (host)); + } + if (pt == null) { + throw new ArgumentNullException (nameof (pt)); + } + var settings = new TemplateSettings (); - bool relativeLinePragmas = host.GetHostOption ("UseRelativeLinePragmas") as bool? ?? false; + bool relativeLinePragmas = host.GetHostOption (nameof (TemplateSettings.UseRelativeLinePragmas)) as bool? ?? false; foreach (Directive dt in pt.Directives) { +#pragma warning disable CA1308 // Normalize strings to uppercase switch (dt.Name.ToLowerInvariant ()) { +#pragma warning restore CA1308 // Normalize strings to uppercase case "template": string val = dt.Extract ("language"); if (val != null) @@ -368,15 +393,15 @@ public static TemplateSettings GetSettings (ITextTemplatingEngineHost host, Pars settings.HostSpecific = string.Compare (val, "true", StringComparison.OrdinalIgnoreCase) == 0; } } - val = dt.Extract ("CompilerOptions"); + val = dt.Extract (nameof (TemplateSettings.CompilerOptions)) ?? host.GetHostOption(nameof(TemplateSettings.CompilerOptions))?.ToString().ToLower(); if (val != null) { settings.CompilerOptions = val; } - val = dt.Extract ("relativeLinePragmas"); + val = dt.Extract ("relativeLinePragmas") ?? host.GetHostOption (nameof (TemplateSettings.UseRelativeLinePragmas))?.ToString().ToLower(); if (val != null) { relativeLinePragmas = string.Compare (val, "true", StringComparison.OrdinalIgnoreCase) == 0; } - val = dt.Extract ("linePragmas"); + val = dt.Extract ("linePragmas") ?? host.GetHostOption (nameof (TemplateSettings.NoLinePragmas))?.ToString ().ToLower (); if (val != null) { settings.NoLinePragmas = string.Compare (val, "false", StringComparison.OrdinalIgnoreCase) == 0; } @@ -440,7 +465,7 @@ public static TemplateSettings GetSettings (ITextTemplatingEngineHost host, Pars //initialize the custom processors foreach (var kv in settings.DirectiveProcessors) { - kv.Value.Initialize (host); + kv.Value.Initialize (host, settings); IRecognizeHostSpecific hs; if (settings.HostSpecific || ( @@ -462,7 +487,7 @@ public static TemplateSettings GetSettings (ITextTemplatingEngineHost host, Pars if (settings.Name == null) settings.Name = "GeneratedTextTransformation"; if (settings.Namespace == null) - settings.Namespace = string.Format (typeof (TextTransformation).Namespace + "{0:x}", new Random ().Next ()); + settings.Namespace = string.Format (CultureInfo.InvariantCulture, typeof (TextTransformation).Namespace + "{0:x}", new Random ().Next ()); //resolve the CodeDOM provider if (String.IsNullOrEmpty (settings.Language)) { @@ -483,7 +508,30 @@ public static TemplateSettings GetSettings (ITextTemplatingEngineHost host, Pars return settings; } - settings.RelativeLinePragmas = relativeLinePragmas; + settings.UseRelativeLinePragmas = relativeLinePragmas; + + if (host.GetHostOption (nameof (TemplateSettings.CachedTemplates)) is bool cachedTemplates) { + settings.CachedTemplates = cachedTemplates; + } + + if (host.GetHostOption (nameof (TemplateSettings.Log)) is TextWriter output) { + settings.Log = output; + } + +#if !NET35 + if (host.GetHostOption (nameof (TemplateSettings.CancellationToken)) is CancellationToken cancellationToken) { + settings.CancellationToken = cancellationToken; + } + else { + settings.CancellationToken = CancellationToken.None; + } + + if ((host.GetHostOption (nameof (TemplateSettings.RuntimeKind)) ?? RuntimeKind.Default) is RuntimeKind runtimeKind) { + settings.RuntimeKind = runtimeKind; + } +#endif + + return settings; } @@ -497,6 +545,10 @@ public static string IndentSnippetText (CodeDomProvider provider, string text, s public static string IndentSnippetText (string text, string indent) { + if (text == null) { + throw new ArgumentNullException (nameof (text)); + } + var builder = new StringBuilder (text.Length); builder.Append (indent); int lastNewline = 0; @@ -528,21 +580,27 @@ public static string IndentSnippetText (string text, string indent) static void AddDirective (TemplateSettings settings, ITextTemplatingEngineHost host, string processorName, Directive directive) { - if (!settings.DirectiveProcessors.TryGetValue (processorName, out IDirectiveProcessor processor)) { - switch (processorName) { - case "ParameterDirectiveProcessor": - processor = new ParameterDirectiveProcessor (); - break; - default: - Type processorType = host.ResolveDirectiveProcessor (processorName); - processor = (IDirectiveProcessor)Activator.CreateInstance (processorType); - break; - } - if (!processor.IsDirectiveSupported (directive.Name)) - throw new InvalidOperationException ("Directive processor '" + processorName + "' does not support directive '" + directive.Name + "'"); + if (settings.DirectiveProcessors.ContainsKey(processorName)) { + return; + } - settings.DirectiveProcessors [processorName] = processor; + IDirectiveProcessor processor; + + switch (processorName) { + case "ParameterDirectiveProcessor": + processor = new ParameterDirectiveProcessor (); + break; + default: + Type processorType = host.ResolveDirectiveProcessor (processorName); + processor = (IDirectiveProcessor)Activator.CreateInstance (processorType); + break; } + + if (!processor.IsDirectiveSupported (directive.Name)) + throw new InvalidOperationException ("Directive processor '" + processorName + "' does not support directive '" + directive.Name + "'"); + + settings.DirectiveProcessors[processorName] = processor; + settings.CustomDirectives.Add (new CustomDirective (processorName, directive)); } @@ -592,6 +650,18 @@ static void ProcessDirectives (string content, ParsedTemplate pt, TemplateSettin public static CodeCompileUnit GenerateCompileUnit (ITextTemplatingEngineHost host, string content, ParsedTemplate pt, TemplateSettings settings) { + if (host == null) { + throw new ArgumentNullException (nameof (host)); + } + + if (pt == null) { + throw new ArgumentNullException (nameof (pt)); + } + + if (settings == null) { + throw new ArgumentNullException (nameof (settings)); + } + ProcessDirectives (content, pt, settings); string baseDirectory = Path.GetDirectoryName (host.TemplateFile); @@ -615,6 +685,8 @@ public static CodeCompileUnit GenerateCompileUnit (ITextTemplatingEngineHost hos type.BaseTypes.Add (new CodeTypeReference (settings.Inherits)); } else if (!settings.IncludePreprocessingHelpers) { type.BaseTypes.Add (TypeRef ()); + // issue #87 because CompilerErrorCollection is referenced by the TextTransformation base class + TextTransformation.AddRequiredReferences (host.StandardAssemblyReferences); } else { type.BaseTypes.Add (new CodeTypeReference (settings.Name + "Base")); } @@ -623,7 +695,7 @@ public static CodeCompileUnit GenerateCompileUnit (ITextTemplatingEngineHost hos //prep the transform method var transformMeth = new CodeMemberMethod { Name = "TransformText", - ReturnType = new CodeTypeReference (typeof (String)), + ReturnType = new CodeTypeReference (typeof (string)), Attributes = MemberAttributes.Public, }; if (!settings.IncludePreprocessingHelpers) @@ -652,7 +724,7 @@ public static CodeCompileUnit GenerateCompileUnit (ITextTemplatingEngineHost hos CodeLinePragma location = null; if (!settings.NoLinePragmas) { var f = seg.StartLocation.FileName ?? host.TemplateFile; - if (settings.RelativeLinePragmas) + if (settings.UseRelativeLinePragmas) f = FileUtil.AbsoluteToRelativePath (baseDirectory, f).Replace ('\\', '/'); location = new CodeLinePragma (f, seg.StartLocation.Line); } @@ -838,6 +910,10 @@ static void GenerateInitializationMethod (CodeTypeDeclaration type, TemplateSett static void GenerateProcessingHelpers (CodeTypeDeclaration type, TemplateSettings settings) { + if (settings == null) { + throw new ArgumentNullException (nameof (settings)); + } + var thisRef = new CodeThisReferenceExpression (); var sbTypeRef = TypeRef (); @@ -1231,9 +1307,18 @@ static CodeMemberField PrivateField (CodeTypeReference typeRef, string name) /// public static void GenerateCodeFromMembers (CodeDomProvider provider, CodeGeneratorOptions options, StringWriter sw, IEnumerable members) { + if (provider == null) { + throw new ArgumentNullException (nameof (provider)); + } + + if (members == null) { + throw new ArgumentNullException (nameof (members)); + } + if (!useMonoHack) { - foreach (CodeTypeMember member in members) + foreach (CodeTypeMember member in members) { provider.GenerateCodeFromMember (member, sw, options); + } return; } diff --git a/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/ProcessTemplateEventArgs.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/ProcessTemplateEventArgs.cs new file mode 100644 index 0000000..0406257 --- /dev/null +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/ProcessTemplateEventArgs.cs @@ -0,0 +1,10 @@ +using System; + +namespace Mono.VisualStudio.TextTemplating.VSHost +{ + public class ProcessTemplateEventArgs : EventArgs + { + public string TemplateOutput { get; set; } + public bool Succeeded { get; set; } + } +} diff --git a/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/TextTemplatingCallback.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/TextTemplatingCallback.cs new file mode 100644 index 0000000..2a69a91 --- /dev/null +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/TextTemplatingCallback.cs @@ -0,0 +1,64 @@ +using System; +using System.Text; + +namespace Mono.VisualStudio.TextTemplating.VSHost +{ + public class TextTemplatingCallback + : ITextTemplatingCallback + { + bool isFromOutputDirective; + + public bool Errors { get; set; } + + public string Extension { get; private set; } = null; + + public Encoding OutputEncoding { get; private set; } = Encoding.UTF8; + + public void Initialize() + { + Errors = false; + Extension = null; + OutputEncoding = Encoding.UTF8; + isFromOutputDirective = false; + } + + public void ErrorCallback (bool warning, string message, int line, int column) + { + Errors = true; + } + + public void SetFileExtension (string extension) + { + Extension = extension ?? throw new ArgumentNullException (nameof (extension)); + } + + public void SetOutputEncoding (Encoding encoding, bool fromOutputDirective) + { + if (!isFromOutputDirective) { + if (fromOutputDirective) { + isFromOutputDirective = true; + OutputEncoding = encoding ?? throw new ArgumentNullException (nameof (encoding)); + } else { + if ((OutputEncoding != null) && !ReferenceEquals (encoding, OutputEncoding)) { + OutputEncoding = Encoding.UTF8; + } + OutputEncoding = encoding; + } + } + } + + public ITextTemplatingCallback DeepCopy () + { + TextTemplatingCallback callback = (TextTemplatingCallback)base.MemberwiseClone (); + + if (Extension != null) { + callback.Extension = (string)Extension.Clone (); + } + if (OutputEncoding != null) { + callback.OutputEncoding = (Encoding)OutputEncoding.Clone (); + } + + return callback; + } + } +} diff --git a/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/TransformationRunFactory.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/TransformationRunFactory.cs new file mode 100644 index 0000000..f8d8106 --- /dev/null +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/TransformationRunFactory.cs @@ -0,0 +1,32 @@ +using System; +using Mono.TextTemplating; + +namespace Mono.VisualStudio.TextTemplating.VSHost +{ + public abstract class TransformationRunFactory + : MarshalByRefObject + , IProcessTransformationRunFactory + { + public const string TransformationRunFactoryPrefix = "TransformationRunFactoryService"; + public const string TransformationRunFactorySuffix = nameof (TransformationRunFactory); + /// + /// Create the transformation runner + /// + /// + /// + /// + /// + /// + /// abstracted, just because I am uncertain on how this would run on multiple platforms. Also visual studio classes may be required to pull of correctly. + /// + public abstract IProcessTransformationRun CreateTransformationRun (Type runnerType, ParsedTemplate pt, ResolveEventHandler resolver); + + public abstract string RunTransformation (IProcessTransformationRun transformationRun); + + public override object InitializeLifetimeService () + { + return null; + } + + } +} diff --git a/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/TransformationRunner.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/TransformationRunner.cs new file mode 100644 index 0000000..b27fa82 --- /dev/null +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating.VSHost/TransformationRunner.cs @@ -0,0 +1,244 @@ +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using Mono.TextTemplating; + +namespace Mono.VisualStudio.TextTemplating.VSHost +{ + public class TransformationRunner + : MarshalByRefObject + , IProcessTransformationRun + { + CompiledTemplate compiledTemplate; + TemplateSettings settings; + ITextTemplatingEngineHost host; + + public CompilerErrorCollection Errors { get; private set; } + + public virtual string PerformTransformation () + { + string errorOutput = VsTemplatingErrorResources.ErrorOutput; + + if (compiledTemplate == null) { + LogError (VsTemplatingErrorResources.ErrorInitializingTransformationObject, false); + + return errorOutput; + } + + object transform = null; + + try { + transform = CreateTextTransformation (settings, host, compiledTemplate.Assembly); + + return compiledTemplate.Process (transform); + } + catch (Exception ex) { + if (TemplatingEngine.IsCriticalException(ex)) { + throw; + } + LogError (ex.ToString (), false); + } + finally { + if (transform is IDisposable disposable) { + disposable.Dispose (); + } + compiledTemplate?.Dispose (); + compiledTemplate = null; + host = null; + } + + return errorOutput; + } + + static PropertyInfo GetDerivedProperty (Type transformType, string propertyName) + { + while(transformType != typeof(object) && transformType != null) { + PropertyInfo property = transformType.GetProperty (propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + if (property != null) { + return property; + } + transformType = transformType.BaseType; + } + return null; + } + + protected virtual object CreateTextTransformation(TemplateSettings settings, ITextTemplatingEngineHost host, Assembly assembly) { + object success = null; + + if (settings == null) { + throw new ArgumentNullException (nameof (settings)); + } + + if (host == null) { + throw new ArgumentNullException (nameof (host)); + } + + if (assembly == null) { + throw new ArgumentNullException (nameof (assembly)); + } + + try { + Type type; + + var result = assembly.CreateInstance (settings.GetFullName ()); + + if (result != null) { + type = result.GetType (); + + if (settings.HostPropertyOnBase || settings.HostSpecific) { + try { + PropertyInfo property = type.GetProperty ("Host"); + + if (property != null) { + property?.SetValue (result, host, null); + } + else { + LogError (string.Format(CultureInfo.CurrentCulture, VsTemplatingErrorResources.HostPropertyNotFound, settings.HostType.Name), false); + } + } + catch(Exception hostException) { + if (TemplatingEngine.IsCriticalException(hostException)) { + throw; + } + LogError (string.Format (CultureInfo.CurrentCulture, VsTemplatingErrorResources.ExceptionSettingHost, settings.GetFullName ()), false); + } + } + + try { + if (host is ITextTemplatingSessionHost sessionHost && + sessionHost.Session != null) { + PropertyInfo property = GetDerivedProperty (type, nameof (TextTransformation.Session)); + + property?.SetValue (result, sessionHost.Session, null); + } + else { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, VsTemplatingErrorResources.SessionHostSessionNotInitialized, settings.GetFullName())); + } + } + catch (Exception sessionException) { + if (TemplatingEngine.IsCriticalException (sessionException)) { + throw; + } + LogError (string.Format (CultureInfo.CurrentCulture, VsTemplatingErrorResources.ExceptionSettingSession, sessionException), false); + } + success = result; + + } else { + LogError (VsTemplatingErrorResources.ExceptionInstantiatingTransformationObject, false); + } + } + catch(Exception instantiatingException) { + if (TemplatingEngine.IsCriticalException (instantiatingException)) { + throw; + } + LogError (VsTemplatingErrorResources.ExceptionInstantiatingTransformationObject + string.Format(CultureInfo.CurrentCulture, VsTemplatingErrorResources.Exception, instantiatingException), false); + success = null; + } + return success; + } + + public virtual bool PrepareTransformation (ParsedTemplate pt, string content, ITextTemplatingEngineHost host, TemplateSettings settings) + { + this.host = host ?? throw new ArgumentNullException (nameof (host)); + this.settings = settings ?? throw new ArgumentNullException (nameof (settings)); + + try { + this.settings.Assemblies.Add (base.GetType ().Assembly.Location); + this.settings.Assemblies.Add (typeof (ITextTemplatingEngineHost).Assembly.Location); + this.compiledTemplate = LocateAssembly (pt, content); + } + catch(Exception ex) { + if (TemplatingEngine.IsCriticalException (ex)) { + throw; + } + LogError (string.Format (CultureInfo.CurrentCulture, VsTemplatingErrorResources.Exception, ex), false); + } + return this.compiledTemplate != null; + } + + CompiledTemplate LocateAssembly (ParsedTemplate pt, string content) + { + CompiledTemplate compiledTemplate = null; + + if (settings.CachedTemplates) { + compiledTemplate = CompiledTemplateCache.Find (settings.GetFullName ()); + } + if (compiledTemplate == null) { + compiledTemplate = Compile (pt, content); + if (settings.CachedTemplates && compiledTemplate != null) { + CompiledTemplateCache.Insert (settings.GetFullName (), compiledTemplate); + } + } + return compiledTemplate; + } + + CompiledTemplate Compile (ParsedTemplate pt, string content) + { + CompiledTemplate compiledTemplate = null; + + if (host is ITextTemplatingComponents Component && + Component.Engine is IProcessTextTemplatingEngine engine) { + compiledTemplate = engine.CompileTemplate (pt, content, host, settings); + + if (settings.CachedTemplates) { + // we will resolve loading of assemblies through the run factory for cached templates + compiledTemplate?.Dispose (); + } + } + + return compiledTemplate; + } + + //public virtual void PreLoadAssemblies (IEnumerable assemblies) + //{ + // try { + // //TODO:: investigate preloading assemblies with the AssemblyLoadContext + // }catch(Exception ex) { + // if (TemplatingEngine.IsCriticalException (ex)) { + // throw; + // } + // } + //} + + protected Assembly AttemptAssemblyLoad(AssemblyName assembly) + { + try { + return Assembly.LoadFrom (assembly.CodeBase); + } + catch(Exception ex) { + LogError (string.Format (CultureInfo.CurrentCulture, VsTemplatingErrorResources.AssemblyLoadError, assembly.Name, ex), false); + return null; + } + } + + public void ClearErrors() + { + Errors.Clear (); + } + + protected void LogError(string message, bool isWarning) + { + CompilerError error = new CompilerError () { + ErrorText = message, + IsWarning = isWarning + }; + + Errors.Add (error); + } + + protected void LogError(string message, bool isWarning, string filename, int line, int column) + { + CompilerError error = new CompilerError () { + ErrorText = message, + IsWarning = isWarning, + FileName = filename, + Line = line, + Column = column + }; + + Errors.Add (error); + } + } +} diff --git a/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/CompiledTemplateCache.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/CompiledTemplateCache.cs new file mode 100644 index 0000000..ad97284 --- /dev/null +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/CompiledTemplateCache.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Mono.TextTemplating; + +namespace Mono.VisualStudio.TextTemplating +{ + public static class CompiledTemplateCache + { + public static Dictionary compiledTemplates = new Dictionary (0x23); + public static DateTime lastUse; + + public static CompiledTemplate Find(string fullClassName) + { + CompiledTemplate compiledTemplate = null; + Dictionary compiledTemplates = CompiledTemplateCache.compiledTemplates; + lock (compiledTemplates) { + lastUse = DateTime.Now; + if (CompiledTemplateCache.compiledTemplates.TryGetValue(fullClassName, out CompiledTemplateRecord record)) { + compiledTemplate = record.CompiledTemplate; + record.LastUse = lastUse; + } + } + return compiledTemplate; + } + + public static void Insert (string classFullName, CompiledTemplate compiledTemplate) + { + Dictionary assemblies = CompiledTemplateCache.compiledTemplates; + lock (assemblies) { + CompiledTemplateCache.compiledTemplates[classFullName] = new CompiledTemplateRecord (compiledTemplate); + lastUse = DateTime.Now; + } + } + } +} diff --git a/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/CompiledTemplateRecord.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/CompiledTemplateRecord.cs new file mode 100644 index 0000000..e117012 --- /dev/null +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/CompiledTemplateRecord.cs @@ -0,0 +1,20 @@ +using System; +using System.Reflection; +using Mono.TextTemplating; + +namespace Mono.VisualStudio.TextTemplating +{ + public class CompiledTemplateRecord + { + readonly CompiledTemplate compiledTemplate; + + public CompiledTemplate CompiledTemplate => compiledTemplate; + public DateTime LastUse { get; set; } + + public CompiledTemplateRecord(CompiledTemplate compiledTemplate) + { + this.compiledTemplate = compiledTemplate; + LastUse = DateTime.Now; + } + } +} diff --git a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/DirectiveProcessor.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/DirectiveProcessor.cs similarity index 84% rename from Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/DirectiveProcessor.cs rename to Mono.TextTemplating/Mono.VisualStudio.TextTemplating/DirectiveProcessor.cs index aae9e09..93e5950 100644 --- a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/DirectiveProcessor.cs +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/DirectiveProcessor.cs @@ -28,27 +28,33 @@ using System.Collections.Generic; using System.CodeDom.Compiler; using System.CodeDom; +using Mono.TextTemplating; -namespace Microsoft.VisualStudio.TextTemplating +namespace Mono.VisualStudio.TextTemplating { public abstract class DirectiveProcessor : IDirectiveProcessor { + protected ITextTemplatingEngineHost Host { get; private set; } + protected TemplateSettings Settings { get; private set; } + CompilerErrorCollection errors; protected DirectiveProcessor () { } - public virtual void Initialize (ITextTemplatingEngineHost host) + public virtual void Initialize (ITextTemplatingEngineHost host, TemplateSettings settings) { - if (host == null) - throw new ArgumentNullException ("host"); + this.Host = host ?? throw new ArgumentNullException (nameof (host)); + this.Settings = settings ?? throw new ArgumentNullException (nameof (settings)); } public virtual void StartProcessingRun (CodeDomProvider languageProvider, string templateContents, CompilerErrorCollection errors) { - if (languageProvider == null) - throw new ArgumentNullException ("languageProvider"); + if (languageProvider == null) { + throw new ArgumentNullException (nameof (languageProvider)); + } + this.errors = errors; } diff --git a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/DirectiveProcessorException.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/DirectiveProcessorException.cs similarity index 97% rename from Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/DirectiveProcessorException.cs rename to Mono.TextTemplating/Mono.VisualStudio.TextTemplating/DirectiveProcessorException.cs index ffc08cd..90cdd84 100644 --- a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/DirectiveProcessorException.cs +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/DirectiveProcessorException.cs @@ -27,7 +27,7 @@ using System; using System.Runtime.Serialization; -namespace Microsoft.VisualStudio.TextTemplating +namespace Mono.VisualStudio.TextTemplating { [Serializable] diff --git a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/EncodingHelper.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/EncodingHelper.cs similarity index 63% rename from Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/EncodingHelper.cs rename to Mono.TextTemplating/Mono.VisualStudio.TextTemplating/EncodingHelper.cs index 89a6a46..8d771e7 100644 --- a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/EncodingHelper.cs +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/EncodingHelper.cs @@ -26,15 +26,27 @@ using System; using System.Text; +using System.IO; -namespace Microsoft.VisualStudio.TextTemplating +namespace Mono.VisualStudio.TextTemplating { - [Obsolete("Not implemented")] public static class EncodingHelper { public static Encoding GetEncoding (string filePath) { - throw new NotImplementedException (); + var bom = new byte[4]; + using (FileStream stream = File.OpenRead (filePath)) { + stream.Read (bom, 0, bom.Length); + } + + if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) { return Encoding.UTF7; } + if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) { return Encoding.UTF8; } + if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0 && bom[3] == 0) { return Encoding.UTF32; } //UTF-32LE + if (bom[0] == 0xff && bom[1] == 0xfe) { return Encoding.Unicode; } //UTF-16LE + if (bom[0] == 0xfe && bom[1] == 0xff) { return Encoding.BigEndianUnicode; } //UTF-16BE + if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) { return new UTF32Encoding (true, true); } //UTF-32BE + + return null; } } } diff --git a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/Interfaces.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/Interfaces.cs similarity index 57% rename from Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/Interfaces.cs rename to Mono.TextTemplating/Mono.VisualStudio.TextTemplating/Interfaces.cs index d2eb60e..1104b6a 100644 --- a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/Interfaces.cs +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/Interfaces.cs @@ -29,18 +29,75 @@ using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; +#if NET45 || NETSTANDARD +using System.Threading.Tasks; +#endif using System.Runtime.Serialization; using System.Text; +using Mono.TextTemplating; -namespace Microsoft.VisualStudio.TextTemplating +namespace Mono.VisualStudio.TextTemplating { + using Mono.VisualStudio.TextTemplating.VSHost; + public interface IRecognizeHostSpecific { void SetProcessingRunIsHostSpecific (bool hostSpecific); bool RequiresProcessingRunIsHostSpecific { get; } } - [Obsolete("Use Mono.TextTemplating.TemplatingEngine directly")] + public interface ITextTemplatingService + : ITextTemplatingEngineHost + , ITextTemplatingSessionHost + , ITextTemplatingComponents + , IProcessTextTemplating + , ITextTemplating + { + new IProcessTextTemplatingEngine Engine { get; } + } + + public interface IProcessTextTemplating + : ITextTemplating + { + event EventHandler TransformProcessCompleted; +#if NETSTANDARD || NET45 + Task ProcessTemplateAsync (string inputFilename, string content, ITextTemplatingCallback callback, object hierarchy, bool debugging = false); +#elif NET35 + void ProcessTemplate (string inputFilename, string content, ITextTemplatingCallback callback, object hierarchy, bool debugging = false); +#endif + } + + public interface ITextTemplating + { + void BeginErrorSession (); + bool EndErrorSession (); + string PreprocessTemplate (string inputFile, string content, ITextTemplatingCallback callback, string className, string classNamespace, out string[] references); + string ProcessTemplate (string inputFile, string content, ITextTemplatingCallback callback = null, object hierarchy = null); + } + + public interface IProcessTransformationRun + { + string PerformTransformation (); + + CompilerErrorCollection Errors { get; } + } + + public interface IProcessTransformationRunFactory + { + IProcessTransformationRun CreateTransformationRun (Type runnerType, ParsedTemplate pt, ResolveEventHandler resolver); + + string RunTransformation (IProcessTransformationRun transformationRun); + } + + public interface IProcessTextTemplatingEngine + : ITextTemplatingEngine + { + IProcessTransformationRun PrepareTransformationRun (string content, ITextTemplatingEngineHost host, IProcessTransformationRunFactory runFactory, bool debugging = false); + + CompiledTemplate CompileTemplate (string content, ITextTemplatingEngineHost host); + CompiledTemplate CompileTemplate (ParsedTemplate pt, string content, ITextTemplatingEngineHost host, TemplateSettings settings = null); + } + public interface ITextTemplatingEngine { string ProcessTemplate (string content, ITextTemplatingEngineHost host); @@ -48,6 +105,31 @@ string PreprocessTemplate (string content, ITextTemplatingEngineHost host, strin string classNamespace, out string language, out string [] references); } + public interface ITextTemplatingComponents + { + ITextTemplatingEngineHost Host { get; } + + ITextTemplatingEngine Engine { get; } + + string TemplateFile { get; set; } + + ITextTemplatingCallback Callback { get; set; } + + object Hierarchy { get; set; } + } + + public interface ITextTemplatingCallback + { + bool Errors { get; set; } + string Extension { get; } + void ErrorCallback (bool warning, string message, int line, int column); + void SetFileExtension (string extension); + void SetOutputEncoding (Encoding encoding, bool fromOutputDirective); + ITextTemplatingCallback DeepCopy (); + + Encoding OutputEncoding { get; } + } + public interface ITextTemplatingEngineHost { object GetHostOption (string optionName); @@ -77,7 +159,8 @@ public interface ITextTemplatingSession : Guid Id { get; } } - public interface ITextTemplatingSessionHost + public interface ITextTemplatingSessionHost + : ISerializable { ITextTemplatingSession CreateSession (); ITextTemplatingSession Session { get; set; } @@ -95,7 +178,7 @@ public interface IDirectiveProcessor string GetPreInitializationCodeForProcessingRun (); string[] GetReferencesForProcessingRun (); CodeAttributeDeclarationCollection GetTemplateClassCustomAttributes (); //TODO - void Initialize (ITextTemplatingEngineHost host); + void Initialize (ITextTemplatingEngineHost host, TemplateSettings settings); bool IsDirectiveSupported (string directiveName); void ProcessDirective (string directiveName, IDictionary arguments); void SetProcessingRunIsHostSpecific (bool hostSpecific); diff --git a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs similarity index 89% rename from Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs rename to Mono.TextTemplating/Mono.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs index 9810385..ee1a3b2 100644 --- a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs @@ -29,8 +29,11 @@ using System.Collections.Generic; using System.IO; using Mono.TextTemplating; +#if NET45 +using Mono.TextTemplating.CodeCompilation; +#endif -namespace Microsoft.VisualStudio.TextTemplating +namespace Mono.VisualStudio.TextTemplating { public sealed class ParameterDirectiveProcessor : DirectiveProcessor, IRecognizeHostSpecific { @@ -143,23 +146,29 @@ public override void ProcessDirective (string directiveName, IDictionary providesDictionary); @@ -120,9 +120,9 @@ void AssertNotProcessing () if (isInProcessingRun) throw new InvalidOperationException (); } - + //FIXME: handle escaping - IEnumerable> ParseArgs (string args) + static IEnumerable> ParseArgs (string args) { var pairs = args.Split (';'); foreach (var p in pairs) { @@ -135,11 +135,12 @@ IEnumerable> ParseArgs (string args) public override void ProcessDirective (string directiveName, IDictionary arguments) { - if (directiveName == null) - throw new ArgumentNullException ("directiveName"); - if (arguments == null) - throw new ArgumentNullException ("arguments"); - + if (directiveName == null) { + throw new ArgumentNullException (nameof (directiveName)); + } + if (arguments == null) { + throw new ArgumentNullException (nameof (arguments)); + } var providesDictionary = new Dictionary (); var requiresDictionary = new Dictionary (); @@ -163,7 +164,7 @@ public override void ProcessDirective (string directiveName, IDictionary, ITextTemplatingSession, ISerializable @@ -40,7 +40,6 @@ public TextTemplatingSession () : this (Guid.NewGuid ()) TextTemplatingSession (SerializationInfo info, StreamingContext context) : base (info, context) { - Id = (Guid)info.GetValue ("Id", typeof (Guid)); } public TextTemplatingSession (Guid id) @@ -49,9 +48,10 @@ public TextTemplatingSession (Guid id) } public Guid Id { - get; private set; + get => (Guid)this[nameof (Id)]; + set => this[nameof (Id)] = value; } - + public override int GetHashCode () { return Id.GetHashCode (); @@ -76,7 +76,6 @@ public bool Equals (ITextTemplatingSession other) void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) { base.GetObjectData (info, context); - info.AddValue ("Id", Id); } } } diff --git a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/TextTransformation.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/TextTransformation.cs similarity index 84% rename from Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/TextTransformation.cs rename to Mono.TextTemplating/Mono.VisualStudio.TextTemplating/TextTransformation.cs index 119a98a..322c9c2 100644 --- a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/TextTransformation.cs +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/TextTransformation.cs @@ -27,9 +27,11 @@ using System; using System.CodeDom.Compiler; using System.Collections.Generic; +using System.Globalization; +using System.Linq; using System.Text; -namespace Microsoft.VisualStudio.TextTemplating +namespace Mono.VisualStudio.TextTemplating { public abstract class TextTransformation : IDisposable { @@ -48,11 +50,13 @@ public virtual void Initialize () } public abstract string TransformText (); - + +#pragma warning disable CA2227 // Collection properties should be read only public virtual IDictionary Session { get; set; } - +#pragma warning restore CA2227 // Collection properties should be read only + #region Errors - + public void Error (string message) { Errors.Add (new CompilerError ("", 0, 0, "", message)); @@ -85,8 +89,9 @@ Stack Indents { public string PopIndent () { - if (Indents.Count == 0) + if (Indents.Count == 0) { return ""; + } int lastPos = currentIndent.Length - Indents.Pop (); string last = currentIndent.Substring (lastPos); currentIndent = currentIndent.Substring (0, lastPos); @@ -95,8 +100,9 @@ public string PopIndent () public void PushIndent (string indent) { - if (indent == null) - throw new ArgumentNullException ("indent"); + if (indent == null) { + throw new ArgumentNullException (nameof (indent)); + } Indents.Push (indent.Length); currentIndent += indent; } @@ -176,7 +182,7 @@ public void Write (string textToAppend) public void Write (string format, params object[] args) { - Write (string.Format (format, args)); + Write (string.Format (CultureInfo.InvariantCulture, format, args)); } public void WriteLine (string textToAppend) @@ -188,7 +194,7 @@ public void WriteLine (string textToAppend) public void WriteLine (string format, params object[] args) { - WriteLine (string.Format (format, args)); + WriteLine (string.Format (CultureInfo.InvariantCulture, format, args)); } #endregion @@ -209,7 +215,20 @@ protected virtual void Dispose (bool disposing) { Dispose (false); } - + + internal static void AddRequiredReferences (IList standardAssemblies) + { + if (standardAssemblies == null) { + throw new ArgumentNullException (nameof (standardAssemblies)); + } + + string codeDom = typeof (CompilerErrorCollection).Assembly.Location; + + if (!standardAssemblies.Any (x => x.Equals (codeDom, StringComparison.CurrentCultureIgnoreCase))) { + standardAssemblies.Add (codeDom); + } + } + #endregion } diff --git a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/ToStringHelper.cs b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/ToStringHelper.cs similarity index 98% rename from Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/ToStringHelper.cs rename to Mono.TextTemplating/Mono.VisualStudio.TextTemplating/ToStringHelper.cs index ab9a0bf..2e4e832 100644 --- a/Mono.TextTemplating/Microsoft.VisualStudio.TextTemplating/ToStringHelper.cs +++ b/Mono.TextTemplating/Mono.VisualStudio.TextTemplating/ToStringHelper.cs @@ -27,7 +27,7 @@ using System; using System.Reflection; -namespace Microsoft.VisualStudio.TextTemplating +namespace Mono.VisualStudio.TextTemplating { public static class ToStringHelper { diff --git a/Mono.TextTemplating/VsTemplatingErrorResources.Designer.cs b/Mono.TextTemplating/VsTemplatingErrorResources.Designer.cs new file mode 100644 index 0000000..a9a5c0b --- /dev/null +++ b/Mono.TextTemplating/VsTemplatingErrorResources.Designer.cs @@ -0,0 +1,162 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Mono.TextTemplating { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class VsTemplatingErrorResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal VsTemplatingErrorResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Mono.TextTemplating.VsTemplatingErrorResources", typeof(VsTemplatingErrorResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Assembly "{0}" Load Error: {1}. + /// + internal static string AssemblyLoadError { + get { + return ResourceManager.GetString("AssemblyLoadError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error initializing the transformation object.. + /// + internal static string ErrorInitializingTransformationObject { + get { + return ResourceManager.GetString("ErrorInitializingTransformationObject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to // Error Generating Output. + /// + internal static string ErrorOutput { + get { + return ResourceManager.GetString("ErrorOutput", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exception: {0}. + /// + internal static string Exception { + get { + return ResourceManager.GetString("Exception", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error Instanciating Object: . + /// + internal static string ExceptionInstantiatingTransformationObject { + get { + return ResourceManager.GetString("ExceptionInstantiatingTransformationObject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exception Processing Template: {0}. + /// + internal static string ExceptionProcessingTemplate { + get { + return ResourceManager.GetString("ExceptionProcessingTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exception setting host property on: {0}. + /// + internal static string ExceptionSettingHost { + get { + return ResourceManager.GetString("ExceptionSettingHost", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exception setting session on: {0}. + /// + internal static string ExceptionSettingSession { + get { + return ResourceManager.GetString("ExceptionSettingSession", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Host Property Not Found On: {0}. + /// + internal static string HostPropertyNotFound { + get { + return ResourceManager.GetString("HostPropertyNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Something bad happened while prepping transformation.. + /// + internal static string SessionHostMarshalError { + get { + return ResourceManager.GetString("SessionHostMarshalError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Session host session is not initialized for {0}.. + /// + internal static string SessionHostSessionNotInitialized { + get { + return ResourceManager.GetString("SessionHostSessionNotInitialized", resourceCulture); + } + } + } +} diff --git a/Mono.TextTemplating/VsTemplatingErrorResources.resx b/Mono.TextTemplating/VsTemplatingErrorResources.resx new file mode 100644 index 0000000..65f4ea5 --- /dev/null +++ b/Mono.TextTemplating/VsTemplatingErrorResources.resx @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Assembly "{0}" Load Error: {1} + + + Error initializing the transformation object. + + + // Error Generating Output + + + Exception: {0} + + + Error Instanciating Object: + + + Exception Processing Template: {0} + + + Exception setting host property on: {0} + + + Exception setting session on: {0} + + + Host Property Not Found On: {0} + + + Something bad happened while prepping transformation. + + + Session host session is not initialized for {0}. + + \ No newline at end of file diff --git a/TextTransform/TextTransform.csproj b/TextTransform/TextTransform.csproj index 71232d6..599c599 100644 --- a/TextTransform/TextTransform.csproj +++ b/TextTransform/TextTransform.csproj @@ -16,6 +16,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -24,4 +28,8 @@ + + + + \ No newline at end of file diff --git a/dotnet-t4-project-tool/dotnet-t4-project-tool.csproj b/dotnet-t4-project-tool/dotnet-t4-project-tool.csproj index 10e2cff..2c7c9ee 100644 --- a/dotnet-t4-project-tool/dotnet-t4-project-tool.csproj +++ b/dotnet-t4-project-tool/dotnet-t4-project-tool.csproj @@ -22,6 +22,10 @@ This package can be installed into a project using `DotNetCliToolReference`. + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -31,4 +35,5 @@ This package can be installed into a project using `DotNetCliToolReference`. + diff --git a/dotnet-t4/TextTransform.cs b/dotnet-t4/TextTransform.cs index 55e2ca7..89a9dab 100644 --- a/dotnet-t4/TextTransform.cs +++ b/dotnet-t4/TextTransform.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) 2009 Novell, Inc. (http://www.novell.com) // Copyright (c) Microsoft Corp. (https://www.microsoft.com) // @@ -27,7 +27,7 @@ using System.Linq; using System.Reflection; using System.Text; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; using Mono.Options; namespace Mono.TextTemplating diff --git a/dotnet-t4/ToolTemplateGenerator.cs b/dotnet-t4/ToolTemplateGenerator.cs index b077e2d..548466f 100644 --- a/dotnet-t4/ToolTemplateGenerator.cs +++ b/dotnet-t4/ToolTemplateGenerator.cs @@ -21,7 +21,7 @@ using System.CodeDom.Compiler; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; namespace Mono.TextTemplating { @@ -32,7 +32,7 @@ public ToolTemplateGenerator () Refs.Add (typeof (CompilerErrorCollection).Assembly.Location); } - protected override ITextTemplatingSession CreateSession () => new ToolTemplateSession (this); + protected override ITextTemplatingSession CreateSession () => new TextTemplatingSession (this); public string PreprocessTemplate ( ParsedTemplate pt, diff --git a/dotnet-t4/ToolTemplateSession.cs b/dotnet-t4/ToolTemplateSession.cs index 8f00a03..592c51e 100644 --- a/dotnet-t4/ToolTemplateSession.cs +++ b/dotnet-t4/ToolTemplateSession.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) Microsoft Corp (https://www.microsoft.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -20,19 +20,19 @@ // THE SOFTWARE. using System; -using Microsoft.VisualStudio.TextTemplating; +using Mono.VisualStudio.TextTemplating; using System.Collections; using System.Collections.Generic; using System.Runtime.Serialization; namespace Mono.TextTemplating { - class ToolTemplateSession : ITextTemplatingSession + class TextTemplatingSession : ITextTemplatingSession { readonly Dictionary session = new Dictionary (); readonly ToolTemplateGenerator toolTemplateGenerator; - public ToolTemplateSession (ToolTemplateGenerator toolTemplateGenerator) + public TextTemplatingSession (ToolTemplateGenerator toolTemplateGenerator) { this.toolTemplateGenerator = toolTemplateGenerator; } @@ -46,7 +46,7 @@ public object this [string key] { public ICollection Keys => session.Keys; public ICollection Values => session.Values; public int Count => session.Count; - public bool IsReadOnly => false; + public bool IsReadOnly => false; public void Add (string key, object value) => session.Add (key, value); public void Add (KeyValuePair item) => session.Add (item.Key, item.Value); public void Clear () => session.Clear (); diff --git a/dotnet-t4/dotnet-t4.csproj b/dotnet-t4/dotnet-t4.csproj index 51d83ee..0425930 100644 --- a/dotnet-t4/dotnet-t4.csproj +++ b/dotnet-t4/dotnet-t4.csproj @@ -24,6 +24,10 @@ This package can be installed as a dotnet global or local tool. + + all + runtime; build; native; contentfiles; analyzers; buildtransitive +