MSBuild Task Generator: Part 5. Introducing the CodeDOM. Hello World was never so much fun.

Yesterday we looked at a basic implementation of ITaskProperty – and I promised we start looking at the code generation today.  We won’t be looking at the XML -> C# rendering but rather the CodeDOM and generating some basic expressions.

 

Before we go on I’m going to assume you are familiar with the CodeDOM is – though perhaps you have never used it.  If you are not then you will want to do some reading:

 

Using the CodeDOM

https://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconusingcodedom.asp

 

Generating Source Code and Compiling a Program from a CodeDOM Graph

https://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconusingcodedom.asp

 

CodeDOM Quick Reference

https://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconCodeDOMQuickReference.asp

 

Generating code really isn’t that hard once you understand the patterns.

 

Let’s look at a few quick examples that will all build on each other into a simple Hello World class.

 

First we need to create our code generator and provider.

 

ICodeGenerator gen;

CSharpCodeProvider prov = new CSharpCodeProvider();

gen = prov.CreateGenerator();

 

That’s what we need to start generating code.  Now we need to figure out what to generate.  I usually start with a namespace:

 

CodeNamespace outerNameSpace = new CodeNamespace("CodeDOMSample");

 

A namespace does not mean much without a class, right?  So let’s put one in it.

 

CodeTypeDeclaration ctd = new CodeTypeDeclaration("HelloWorld");

ctd.IsClass = true;

 

Now we’re cooking with gas.  So let’s stop right here and see what would happen if we add the class to the namespace and then generate some code.  First we need to figure out how we want to generate the code using an instance of the CodeGeneratorOptions class.  These are the options I usually use when generating code:

 

CodeGeneratorOptions co = new CodeGeneratorOptions();

co.BracingStyle = "C";

co.BlankLinesBetweenMembers = true;

co.VerbatimOrder = true;

Then we add the type to the namespace:

 

outerNameSpace.Types.Add(ctd);

 

And generate to stdout (Console.Out):

 

gen.GenerateCodeFromNamespace(outerNameSpace, Console.Out, co);

 

This produces the following (I’ve left all formatting un-changed):

 

namespace CodeDOMSample

{

   

   

    public class HelloWorld

    {

    }

}

 

That looks good but it’s missing something.  I’d like it to have at last one namespace (System) imported.  Thankfully that is really easy to do:

 

outerNameSpace.Imports.Add(new CodeNamespaceImport("System"));

 

 

Now when we render the C# we get this:

 

namespace CodeDOMSample

{

    using System;

   

   

    public class HelloWorld

    {

    }

}

 

That looks a little better.  Now let’s add a public method “SayHello” that will print “Hello World” to the console.

 

First we need to create a public method.  We want a public method named “SayHello” that returns void.

 

CodeMemberMethod sayHello = new CodeMemberMethod();

sayHello.Name = "SayHello";

sayHello.Attributes = MemberAttributes.Public;

sayHello.ReturnType = new CodeTypeReference(typeof(void));

 

We’d like this method to perform a Console.WriteLine(“Hello World”);

 

So first we need to create a reference to the Console type:

 

CodeTypeReferenceExpression console = new CodeTypeReferenceExpression(typeof(System.Console));

 

Next we create a reference to the function WriteLine bound to the console type (this is a static method so our caller target is the type, not an instance):

 

CodeMethodReferenceExpression consoleWriteLine = new CodeMethodReferenceExpression(console, "WriteLine");

 

Next we create the string literal “Hello World” as a primitive expression:

 

CodePrimitiveExpression helloWorld = new CodePrimitiveExpression("Hello World");

 

And finally we bind them all together to invoke the method call with the appropriate parameters:

 

CodeMethodInvokeExpression sayHelloWorld = new CodeMethodInvokeExpression(

                                                   consoleWriteLine,

                            new CodeExpression[] { helloWorld }

                                               );

 

Finally add the method call to the member method:

 

sayHello.Statements.Add(sayHelloWorld);

 

And add the method to the type:

 

ctd.Members.Add(sayHello);

 

Now render one last time:

 

namespace CodeDOMSample

{

    using System;

   

   

    public class HelloWorld

    {

       

        public virtual void SayHello()

        {

            System.Console.WriteLine("Hello World");

        }

    }

}

 

Tomorrow we’ll tackle conditional expressions and loops.

 

As always – this is sample code.  No warranty expressed or implied.  Your mileage may vary. Etc, etc, etc.