Become a Task Master - Customizing the build process using Visual Studio - Part 3

Hello everyone! Man I am in love with this cooler weather. I've got a faculty conference coming up this week for ECPI so I wanted to get this last piece out there.

This is part 3 in a 3 part series and should give you a decent overview of customized builds through custom tasks using MSBuild with Visual Studio (2005 I hope) or using TeamBuild with Team Foundation Server and Visual Studio Team System.

In case you are just joining us I have included two links for your reference:

Now that we have covered how to add MSBuild to your VS IDE and how to create a custom build Task, the final destination on our path to a customized build process lays just in front of us.

The file that the build uses is a project file. Some of the characteristics of a project file are that its extension ends in .csproj or .vbproj  (or simply .proj if using TeamBuild), that it contains metadata about a project and holds this metadata in an XML format and that it "communicates" with the build engine via a set of predefined "events" called targets and predefined instructions called tasks. Of course there are other cool things in the project file: Item Groups, Property Groups, Compile includes etc.

OK that's it...secrets out. Nothing magic. Predefined targets (sequential "events") in the project file that the build engine understands. We are done! Ok maybe not quite. Almost though.

Consider the following:
  <Target Name="AfterBuild"> 
     <Message Text="Executing AfterBuild target..." />
  </Target>

The target name is "AfterBuild" and I bet you can't guess when it executes can you? If you said "Hey Kevin after the build completes!"  then I would "Brilliant!". When watching your build progress output you will see a line of output that says "Executing AfterBuild target...". To recap, the target is "AfterBuild" and the task is "Message". Message is an "out of the box" task which has an attribute (property) called Text, which is set by an author when customizing the build.

Let's pretend that we need to implement our awesome BuildLogger Task which is part of our DemoCustomTasks project. We compile our project and create a folder called CustomTasks on our C drive (on the build machine). We stick the compiled DemoCustomTasks assembly in the CustomTasks folder.

Consider the following:


<UsingTask TaskName="DemoCustomTasks.BuildLogger" AssemblyFile="C:\CustomTasks\DemoCustomTasks.dll" />
  <Target Name="BeforeBuild">
    <Message Text="Executing BeforeBuild target..." />
               <BuildLogger LogFileLocation="C:\test.txt" />
    <Message Text="Completed BeforeBuild target..." />
  </Target>

In the above code we use the UsingTask build node to identify 1) the namespace and type name of our custom Task and 2) the physical assembly that contains the custom Task. We place our custom Task within a Target, in this case "BeforeBuild".  When BeforeBuild gets called by the build engine, the Tasks contained therein get called namely Message, BuildLogger and Message. Notice the correlation between:

   <BuildLogger LogFileLocation="C:\test.txt" />

and


   public class BuildLogger : Task    
   [RequiredAttribute] 
   public string LogFileLocation       

Wow. It all seems to make sense somehow! Just a little more and we will finish this 3 part series. I want to show you a couple of additional things we can do in builds. The following example shows the use of a Property Group to create one or more reusable constants that can be referenced in the build.

 <PropertyGroup>
     <CustomTaskPath>C:\ CustomTasks </CustomTaskPath>    
     <LogFilePath>C:\ Build Log Files</LogFilePath>
</PropertyGroup>

<UsingTask TaskName="DemoCustomTasks.BuildLogger" AssemblyFile="$(CustomTaskPath)\ DemoCustomTasks.dll" />
  <Target Name="BeforeBuild">
               <BuildLogger LogFileLocation="$(LogFilePath)\buildlog.txt" />
  </Target>

We can also create and call custom Targets that can then be called from pre-defined targets. This is done using the "DependsOnTargets" attribute of the pre-defined target.

<Target Name="BeforeBuild" DependsOnTargets="LogBuild" />
<UsingTask TaskName="DemoCustomTasks.BuildLogger" AssemblyFile="$(CustomTaskPath)\CustomTasks\DemoCustomTasks.dll" />
<Target Name="LogBuild">
   <Message Text="Executing LogBuild target..." />
   <BuildLogger LogFileLocation="$(LogFilePath)\buildlog.txt" />
   <Message Text="Completed LogBuild target..." />
 </Target>

It is my sincere hope that this has shed some light on how to customize your build process using either MSBuild or Team Build. In the future I will try to cover NAnt and CruiseControl.Net and some other topics like how to get information from the custom Task back to the build.

Take care! -Kevin

Comments

No Comments
Anonymous comments are disabled