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

2019-07-04 21:20发布

我试图让我们的T4模板在构建时运行,而不会在Visual Studio的建模SDK添加依赖。 我已经成功地使用所示的批处理文件的一个变种在这里 ,但我现在已经在我的.TT文件使用一个问题$(SolutionDir)变量引用其他项目(现在这样不编译)。

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

编辑:我看到有该-a可以传递到TextTransform.exe的说法,是有可能使用这种方法来定义$(SolutionDir)

Answer 1:

通过源代码TextTransformation.exe(与ILSpy)看,我不认为这是可能的,而无需修改模板(但我有一个解决方案)。

最终,我们关心的就是把模板解析其中Microsoft.VisualStudio.TextTemplating.Engine.ResolveAssemblyReferences()被调用过程中的步骤。 这代表们ITextTemplatingEngineHost.ResolveAssemblyReference()(尽管它首先展开的环境变量)

当模板从命令行运行,所使用的实现是通过提供CommandLineHost,并且如参考路径和GAC供给它的实现简单地查找该文件。 由于文件名会在这一点上仍然有,它永远不会获得成功的$(SolutionPath)位。

你可以实现自己的TextTransform.exe的版本,但你不得不从头开始大部分(或使用反射),因为CommandLineHost是内部:-(或者你可能会充分利用单端口https://stackoverflow.com/一个/二万六千一百六十七分之一百三十九万五千三百七十七

我不能说我很高兴这一点,因为我发现自己在同一条船上...

编辑:但是......因为最终所有你需要做的是改变模板,我把一个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;
}


Answer 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;
}


Answer 3:

Based on piers7 code.

Powershell script 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)

Then from the pre-build event:

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


文章来源: Using $(SolutionDir) when running template via TextTransform.exe