24

Attempting to run the T4 templates for Immutable Object Graph gives errors of

╔═══════╦═══╦══════════════════════════════════════════════════════════════════════════════════════════════════╦═════════════════════════════════════════════════════════╦═══╦════╦══════╗ ║ Error ║ 5 ║ Compiling transformation: Invalid token 'this' in class, struct, or interface member declaration ║ c:\dev\ImmutableObjectGraph-master\2013\Demo\Message.tt ║ 1 ║ 1 ║ Demo ║ ║ Error ║ 6 ║ Compiling transformation: Method must have a return type ║ c:\dev\ImmutableObjectGraph-master\2013\Demo\Message.tt ║ 1 ║ 6 ║ Demo ║ ║ Error ║ 7 ║ Compiling transformation: Type expected ║ c:\dev\ImmutableObjectGraph-master\2013\Demo\Message.tt ║ 1 ║ 12 ║ Demo ║ ╚═══════╩═══╩══════════════════════════════════════════════════════════════════════════════════════════════════╩═════════════════════════════════════════════════════════╩═══╩════╩══════╝ 

The line reported is always line 1, and the full set of t4 templates is many hundreds of lines. How do I troubleshoot and fix this issue?

2
  • While below answer is correct, if you are using GitHub, the following article may also explain an alternative solution - teamdevelopmentforsitecore.com/Blog/… Commented Jul 11, 2017 at 21:15
  • @woodyiii, you may find that adding the correct eol specifier to .gitattributes will more permanently solve your problem. Changing it manually can get old quick if you have team members on other platforms. Commented Jul 12, 2017 at 15:06

4 Answers 4

51

You cannot have literals in a T4 template after a scriptlet.

Change

<#@ template debug="true" language="C#" #> <#+ // scriptlet #> <-- empty line here 

To

<#@ template debug="true" language="C#" #> <#+ // scriptlet #> 

Debugging

You can see the C# being generated by the T4 engine by calling PreProcessTemplate with a custom templating host.

I modified the Custom Template Host sample for this purpose:

using Microsoft.VisualStudio.TextTemplating; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.IO; using System.Text; namespace CustomHost { class CustomCmdLineHost : ITextTemplatingEngineHost { public string TemplateFile { get; private set; } public string FileExtension { get; private set; } public Encoding FileEncoding { get; private set; } public CompilerErrorCollection Errors { get; private set; } public IList<string> StandardAssemblyReferences { get { return new[] { typeof(System.Uri).Assembly.Location }; } } public IList<string> StandardImports { get { return new string[] { "System" }; } } public CustomCmdLineHost(string file) { this.TemplateFile = file; this.FileEncoding = Encoding.UTF8; this.FileExtension = ".txt"; } public bool LoadIncludeText(string requestFileName, out string content, out string location) { content = location = String.Empty; if (File.Exists(requestFileName)) { content = File.ReadAllText(requestFileName); return true; } return false; } public object GetHostOption(string optionName) { object returnObject; switch (optionName) { case "CacheAssemblies": returnObject = true; break; default: returnObject = null; break; } return returnObject; } public string ResolveAssemblyReference(string assemblyReference) { return ResolvePath(assemblyReference); } public Type ResolveDirectiveProcessor(string processorName) { throw new Exception("Directive Processor not found"); } public string ResolvePath(string fileName) { if (File.Exists(fileName)) { return fileName; } string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName); if (File.Exists(candidate)) { return candidate; } return fileName; } public string ResolveParameterValue(string directiveId, string processorName, string parameterName) { return String.Empty; } public void SetFileExtension(string extension) { FileExtension = extension; } public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective) { FileEncoding = encoding; } public void LogErrors(CompilerErrorCollection errors) { Errors = errors; } public AppDomain ProvideTemplatingAppDomain(string content) { return AppDomain.CreateDomain("Generation App Domain"); } } class Program { static void Main(string[] args) { var templateFileName = args[0]; CustomCmdLineHost host = new CustomCmdLineHost(templateFileName); Engine engine = new Engine(); string language; string[] refs; var output = engine.PreprocessTemplate( // input file File.ReadAllText(templateFileName), host, "testClass", "testNamespace", out language, out refs); string outputFileName = Path.Combine( Path.GetDirectoryName(templateFileName), templateFileName + ".generator.cs"); File.WriteAllText(outputFileName, output, host.FileEncoding); foreach (CompilerError error in host.Errors) Console.WriteLine(error.ToString()); Console.ReadLine(); } } } 

Examining the transformer generated from the template showed lines like the following outside the TransformText() method. Seemingly, any literals in the source templates which came after a scriptlet (<#+ #>) would be placed in-fix into the generated generator class.

 #line 1 "C:\dev\ImmutableObjectGraph-master\2013\Demo\Fruit.tt" this.Write("\n"); 

Removing the newline characters at the end of each template file resolved the issue.

Sign up to request clarification or add additional context in comments.

3 Comments

I guess this info is most important for 99% of readers: "Removing the newline characters at the end of each template file resolved the issue."
Thank you so much, I've been tearing my hair out for an hour trying to comprehend what was going on
For me problem was somewhere else. 1. Take Szymon Seliga's sample code from above 2. Paste into Console app 3. Install nuget "Microsoft.VisualStudio.TextTemplating.15.0" 4. hardcode path to your *.tt file (should be on lien 111) 5. this will generate *.tt.generator.cs 6. open this file in Visual Studio 7. look for this.Write("\n"); that are outside methods (visual studio syntax intelisense wil lactually underline it with red
20

For me, the trailing newline at the end of the file was not a problem, but having Unix line endings (\n) instead of Windows line endings (\r\n) broke the T4 engine.

Very frustrating to debug when you have two files that look identical but one of them won't compile!

2 Comments

In my case, this answer solved my problem & this article explains how to fix the issue in detail - teamdevelopmentforsitecore.com/Blog/… Whenever we converted our version control to GitHub, there have been random CR LF problems that do still come up, like this one.
Also worth noting that the Git installer for Windows sets it to transform newlines on checkout/commit by default (core.autocrlf=true). So when building on a server the line endings may not be the same as locally.
1

Yes, this was something to do with line ending for me too, possible due to source control.

To resolve, I copied and pasted the template code into Notepad++ and saved as a normal txt file then copy and pasted back.

(notepad didn't work as the line endings were wrong)

Comments

1

The line endings were the problem for me, too. (VS 2019) Edit > Advanced > Set End of Line Sequence > CRLF, then then transform worked.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.