Customizing Generated Resources in DSL Tools

Most of the code we generate with DSL Tools is pretty agnostic about its location or the filename you use for the template or the generated output.

The one exception to this is the generated resource file (usually a pair called DomainModelResx.tt/DomainModelResx.resx)

The reason this puppy is a little more sensitive is that the output resx is generated as item with a compile type of  Embedded Resource in the Dsl csharp project that it lives in.

This causes the project system to pick up the relative path from the project root to the resx file and use that to form the Id that is used to embed the resource into the assembly.

Naturally, we also have to reference the resource at runtime in order to make use of its contents and to do that, we need to encode its Id at the point of use.  This happens to be in the DomainModel.tt/DomainModel.cs pair.

What this means is that we have to provide some logic for generating that resource Id in our T4 templates as there isn't an API in Visual Studio to get hold of it :-(

Rather than trying to be too clever, we just use a simple scheme that expects the resource to be in the default GeneratedCode folder structure.

OK, that's all a bit abstract, so here's an example.

Given a normal folder structure like this:

image

You'll get a resource created like this:

image

You can see that the GeneratedCode folder name has been encoded into the resource Id.

We'll match that in the generated DomainModel.cs file with the following snippet

    1: /// <summary>
    2: /// The base name of this model's resources.
    3: /// </summary>
    4: public const string ResourceBaseName = "MicrosoftCorp.DemoLanguage.GeneratedCode.DomainModelResx";

 

So now suppose you go and rearrange your DSL project and now there's an intermediate directory called Dummy? And how about you also rename the template and generated file to be simply DslResources.tt/resx?

image

You'll get an embedded resource like this.

image

So far, this is all just regular VS stuff and not much to do with DSL Tools, but if we peek into DomainModel.tt/.cs, we'll see that the generated code has no idea that it has been moved and doesn't change that ResourceBaseName constant.

So how do we fix it up? We wouldn't leave you in the lurch now would we? No, of course not.

As an aside, we've done some work in DSL 2010 to let that directory change peek through into the template, but that doesn't help with the resource file name change.

To allow fine-grained control over matching the generated code path and resource name, we added three optional parameters to the DSL directive processor used in DSL Tools' .tt files:

  • GeneratedCodeFolder: allow you to specify the folder relative to the project root where the generated resources live.
  • GeneratedResourceFile: allows you to specify the filename for the resource base name
  • ProjectDefaultNamespace: allows you to override the project file's default namespace as the starting point of the resource base name

Here's an example of using the first two to match up our project changes

 <#@ Dsl processor="DslDirectiveProcessor"  requires="fileName='..\DslDefinition.dsl';GeneratedCodeFolder='Dummy.GeneratedCode';GeneratedResourceFile='DslResources'" #>

We simply catenate the three values to form the overall base name, but keeping them separate allows us to reuse them in other scenarios. Here's the result, which matches the actual embedded resource:

    1: /// <summary>
    2: /// The base name of this model's resources.
    3: /// </summary>
    4: public const string ResourceBaseName = "MicrosoftCorp.DemoLanguage.Dummy.GeneratedCode.DslResources";

These extra parameters are pretty undiscoverable options on the DSL directive processor, so I hope this tip helps someone out.  It's also a good example of how you can easily expand the directive processor we generate for you with your own DSL.  These three parameters were all added purely additively in a partial class next to the generated code for the dsl directive processor.  (Yes, we do generate the dsl directive processor that you use for parsing dsls to generate the directive processors for other dsls :-) )

Have fun rearranging your projects.

Technorati Tags: DSL Tools, DirectiveProcessor, Resources, ResX