8

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

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

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

4

3 回答 3

4

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

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

当模板从命令行运行时,所使用的实现是由CommandLineHost提供的,它的实现只是查找参考路径和 GAC 中提供的文件。鉴于此时文件名仍然包含 $(SolutionPath) 位,它永远不会成功。

您可以实现自己的 TextTransform.exe 版本,但您必须从头开始(或使用反射),因为 CommandLineHost 是内部的 :-( 或者您可能会利用 Mono 端口https://stackoverflow.com/一个/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;
}
于 2013-06-06T05:56:16.820 回答
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;
}
于 2015-01-22T21:16:53.983 回答
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
于 2018-06-08T01:40:01.737 回答