Using $(SolutionDir) when running template via TextTransform.exe

后端 未结 3 1226
我在风中等你
我在风中等你 2020-12-19 05:59

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

相关标签:
3条回答
  • 2020-12-19 06:09

    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;
    }
    
    0 讨论(0)
  • 2020-12-19 06:19

    I used an approach very similar to piers7 -- except in my case, I actually needed to keep the *.tt file in the same directory (because of runtime lookup of files based on relative paths), but didn't care if the file itself was named differently. As such, instead of having $templateTemp create a temporary file in a temp directory, I kept it in the same folder.

    I also needed to recursively search for the *.tt files anywhere in the solution directory.

    Here is the resulting script, taking piers7's and modifying it:

    <#
    .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;
    }
    
    0 讨论(0)
  • 2020-12-19 06:22

    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
    
    0 讨论(0)
提交回复
热议问题