Get Visual Studio to run a T4 Template on every build
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:
T4 transformation and build order in Visual Studio (unanswered)
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!
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 ..\.."
@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
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:
- Install Visual Studio SDK
- Install Visual Studio 2010 Modeling and Visualization SDK
- 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" />
There is a great NuGet package that does just this:
PM> Install-Package Clarius.TransformOnBuild
Details about the package can be found here
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
Recently found this great VS plugin, Chirpy.
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.
Probably the simplest way is to install a Visual Studio extension called AutoT4.
It runs all T4 templates on build automagically.
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.
- 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
@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
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.
.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.)
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)
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
Hey, 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.
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;
Another good article on this: Code Generation in a Build Process
2012 Modeling and Visualization SDK download link :
Please see mhutch's answer https://stackoverflow.com/a/1395377/9587
IMHO, this is the best build server and dev environment friendly option.
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:
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:
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.
In visual studio 2013, right click the T4 template and set the transform on build property to true.
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:
- 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
Remove the assembly references from .tt
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, '&quot; -r &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=""$(_TransformExe)" "@(_TextTransform)" -r "$(IncludeForTransform)"" 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"
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
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.