让 VisualStudio 在每个生成上运行 T4模板

如何让 T4模板在每个构建上生成它的输出?就像现在一样,它只在我对模板进行更改时才重新生成。

我还发现了其他类似的问题:

VisualStudio 中的 T4转换和构建顺序(未回答)

如何让 t4文件建立在视觉工作室?(答案不够详细[同时还很复杂] ,甚至没有意义)

一定有更简单的方法!

71724 次浏览

看看这个 C: Program Files (x86) Common Files 微软共享文本模板 里面有一个命令行转换 exe。或者使用自定义主机编写 MSBuild 任务,然后自己进行转换。

我用马克格瑞的答案开发了这个解决方案。首先,在主解决方案文件夹上方的单独 工具文件夹中创建一个名为 RunTemplate.bat的批处理文件。批处理文件只有一行:

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

这个批处理文件有两个参数... ... % 1是到。没有参数的 tt 文件。分机。% 2是模板中 集合指令引用的所有 DLL 的路径。

接下来,进入包含 T4模板的项目的 ProjectProperties。进入 生成事件并添加以下 预生成事件命令行:

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

我的模板替换为文件名。Tt 文件(即 MyTemplate.tt)。分机。这将导致在构建项目之前扩展模板以生成 MyTemplate.cs。然后实际的构建将编译 MyTemplate.cs

如果您正在使用 VisualStudio2010,您可以使用 VisualStudio 建模和可视化 SDK: Http://code.msdn.microsoft.com/vsvmsdk

它包含在生成时执行 T4模板的 msbuild 任务。

看看奥列格的博客,你会得到更多的解释: Http://www.olegsych.com/2010/04/understanding-t4-msbuild-integration

我用了 JoelFan 的答案。我更喜欢它,因为您不必记得每次添加新内容时都要修改预构建事件。对项目的 tt 文件。

  • 将 TextTransform.exe 添加到 %PATH%
  • 创建了一个名为 change _ all.bat 的批处理文件(见下文)
  • 创建预生成事件“ 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

我同意 GarethJ-在 VS2010中,在每个构建上重新生成 tt 模板要容易得多。 Oleg Sych 的博客描述了如何做到这一点。简而言之:

  1. 安装 Visual Studio SDK
  2. 安装 < a href = “ http://www.microsoft.com/downloads/details.aspx? FamilyID = 0def949d-2933-49c3-ac50-e884e0ff08a7 & amp 和可视化 SDK
  3. 在文本编辑器项目文件中打开 添加到文件末尾但在 </Project>之前

就是这样。打开您的项目。在每次构建时,所有 * . tt 模板都将被重新处理

<!-- 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" />

最近发现这个伟大的 VS 插件,快活

它不仅可以在构建时生成您的 T4,而且它允许基于 T4的方法来缩小 javascript、 CSS,甚至允许您为 CSS 使用 LESS 语法!

嘿, 我的脚本也可以解析输出扩展名

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%

只要创建 transform_all.bat $(SolutionDir)预构建事件,解决方案中的所有 * . tt 文件就会自动转换。

预构建可以简化为一行:

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

这将转换项目中的所有 .tt文件,并将它们列入生成输出。

如果您不想要构建输出,那么您必须围绕某些 “有趣的行为”开展工作:

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

当然,如果愿意的话,您可以将它拉出到一个批处理文件中,并将项目目录路径传递给该文件。

路径可能需要一些调整。上面的路径是 VS2008在我的机器上安装它的地方; 但是您可能会发现 TextTemplatingTextTransform.exe之间的版本号是不同的。

发电机。AutoTT 会满足你的要求。您可以将其配置为通过正则表达式监视文件或在构建时生成文件。它还允许您指定要触发的 T4模板。

你可以从这里下载: https://github.com/MartinF/Dynamo.AutoTT

只要构建它,将 dll 和 AddIn 文件复制到

C: 用户文档 VisualStudio2012外接程序

开始吧。

如果你想让它在 VS2012中运行,你需要修改一个 Dynamo。AutoTT.AddIn 文件,并在 AddIn 文件中将 Version 设置为11.0;

这是我的解决方案——类似于公认的答案。 我们的源代码控制出现了问题。 target.cs 文件是只读的,而 T4出现了故障。 下面是在临时文件夹中运行 T4的代码,它比较目标文件,并仅在发生相同更改时复制它。它没有修复 read.only 文件的问题,但至少它不经常出现:

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... .)中添加签出命令

在您的项目中,将其设置为预生成操作:

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

扩展 Seth RenoJoelFan 的的答案,我想出了这个。使用此解决方案,不需要记得每次添加新的。对项目的 tt 文件。

实施程序

  • 创建一个名为 change _ all.bat 的批处理文件(见下文)
  • 使用要生成的. tt 为每个项目创建一个预生成事件 transform_all.bat "$(ProjectDir)" $(ProjectExt)

转换 _ 所有。蝙蝠

@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


注意:

  1. 文本转换假定 T4模板中的代码与项目类型的语言相同。如果这种情况不适用于您,那么您将不得不将 $(ProjectExt)参数替换为您希望代码生成的文件的扩展名。

  2. .TT文件必须在项目目录中,否则它们不会生成。可以通过指定不同的路径作为第一个参数(也就是说。用包含 TT 文件的路径替换 "$(ProjectDir)")在项目目录外构建 TT 文件

  3. 还要记住设置到 transform_all.bat批处理文件的正确路径。
    例如,我将它放在我的解决方案目录中,因此预构建事件如下 "$(SolutionDir)transform_all.bat" "$(ProjectDir)" $(ProjectExt)

您只需要将这个命令添加到项目的预生成事件:

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

当您在 TFS 构建服务器上进行构建时,对 configuration = debug 的检查确保您不会在发布模式下重新生成代码。

在 VisualStudio2013中,右键单击 T4模板并将构建属性上的转换设置为 true。

有一个很棒的 NuGet 软件包就是这样做的:

PM> Install-Package Clarius.TransformOnBuild

该软件包的详细信息可以是 在这里发现的,而 GitHub 的回购信息是 给你

可能最简单的方法是安装名为 AutoT4的 VisualStudio 扩展。

它在构建时自动运行所有 T4模板。

有人为此建了一个 Nuget 套餐

附注: 我从 TextTemplate.exe 和那个包(因为那个包调用 TextTemplate.exe)得到编译错误,但不是从 Visual Studio 得到。所以显然行为是不一样的,注意。

编辑: 这个最终成为我的问题。

我是这么做的。林克.基本上建立在一个伟大的博客( blogs.clariusconsulting.net/kzu/how-to-transform-t4-templates-on-build-without-installing-a-visual-studio-sdk/不能发布超过2个链接: ()我想出了这个与可视化工作室 proj 文件使用的 。目标文件。

当您在您的。Tt 和您希望结果随着 dll-s 的变化而变化。

工作原理:

  1. 创建 tt,将程序集名称 = “ $(SolutionDir)路径添加到其他项目输出 foo.dll,并按预期设置转换和结果
  2. 从. tt 中删除程序集引用

  3. 在 proj 文件中,使用下面的代码设置构建时的转换:

    <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>
    
    • 第一部分定位 TextTransform.exe

    • $(IncludeForTransform)将等于 c:\path\to\dll\foo.dll' -r c:\path\to\dll\bar.dll,因为这是在命令行上添加 TextTransform 引用的方法

       <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"

  4. 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>
    

    在这里,<InProject>False</InProject>从解决方案视图中隐藏这些项

因此,现在您应该能够在构建和更改 dll-s 的基础上生成代码。

您可以删除自定义工具(从 VisualStudio 内部的属性中) ,这样 VS 就不会尝试转换,并且每次都会悲惨地失败。因为我们在步骤2中删除了程序集引用

T4Execter 为 VS2019执行此操作。您可以在生成时指定要忽略的模板,并且有一个“生成后执行”选项。

只需要安装 NuGet Package: 克莱瑞斯,变形金刚

然后,每次单击 重建项目(或解决方案)时,将运行 没错文件

在 VisualStudio2017(可能也是下一个版本)中,应该在预生成事件中添加以下内容:

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

如果模板不在根项目目录中,则更改模板的路径。

感谢 Github.com/mono/t4,现在你可以做到这两点。NET 核心和 VisualStudio 通过将其添加到 .csproj文件来构建:

  <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>

如果您将您的模板转换为不同的编程语言,您应该添加类似于 <Compile Remove="**\*.vb" /><Compile Include="**\*.vb" />的东西,以便得到这些文件编译,即使您还没有生成文件。

RemoveInclude技巧只需要在第一次生成时使用,或者你可以使 XML 变得更短,如下所示:

  <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>

并且只运行 build 两次(第一次)。如果您已经生成了提交到存储库的文件,那么这两个示例在重新构建时都不会出现问题。

在 VisualStudio 中,您可能希望看到如下内容:

enter image description here

而不是这样:

enter image description here

因此,在你的项目文件中添加如下内容:

  <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>

这里的完整示例: Github.com/konard/t4genericsexample(包括从单个模板生成多个文件)。

下面是一个只使用 Microsoft 工具和标准路径的预构建事件,它在 vs2019/netcore3.1中进行了测试。

将“ AppDbContext.tt”替换为与项目相关的文件路径:

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

Microsoft 还有一个指南,可以通过在项目文件中使用 T4参数值使模板中的“ $(SolutionDirectory)”之类的宏可用。