Determine solution configuration (debug/release) when running a T4 template
Asked Answered
K

3

27

I have a T4 template that can output either optimized content or standard content based on a flag. Currently I'm manually changing the flag based on my needs.

What I'd love to do is set the flag based on the Configuration of the Solution in Visual Studio. If set to build in Debug mode, I would output standard content. If set to build in Release mode, I would optimize the content instead. I found another T4 question that looks promising: T4 Text Template - Is it possible to get compilation symbols from host?

However, in my case I would want to do something like the following:

<#@ template language="C#" hostspecific="True" 
    compilerOptions="/d:$(ConfigurationName)" #>

Since I can use $(SolutionDir) in an assembly directive:

<#@ assembly name="$(SolutionDir)\myreference.dll" #>

I would think the /d:$(ConfigurationName) would get me where I needed to go, and then I could do the following to set my flag:

<#
#if Debug 
 optimize = false;
#else 
 optimize = true;
#endif 
#>

Alas, this doesn't seem to work. I've also attempted using:

Host.ResolveParameterValue("-", "-", "ConfigurationName");

Also to no avail. Any ideas?

Klenk answered 7/4, 2011 at 23:28 Comment(0)
K
26

No sooner do I ask but I find a snippet at the bottom of this MSDN article that gets me where I need to be. The answer here is to use the IServiceProvider interface to get the Visual Studio DTE. Here's code that is getting it done (apologies in advance for the hard-coded "Debug"):

    var serviceProvider = Host as IServiceProvider;
    var dte = serviceProvider.GetService(typeof(DTE)) as DTE;
    var configName = dte.Solution.SolutionBuild.ActiveConfiguration.Name;
    optimize = (configName != "Debug"); 

UPDATE

This code will check to see if the active project's current configuration has optimizations turned on. It still has a hard-coded property name, but one that's much less likely to change. Also, using the project's optimization flag makes a lot of sense for my scenario (trying to decide if I should turn on optimizations in my own code):

    var serviceProvider = Host as IServiceProvider;
    var dte = serviceProvider.GetService(typeof(EnvDTE.DTE)) as DTE;
    config = dte.Solution
                .FindProjectItem(Host.TemplateFile)
                .ContainingProject
                .ConfigurationManager
                .ActiveConfiguration;
    foreach(Property prop in config.Properties)
    {
        if (prop.Name == "Optimize")
        {
            optimize = (bool)prop.Value;
            break;
        }
    }
Klenk answered 7/4, 2011 at 23:48 Comment(5)
Is there a page for the DTE class in the MSDN docs? I've been searching for it and having difficulty finding one.Jeri
Nevermind. I think I just found it: msdn.microsoft.com/en-us/library/vstudio/EnvDTE(v=vs.100).aspxJeri
SDTE? Is that a typo?Dilation
@RonnieOverby No - that's the interface name: msdn.microsoft.com/en-us/library/…Klenk
How can I get ProjectConfigurationPlatforms section, for each project ?Charqui
B
13

For people trying to get this work at design-time (file save) as well as at build-time (F5/F6), two methods are necessary.

Emil describes the design-time method. For build-time, you first have to specify a T4 parameter in your project file:

<ItemGroup>
  <T4ParameterValues Include="BuildConfiguration">
    <Value>$(Configuration)</Value>
    <Visible>false</Visible>    
  </T4ParameterValues>
</ItemGroup>

Then you have to reference it at the top of your .tt:

<#@ parameter type="System.String" name="BuildConfiguration" #>

And then look for whichever of them happens to be provided:

string configurationName = Host.ResolveParameterValue("-", "-", "BuildConfiguration");
if (string.IsNullOrWhiteSpace(configurationName))
{
    var serviceProvider = (IServiceProvider)Host;
    var dte = (DTE)serviceProvider.GetService(typeof(DTE));
    configurationName = dte.Solution.SolutionBuild.ActiveConfiguration.Name;
}

It is necessary to include the logic for both if you want your template to work in both scenarios. The design-time method does not work at build-time (the DTE Host is not around to provide the Solution), and the build-time method does not work at design-time (MSBuild is not around to provide the parameter).

Biskra answered 26/11, 2014 at 20:53 Comment(0)
U
1

If trying to do this in a VS2017 ASP.Net Core project then following is a solution that worked for me with highlights from my post here.

This MSDN blog by Jeremy Kuhne and this blog by Thomas Levesque and several other links such as this MSDN doc helped get it working in VS2017.

I did not have to add anything to the beginning of the .csproj file since VS2017 has the files already included by default.

In Visual Studio 2017, the Text Template Transformation component is automatically installed as part of the Visual Studio extension devlopment workload. You can also install it from the Individual components tab of Visual Studio Installer, under the Code tools category. Install the Modeling SDK component from the Individual components tab.

I ended up with the following .csproj changes at the end of the file:

  <PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
    <!-- Run the Transform task at the start of every build -->
    <TransformOnBuild>true</TransformOnBuild>
    <!-- -->
    <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
    <!-- Transform every template every time -->
    <TransformOutOfDateOnly>false</TransformOutOfDateOnly>
  </PropertyGroup>

  <!-- add AFTER import for $(MSBuildToolsPath)\Microsoft.CSharp.targets -->
  <Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />

    <ItemGroup>
    <T4ParameterValues Include="BuildConfiguration">
        <Value>$(Configuration)</Value>
        <Visible>False</Visible>
    </T4ParameterValues>
  </ItemGroup>

    <Target Name="CreateT4ItemListsForMSBuildCustomTool" BeforeTargets="CreateT4ItemLists" AfterTargets="SelectItemsForTransform">
    <ItemGroup>
        <T4Transform Include="@(CreateT4ItemListsInputs)" Condition="'%(CreateT4ItemListsInputs.Generator)' == 'MSBuild:TransformAll'" />
    </ItemGroup>
  </Target>

This in the T4 Template:

<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="EnvDTE" #>
<#  
    //Build time
    string configName = Host.ResolveParameterValue("-", "-", "BuildConfiguration");
    if (string.IsNullOrWhiteSpace(configName))
    {
        try
        {
            //Design time.
            var serviceProvider = (IServiceProvider)Host;
            EnvDTE.DTE dte = (EnvDTE.DTE)serviceProvider.GetService(typeof(EnvDTE.DTE));
            configName = dte.Solution.SolutionBuild.ActiveConfiguration.Name;
        }
        catch(Exception ex)
        {
            configName = ex.Message;
        }
    }
#>
<#=configName#>

The following property settings on the .tt file:

Build Action: None
Copy to Output Directory: Do Not Copy
Custom Tool: MSBuild:TransformAll
Uropygium answered 24/10, 2018 at 13:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.