I'm trying to get our T4 templates to run at build time, without adding dependencies on the Visual Studio Modeling SDK. I've successfully used a variant of the batch file shown here, but I now have a problem in that my .tt files use the $(SolutionDir)
variable to reference other projects (and thus are now not compiling).
What is the best way to handle this? What have other people done? (Hard-coding absolute paths is not an option)
EDIT:
I see there's the -a
argument that can be passed to TextTransform.exe, is it possible to use this to define $(SolutionDir)
?
Looking through the source code from TextTransformation.exe (with ILSpy) I don't think this is possible without modifying the template (but I do have a solution).
Ultimately what we care about here is the step during the template parsing where Microsoft.VisualStudio.TextTemplating.Engine.ResolveAssemblyReferences() is called. This delegates to ITextTemplatingEngineHost.ResolveAssemblyReference() (though it does expand environment variables first)
When the template is run from the command line, the implementation being used is that provided by the CommandLineHost, and its implementation simply looks for the file as supplied in reference paths and the GAC. Given the file name will at this point still have the $(SolutionPath) bit in, it's never going to succeed.
You could implement your own version of TextTransform.exe, but you'd have to start largely from scratch (or use reflection), since CommandLineHost is internal :-( Or you could potentially leverage the Mono port https://stackoverflow.com/a/1395377/26167
I can't say I'm happy about this, because I find myself in the same boat...
Edit: However... since ultimately all you need to do is change the template, I put together a PowerShell script to copy the templates to the temp directory, manually expanding the $(SolutionDir) macro in the process, and execute them from there. That seems to work just fine.
Drop this into the offending project (you might want to change the file extension) and you should be good to go:
<#
.Synopsis
Executes all the T4 templates within designated areas of the containing project
.Description
Unfortunately the Visual Studio 2010 'Transform All Templates' function doesn't appear
to work in SSDT projects, so have to resort to hackery like this to bulk-execute templates
#>
param(
)
$ErrorActionPreference = 'stop';
$scriptDir = Split-Path $MyInvocation.MyCommand.Path
$commonProgramFiles32 = $env:CommmonProgramFiles
if (Test-Path environment::"CommonProgramFiles(x86)") { $commonProgramFiles32 = (gi "Env:CommonProgramFiles(x86)").Value };
$t4 = Resolve-Path "$commonProgramFiles32\Microsoft Shared\TextTemplating\10.0\texttransform.exe";
$solutionDir = Resolve-Path "$scriptDir\..\"
$templates = @(dir "$scriptDir\Database Objects\load\*.tt")
# Cloning to temp dir originally caused issues, because I use the file name in the template (doh!)
# Now I copy to temp dir under the same name
pushd $scriptDir;
try{
foreach($template in $templates){
$templateTemp = Join-Path ([IO.Path]::GetTempPath()) $template.Name;
$targetfile = [IO.Path]::ChangeExtension($template.FullName, '.sql');
Write-Host "Running $($template.Name)"
Write-Host "...output to $targetFile";
# When run from outside VisualStudio you can't use $(SolutionDir)
# ...so have to modify the template to get this to work...
# ...do this by cloning to a temp file, and running this instead
Get-Content $template.FullName | % {
$_.Replace('$(SolutionDir)',"$solutionDir")
} | Out-File -FilePath:$templateTemp
try{
& $t4 $templateTemp -out $targetfile -I $template.DirectoryName;
}finally{
if(Test-Path $templateTemp){ Remove-Item $templateTemp; }
}
}
}finally{
popd;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With