MSBuild Task Generator: Part 8. Implementing ITaskGenerator (a series within a series)

Now that we’re all good friends with the CodeDOM it’s time to look at the far less interesting reason for this whole series … generating MSBuild tasks.

 

Since we’ve implemented ITask and ITaskProperty it’s time to implement ITaskGenerator.

 

We’ll be taking a top-down approach to this.  We’ll start by looking at the implementation of ITaskGenerator.Generate and then drilling down into each method.

 

Well, not quite.  Generate, for the most part, calls another function named GenerateFile.  Let’s start there instead.

 

CodeNamespace GenerateFile(MBFTaskFile tf)

{

      CodeNamespace outerNameSpace = new CodeNamespace(tf.Namespace);

      CodeTypeDeclaration ctd = new CodeTypeDeclaration(tf.Class);

      ctd.IsClass = true;

      ctd.TypeAttributes |= System.Reflection.TypeAttributes.Abstract;

      if (StringHasData(tf.BaseClass))

      {

      ctd.BaseTypes.Add(new CodeTypeReference(tf.BaseClass));

      }

      outerNameSpace.Types.Add(ctd);

      foreach (ITaskProperty tp in tf.Properties)

      {

            MBFTaskProperty mpfTp = tp as MBFTaskProperty;

            if (mpfTp == null)

            {

                  throw new TaskGeneratorException(

"Unable to convert ITaskProperty to MBFTaskProperty - type is: {0}",

 tp.GetType().ToString()

);

            }

            CodeMemberField field = GenerateField(mpfTp);

            field.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, tp.Name));

            CodeMemberProperty prop = GenerateProperty(mpfTp, field);

            prop.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, tp.Name));

            ctd.Members.Add(field);

            ctd.Members.Add(prop);

      }

      return outerNameSpace;

}

 

Easy enough.  Create the namespace (we’ll be generating our code using ICodeGenerator.GenerateCodeFromNamespace so this is our primary container for code artifacts).

 

Next we generate the type and make it an abstract class.

 

Finally if a BaseClass is defined (StringHasData checks that the string is not null, and contains a non-zero-length string) add it to the base types of the new class.

 

Then for each property associated with the task we:

 

1) Validate that it is an MBFTaskProperty type (since this is a plug-able architecture we need to make sure that we’re using the right parts.  This will be clearer when we look at the deployment of the application).

2) Generate the field that backs the property.

3) Generate a region “start” directive based on the property name

4) Generate the property setter/getter

5) Generate the region “end” directive

6) Add the field and property to the class type.

 

Tomorrow we’ll look at GenerateField and find out why something that seems like it should be very simple to implement has a few gotchas that make it a bit more complex (hint: think about the new attributes introduced in Part 4 of this series and think about how they might affect initialization … specifically IsArray).

 

Questions?  Comments?  Insults?  Let me know.

 

And as always – this is sample code.  No warranty expressed or implied.