Introduction
This article describes how we can configure and mandate auto transformation of a text or code in the project when a build happens.
In Visual Studio, a T4 text template is a mixture of text blocks and control logic that can generate a text file.
The generated file can be text of any kind, such as a web page, or a resource file, or program source code in any language.
You can select all templates to execute or only specific ones whenever a Project/Solution Build.
It's useful when T4 templates generate text from dynamic data and help collaborate team members. There may be a situation where T4 templates get data from database and code is generated based on that data. In Source control environment where there are multiple check ins by multiple developers, this could really be an important piece of work where we could mandate/enforce auto transform for consistency and help resolve merge conflict in advance.
There might be a situation where developers ignore manual transformation of T4 templates but still modify the data which leaves the code in inconsistent stage.
Auto transform could also be integrated to existing CI/CD process.
Background
- Auto execute T4 template on build
- Show pre and post transformation messages and list of files
- Select specific or all T4 files for auto transform
Using the Code
- Download the attached project.
- Modify the csproj file to add transformation to html2 and remove it from html1 and see the result.
- Build (Currently the project is configured to transform only html1).
- Look for messages in Output window.
Steps involved and details below:
Start with creating a new console application or web application:
For demo, we will use HTML Generators which generate .html file containing datetime
.
Add 2 new Text
templates, HtmlGenerator1.tt and HtmlGenerator2.tt.
Add the below code in the .tt files.
HtmlGenerator1.tt:
<#@ output extension=".html" #>
<html>
<body>
HTML Generator 1
The date and time now is: <#= DateTime.Now #>
</body>
</html>
HtmlGenerator2.tt:
<#@ output extension=".html" #>
<html>
<body>
HTML Generator 2
The date and time now is: <#= DateTime.Now #>
</body>
</html>
The only code to execute in this HTML template will be DateTime.Now
.
In real life, this could be enum
s generating from database data, C# code generating from a for
loop, complete email template, etc.
Now let's run a template:
The HTML file is generated as below:
<html>
<body>
HTML Generator 1
The date and time now is: 06/25/2019 15:44:44
</body>
</html>
<html>
<body>
HTML Generator 2
The date and time now is: 06/25/2019 15:44:44
</body>
</html>
Note that both the times are the same because I executed both templates together at the same time.
Let's configure HtmlGenerator1
to be auto transform whenever we do a rebuild and keep HtmlGenerator2
on its own. This would mean on build, the time will be updated for HtmlGenerator1
but not for HtmlGenerator2
.
Edit the project and use the below configs:
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />
Run the Transform
task at the start of every build. Simply switch it off to disable auto transform on build.
TransformOnBuild=true
Overwrite files that are read-only, for example because they are not checked out.
OverwriteReadOnlyOutputFiles=true
Transform every template every time:
TransformOutOfDateOnly=false
Complete Configuration below:
<PropertyGroup>
<BeforeTransform>CustomPreTransform</BeforeTransform>
<AfterTransform>CustomPostTransform</AfterTransform>
</PropertyGroup>
<Target Name="CustomPreTransform">
<Message Text="In CustomPreTransform..." Importance="High" />
</Target>
<Target Name="CustomPostTransform">
<Message Text="In CustomPostTransform...@(GeneratedFiles)" Importance="High" />
</Target>
<PropertyGroup>
<VisualStudioVersion Condition="
'$(VisualStudioVersion)' == ''">16.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">
$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>
<PropertyGroup>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
</PropertyGroup>
<PropertyGroup>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>
<Target Name="CreateT4ItemLists">
<Message Importance="low" Text="Creating T4 items lists for
project $(ProjectName) ($(ProjectPath))..." />
<ItemGroup>
<T4TransformInputs Include="@(CreateT4ItemListsInputs)"
Condition=" %(CreateT4ItemListsInputs.AutoTransformOnBuild) == true " />
<T4TransformInputs Include="@(T4Transform)" />
</ItemGroup>
</Target>
Results
Points of Interest
- With CI/CD already configured to build the project, it will automatically execute the T4 template and merge conflicts can be handled when the code is committed.
- Enforcing policies in the project is easy, important messages can be written in the output window.
History
- Initial version published
- Attached project files and github link