Thursday, April 21, 2011

Reading local properties from XML file in a MsBuild script

Sometimes it’s convenient to store values of some build properties in an external XML file so that instead of modifying the script itself, you’d rather want to modify only this external XML. In this XML file you’d like to store connection strings, build paths and other values which are “local” in a sense that they apply only to specific build environment (your machine, coninuous integration server etc.)

NAnt folks use a technique involving “include” directive which normally is used to cross-reference other scripts. However, it’s possible to “include” a script containing only a list of properties.

So, how do you achieve such functionality in MsBuild? Let’s start with the local.properties.xml file:

<?xml version="1.0"?>
<project name="TestProject" xmlns="http://nant.sf.net/release/0.85/nant.xsd">
  <property name="sql.server" value="mymachine\sql2008" />  
</project>

Note the XML namespace – this is to preserve the compatibility with NAnt scripts.

Then comes the MsBuild script, which uses the XmlRead task from community tasks:

<Project DefaultTargets="Main" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 
  <PropertyGroup>
    
    <WebFolder>WebApplication</WebFolder>
    <MSBuildCommunityTasksPath>MsBuildTasks</MSBuildCommunityTasksPath>
        
  </PropertyGroup>
 
  <UsingTask 
      AssemblyFile="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.dll" 
      TaskName="MSBuild.Community.Tasks.XmlRead"></UsingTask>
 
  <Target Name ="Main">
    
    <!-- this property is defined in an explicit way above -->
    <Message Text="WebFolder: $(WebFolder)"/>
 
    <XmlRead Namespace="http://nant.sf.net/release/0.85/nant.xsd" Prefix="n"
       XPath="/n:project/n:property[@name='sql.server']/@value" XmlFileName="local.properties.xml">    
      <Output TaskParameter="Value" PropertyName="MyServer" />
    </XmlRead>
 
    <!-- this property is created dynamically using XmlRead -->
    <Message Text="MyServer: $(MyServer)"/>    
    
  </Target>
  
</Project>

The output of this script would be:

Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.225]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 2011-04-21 11:43:00.
Project "C:\tmp\msbuild_test\testbuild.xml" on node 1 (default targets).
Main:
  WebFolder: WebApplication
  Reading Xml Document "local.properties.xml".
    1 node(s) selected for read.
  XmlRead Result: "mymachine\sql2008"
  MyServer: mymachine\sql2008
Done Building Project "C:\tmp\msbuild_test\testbuild.xml" (default targets)

Of course you can directly use MsBuild's Import directive to include the content of one file in another file but there doesn't seem to be an easy way to preserve the compatibility with NAnt scripts syntax. In such approach, the list of properties in defined in an external MsBuild file and is included in the main project file with include.

1 comment:

Raghavendra Prasad Vanagondi said...

This post was helpful for me in resovling an issue. but I have a question. What if the Property value in XML is assigned during the build time. how should we read that value in msbuild file.