Using MSBuild properties in T4 templates

The Problem

Text Templates in VS are cool, but they have some rough edges. One in particular is getting at project properties from your templates.

One of the biggest problems is that the generator for the project build and the generator that runs inside VS are different. The project build generator will allow you to define your own parameters to send to the build task, but the VS generator won't read them. The VS generator will let you look at only 4 things:

  1. "namespaceHint"
  2. "projects" (returns a vertical bar separated list of projects)
  3. "projectDefaultNamespace"
  4. "projectName:OutputPath" (where projectName is the name of the project)

While you could match these names and values in your MSBuild project files it's pretty limiting. It's most unfortunate that the VS generator doesn't pick up <T4ParameterValues>.

A Solution

Utilizing a little-known ability to run arbitrary MSBuild tasks as a custom tool you can unify the behavior on the generator the project build uses. You'll need to make a small tweak to your project file to set this up.

  1. Edit your .cs/.vbproj and include the CustomT4.targets at the end of the file.
  2. Reload your project.
  3. Change the "Custom Tool" property from "TextTemplatingFileGenerator" to "MSBuild:TransformAll".
  4. Change hostSpecific="false" to "true" in the <#@template> directive.
  5. Use this.Host.ResolveParameterValue(".", ".", "TargetPath") to get at the TargetPath property.

The simple test template looks like the following:

 <#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
Test "<#= this.Host.ResolveParameterValue(".", ".", "TargetPath") #>"

And here is the target file with a number of properties you're likely to want.

Notes

The first two arguments to ResolveParameterValue() are meaningless, but can not be empty strings despite the documentation. I decided to use period because it's unobtrusive.