А кто у нас по T4 спец?
От: Sinix  
Дата: 01.04.16 13:18
Оценка: 1 (1)
UPD Мда, с тех пор как я в нём копался, на T4 сделали отличную документацию. Вроде все вопросы, кроме "что ставить из расширений?" отпали.

Но если есть примечания/замечания, то велкам!

Предыдущий вариант:

Подскажите, в какую сторону копать, чтоб правильно сделать следующее:

1. Есть файл \src\Assertions\Code.cs.

2. Надо рядышком положить DebugCode.tt, который будет копировать содержимое Code.cs, добавлять к имени класса префикс Debug и заменять
[DebuggerHidden]
на
[Conditional(DebugCode.DebugCondition), DebuggerHidden]


3. С code model извращаться не надо, достаточно простой дубовой замены регексами. То, что не должно попасть в DebugCode вытащу в partial-класс.

4. Очень желательно, чтобы шаблон можно было использовать в нескольких местах (T4 вроде бы умеет include?)
Чтобы если в будущем появился другой класс ассертов, можно было сослаться на тот же шаблон, а не копипастить и не поддерживать правки в обоих копиях.


Сам что-то похожее уже делал, но результат получился абсолютно неподдерживаемый. Да и T4 с тех пор основательно подзабыл.

Собственно что интересует:

1. Что из инструментов/расширений студии нужно ставить?
2. Ссылки на статьи/похожие примеры.

Заранее спасиб!
Отредактировано 01.04.2016 13:25 Sinix . Предыдущая версия . Еще …
Отредактировано 01.04.2016 13:19 Sinix . Предыдущая версия .
Re: А кто у нас по T4 спец?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 01.04.16 13:30
Оценка:
Здравствуйте, Sinix, Вы писали:


S>UPD Мда, с тех пор как я в нём копался, на T4 сделали отличную документацию. Вроде все вопросы, кроме "что ставить из расширений?" отпали.


Из расширений ничего вроде как ставить не нужно.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: А кто у нас по T4 спец?
От: Sinix  
Дата: 01.04.16 13:41
Оценка:
Здравствуйте, AndrewVK, Вы писали:


S>>UPD Мда, с тех пор как я в нём копался, на T4 сделали отличную документацию. Вроде все вопросы, кроме "что ставить из расширений?" отпали.


AVK>Из расширений ничего вроде как ставить не нужно.

Редактор — их несколько было. Не хочется перебирать)

Раньше надо было ставить что-то из инструментов чтоб автоматом запускался t4 при правке зависимого файла. Вот этот по-моему.

+ Надо было прописывать автозапуск t4 при сборке, чтоб оно и на билд-сервере работало.


Или с этим можно не заморачиваться и сделатьвсё вручную: "поправил файл, ручками запустил, закоммитил, забыл — сам себе буратин"?
Re: А кто у нас по T4 спец?
От: MozgC США http://nightcoder.livejournal.com
Дата: 01.04.16 14:14
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Вроде все вопросы, кроме "что ставить из расширений?" отпали.


У меня на работе стоит плагин для решарпера — https://github.com/MrJul/ForTea
Re[3]: А кто у нас по T4 спец?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 01.04.16 14:23
Оценка: 2 (1)
Здравствуйте, Sinix, Вы писали:

AVK>>Из расширений ничего вроде как ставить не нужно.

S>Редактор — их несколько было. Не хочется перебирать)

Никаким ни разу не пользовался.

S>+ Надо было прописывать автозапуск t4 при сборке, чтоб оно и на билд-сервере работало.


У нас сейчас генеренный файл коммитится в реп.

S>Или с этим можно не заморачиваться и сделатьвсё вручную: "поправил файл, ручками запустил, закоммитил, забыл — сам себе буратин"?


Зависит от сложности. С простенькими скриптами, имхо, проще не заморачиваться.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[4]: А кто у нас по T4 спец?
От: Sinix  
Дата: 01.04.16 14:26
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Зависит от сложности. С простенькими скриптами, имхо, проще не заморачиваться.

Понял, обвеску инструментами будем добавлять если понадобится.

Скрипт хочу как можно проще сделать. Вариант с сложным в своё время пробовал, не понравилось.
Re: А кто у нас по T4 спец?
От: hi_octane Беларусь  
Дата: 01.04.16 14:42
Оценка: 22 (1) +1
S>1. Что из инструментов/расширений студии нужно ставить?
Для решарпера в его собственном Resharper Extensions можно найти t4tea. Имхо лучший (перебрал много), но при каких-то условиях тупит и всё красит красным. Если не тупит — то работают многие полезные решарперовские фичи.
Re: А кто у нас по T4 спец?
От: IT Россия linq2db.com
Дата: 01.04.16 17:49
Оценка: 66 (1)
Здравствуйте, Sinix, Вы писали:

S>UPD Мда, с тех пор как я в нём копался, на T4 сделали отличную документацию. Вроде все вопросы, кроме "что ставить из расширений?" отпали.


Если есть решарпер, то однозначно его плагин. Если нет то из всего остального глючного по выбору.

S>1. Есть файл \src\Assertions\Code.cs.


Посмотрел на этот класс, его тоже можно легко сгенерировать

S>2. Надо рядышком положить DebugCode.tt, который будет копировать содержимое Code.cs, добавлять к имени класса префикс Debug и заменять


Считать файл с использованием Host.ResolvePath. Сделать необходимые замены. Сохранить с помощью WriteLine. Всё.

S>4. Очень желательно, чтобы шаблон можно было использовать в нескольких местах (T4 вроде бы умеет include?)


Без проблем. Пишешь всю трансформацию ввиде метода вот в таких скобках <#+ ... #>, подключаешь через include.

S>Чтобы если в будущем появился другой класс ассертов, можно было сослаться на тот же шаблон, а не копипастить и не поддерживать правки в обоих копиях.


Можно не только асертов. Если туда передавать имя файла и список трансформаций, то будет применимо для чего угодно.

S>Сам что-то похожее уже делал, но результат получился абсолютно неподдерживаемый. Да и T4 с тех пор основательно подзабыл.


Чтобы больше не подзабывать проще один раз заглянуть внутрь. T4 — это простейший генератор, вся ценность которого состоит в том, что он входит в студию из коробки. А внутри у него вот что.

Создаём такой T4:

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
class MyClass
{
<#
    foreach (var item in new[] { 1, 2, 3 })
    {
#>
        int _v1<#= item #> = <#= GetValue1(item) #>;
        int _v2<#= item #> = <# GetValue2(item); #>;
<#
        WriteLine("\t\tint _v3{0} = {1};", item, 3 + item);

        GenerationEnvironment
            .AppendFormat("\t\tint _v4{0} = 3 + {0};", item)
            .AppendLine();
    }

    #>--------------------<#

    while (GenerationEnvironment[GenerationEnvironment.Length - 1] == '-')
        GenerationEnvironment.Length--;
#>
}

<#+
static int GetValue1(int i)
{
    return 3 + i;
}

void GetValue2(int i)
{
    #>3 + <#= i #><#+
}

public class MyClass
{
     
}
#>


Получаем такой результат:

class MyClass
{
        int _v11 = 4;
        int _v21 = 3 + 1;
        int _v31 = 4;
        int _v41 = 3 + 1;
        int _v12 = 5;
        int _v22 = 3 + 2;
        int _v32 = 5;
        int _v42 = 3 + 2;
        int _v13 = 6;
        int _v23 = 3 + 3;
        int _v33 = 6;
        int _v43 = 3 + 3;
}


Теперь идём в C:\Users\{Account}\AppData\Local\Temp\ и находим самый свежий .cs файл.

namespace Microsoft.VisualStudio.TextTemplating4ED5CCFF16531F2DB9FFA1638F6F33A42F0D66D58DE141248E97778336307295858447589B73CEF4BCD41D84119D13550D141744006D5E9FCC05ED0C4F590278
{
    using System;
    
    /// <summary>
    /// Class to produce the template output
    /// </summary>
    
    #line 1 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"
    public class GeneratedTextTransformation : Microsoft.VisualStudio.TextTemplating.TextTransformation
    {
#line hidden
        /// <summary>
        /// Create the template output
        /// </summary>
        public override string TransformText()
        {
            try
            {
                this.Write("class MyClass\r\n{\r\n");
                
                #line 5 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"

    foreach (var item in new[] { 1, 2, 3 })
    {

                
                #line default
                #line hidden
                this.Write("\t\tint _v1");
                
                #line 9 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"
                this.Write(Microsoft.VisualStudio.TextTemplating.ToStringHelper.ToStringWithCulture(item));
                
                #line default
                #line hidden
                this.Write(" = ");
                
                #line 9 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"
                this.Write(Microsoft.VisualStudio.TextTemplating.ToStringHelper.ToStringWithCulture(GetValue1(item)));
                
                #line default
                #line hidden
                this.Write(";\r\n\t\tint _v2");
                
                #line 10 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"
                this.Write(Microsoft.VisualStudio.TextTemplating.ToStringHelper.ToStringWithCulture(item));
                
                #line default
                #line hidden
                this.Write(" = ");
                
                #line 10 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"
 GetValue2(item); 
                
                #line default
                #line hidden
                this.Write(";\r\n");
                
                #line 11 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"

        WriteLine("\t\tint _v3{0} = {1};", item, 3 + item);

        GenerationEnvironment
            .AppendFormat("\t\tint _v4{0} = 3 + {0};", item)
            .AppendLine();
    }

    
                
                #line default
                #line hidden
                this.Write("--------------------");
                
                #line 19 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"


    while (GenerationEnvironment[GenerationEnvironment.Length - 1] == '-')
        GenerationEnvironment.Length--;

                
                #line default
                #line hidden
                this.Write("}\r\n\r\n");
            }
            catch (System.Exception e)
            {
                e.Data["TextTemplatingProgress"] = this.GenerationEnvironment.ToString();
                throw new System.Exception("Template runtime error", e);
            }
            return this.GenerationEnvironment.ToString();
        }
        private global::Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost hostValue;
        /// <summary>
        /// The current host for the text templating engine
        /// </summary>
        public virtual global::Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost Host
        {
            get
            {
                return this.hostValue;
            }
            set
            {
                this.hostValue = value;
            }
        }
        
        #line 26 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"

static int GetValue1(int i)
{
    return 3 + i;
}

void GetValue2(int i)
{
    
        
        #line default
        #line hidden
        
        #line 34 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"
this.Write("3 + ");

        
        #line default
        #line hidden
        
        #line 34 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"
this.Write(Microsoft.VisualStudio.TextTemplating.ToStringHelper.ToStringWithCulture(i));

        
        #line default
        #line hidden
        
        #line 34 "I:\linq2db.LINQPad\Source\TextTemplate1.tt"

}

public class MyClass
{
     
}

        
        #line default
        #line hidden
    }
    
    #line default
    #line hidden
}


Это то, что сгенерировано T4.

Ключевые моменты:

— Имеем один сгенерированный класс GeneratedTextTransformation, наследованный от Microsoft.VisualStudio.TextTemplating.TextTransformation.
— Имеем один виртуальный метод TransformText.
— Всё, что находится внутри <# ... #> в нашем шаблоне попадает внутрь TransformText.
— Всё, что находится внутри <#+ ... #> включается в класс GeneratedTextTransformation в виде мемберов, включая объявленные классы.
— Генерируемый код — это всё, что вне <#(+) #> или внутри <#= #> как в старом ASP.NET.
— <#= #> — это по сути вызов метода Write базового класса TextTransformation.
— Методы Write, WriteLine можно вызывать напрямую в ассортименте.
— Их вызов, фактически заполняет StringBuilder, доступ к которому осуществляется напрямую через свойство GenerationEnvironment.
— Свойством GenerationEnvironment можно манипулировать напрямую как вздумается, как показано выше в примере.

В общем, тупая генерилка. Вся мощь инструмента зависит исключительно от ваших рук и фантазии.

Ну и про возможности partial классов тоже не забываем.

Если интересно посмотреть на примеры, то можно здесь https://github.com/linq2db/t4models/blob/master/Templates/DataModel.ttinclude
и результаты работы здесь https://github.com/linq2db/t4models/tree/master/Tests .
Если нам не помогут, то мы тоже никого не пощадим.
Отредактировано 01.04.2016 18:40 IT . Предыдущая версия . Еще …
Отредактировано 01.04.2016 17:58 IT . Предыдущая версия .
Re[2]: А кто у нас по T4 спец?
От: Sinix  
Дата: 01.04.16 19:09
Оценка:
Здравствуйте, IT, Вы писали:

IT>В общем, тупая генерилка. Вся мощь инструмента зависит исключительно от ваших рук и фантазии.


Огроменное спасибо!

Этот пост надо прилепить в wiki форума .NET, чтоб не потерялся. Можешь сделать?
Re[4]: А кто у нас по T4 спец?
От: Sinix  
Дата: 01.04.16 20:54
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Зависит от сложности. С простенькими скриптами, имхо, проще не заморачиваться.

Скинул, посмотри плиз

Если есть какие-то code issueв в ассертах / тестах, то надо что-то с решарпером будет делать. По моему коду "всё чисто" показывает, зарраза.
Re[2]: А кто у нас по T4 спец?
От: Sinix  
Дата: 01.04.16 20:57
Оценка:
Здравствуйте, IT, Вы писали:

И ещё раз огроменное спасибо, работает вроде!

Скинул. Если будет возможность, посмотри плиз на предмет косяков
\T4.Reusable\DebugCodeGenerator.include.tt
и
\Main\src\Assertions\DebugCode.tt
Re: А кто у нас по T4 спец?
От: _Raz_  
Дата: 01.04.16 21:25
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Чтобы если в будущем появился другой класс ассертов, можно было сослаться на тот же шаблон, а не копипастить и не поддерживать правки в обоих копиях.


Тогда может лучше заменить
<#@ include file="..\..\..\T4.Reusable\DebugCodeGenerator.include.tt"#>

на
<#@ include file="$(SolutionDir)\T4.Reusable\DebugCodeGenerator.include.tt"#>
... << RSDN@Home (RF) 1.2.0 alpha 5 rev. 78>>
Re: А кто у нас по T4 спец?
От: _Raz_  
Дата: 01.04.16 21:32
Оценка: 44 (1)
Здравствуйте, Sinix, Вы писали:


S>Но если есть примечания/замечания, то велкам!


throw new ArgumentException(newClassName, message);


1. Перепутаны местами paramName и message
2. У T4 шаблонов есть свои параметры. Не будет ли путаницы с ними при таком типе исключения
... << RSDN@Home (RF) 1.2.0 alpha 5 rev. 78>>
Re[2]: А кто у нас по T4 спец?
От: Sinix  
Дата: 01.04.16 21:33
Оценка:
Здравствуйте, _Raz_, Вы писали:

_R_>Тогда может лучше заменить

А оно будет работать, если хост от MsBuild будет?
лет 7 назад не работало, но это когда ещё было

_R_>1. Перепутаны местами paramName и message

_R_>2. У T4 шаблонов есть свои параметры. Не будет ли путаницы с ними при таком типе исключения

На InvalidOperation поменяю.
Отредактировано 01.04.2016 21:35 Sinix . Предыдущая версия .
Re[5]: А кто у нас по T4 спец?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 01.04.16 21:55
Оценка:
Здравствуйте, Sinix, Вы писали:

AVK>>Зависит от сложности. С простенькими скриптами, имхо, проще не заморачиваться.

S>Скинул, посмотри плиз

У меня он вообще не запускается:
Error        Compiling transformation: Type expected    T4.Reusable    s:\Work\CodeJam\T4.Reusable\DebugCodeGenerator.include.tt    1    
Error        Compiling transformation: Invalid token 'this' in class, struct, or interface member declaration    T4.Reusable    s:\Work\CodeJam\T4.Reusable\DebugCodeGenerator.include.tt    1    
Error        Compiling transformation: Method must have a return type    T4.Reusable    s:\Work\CodeJam\T4.Reusable\DebugCodeGenerator.include.tt    1


Ну и в сгенеренном коде неплохо бы using static DebugCode; добавить.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[6]: А кто у нас по T4 спец?
От: Sinix  
Дата: 01.04.16 22:01
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>У меня он вообще не запускается:

Вот это косяк
Завтра на свежую голову буду смотреть.

AVK>Ну и в сгенеренном коде неплохо бы using static DebugCode; добавить

Ок.
Re[6]: А кто у нас по T4 спец?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 01.04.16 22:03
Оценка:
Лишний перевод строк в конце файла.
http://stackoverflow.com/questions/23281434/t4-reports-compiling-transformation-invalid-token-this-in-class-struct
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: А кто у нас по T4 спец?
От: _Raz_  
Дата: 01.04.16 22:05
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>А оно будет работать, если хост от MsBuild будет?

S>лет 7 назад не работало, но это когда ещё было

Говорят, что нет

Visual Studio macros like $(SolutionDir) don’t work in MSBuild. You can use project properties instead.


Code Generation in a Build Process
... << RSDN@Home (RF) 1.2.0 alpha 5 rev. 78>>
Re[7]: А кто у нас по T4 спец?
От: Sinix  
Дата: 02.04.16 06:13
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Лишний перевод строк в конце файла.

AVK>http://stackoverflow.com/questions/23281434/t4-reports-compiling-transformation-invalid-token-this-in-class-struct

Опс. Интересно, почему на моей машине работало. Или у тебя не vs2015u1?
Re[4]: А кто у нас по T4 спец?
От: Sinix  
Дата: 02.04.16 06:24
Оценка:
Здравствуйте, _Raz_, Вы писали:

_R_>Говорят, что нет

_R_>

Visual Studio macros like $(SolutionDir) don’t work in MSBuild. You can use project properties instead.


Ну вот значит правильно запомнил. Пусть пока так будет, пока меня не пропрёт на пободаться с генерацией T4 при сборке.
Исключение поправил, спасиб!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.