MSBuild Tasks, code generation and a tangent I’ve been stuck on for a while…

I’m still quite distracted by MSBuild.  I’m also a big fan of code generation.  Any time you can combine a few passions you should.  If I could find a way to mix in some seafood, a Willamette Valley (Oregon) wine and a Dave Matthew’s Band album believe me, I would.


As I noted in an earlier post I needed a simple task that would validate that a project contained a file link to a specific file (BldVer.cs).  Soon after I needed several other tasks.  One needed many (over 100) potential properties.  Certainly writing this all by hand was going to be a tedious process.  I started looking at ways I could take the common tasks, like the property accessors and generic validation, and generate that all from a simple XML file.


In the next several posts I’ll be walking through a simple implementation of that idea.


This first post will introduce you to the intended input and output.


The input is an XML file.  It contains a collection (Tasks) of one or more Task objects.  Each Task has several attributes and one or more Property objects.  Each Property has several possible attributes.  Since we’re using XML the easiest way to describe and validate that XML is an XSD.  The following is the schema for the input file format.


<?xml version=1.0 encoding=utf-8?>

<xs:schema attributeFormDefault=unqualified elementFormDefault=qualified xmlns:xs=>

    <xs:element name=Tasks>



                <xs:element maxOccurs=unbounded name=Task>



                            <xs:element maxOccurs=unbounded name=Property>


                                    <xs:attribute name=Name type=xs:string use=required />

                                    <xs:attribute name=Required type=xs:boolean use=optional default=false/>

                                    <xs:attribute name=Type type=PropertyType use=required />

                                    <xs:attribute name=IsArray type=xs:boolean use=optional default=false />

                                    <xs:attribute name=OneOf type=xs:string use=optional />

                                    <xs:attribute name=Range type=xs:string use=optional />

                                    <xs:attribute name=Default type=xs:string use=optional />




                        <xs:attribute name=Namespace type=xs:string use=required/>

                        <xs:attribute name=Class type=xs:string use=required />

                        <xs:attribute name=BaseClass type=xs:string use=optional />






    <xs:simpleType name=PropertyType>

            <xs:restriction base=xs:string>

                  <xs:enumeration value=ITaskItem />

                  <xs:enumeration value=System.Boolean />

                  <xs:enumeration value=System.Byte />

                  <xs:enumeration value=System.Char />

                  <xs:enumeration value=System.DateTime />

                  <xs:enumeration value=System.Decimal />

                  <xs:enumeration value=System.Double />

                  <xs:enumeration value=System.Int16 />

                  <xs:enumeration value=System.Int32 />

                  <xs:enumeration value=System.Int64 />

                  <xs:enumeration value=System.SByte />

                  <xs:enumeration value=System.Single />

                  <xs:enumeration value=System.String />

                  <xs:enumeration value=System.UInt16 />

                  <xs:enumeration value=System.UInt32 />

                  <xs:enumeration value=System.UInt64 />





As you can see it’s quite straight forward.  Each Task can have a Namespace, Class and BaseClass and each Property can have a Name, Required, Type, IsArray, OneOf, Range and Default attribute.


To make this clearer let’s look at a simple example:


<?xml version=1.0 encoding=utf-8 ?>


      <Task Namespace=SampleNamespace Class=SampleClass>

            <Property Name=Property1 Type=System.String/>




Should generate a the following:


// Auto-generated Task: 3/4/2004 2:13:13 PM

namespace SampleNamespace


    using System;

    using Microsoft.Build.Framework;

    using Microsoft.Build.Utilities;   


    public abstract class SampleClass : Microsoft.Build.Utilities.Task



        #region Property1

        private string m_Property1;


        public virtual string Property1




                return this.m_Property1;




                this.m_Property1 = value;







Now we have a foundation on which to work.  The steps it took to get from the concept to here will be the next topic of discussion … tomorrow (or when I get a chance).



Comments (0)