If you haven’t explored T4 yet, you’re missing one of Visual Studio’s best, and best kept, secrets. T4, which stands for Text Templating Transformation Toolkit, is code generation built right into Visual Studio 2008. It is also available in Visual Studio 2005 if you install the Visual Studio SDK. It is a great feature that I use quite frequently to automate repetitive tasks.
T4 template execution only occurs though when the template file is open in the editor and saved. In other words, template execution does not occur when you type Ctrl+B to build the project. There are scenarios however where execution of the templates at every build is the desired behavior.
I did find several custom MSBuild tasks to perform this behavior, but I wanted to avoid additional assembly references. Instead, the following custom MSBuild target was created to address this.
First and foremost, a .targets file is created in the MSBuild directory (%systemdrive%:\Windows\Microsoft.NET\Framework\v3.5):
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ExecuteT4Templates">
<ItemGroup>
<T4Templates Include=".\**\*.tt" />
</ItemGroup>
<Exec
WorkingDirectory="C:\Program Files\Common Files\microsoft shared\TextTemplating\1.2\"
Command="TextTransform "%(T4Templates.FullPath)"" />
</Target>
</Project>
There are a couple important things to note. First, a custom target is created, and an item group is added to the target. In the item group, we create a new item called T4Templates that will recurse the project and hold information for each text template that is found. Finally, an execute task is added which calls TextTransform.exe for each file found.
Once the targets file is create, the project file is edited to import the new targets file using Import. I put it above the first PropertyGroup node so it’s visible when future maintenance is performed, but it can be placed anywhere within the root Project node:
<Import Project="$(MSBuildToolsPath)\T4Execution.targets" />
Finally, the DefaultTargets attribute in the Project node is modified to include the ExecuteT4Templates target: Note: This change is required for any project where this behavior is desired.
DefaultTargets="ExecuteT4Templates;Build"
Now whenever the project is built (
Ctrl+B), template execution occurs. To be fair, there are several issues with the above approach, including:
- Template execution does not occur when Debug (F5) is triggered unless a project file or property has changed.
- There is no built-in way to get the number of items in an ItemGroup to use conditional execution.
How important is this functionality in your projects? How would you do it differently?