Contact

admin

About Me · Send mail to the author(s) E-mail · Twitter

At GROSSWEBER we practice what we preach. We offer trainings for modern software technologies like Behavior Driven Development, Clean Code and Git. Our staff is fluent in a variety of languages, including English.

Feed Icon

Tags

Open Source Projects

Archives

Blogs of friends

Now playing [?]

Error retrieving information from external service.
Audioscrobbler/Last.fm

ClustrMap

Debugging/Printing Custom NAnt Properties When Building A Project

Posted in Build at Monday, March 10, 2008 9:06 PM W. Europe Standard Time

A while ago, after reading Jean-Paul Boodhoo's excellent NAnt starter series, I switched my builds to NAnt (before I had just used VS to build). After a couple of hours of figuring out how to organize the structure of the build script, I was delighted how easy it could be to build whole Visual Studio solutions on the command line. NAnt also makes the development environment a lot easier to manage (and more fun) in regard to different configurations on the developer machine vs. test and production. Several projects come to mind in which I would have loved to use NAnt for a better experience when working in a team of developers.

NAnt allows us to apply different settings (think of a database connection string) for various environments and configurations, for which it relies heavily on the easy to grasp concept of properties. Properties may be defined anywhere within a NAnt project file, but I like to keep them separate in a file called default.properties, which might look like this:

<?xml version="1.0"?>
<project xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">
    <
property name="db.connectionstring"               value="Data Source=CRM03; Integrated Security=true" />
</project>

In the contrived example above, there is one property defined, db.connectionstring. A developer might want to use another database name on his machine, so he creates another properties file, local.properties, which is loaded by the build script and overwrites the default value of db.connectionstring.

<?xml version="1.0"?>
<project xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">
    <
property name="db.connectionstring"               value="Data Source=CRM_Database; Integrated Security=true" />
</project>

The build script loading both files (in case they exists):

<?xml version="1.0"?>
<project name="Project"
         default="all"
         xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">

    <!--
Load default configuration. -->     <if test="${file::exists('default.properties')}">         <echo message="Loading default.properties" />         <include buildfile="default.properties" />     </if>

    <!--
Load developer-specific configuration. -->     <if test="${file::exists('local.properties')}">         <echo message="Loading local.properties" />         <include buildfile="local.properties" />     </if>
    ... </project>

Read more about this concept in part 6 of Jean-Paul's series.

The interesting part is now how to output the properties when you run a build. It's easy to print property values using NAnt's built-in <echo> task:

<?xml version="1.0"?>
<project name="Project"
         default="all"
         xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">

    <!--
Load default configuration. -->     <if test="${file::exists('default.properties')}">         <echo message="Loading default.properties" />         <include buildfile="default.properties" />     </if>

    <!--
Load developer-specific configuration. -->     <if test="${file::exists('local.properties')}">         <echo message="Loading local.properties" />         <include buildfile="local.properties" />     </if>
    <echo message="Build configuration:" />     <echo message="db.connectionstring: ${db.connectionstring}" />
    ... </project>

In a larger project the NAnt properties file might grow pretty quick, depending on how many aspects of the system are dependent on environment specifics or build configurations. If you have 25 properties, it becomes a hassle to write an echo task for each of them. Also, when adding a new property, the developer has to remember to add the corresponding echo task.

The NAnt <script> task comes in handy here, which allows us to iterate through all properties and print them using an echo task we create on the fly. Note that we also get back NAnt's own properties starting with "nant.", which will be excluded from the output.

<?xml version="1.0"?>
<project name="Project"
         default="all"
         xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">
 
    <!--
Load default configuration. -->     <if test="${file::exists('default.properties')}">         <echo message="Loading default.properties" />         <include buildfile="default.properties" />     </if>
 
    <!-- Load developer-specific configuration. -->     <if test="${file::exists('local.properties')}">         <echo message="Loading local.properties" />         <include buildfile="local.properties" />     </if>
 
    <echo message="Build configuration:" />     <script language="C#">         <code>             <![CDATA[                 public static void ScriptMain(Project project)                 {                     System.Collections.Generic.SortedDictionary<string, string> sortedByKey = new System.Collections.Generic.SortedDictionary<string, string>();                     foreach(DictionaryEntry de in project.Properties)                     {                         sortedByKey.Add(de.Key.ToString(), de.Value.ToString());                     } 
 
                    NAnt.Core.Tasks.EchoTask echo = new NAnt.Core.Tasks.EchoTask();
                    echo.Project = project;                     foreach(System.Collections.Generic.KeyValuePair<string, string> kvp in sortedByKey)                     {                         if(kvp.Key.StartsWith("nant."))                         {                             continue;                         } 
                       echo.Message = String.Format("{0}: {1}", kvp.Key, kvp.Value);                         echo.Execute();                     }                 }             ]]>         </code>     </script>
    ... </project>
All comments require the approval of the site owner before being displayed.
(will show your gravatar icon)
 
[Captcha]Enter the code shown (prevents robots):

Live Comment Preview