Documentation/codegen.md
The following document describes how code generation in this repo works. The goal is to have all our code generation done through the use of T4 text generation. See the offical Visual Studio T4 documentation for more information.
Currently, we are evaluating the use of design-time text templates. This gives us the ability to simply add the templates and associated targets to the build, without the need of maintaining a separate tool to do run-time generation. When using the SDK-style project format, including the Microsoft.TextTemplating.targets requires us to manually import Sdk.Targets because the BuildDependsOn variable, which is modified by the T4 targets, would otherwise be overwritten by the automatic inclusion of Sdk.targets. This causes the TransformAll target to not run before the Build target. The boilerplait for including design-time templates has been encapsulated in the $(WpfCodeGenDir)DesignTimeTextTemplating.targets file, so the pattern for enabling these in a project looks like this:
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<Import Project="$(WpfCodeGenDir)AvTrace\GenTraceSources.targets" />
<Import Project="$(WpfCodeGenDir)AvTrace\GenAvMessages.targets" />
<Import Project="$(WpfCodeGenDir)DesignTimeTextTemplating.targets" />
T4 templates can be a powerful tool, however without a conscious effort, they can quickly become unmaintainable and difficult to understand. When authoring/modifying a T4 template, extra care should be taken to ensure that the templates are as readable as possible. While the "readability" of a template may be a bit subjective, there are a few common guidelines that really help. Note that these are guidelines, and are not mandatory. The readability of the template is paramount to all things.
Correct
<#
string helloWorld = " Hello World ";
hellowWorld = helloWorld.Trim();
#>
<#= hellowWorld #>
Incorrect
<# string helloWorld = " Hello World ";
helloWorld = helloWorld.Trim(); #>
<#= hellowWorld #>
In similar fashion, single-line code statements should contain the "<#" and "#>" tags on the same line as the code. This way we can know that any line that starts with a "<#" that has code next to it is only a one-line statement.
if/else/elseif statements, and the closing bracket, should all be contained on a single line
Correct
<# if (WriteAsFunction()){ #>
bool GetFoo()
{
...
}
<# } else { #>
bool Foo
{
get {...}
}
<# } #>
Incorrect
<#
if (WriteAsFunction())
{
#>
bool GetFoo()
{
...
}
<#
}
else
{
#>
bool Foo
{
get {...}
}
<#
}
#>
Correct
<#+ void OutputFooFunction() { #>
bool GetFoo()
{
...
}
<#+ } #>
Incorrect
<#+ void FooFunction() { #>
bool GetFoo()
{
...
}
<#+ } #>
Unless there is a good reason (that should be documented), all codegen related targets should go into the $(WpfArcadeSdk)tools\CodeGen folder. This way we have a clean and clear location where we are able to keep track of all code generation in the codebase.
These two projects codegen the files the WPF codebase uses for tracing, and both use the AvTraceMessages.xml files located in the project location that includes it, located via the MSBuild property $(MSBuildProjectFileDirectory).
Note: GenTraceSources should currently only be used by WindowsBase. It generates the PresentationTraceSources class, which is a public class. Changing this file can impact the public API surface of WindowsBase or other WPF assemblies.