2010/01/20

Working with XML in PowerShell (1)

During the last days, I started reading about PowerShell and PSake. While looking at the NAnt scripts that I currently use, I recognized that among others the capability of examining and editing XML files is required.

Examining XML in NAnt

In NAnt, inspecting XML is done with the xmlpeek-task. It takes a file name, an XPath and a property name to fill with the result.

One of the uses of xmlpeek is to find out the current revision of a code base, for example to set the private part of the version number with it. In the Castle Project’s build script this is done this way:

  <target name="common.find-svninfo">
    <!-- For adding SVN revision to builds -->
    <property name="svn.revision" value="0" overwrite="false" />
    <!-- try to update the revision -->
    <exec
      program="svn"
      commandline='info "${project::get-base-directory()}" --xml'
      output="_revision.xml"
      failonerror="false"/>
    <xmlpeek
      file="_revision.xml"
      xpath="/info/entry/@revision"
      property="svn.revision"
      failonerror="false"/>
    <delete file="_revision.xml" failonerror="false" />
    <echo message="INFO: Using Subversion revision number: ${svn.revision}"/>
  </target>

So while this is quite a clever solution to find out the projects current revision, it has some drawbacks, such as being nearly 20 lines of code and that it uses a temporary file. But it does the job, reading the revision from the project folder.

Examining XML in PowerShell

PowerShell treats XML as a first class type and also has some shortcuts available. If we take a look at the result of svn info, we see that it is thankfully simple:

<?xml version="1.0"?>
<info>
  <entry
     kind="dir"
     path="."
     revision="6536">
...
  </entry>
</info>

And because we can traverse XML nodes and attributes in PowerShell like object properties, it is possible to get the revision number even without an XPath expression.

$revision = $rev_xml.info.entry.revision

So now it is only necessary to fill the $rev_xml variable with the output of the svn info command. The NAnt script does this using a temporary file but PowerShell allows to assign the standard output of a command line program directly to a variable:

$rev_xml = svn.exe info --xml 

But PowerShell treats the output of such a program as an array of objects, in this case strings. We have to hint PowerShell that what we want is an XMLDocument instead of an array of strings: 

[xml] $rev_xml = svn.exe info --xml 

That’s all. We now only have to put this together into a PSake build script for testing and reference.

properties {
    [string] $project_path = "C:\dev\castle\SVN\ActiveRecord"
    $revision = 0
}

task default -depends get_revision
task get_revision {
    [xml] $rev_xml = svn.exe info $project_path --xml 
    $revision = $rev_xml.info.entry.revision
    "INFO: Using Subversion revision number: $revision"
}

This whole script is shorter than the snippet of NAnt script I’ve shown at the beginning. Taking only the relevant work, 5 lines of PS script replace 15 lines of NAnt XML.

The script of course ignores the fact that getting the revision number should not be a task but rather a function that is called by a build task’s precondition for example. I will get back this in a while in another post.

No comments: