Macro construction

Most of the macros follow a pattern. Firstly, the main entry point is a macro of the same name as the module (or file). So, for example, the compile-schematron.xml file contains a top level <compile-schematron> macro. This will detect the type of arguments given to it and run either the <compile-schematorn-file> macro if a single file is supplied or the <compile-schematron-fileset> macro if a fileset (or folder of files) has been given. Note that in the latter case the -fileset macro will call the -file macro in a loop over all files in the fileset. This pattern is typical of all macros that can take either a file or a fileset specification.

Macros will also use other macros in the library as required. For example, almost all macros use macros from the attr-checks.xml module to provide additional checks on their arguments, beyond that provided by the Ant <macrodef>facility itself.

Although considerable use of the Ant Contrib <for> macro is used (simply due to the utility it provides) an attempt has been made to avoid other logic constructs such as the <if>/<then>/<else> macros. As Ant is generally declarative in nature rather than imperative, extensive use has been made of its built in if/unless attributes.

As an example of macro construction consider the <xspec-file> macro below:

<macrodef
    name="xspec-file"
    description="Run a single XSpec file">

    <attribute
        name="file"
        default=""
        description="XSpec file"/>

    <attribute
        name="test"
        default=""
        description="Type of test to run [xslt,xquery,schematron]"/>

    <attribute
        name="coverage"
        default="false"
        description="Output test coverage report [true,false]"/>

    <attribute
        name="junit"
        default="false"
        description="Output JUnit report [true,false]"/>

    <sequential>

        <!-- fail if required attributes not set -->
        <check-attr-set
          macro="xspec-file" 
          name="file" 
          value="@{file}"/>

        <check-attr-list 
          macro="xspec-file" 
          name="test" 
          value="@{test}" 
          list="xslt,xquery,schematron"/>

        <check-attr-bool 
          macro="xspec-file" 
          name="coverage" 
          value="@{coverage}"/>
        <check-attr-bool 
          macro="xspec-file" 
          name="junit" 
          value="@{junit}"/>

        <!-- find XSpec command -->
        <check-exe-path 
          property="xspec.exe" 
          unix="xspec.sh" 
          windows="xspec.bat" 
          unless:set="xspec.exe"/>

        <!-- get test type -->
        <local name="test.@{test}"/>
        <property name="test.@{test}" value="true"/>

        <!-- run tidy -->
        <property name="exec.failonerror" value="true"/>

        <exec
            executable="${xspec.exe}"
            failonerror="${exec.failonerror}">
            <arg line="-t" if:set="test.xslt"/>
            <arg line="-q" if:set="test.xquery"/>
            <arg line="-s" if:set="test.schematron"/>
            <arg line="-c" if:true="@{coverage}"/>
            <arg line="-j" if:true="@{junit}"/>
            <arg line="@{file}"/>
        </exec>

    </sequential>
</macrodef>

All <macrodef> elements and their related <attribute>/<element> children have description attributes—these are used to generate documentation via the extract-markdown.xml module. In the first section, the attributes are checked: the file name cannot be blank, the type of test must be one of xslt, xquery or schematron and the values of the coverage and junit attributes must be true or false. The various <check-attr-...> macros will fail the build if these conditions are not met. (Note that Ant itself will only check if attributes are required or optional, and will apply their default values. It cannot check attribute types or lists of allowed values). Next the system’s path is checked for the presence of the XSpec executable (this is xspec.sh in Unix based systems and xspec.bat for Windows). Again the build will fail if the executable cannot be found.

Next a property is set whose name is based on the test type. So, therefore, if the xquery test was selected the test.xquery property will be created. This is used in the <exec> macro to set the correct command line arguments: the test.xslt and test.schematron properties will not exist and so the -t and -s arguments will not be included in the call. This pattern is used extensively in the XPantS library to keep the code readable (although it does require familiarity with how Ant properties work).