Getting a CodeDomProvider in an MSBuild Task

Trying to get the correct CodeDomProvider inside of an MSBuild task wasn’t as easy as I would have liked. Well actually the code itself is pretty simple but finding out how to actually do it was difficult. There doesn’t appear to be a profusion of people doing such a thing so the blogosphere and forums are fairly sparse with related information. After messing around with it for a couple of hours I think I finally found a pretty reliable (aka it doesn’t feel like a dirty hack) way to do it.

The two key bits of information is the <Language /> PropertyItem that each language defines in its own common targets file (by convention) and the CodeDomProvider.GetAllCompilerInfo() method. Here is my targets file.
 
<!–Reference the assembly where our tasks are defined–>
<UsingTask TaskName=“MetaSharp.MSBuild.TemplateTask” AssemblyFile=“$(MSBuildExtensionsPath)\MetaSharp\MetaSharp.MSBuild.dll” />
 
<!–Compile target (this is the target that calls the compiler task)–>
<Target Name=“BeforeBuild”>
    <Message Text=“Building: @(MetaSharpTemplate)” />
    <TemplateTask Templates=“@(MetaSharpTemplate)” Language=“$(Language)”>
      <Output TaskParameter=“Generated” ItemName=“Compile” />
    </TemplateTask>
</Target>
</Project>
The key here is the Language=”$(Language)” property on the task. And here is my task with a Linq statement to find the correct CodeDomProvider.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Collections;
using System.CodeDom;
using System.CodeDom.Compiler;
 
namespace MetaSharp.MSBuild
{
      publicclassTemplateTask : Task
      {
            // Properties
            [Required]
            publicITaskItem[] Templates { get; set; }
 
            [Required]
            publicITaskItem Language { get; set; }
 
            [Output]
            publicITaskItem[] Generated { get; set; }
 
            publicoverridebool Execute()
            {
                  base.Log.LogMessage(0, “Building MetaSharp templates.”);
 
                  if (this.Language == null || string.IsNullOrEmpty(this.Language.ItemSpec))
                  {
                        base.Log.LogError(“You must have a Language PropertyItem defined somewhere in your project files to specify which CodeDomProvider to use (i.e <Language>C#</Language>)”);
                        returnfalse;
                  }
 
                  CodeDomProvider provider = FindProvider(this.Language.ItemSpec);
 
                  returntrue;
            }
 
            privateCodeDomProvider FindProvider(string language)
            {
                  CodeDomProvider[] providers = (from info inCodeDomProvider.GetAllCompilerInfo()
                                                               from l in info.GetLanguages()
                                                               where l.ToUpperInvariant() == language.ToUpperInvariant()
                                                               selectCodeDomProvider.CreateProvider(l))
                                                               .ToArray();
                 
                  CodeDomProvider provider = null;
                  if (providers.Length == 0)
                  {
                        Log.LogError(“Unable to find a valid CodeDomProvider for this project type. Try adding a valid Language property item to your msbuild project file”);
                  }
                  elseif (providers.Length > 1)
                  {
                        // It would be surprising if this ever happened…
                        Log.LogError(“Found multiple valid CodeDomProviders for this Language type. Try adding a less ambiguous Language property item to your msbuild project file”);
                  }
                  else provider = providers[0];
 
                  return provider;
            }
      }
}
For those of you who are curious MetaSharp is the tentative name of the CodeDom DSL I have been working on using MGrammar. It’s coming along pretty well, this task will build MetaSharp code files to be compiled along with the project they are contained in. There are lots of details to be shored up but the basic use cases are working right now. When I have things a little more polished I will probably create another post with some samples.

Drop a brain bomb

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s