开发者

Cannot reference dependency assemblies in T4 template when using TransformOnBuild

开发者 https://www.devze.com 2023-03-03 12:10 出处:网络
We\'re trying to use T4 with Visual Studio 2010 (SP1) to build scripts for another language that are based upon some of our existing C# 开发者_如何学运维classes. I\'m hoping for the following:

We're trying to use T4 with Visual Studio 2010 (SP1) to build scripts for another language that are based upon some of our existing C# 开发者_如何学运维classes. I'm hoping for the following:

  1. The template needs to load our existing assembly and use objects from a namespace in that assembly.
  2. The transformation needs to run on every build on every development machine and build server without any additional installations.
  3. (1) and (2) need to work together.

(1) was fairly straightforward:

<#@ assembly name="$(TargetDir)RequiredProject.dll" #>
<#@ import namespace="RequiredProject.RequiredNamespace" #>

Using the $(TargetDir) macro allowed me to reference the dll with a fully qualified UNC path (per the instructions found here).

(2) is a bit roundabout, but I think I've got it solved: I installed the required text transformation SDKs on a different machine and copied the required .targets and .dlls into a folder in my solution and then updated my .csproj file to reference the local .targets file.

(3) is where I run into problems. It seems like the <TransformOnBuild>true</TransformOnBuild> property doesn't play nicely when a referenced assembly needs to be built prior to the transformation. Everytime I enable transform on build with referenced assemblies, I get the following error:

Compiling transformation: Metadata file '$(TargetDir)RequiredProject.dll' could not be found.

However, I'm using the same assembly instruction that I was using in (1) to reference the assembly. In fact, going to the .tt template directly and saving it still produces the expected output -- it just doesn't work during the "build" step. Am I doing something wrong, or is there a way to ensure that the template transformations occur after the assemblies they depend on are built? (Or, more simply, that template transformations occur last?)


Unfortunately, the msbuild T4 host doesn't yet support embedded macro or msbuild variables in assembly names.

However, it does support Windows environment variables "%foo%", so although it means some machine-level setup, you can get something that works across in-IDE and build time transforms.


My understanding is that Visual Studio 2013 will finally solve this problem, but that doesn't do me much good as I'm still on Visual Studio 2012. After a lot of effort I finally ran across a solution.

In the project that has the template you wish to run, add the following as a pre-build step on the Build Events tab of the project properties page.

set textTransformPath="%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\TextTransform.exe"
if %textTransformPath%=="\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\TextTransform.exe" set textTransformPath="%CommonProgramFiles%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\TextTransform.exe"
set ProjectDir=$(ProjectDir)
%textTransformPath% "%ProjectDir%StringGenerator.tt"

The first two lines take care of the differences between locating TextTransform.exe on 32-bit and 64-bit systems. The third line is the key. I need the path to the project location inside my template, so I set a local environment variable equal to the value of the build's $(ProjectDir) property. Inside my template, just use the following:

var projectDir = Environment.GetEnvironmentVariable("ProjectDir");

This has solved my issue.


I created a seperate solution that contained my needed referenced assemblies. The I had my buildscript build the reference solution first, then transform the templates, then build the solution containing the generated code.


If you want to reference dependency assemblies within a T4 script using macros and have text templating succeed during build-time, then you have to use project properties.

Within your project:

  <Import Project="$(ProgramFiles)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\TextTemplating\Microsoft.TextTemplating.targets" />
  <PropertyGroup>
    <T4ProjectDir>$(ProjectDir)</T4ProjectDir>
  </PropertyGroup>
  <ItemGroup>
   <T4ParameterValues Include="T4ProjectDir">
    <Value>$(T4ProjectDir)</Value>
    <Visible>false</Visible>
   </T4ParameterValues>
  </ItemGroup>

Where the path to your text templating environment may be different.

Then use $(T4ProjectDir) as you would use any other macro in your text template.

Or you could also simply refer to existing properties:

  <ItemGroup>
   <T4ParameterValues Include="ProjectDir">
    <Value>$(ProjectDir)</Value>
    <Visible>false</Visible>
   </T4ParameterValues>
  </ItemGroup>
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号