Automating SourceMonitor with NAnt

September 6, 2008

SourceMonitor from Campwood Software is a freeware tool to gather software metrics for projects written in C, C++, Java, C#, VB.NET, Delphi, Visual Basic (VB6) or HTML. The metrics supported vary depending on the language of the project being analyzed; refer to the application's on-line help for the complete list of metrics by language.

SourceMonitor can be fully operated through its graphical interface, but in this post I'll focus instead in the possibilities of automation that its command line interface offers (be aware that not all features can be used this way, creation of charts is one example).

If you are new to SourceMonitor, there is one central concept you should be familiar with before going any further: the concept of checkpoint. From the documentation:

"A checkpoint contains metrics data for a collection of source files as well as a summary of the metrics for all files, collected at a particular time. (...) Checkpoints capture metrics data during the development process so that you can track your progress as a function of time."
So, a checkpoint is just an image of the project's metrics at a given moment. For each project, you can create as many as you want (e.g.: after major code changes, after releases, ...).

Basically, you can see a checkpoint as a baseline.

SourceMonitor's graphical user interface
SourceMonitor's graphical user interface

SourceMonitor command line interface

Before jumping into the NAnt automation stuff, I'll first give an overview of how the SourceMonitor command line interface works.

SourceMonitor supports command files, which are nothing more than XML files containing a sequence of commands that the application interprets and executes. This is how you instruct SourceMonitor to load and run a command file:

C:\Program Files\SourceMonitor\SourceMonitor.exe /C SMCommand.xml

The on-line help of the application describes thoroughly the Batch File Command Language and contains a very detailed sample file. Bellow I'll just show the structure of the commands more likely to be automated in a building process.

Command file to create a new project file:

<?xml version="1.0" encoding="UTF-8" ?>
<sourcemonitor_commands>

   <write_log>true</write_log>

   <command>
       <project_file>C:\Projects\SampleProject\SampleProject.smp</project_file>
       <project_language>C#</project_language>
       <file_extensions>*.cs|*.Designer.cs</file_extensions>
       <source_directory>C:\Projects\SampleProject\Code</source_directory>
       <include_subdirectories>true</include_subdirectories>
       <checkpoint_name>Baseline</checkpoint_name>
   </command>

</sourcemonitor_commands>

The example above is quite self-explanatory but there is one element worth to explore a bit further: file_extensions. This element is used to define which files will be considered when calculating the metrics; its value contains a comma separated list of inclusions, followed by the pipe character ("|") and a comma separated list of exclusions. The second list is optional.

The file_extensions element can be omitted from the command file altogether, in that case the default inclusions/exclusions for the selected language would be used. These defaults can be changed through the "Options" dialog.

As rule of thumb, one should never rely on defaults that can be overridden on a user or machine basis. My advice is to always include the file_extensions element, thus guaranteeing that the result is the same no matter on which machine the command is run.

Command file to add new checkpoint to existing project file:

<?xml version="1.0" encoding="UTF-8" ?>
<sourcemonitor_commands>

   <write_log>true</write_log>

   <command>
       <project_file>C:\Projects\SampleProject\SampleProject.smp</project_file>
       <source_directory>C:\Projects\SampleProject\Code</source_directory>
       <include_subdirectories>true</include_subdirectories>
       <checkpoint_name>Checkpoint 1</checkpoint_name>
   </command>

</sourcemonitor_commands>

When adding a checkpoint to an existing project file the project_language and file_extensions elements can be omitted, if present they are ignored. This means that the same command used to create the project file can also be used to add subsequent checkpoints solely by changing the checkpoint_name element.

Command file to export checkpoint metrics:

<?xml version="1.0" encoding="UTF-8" ?>
<sourcemonitor_commands>

   <write_log>true</write_log>

    <command>
       <project_file>C:\Projects\SampleProject\SampleProject.smp</project_file>
       <checkpoint_name>Checkpoint 1</checkpoint_name>
       <export>
           <export_file>C:\Projects\SampleProject\Checkpoint1.xml</export_file>
           <export_type>2 (checkpoint details as XML)</export_type>
           <export_option>1 (do not use any of the options set in the Options dialog)</export_option>
       </export>
   </command>

</sourcemonitor_commands>

At the time of this writing, SourceMonitor supports 3 export types which can be combined with one or more export options (refer to the on-line help for the complete list of options).

One interesting thing is that both the export type and the export options are numeric codes, but you are free to add additional text to the element's value to make it more self-explanatory. For example, the following are all equivalent valid values for the export type:

  • "2 (checkpoint details as XML)"
  • "Export checkpoint details in XML format (2)"
  • "2 => Export checkpoint details in XML format."

Keep in mind that several commands can be combined in the same command file. This means that one single file can be used create the project, add one or more checkpoints and export the final result.

Automation with NAnt

SourceMonitor automation can be achieved by calling its executable with the correct command-line arguments from within a script. NAnt's exec task can be used for that purpose.

In a real scenario, you'll most probably need to generate the SourceMonitor's command file on-the-fly based on some template. The example bellow shows two alternatives to achieve this using NAnt.

The first alternative - target create-checkpoint-alt1 - uses a template file whose placeholders get bound on copy. The second alternative - target create-checkpoint-alt2 - uses the echo task to generate the command file.

<?xml version="1.0"?>
<project name="SampleProject" default="help" basedir=".">
    <description>Sample project to illustrate how to automate SourceMonitor using NAnt</description>

    <property name="sourcemonitor.exe" value="C:\Program Files\SourceMonitor\SourceMonitor.exe"/>
    <property name="project.file" value="C:\Projects\SampleProject\SampleProject.smp"/>
    <property name="source.directory" value="C:\Projects\SampleProject\Code"/>
    <property name="build.number" value="b123"/>

    <target name="help" description="">
        <echo message="Alternative 1: nant create-checkpoint-alt1"/>
        <echo message="Alternative 2: nant create-checkpoint-alt2"/>
    </target>

    <target name="create-checkpoint-alt1" description="">
        <copy file="cmd-create-project-template.xml" tofile="command.xml" overwrite="true">
            <filterchain>
                <replacetokens>
                    <token key="PROJECT_FILE" value="\${project.file}" />
                    <token key="SOURCE_DIRECTORY" value="\${source.directory}" />
                    <token key="CHECKPOINT_NAME" value="\${build.number}" />
                </replacetokens>
            </filterchain>
        </copy>
        <exec program="\${sourcemonitor.exe}" workingdir=".">
            <arg value="/C" />
            <arg value="command.xml" />
        </exec>
        <delete file="command.xml"/>
    </target>

    <target name="create-checkpoint-alt2" description="">
        <echo file="command.xml" append="false">
            <![CDATA[
            <?xml version="1.0" encoding="UTF-8" ?>
            <sourcemonitor_commands>
               <write_log>true</write_log>
               <command>
                   <project_file>\${project.file}</project_file>
                   <project_language>C#</project_language>
                   <file_extensions>*.cs|*.Designer.cs</file_extensions>
                   <source_directory>\${source.directory}</source_directory>
                   <include_subdirectories>true</include_subdirectories>
                   <checkpoint_name>\${build.number}</checkpoint_name>
               </command>
            </sourcemonitor_commands>
              ]]>
        </echo>
        <exec program="\${sourcemonitor.exe}" workingdir=".">
            <arg value="/C" />
            <arg value="command.xml" />
        </exec>
        <delete file="command.xml"/>
    </target>

</project>

The file cmd-create-project-template.xml referenced in the script above contains the following command template:


<?xml version="1.0" encoding="UTF-8" ?>
<sourcemonitor_commands>

   <write_log>true</write_log>

   <command>
       <project_file>@PROJECT_FILE@</project_file>
       <project_language>C#</project_language>
       <file_extensions>*.cs|*.Designer.cs</file_extensions>
       <source_directory>@SOURCE_DIRECTORY@</source_directory>
       <include_subdirectories>true</include_subdirectories>
       <checkpoint_name>@CHECKPOINT_NAME@</checkpoint_name>
   </command>

</sourcemonitor_commands>

The NAnt script presented here is very simplistic because its only objective is to illustrate the generation of a command file on-the-fly, followed by the invocation of SourceMonitor with the correct arguments. In a real-world scenario, SourceMonitor could be called from within a much more complex continuous integration script as a way to gather data for static code analysis.