T4 reports Compiling transformation: Invalid token 'this' in class, struct
Asked Answered
C

4

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?

Crescantia answered 24/4, 2014 at 23:5 Comment(2)
While below answer is correct, if you are using GitHub, the following article may also explain an alternative solution - teamdevelopmentforsitecore.com/Blog/…Quenelle
@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.Crescantia
C
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.

Crescantia answered 24/4, 2014 at 23:5 Comment(3)
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."Wheels
Thank you so much, I've been tearing my hair out for an hour trying to comprehend what was going onMover
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 redCasteel
K
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!

Kun answered 30/9, 2016 at 20:21 Comment(2)
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.Quenelle
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.Kun
S
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)

Sager answered 7/8, 2019 at 15:32 Comment(0)
C
1

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

Celom answered 1/10, 2022 at 1:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.