【问题标题】:Using $(SolutionDir) when running template via TextTransform.exe通过 TextTransform.exe 运行模板时使用 $(SolutionDir)
【发布时间】:2012-12-10 17:00:34
【问题描述】:

我试图让我们的 T4 模板在构建时运行,而不添加对 Visual Studio 建模 SDK 的依赖项。我已经成功使用了显示为here 的批处理文件的变体,但我现在遇到的问题是我的.tt 文件使用$(SolutionDir) 变量来引用其他项目(因此现在没有编译)。

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

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

【问题讨论】:

  • 你试过set SolutionDir=%cd%把变量设置为当前目录吗?
  • 不行,-a !!$(SolutionDir)!C:\dev\mysolutionroot
  • 有一个 T4 的 Mono 端口。我想知道答案是否在代码库中的某个地方。见stackoverflow.com/a/1395377/26167

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


【解决方案1】:

查看来自 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;
}

【讨论】:

  • 您能否详细说明一下 - 将其“放入”项目的哪个位置?您会在构建前或构建后操作中调用它吗?
  • 所写的代码假定解决方案目录是一个文件夹,并找到与其自身相关的 TT 文件。所以只需将它添加到根目录下的项目中。您可以从命令行(在 PowerShell 中)执行它,是的 - 如果需要,您可以在预构建步骤中调用它(我手动执行)。
  • @piers7 感谢分享。我已经用正则表达式从 tt 文件本身提取的扩展名替换了您的固定文件扩展名...$extension = Select-String -Path $template.FullName -Pattern '&lt;#@\s*output\s+extension="(.*)"\s*#&gt;' | ForEach-Object {$_.Matches.Groups[1].Value}
【解决方案2】:

我使用了一种与 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;
}

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-02
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 2012-05-31
      • 2018-05-27
      相关资源
      最近更新 更多