Get Visual Studio to run a T4 Template on every build

Visual StudioTfsMsbuildT4

Visual Studio Problem Overview


How do I get a T4 template to generate its output on every build? As it is now, it only regenerates it when I make a change to the template.

I have found other questions similar to this:

https://stackoverflow.com/questions/1293320/t4-transformation-and-build-order-in-visual-studio (unanswered)

https://stackoverflow.com/questions/405560/how-to-get-t4-files-to-build-in-visual-studio (answers are not detailed enough [while still being plenty complicated] and don't even make total sense)

There has got to be a simpler way to do this!

Visual Studio Solutions


Solution 1 - Visual Studio

I agree with GarethJ - in VS2010 it is much easier to regenerate tt templates on each build. Oleg Sych's blog describes how to do it. In short:

  1. Install Visual Studio SDK
  2. Install Visual Studio 2010 Modeling and Visualization SDK
  3. Open in text editor project file and add to the end of file but before </Project>

That's it. Open your project. On each build all *.tt templates will be reprocessed

<!-- This line could already present in file. If it is so just skip it  -->
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- process *.tt templates on each build  -->
<PropertyGroup>
	<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TextTemplating\v10.0\Microsoft.TextTemplating.targets" />

Solution 2 - Visual Studio

I used JoelFan's answer to come up w/ this. I like it better because you don't have to remember to modify the pre-build event every time you add a new .tt file to the project.

  • add TextTransform.exe to your %PATH%
  • created a batch file named transform_all.bat (see below)
  • create a pre-build event "transform_all ..\.."

transform_all.bat

@echo off
SETLOCAL ENABLEDELAYEDEXPANSION

:: set the working dir (default to current dir)
set wdir=%cd%
if not (%1)==() set wdir=%1

:: set the file extension (default to vb)
set extension=vb
if not (%2)==() set extension=%2

echo executing transform_all from %wdir%
:: create a list of all the T4 templates in the working dir
dir %wdir%\*.tt /b /s > t4list.txt

echo the following T4 templates will be transformed:
type t4list.txt

:: transform all the templates
for /f %%d in (t4list.txt) do (
set file_name=%%d
set file_name=!file_name:~0,-3!.%extension%
echo:  \--^> !file_name!	
TextTransform.exe -out !file_name! %%d
)

echo transformation complete

Solution 3 - Visual Studio

There is a great NuGet package that does just this:

PM> Install-Package Clarius.TransformOnBuild

Details about the package can be found here and the GitHub repo is here.

Solution 4 - Visual Studio

I used MarkGr's answer and developed this solution. First, create a batch file called RunTemplate.bat in a separate tools folder above the main solution folder. The batch file just has the line:

"%CommonProgramFiles%\Microsoft Shared\TextTemplating\1.2\texttransform.exe" -out %1.cs -P %2 -P "%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.5" %1.tt

This batch file takes 2 parameters... %1 is the path to the .tt file without the .tt extension. %2 is the path to any DLLs referred to by Assembly directives in the template.

Next, go into the Project Properties of the project containing the T4 template. Go into Build Events and add the following Pre-build event command line:

$(SolutionDir)..\..\tools\RunTemplate.bat $(ProjectDir)MyTemplate $(OutDir)

replacing MyTemplate with filename of your .tt file (i.e. MyTemplate.tt) without the .tt extension. This will have the result of expanding the template to produce MyTemplate.cs before building the project. Then the actual build will compile MyTemplate.cs

Solution 5 - Visual Studio

Recently found this great VS plugin, Chirpy.

Not only does it generate your T4 on a build, but it allows T4-based approach to minification of javascript, CSS, and even lets you use LESS syntax for your CSS!

Solution 6 - Visual Studio

Probably the simplest way is to install a Visual Studio extension called AutoT4.

It runs all T4 templates on build automagically.

Solution 7 - Visual Studio

The pre-build can be reduced to a single line:

forfiles /p "$(ProjectDir)." /m "*.tt" /s /c "cmd /c echo Transforming @path && \"%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\1.2\TextTransform.exe\" @file"

This transforms all .tt files in the project and lists them to the build output.

If you don't want the build output then you have to work around some "interesting behaviour":

forfiles /p "$(ProjectDir)." /m "*.tt" /s /c "cmd /c @\"%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\1.2\TextTransform.exe\" @file"

Of course, you can pull this out into a batch file to which you pass the project directory path if you wish.

NB The path may require some tweaking. The path above is where VS 2008 installed it on my machine; but you might find that the version number between TextTemplating and TextTransform.exe is different.

Solution 8 - Visual Studio

Check out C:\Program Files (x86)\Common Files\Microsoft Shared\TextTemplating there is a command line transformation exe in there. Alternatively write a MSBuild task with a custom host and do the transform yourself.

Solution 9 - Visual Studio

Thanks to GitHub.com/Mono/T4, at the moment you can do it for both .NET Core and Visual Studio builds by adding this to your .csproj file:

  <ItemGroup>
    <DotNetCliToolReference Include="dotnet-t4-project-tool" Version="2.0.5" />
    <TextTemplate Include="**\*.tt" />
  </ItemGroup>

  <Target Name="TextTemplateTransform" BeforeTargets="BeforeBuild">
    <ItemGroup>
      <Compile Remove="**\*.cs" />
    </ItemGroup>
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet t4 %(TextTemplate.Identity)" />
    <ItemGroup>
      <Compile Include="**\*.cs" />
    </ItemGroup>
  </Target>

If you transform your templates to different programming languages you should add something like <Compile Remove="**\*.vb" /> and <Compile Include="**\*.vb" /> in order to get these files compiled even if you don't have generated files yet.

Remove and Include trick only needed for first time generation, or you can make the XML-shorter like this:

  <ItemGroup>
    <DotNetCliToolReference Include="dotnet-t4-project-tool" Version="2.0.5" />
    <TextTemplate Include="**\*.tt" />
  </ItemGroup>

  <Target Name="TextTemplateTransform" BeforeTargets="BeforeBuild">
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet t4 %(TextTemplate.Identity)" />
  </Target>

and just run build twice (for the first time). If you already have generated files committed to the repository there will be no problems on rebuilds with both examples.

In the Visual Studio you might want to see something like this:

enter image description here

instead of this:

enter image description here

So add something like this to your project file:

  <ItemGroup>
    <Compile Update="UInt16Class.cs">
      <DependentUpon>UInt16Class.tt</DependentUpon>
    </Compile>
    <Compile Update="UInt32Class.cs">
      <DependentUpon>UInt32Class.tt</DependentUpon>
    </Compile>
    <Compile Update="UInt64Class.cs">
      <DependentUpon>UInt64Class.tt</DependentUpon>
    </Compile>
    <Compile Update="UInt8Class.cs">
      <DependentUpon>UInt8Class.tt</DependentUpon>
    </Compile>
  </ItemGroup>

Complete example here: GitHub.com/Konard/T4GenericsExample (includes generation of multiple files from single template).

Solution 10 - Visual Studio

Expanding on Seth Reno and JoelFan's answers, I came up with this. With this solution don't need to remember to modify the pre-build event every time you add a new .tt file to the project.

##Implementation Procedure

  • Create a batch file named transform_all.bat (see below)
  • Create a pre-build event transform_all.bat "$(ProjectDir)" $(ProjectExt) for each project with a .tt you want to build

transform_all.bat

@echo off
SETLOCAL ENABLEDELAYEDEXPANSION

:: set the correct path to the the app
if not defined ProgramFiles(x86). (
  echo 32-bit OS detected
  set ttPath=%CommonProgramFiles%\Microsoft Shared\TextTemplating\1.2\
) else (
  echo 64-bit OS detected
  set ttPath=%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\1.2\
)

:: set the working dir (default to current dir)
if not (%1)==() pushd %~dp1

:: set the file extension (default to vb)
set ext=%2
if /i %ext:~1%==vbproj (
  set ext=vb
) else if /i %ext:~1%==csproj (
  set ext=cs
) else if /i [%ext%]==[] (
  set ext=vb
)

:: create a list of all the T4 templates in the working dir
echo Running TextTransform from %cd%
dir *.tt /b /s | findstr /vi obj > t4list.txt

:: transform all the templates
set blank=.
for /f "delims=" %%d in (t4list.txt) do (
  set file_name=%%d
  set file_name=!file_name:~0,-3!.%ext%
  echo:  \--^> !!file_name:%cd%=%blank%!
  "%ttPath%TextTransform.exe" -out "!file_name!" "%%d"
)

:: delete T4 list and return to previous directory
del t4list.txt
popd

echo T4 transformation complete


NOTES

  1. The text transformation assumes the code in the T4 template is the same language as your project type. If this case does not apply to you, then you will have to replace the $(ProjectExt) argument with the extension of the files you want the code generate.

  2. .TT files must be in the project directory else they won't build. You can build TT files outside the project directory by specifying a different path as the first argument (i.e. replace "$(ProjectDir)" with the path containing the TT files.)

  3. Remember also to set the correct path to the transform_all.bat batch file.
    For example, I placed it in my solution directory so the pre-build event was as follows "$(SolutionDir)transform_all.bat" "$(ProjectDir)" $(ProjectExt)

Solution 11 - Visual Studio

If you're using Visual Studio 2010, you can use the Visual Studio Modeling and Visualization SDK: http://code.msdn.microsoft.com/vsvmsdk

This contains msbuild tasks for executing T4 templates at build time.

Have a look at Oleg's blog for more explanation: http://www.olegsych.com/2010/04/understanding-t4-msbuild-integration

Solution 12 - Visual Studio

my script can also parse output extension

for /r %1 %%f in (*.tt) do (
 for /f "tokens=3,4 delims==, " %%a in (%%f) do (
  if %%~a==extension "%CommonProgramFiles%\Microsoft Shared\TextTemplating\1.2\texttransform.exe" -out %%~pnf.%%~b -P %%~pf -P "%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.5" %%f
 )
)
echo Exit Code = %ERRORLEVEL%

Just create transform_all.bat $(SolutionDir) pre-build event, and all *.tt files in your solution will be transformed automaticaly.

Solution 13 - Visual Studio

Dynamo.AutoTT will do what you need. You can configure it to watch files via a regex or generate on build. It also allows you to specify which T4 templates you want it to trigger.

You can download it from here : https://github.com/MartinF/Dynamo.AutoTT

Just build it, copy the dll and AddIn files into

C:\Users\Documents\Visual Studio 2012\Addins\

and away you go.

If you want to get it going in VS2012 you will need to modify the a Dynamo.AutoTT.AddIn file and set the Version to 11.0 inside the AddIn file;

Solution 14 - Visual Studio

In Visual Studio 2017 (probably next versions too), you should add this in Pre-build event:

"$(DevEnvDir)TextTransform.exe" -out "$(ProjectDir)YourTemplate.cs" "$(ProjectDir)YourTemplate.tt"

p.s. Change path to your template if it's located not in root project directory.

Solution 15 - Visual Studio

You just need to add this command to the pre-build event of the project:

if $(ConfigurationName) == Debug $(MSBuildToolsPath)\Msbuild.exe  /p:CustomBeforeMicrosoftCSharpTargets="$(ProgramFiles)\MSBuild\Microsoft\VisualStudio\v11.0\TextTemplating\Microsoft.TextTemplating.targets"  $(ProjectPath) /t:TransformAll 

The check on configuration = debug, makes sure that you don't regenerate the code in the release mode, when you do the build on the TFS build server for instance.

Solution 16 - Visual Studio

T4Executer does this for VS2019. You can specify templates to ignore on build, and there is a execute after build option.

Solution 17 - Visual Studio

Here is my solution - similar to the accepted answer. We had a problem with our source control. The target .cs files are read-only and the T4 was failing. Here is the code, that runs T4 in temp folder, compares target files, and copies it only in case of same change. It does not fix the problem with read.only files, but at least it does not occur very often:

Transform.bat

ECHO Transforming T4 templates
SET CurrentDirBackup=%CD%
CD %1
ECHO %1
FOR /r %%f IN (*.tt) DO call :Transform %%f
CD %CurrentDirBackup%
ECHO T4 templates transformed
goto End

:Transform
set ttFile=%1
set csFile=%1

ECHO Transforming %ttFile%:
SET csFile=%ttFile:~0,-2%cs
For %%A in ("%ttFile%") do Set tempTT=%TEMP%\%%~nxA
For %%A in ("%csFile%") do Set tempCS=%TEMP%\%%~nxA

copy "%ttFile%" "%tempTT%
"%COMMONPROGRAMFILES(x86)%\microsoft shared\TextTemplating\11.0\TextTransform.exe"  "%tempTT%"

fc %tempCS% %csFile% > nul
if errorlevel 1 (
 :: You can try to insert you check-out command here.
 "%COMMONPROGRAMFILES(x86)%\microsoft shared\TextTemplating\11.0\TextTransform.exe"  "%ttFile%"
) ELSE (
 ECHO  no change in %csFile%
)

del %tempTT%
del %tempCS%
goto :eof

:End

You can try to add your check-out command on a line (:: You can try ....)

In your project set this as a prebuild action:

Path-To-Transform.bat "$(ProjectDir)"

Solution 18 - Visual Studio

In visual studio 2013, right click the T4 template and set the transform on build property to true.

Solution 19 - Visual Studio

Here is how I tacked it. Link. Basically building on top of a great blog( blogs.clariusconsulting.net/kzu/how-to-transform-t4-templates-on-build-without-installing-a-visual-studio-sdk/ can't post more that 2 links :( ) I came up with This .targets file for use with visual studio proj files.

It's useful when you are using other dll-s inside of your .tt and you want the result to change as the dll-s are changing.

How it works:

  1. Create the tt, add the assembly name="$(SolutionDir)path\to\other\project\output\foo.dll and set up the transformation and result to be as expected

  2. Remove the assembly references from .tt

  3. Inside the proj file use this code to set up transform on build:

    <PropertyGroup>
      <!-- Initial default value -->
      <_TransformExe>$(CommonProgramFiles)\Microsoft Shared\TextTemplating\10.0\TextTransform.exe</_TransformExe>
      <!-- If explicit VS version, override default -->
      <_TransformExe Condition="'$(VisualStudioVersion)' != ''">$(CommonProgramFiles)\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\TextTransform.exe</_TransformExe>
      <!-- Cascading probing if file not found -->
      <_TransformExe Condition="!Exists('$(_TransformExe)')">$(CommonProgramFiles)\Microsoft Shared\TextTemplating\10.0\TextTransform.exe</_TransformExe>
      <_TransformExe Condition="!Exists('$(_TransformExe)')">$(CommonProgramFiles)\Microsoft Shared\TextTemplating\11.0\TextTransform.exe</_TransformExe>
      <_TransformExe Condition="!Exists('$(_TransformExe)')">$(CommonProgramFiles)\Microsoft Shared\TextTemplating\12.0\TextTransform.exe</_TransformExe>
      <!-- Future proof 'til VS2013+2 -->
      <_TransformExe Condition="!Exists('$(_TransformExe)')">$(CommonProgramFiles)\Microsoft Shared\TextTemplating\13.0\TextTransform.exe</_TransformExe>
      <_TransformExe Condition="!Exists('$(_TransformExe)')">$(CommonProgramFiles)\Microsoft Shared\TextTemplating\14.0\TextTransform.exe</_TransformExe>
      <_TransformExe Condition="!Exists('$(_TransformExe)')">$(CommonProgramFiles)\Microsoft Shared\TextTemplating\15.0\TextTransform.exe</_TransformExe>
    
      <IncludeForTransform>@(DllsToInclude, '&amp;quot; -r &amp;quot;')</IncludeForTransform>
    </PropertyGroup>
    
  • First part Locates TextTransform.exe

  • $(IncludeForTransform) will be equal to c:\path\to\dll\foo.dll' -r c:\path\to\dll\bar.dll because that's the way to add references for the TextTransform on the command line

          <Target Name="TransformOnBuild" BeforeTargets="BeforeBuild">
            <!--<Message Text="$(IncludeForTransform)" />-->
            <Error Text="Failed to find TextTransform.exe tool at '$(_TransformExe)." Condition="!Exists('$(_TransformExe)')" />
            <ItemGroup>
              <_TextTransform Include="$(ProjectDir)**\*.tt" />
            </ItemGroup>
            <!-- Perform task batching for each file -->
            <Exec Command="&quot;$(_TransformExe)&quot; &quot;@(_TextTransform)&quot; -r &quot;$(IncludeForTransform)&quot;" Condition="'%(Identity)' != ''" />
          </Target>
    
  • <_TextTransform Include="$(ProjectDir)**\*.tt" />this creates a list of all tt files inside the project and subdirectories

  • <Exec Command="... produces a line for each of the found .tt files that looks like "C:\path\to\Transform.exe" "c:\path\to\my\proj\TransformFile.tt" -r"c:\path\to\foo.dll" -r "c:\path\to\bar.dll"

  1. The only thing left to do is add the paths to the dlls inside of:

        <ItemGroup>
          <DllsToInclude Include="$(ProjectDir)path\to\foo.dll">
            <InProject>False</InProject>
          </DllsToInclude>
          <DllsToInclude Include="$(ProjectDir)path\to\bar.dll">
            <InProject>False</InProject>
          </DllsToInclude>
        </ItemGroup>
    

Here <InProject>False</InProject> hides these items from the Solution View

So now you should be able to generate your code on build and on change of dll-s.

You can remove the custom tool (from properties inside of Visual Studio) so the VS does not try to transform and fail miserably every time. Because we removed the assembly references in step 2

Solution 20 - Visual Studio

You just install NuGet Package: Clarius.TransformOnBuild

Then, every time you click Rebuild project (or Solution), your .tt files will run

Solution 21 - Visual Studio

Here's a pre-build event using only Microsoft Tooling and standard paths. It's tested in vs2019/netcore3.1.

Replace "AppDbContext.tt" with your project-relative file path:

"$(MSBuildBinPath)\msbuild" "$(SolutionPath)" /t:$(ProjectName):Transform /p:TransformFile="AppDbContext.tt" /p:CustomAfterMicrosoftCommonTargets="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TextTemplating\Microsoft.TextTemplating.targets"

Microsoft also has a guide to make macros like "$(SolutionDirectory)" available in the template by using T4ParameterValues in your project file.

Solution 22 - Visual Studio

Some guy built a nuget package for this.

Side note: I get compile errors from both TextTemplate.exe and that package (because that package calls TextTemplate.exe) but not from Visual Studio. So apparently the behavior is not the same; heads up.

EDIT: This ended up being my problem.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJoelFanView Question on Stackoverflow
Solution 1 - Visual StudioCheburekView Answer on Stackoverflow
Solution 2 - Visual StudioSeth RenoView Answer on Stackoverflow
Solution 3 - Visual StudioGavKilbrideView Answer on Stackoverflow
Solution 4 - Visual StudioJoelFanView Answer on Stackoverflow
Solution 5 - Visual StudioMark MelvilleView Answer on Stackoverflow
Solution 6 - Visual StudioSaulView Answer on Stackoverflow
Solution 7 - Visual StudioPeter TaylorView Answer on Stackoverflow
Solution 8 - Visual StudioMarkGrView Answer on Stackoverflow
Solution 9 - Visual StudioKonardView Answer on Stackoverflow
Solution 10 - Visual StudioAlex EssilfieView Answer on Stackoverflow
Solution 11 - Visual StudioGarethJView Answer on Stackoverflow
Solution 12 - Visual StudioMadRabbitView Answer on Stackoverflow
Solution 13 - Visual StudioMatwareView Answer on Stackoverflow
Solution 14 - Visual Studiorandom oneView Answer on Stackoverflow
Solution 15 - Visual StudioanoopView Answer on Stackoverflow
Solution 16 - Visual StudioTim MaesView Answer on Stackoverflow
Solution 17 - Visual StudioOndraView Answer on Stackoverflow
Solution 18 - Visual Studiouser1607685View Answer on Stackoverflow
Solution 19 - Visual StudioGeorgiView Answer on Stackoverflow
Solution 20 - Visual StudiodqtheView Answer on Stackoverflow
Solution 21 - Visual StudionpjohnsView Answer on Stackoverflow
Solution 22 - Visual StudioGraultView Answer on Stackoverflow