Отчеты T4 Компиляция преобразования: недопустимый токен 'this' в классе, struct

Попытка запуска шаблонов T4 для неизменяемого графического объекта дает ошибки

╔═══════╦═══╦══════════════════════════════════════════════════════════════════════════════════════════════════╦═════════════════════════════════════════════════════════╦═══╦════╦══════╗
║ 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 ║
╚═══════╩═══╩══════════════════════════════════════════════════════════════════════════════════════════════════╩═════════════════════════════════════════════════════════╩═══╩════╩══════╝

Строка, о которой идет речь, всегда является строкой 1, а полный набор шаблонов t4 - это много сотен строк. Как устранить и устранить эту проблему?

Ответ 1

У вас не может быть литералов в шаблоне T4 после скрипта.

Изменить

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

Для

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

Отладка

Вы можете видеть, что С# генерируется движком T4, вызывая PreProcessTemplate с помощью настраиваемого хоста для шаблонов.

Я изменил Пользовательский шаблон Host для этой цели:

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();
        }
    }
}

Изучение трансформатора, сгенерированного из шаблона, показало строки, подобные следующим за пределами метода TransformText(). По-видимому, любые литералы в исходных шаблонах, которые появились после scriptlet (<#+ #>), были помещены в исправление в сгенерированный класс генератора.

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

Удаление символов новой строки в конце каждого файла шаблона устраняет проблему.

Ответ 2

Для меня конечная новая строка в конце файла не была проблемой, но с окончанием строки Unix (\n) вместо окончаний строки Windows (\ r\n) был поврежден движок T4.

Очень неприятно отлаживать, когда у вас есть два файла, которые выглядят одинаково, но один из них не будет компилироваться!

Ответ 3

Да, это было как-то связано с окончанием строки для меня, возможно, из-за контроля над источниками.

Для разрешения я скопировал и вставил код шаблона в Notepad++ и сохранил как обычный текстовый файл, затем скопировал и вставил обратно.

(Блокнот не работал, так как окончания строк были неправильными)