通过TextTransform.exe运行模板时使用$(SolutionDir)

时间:2012-12-10 17:00:35

标签: .net visual-studio batch-file t4

我正在尝试让我们的T4模板在构建时运行,而不会在Visual Studio Modeling SDK上添加依赖项。我已成功使用显示here的批处理文件的变体,但我现在遇到一个问题,即我的.tt文件使用$(SolutionDir)变量来引用其他项目(因此现在不编译)

处理此问题的最佳方法是什么?其他人做了什么? (硬编码绝对路径不是一种选择)

编辑: 我看到有-a参数可以传递给TextTransform.exe,是否可以使用它来定义$(SolutionDir)

3 个答案:

答案 0 :(得分:2)

查看TextTransformation.exe(使用ILSpy)的源代码,如果不修改模板,我认为这是不可能的(但我确实有解决方案)。

最终我们关心的是模板解析过程中调用 Microsoft.VisualStudio.TextTemplating.Engine.ResolveAssemblyReferences()的步骤。这委托 ITextTemplatingEngineHost.ResolveAssemblyReference()(虽然它确实首先扩展了环境变量)

当从命令行运行模板时,正在使用的实现是 CommandLineHost 提供的实现,其实现只是查找引用路径和GAC中提供的文件。鉴于文件名此时仍会有$(SolutionPath)位,它永远不会成功。

您可以实现TextTransform.exe的自己的版本,但是您必须从头开始(或使用反射),因为CommandLineHost是内部的:-(或者您可以利用单声道端口https://stackoverflow.com/a/1395377/26167

我不能说我对此感到高兴,因为我发现自己在同一条船上......

编辑但是......因为最终您需要做的就是更改模板,我将PowerShell脚本放在一起,将模板复制到临时目录,手动展开$(SolutionDir)宏在这个过程中,并从那里执行它们。这似乎工作很好

将其删除到违规项目中(您可能想要更改文件扩展名),您应该好好去:

<#
.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;
}

答案 1 :(得分:0)

我使用的方法非常类似于piers7 - 除了在我的情况下,我实际上需要将* .tt文件保存在同一目录中(因为基于相对路径的文件的运行时查找),但是并不关心如果文件本身的名称不同。因此,我没有让$ templateTemp在临时目录中创建临时文件,而是将其保存在同一个文件夹中。

我还需要在解决方案目录中的任何地方递归搜索* .tt文件。

这是生成的脚本,使用piers7并修改它:

<#
.Synopsis
Executes all the T4 templates within designated areas of the containing project
#>
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\12.0\texttransform.exe";
$solutionDir = Resolve-Path "$scriptDir\"
$templates = Get-ChildItem -Path $scriptDir -Filter *.tt -Recurse
$extension = '.ts';

pushd $scriptDir;
try{
    foreach($template in $templates){
        # keeping the same path (because my template references relative paths), 
        #    but copying under different name:
        $templateTemp = $template.FullName + "____temporary"
        $targetfile = [IO.Path]::ChangeExtension($template.FullName, $extension);
        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;
}

答案 2 :(得分:0)

基于piers7代码。

Powershell脚本Transform.ps1

Param(
  [string]$variablesPath,
  [string]$t4File,
  [string]$targetfile
)

# Get C:\Program Files (x86)\Common Files
$commonProgramFiles32 = $env:CommmonProgramFiles
if (Test-Path environment::"CommonProgramFiles(x86)") { $commonProgramFiles32 = (gi "Env:CommonProgramFiles(x86)").Value };

# Get path t4 transformer executable
$t4 = Resolve-Path "$commonProgramFiles32\Microsoft Shared\TextTemplating\14.0\texttransform.exe";

# File object for the $t4 file (.tt)
$template = (get-item $t4File)

# Create a dictionary from the contents of system variables file
$vars = @{}
get-content $variablesPath | Foreach-Object {
    $split = $_.split("=")
    $vars.Add($split[0], $split[1])
}

# Temp file name to store the modified template.
$templateTemp = Join-Path ([IO.Path]::GetTempPath()) $template.Name;

# Read the content of the template
$content = [IO.File]::ReadAllText($template.FullName)

# Replace the variables in the template with the actual values.
($vars.Keys | Foreach-Object {
    $content = $content.Replace("`$($_)",$vars[$_])
})

# Write the modified template to the original location
$content > $templateTemp

# Execute the transformation, and delete the temporary template after done.
try{
    & $t4 $templateTemp -out $targetfile -I $template.DirectoryName;
}finally{
    if(Test-Path $templateTemp){ Remove-Item $templateTemp; }
}

# texttransform.exe seems to be messing up the BOM of the file.  Fixing.
[IO.File]::WriteAllText($targetfile, [IO.File]::ReadAllText($targetfile), [System.Text.Encoding]::UTF8)

然后从预构建事件:

pushd $(ProjectDir)
if exist "$(ProjectDir)var.tmp.txt" del "$(ProjectDir)var.tmp.txt"
echo SolutionDir=$(SolutionDir)>>"$(ProjectDir)var.tmp.txt"
echo ProjectDir=$(ProjectDir)>>"$(ProjectDir)var.tmp.txt"
if exist "$(ProjectDir)my.cs" del "$(ProjectDir)my.cs"
Powershell.exe -ExecutionPolicy Unrestricted -File "..\..\..\Transform.ps1" "$(ProjectDir)var.tmp.txt" "$(ProjectDir)My.tt" "$(ProjectDir)My.cs"
if exist "$(ProjectDir)var.tmp.txt" del "$(ProjectDir)var.tmp.txt"
popd