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?
To debug a design-time text template, save the text template file, and then choose Debug T4 Template on the shortcut menu of the file in Solution Explorer. To debug a run-time text template, simply debug the application to which it belongs.
TT stands for - Visual Studio Text Template is a software development tool created by the Microsoft. Further explanation - TT file contains text block and control logic used for generating new files. To write the Text Template file we can use either - Visual C# or Visual Basic Code.
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;
}
}
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).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With