A few weeks ago Carl Franklin interviewed me for his site dnrtv.com on the topic of MSBuild. I'm on the home page right now but the permalink is http://www.dnrtv.com/default.aspx?showNum=145. Check it out. I think we may do a couple other shows to cover a bit more detail; we just got started with MSBuild in that video.

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


I am now offering FREE MSBuild training / Team Build training. Here is the deal. For those who do not know me I am the lead author for Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build, co-author for Deploying .NET Applications: Learning MSBuild and ClickOnce and have written several related articles.

I am beginning a new program where I will personally come out to your site and provide your team (from as little as 1 or as many as 10) 8 hours of free MSBuild / Team Build training. Here are the conditions; the training will be performed on a Saturday and you will have to cover the cost of airfare, hotel and meals for the weekend. You will also need to purchase X number of copies of my book where X is the number of people being trained. The book will be used throughout the session. Also you would have to provide the facilities where the training would take place. This will be a personalized training session. Ahead of time you tell me what topics you want to be trained on, I will create the specific training materials and then train you on that. If your team is 100% new to MSBuild I can save them countless hours of learning and pain on their own. If you already have a knowledgeable group then I can take them to the next level by covering advanced topics and best practices. I want to be clear on this, you will not be paying me any fee for the training and I will personally be delivering the training.

I am making a very limited number of slots available for this extremely rare special training, so if you are interested you should contact me very soon, because this will not be available for long. Send me an email [sayed DOT hashimi AT gmail DOT com] stating a bit about your organization, where the training would take place, what weekends are good (at least 4), how many people you want to be trained, and the specific topics that you would like to cover.

Looking forward to hearing from you guys!

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


Blog problems

Comments [1]

The past few days I was having some issues with my blogs hosting provider, everything is squared away and back to normal.

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


If you picked up a copy of my book then in Chapter 7 External Tools you will find my rules for Creating Reusable Build Elements. In case you don’t have it here are the rules that I’ve outlined.

1.        Needs to be self-contained

2.        Process needs to be transparent and extensible by the consumer

3.        Overridable behavior

4.        A contract should be defined and validated

I personally think that this section is one of the most important sections in the book. The ideas there are not just how to use MSBuild but more like best practice guidance on creating uber-reusable build scripts. It took me a while to come up with those particular rules and I am continuing to evolve them. I’m not going to cover these topics here because it would take up too much space and that information is already available to you. I have evolved my validation technique and would like to discuss that here. If you have a copy of the book then you know that I am a big fan of the pattern of splitting up data and behavior in build scripts. To expand on this consider the project files that are created by Visual Studio. Those project files just define a bunch of properties and items, i.e. data. Then another file is imported, in the case of C# projects Microsoft.CSharp.targets, which contains all the targets, i.e. behavior. By doing this you can re-use the logic contained in the .targets files. This is the best way that I’ve found to create good build scripts. I prefer this over using the MSBuild task to just build another project file. There are a lot of strange problems with that technique and it can be very confusing to debug, especially if you are building out a bunch of different files. I think it’s better to build up one build script (via the Import element ) and go with that. Anywayz, this is a topic for another day.

Today we will talk about how we can have an MSBuild script validate itself. From the samples provided with the book I deliver the nunit.targets file which contains a target, ValidateNUnitSettings, which is shown below. You can download these files at the very end of this post.

< Target Name = " ValidateNUnitSettings " >

 

  < Message Text = " NUnitAssemblies: @(NUnitAssemblies) " Importance = " low " />

 

  < Error Condition = " '$(NUnitOutputDir)'=='' "

    Text = " NUnitOutputDir property not defined " />

 

  < Error Condition = " '@(NUnitAssemblies)'=='' "

    Text = " NUnitAssemblies not defined " />

  < Error Condition = " '%(NUnitAssemblies.ProjectName)'=='' "

    Text = " Atleast 1 item in NuitAssemblies doesn't have metadata 'ProjectName' defined. " />

  < Error Condition = " !Exists('%(NUnitAssemblies.FullPath)') "

    Text = " Couldn't locate assembly at: %(NUnitAssemblies.FullPath) " />   

Target >

So we can see that this target expects there to be one property, NUnitOutputDir, and an item, NUnitAssemblies, to be defined. If not then an error is raised. This target is placed on the dependency list for the UnitTest target so we know that it will be executed before that target. Since I wrote that section in the book I noticed myself “copying and pasting” these various validate targets and just changing the property and item names. Being a fan of the DRY principal this didn’t sit right with me. So I began to explore better options. This is what I’ve come up with. Imagine that you have a shared .targets file, Build.Common.targets, which just takes a bunch of project and runs the same build process on it. This is the file which contains the targets (behavior) and then you have another file, in this example YourProject.proj, which is the driver for the build process. This file mostly contains properties and item (data). Below is the YourProject.proj file.

YourProject.proj

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

< Project ToolsVersion = " 3.5 " DefaultTargets = " Build " xmlns = " http://schemas.microsoft.com/developer/msbuild/2003 " >

 

  < PropertyGroup >

    < Root Condition = " '$(Root)'=='' " > .\ Root >

 

    < BuildInstallRoot Condition = " '$(BuildInstallRoot)'=='' " > $(Root)\Build\ BuildInstallRoot >

 

    < SourceRoot Condition = " '$(SourceRoot)'=='' " > $(Root) SourceRoot >

    < OutputRoot Condition = " '$(OutputRoot)'=='' " > OutputRoot >

  PropertyGroup >

 

 

  < ItemGroup >

    < AllConfigurations Include = " Debug " >

      < Configuration > Debug Configuration >

    AllConfigurations >

    < AllConfigurations Include = " Release " >

      < Configuration > Release Configuration >

    AllConfigurations >

  ItemGroup >

 

 

  < ItemGroup >

    < ProjectsToBuild Include = " $(SourceRoot)Sedo.ProjectOne.csproj " />

    < ProjectsToBuild Include = " $(SourceRoot)Sedo.ProjectTwo.csproj " />

  ItemGroup >

 

  < Import Project = " Build.Common.targets " />

 

Project >

Here are the contents of a very crude and simple Build.Common.targets.

Build.Common.targets

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

< Project ToolsVersion = " 3.5 " DefaultTargets = " Build " xmlns = " http://schemas.microsoft.com/developer/msbuild/2003 " >

  < Target Name = " ValidateBuildSettings " >

    < ItemGroup >

      < _RequiredProperties Include = " Root " >

        < Value > $(Root) Value >

      _RequiredProperties >

      < _RequiredProperties Include = " BuildInstallRoot " >

        < Value > $(BuildInstallRoot) Value >

      _RequiredProperties >

      < _RequiredProperties Include = " SourceRoot " >

        < Value > $(SourceRoot) Value >

      _RequiredProperties >

 

     

 

      < _RequiredItems Include = " AllConfigurations " >

        < RequiredValue > @(AllConfigurations) RequiredValue >

      _RequiredItems >

      < _RequiredItems Include = " AllConfigurations.Configuration " >

        < RequiredValue > %(AllConfigurations.Configuration) RequiredValue >

      _RequiredItems >

      < _RequiredItems Include = " ProjectsToBuild " >

        < RequiredValue > %(ProjectsToBuild.Identity) RequiredValue >

        < RequiredFilePath > %(ProjectsToBuild.Identity) RequiredFilePath >

      _RequiredItems >

    ItemGroup >

 

 

   

    < Error Condition = " '%(_RequiredProperties.Value)'=='' "

           Text = " Missing required property [%(_RequiredProperties.Identity)] " />

 

   

    < Error Condition = " '%(_RequiredItems.RequiredValue)'=='' "

           Text = " Missing required item value [%(_RequiredItems.Identity)] " />

 

   

    < Error Condition = " '%(_RequiredItems.RequiredFilePath)' != '' and !Exists('%(_RequiredItems.RequiredFilePath)') "

           Text = " Unable to find expeceted path [%(_RequiredItems.RequiredFilePath)] on item [%(_RequiredItems.Identity)] " />

  Target >

 

  < PropertyGroup >

    < BuildDependsOn >

      ValidateBuildSettings;

      BeforeBuild;

      CoreBuild;

      AfterBuild;

      $(BuildDependsOn)

    BuildDependsOn >

  PropertyGroup >

  < Target Name = " Build " DependsOnTargets = " $(BuildDependsOn) " />

  < Target Name = " BeforeBuild " />

  < Target Name = " AfterBuild " />

  < Target Name = " CoreBuild " Outputs = " %(AllConfigurations.Configuration) " >

   

    < PropertyGroup >

      < CurrentConfig > %(AllConfigurations.Configuration) CurrentConfig >

    PropertyGroup >

   

   

    < Message Text = " Building project [%(ProjectsToBuild.Identity)] for configuration [$(CurrentConfig)] " Importance = " high " />

   

  Target >

 

Project >

Focus your attention on the ValidateBuildSettings target. Instead of manually validating each property and item, I create an item _RequiredProperties for property validation and an item _RequiredItems for item validation. Notice the leading _ which says “Don’t touch me I’m private!” The target populates both of those items with the values that are required for the build script to be able to execute. Then using batching those assumptions are validated. The property validation is pretty easy, if any value for _RequiredProperties.Value is empty then raises an error. For items I wanted to be able to not only be able to assert the following

1.        The item was defined

2.        Certain metadata values were defined

3.        The file exists on disk

So I came up with the concept of having three metadata values on that item

·          Identity

·          RequiredValue

·          RequiredFilePath

Identity, this is just the contents of the Include attribute on the item itself. The RequiredValue is the metadata that will be checked to ensure that a value exists. So if you want to make sure that an item was simply declared then you would do

< _RequiredItems Include = " AllConfigurations " >

  < RequiredValue > @(AllConfigurations) RequiredValue >

_RequiredItems >

If the AllConfigurations item wasn’t declared then @(AllConfigurations) would evaluate to empty and an error would be raised. You can also do this to assert an items metadata like %(AllConfigurations.Configuration) . And for RequiredFilePath, if that metadata value was defined then the validation target will make sure that the file is located on disk as expected.

You have to keep in mind that this is not just about validation. It’s also about usability. You may be wondering when I say that. But think about it, in one target I have been able to express to you (the person consuming the .targets file) everything that you need to define in great detail. And if you get it wrong then the process will stop itself, instead of potentially continuing in an erroneous scenario.

Here is what the result would look like if I had forgotten to define the AllConfigurations item.

And here it is after I inserted it.

This was one of my longer posts in a while, but I think that there is much more to this topic. I think this goes pretty deep, but if you understand these ideas then you are well on your way to writing some sweet reusable build scripts.

 

Build.Common.targets

YourProject.proj

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


The other day Dan Moseley from the MSBuild team wrote up a review on the books Amazon page. Here are the contents of the review

I'm a developer on MSBuild; Sayed wrote this book with our encouragement, and we reviewed it for accuracy and completeness, so I can recommend it. The documentation for MSBuild in 2.0 and 3.5 was not great; I consider this something like the missing manual. Unfortunately there aren't many other MSBuild books; fortunately Sayed did a good job on this one.

We're fixing a lot of what's "missing" in MSBuild in the upcoming version 4.0 -- I hope Sayed can do a 2nd edition when that comes out. Plus, our docs should be better then :-)

I'm glad to say that this review was posted as 5 out of 5 and that is the 9th review (out of 9) which has been given 5 stars. When we wrote the book I knew that we had put something together that would really meet a specific need. I'm happy to see that the book has been accepted soo well by everyone and I hope that we are able to write a second edition as Dan mentioned.

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


<< Older Posts | Newer Posts >>