13.8. The local.build.xml file

The Hackystat Build system consists of three basic components. First, there is the set of Ant files in the hackyCore_Build module that provide the "core" implementation of the build system, but do not contain any module-specific code. Second, each module must contain a top-level file called "local.build.xml", which contains Ant targets implementing module-specific build functionality. The third component of the Hackystat build system is the AutoConfig system, which creates a file called "modules.build.xml" containing targets that essentially "glue together" the core build files with the module-specific local.build.xml files. This section provides information to help you successfully implement a local.build.xml file that will play well with the other components of the Hackystat build system.

[Note]Note

This section assumes you are familiar with the sequence of events that occur during the basic build process, as documented in Section 13.5, “The annotated freshStart”.

13.8.1. The local.build.xml for hackyApp_Template

Consider a hypothetical module called hackyApp_Template, and its local.build.xml file provided in Example 13.2, “A sample local.build.xml file for hackyApp_Template”.

Example 13.2. A sample local.build.xml file for hackyApp_Template

<project name="hackyApp_Template.local" default="hackyApp_Template.default">
  <description>
  This template module provides the skeletal structure for a Hackystat module. 
  </description>
   
  <dirname property="hackyApp_Template.local.basedir" file="${ant.file.hackyApp_Template.local}"/>
  <property name="hackyApp_Template.src.dir" location="${hackyApp_Template.local.basedir}/src"/>
  <property name="hackyApp_Template.required.modules"  value="hackyCore_Kernel"/>

  <path id="hackyApp_Template.classpath">
    <pathelement location="${install.war.web-inf.classes.dir}"/>
    <pathelement location="${tomcat.servlet.jar}"/>
    <fileset dir="${install.war.web-inf.lib.dir}">
      <include name="*.jar"/>
    </fileset>
  </path>
   
  <target name="hackyApp_Template.compile" if="hackyApp_Template.available"
    depends="hackyCore_Kernel.compile"
    description="Compiles changed classes to WEB-INF/classes directory.">   
    <hackyCore_Build.javac srcdir="${hackyApp_Template.src.dir}">
      <classpath refid="hackyApp_Template.classpath"/>           
    </hackyCore_Build.javac>
  </target>
  
  <target name="hackyApp_Template.install.pre-sensorshell" if="hackyApp_Template.available">
    <makeUpdateWebAppContent module.src.dir="${hackyApp_Template.src.dir}"/>
  	
    <copy todir="${install.testdata.users.dir}">
      <fileset dir="${hackyApp_Template.src.dir}/org/hackystat/core/common/project" 
               includes="testprojectdataset/**" />
    </copy>
  </target>
    
  <target name="hackyApp_Template.install.post-sensorshell" if="hackyApp_Template.available" />

  <target name="hackyApp_Template.junit" if="hackyApp_Template.available" description="Runs JUnit.">
    <makeJUnit module.name="hackyApp_Template" module.package.prefix="org.hackystat.core.common"/>
    <makeEcho message="Completed hackyApp_Template.junit." prefix="hackyApp_Template.junit"/>
  </target>

  <target name="hackyApp_Template.default" description="Does nothing. Required by Ant."/>
</project>

In order for the elements of a local.build.xml file to integrate smoothly with the core build system and the other modules in the configuration, they must be named and defined in a specific way. Table 13.1, “Standard elements for the local.build.xml file” summarizes the constraints on the standard local.build.xml file elements, each of which are illustrated by the hackyApp_Template local.build.xml. Note that all named elements must begin with the module name in order to avoid conflicts with elements defined in other local.build.xml files.

Table 13.1. Standard elements for the local.build.xml file

ElementNameDescription
Project "name" attribute <module>.local Project attribute
Project "default" attribute<module>.defaultThe default target for this build file.
Project "description" element<description> Provides a one paragraph description of the functionality provided by this module.
Module basedir<module>.local.basedir The directory location for this module.
Module Java srcdir <module>.src.dirThe directory containing Java sources (if any)
Required modules <module>.required.modules A comma-separated list of modules that this module depends upon.
Target "if" attribute <module>.available All target execution must be conditioned on the existence of the <module>.available property
Module Java classpath <module>.classpath The classpath required for compilation of Java sources. Also used by other utilities such as JavaDoc, Checkstyle, PMD, and FindBugs.
Module compile target <module>.compile Performs compilation
Module pre-sensorshell target<module>.install.pre-sensorshell Copies files to build area prior to sensorshell.jar construction
Module post-sensorshell target <module>.install.post-sensorshell Performs any build actions requiring access to constructed sensorshell.jar
Module JUnit target <module>.junit Invokes JUnit on this module's test cases
Module default target <module>.default The default target (typically empty)

Let's now look at the standard components of the hackyApp_Template local.build.xml file in more detail.

13.8.2. The "header" section

The first few lines of each local.build.xml file can be viewed as a "header" containing several important declarations, as illustrated below.

<project name="hackyApp_Template.local" default="hackyApp_Template.default">
  <description>
  This template module provides the skeletal structure for a Hackystat module. 
  </description>
   
  <!-- Pointers to local subdirectories. -->
  <dirname property="hackyApp_Template.local.basedir" file="${ant.file.hackyApp_Template.local}"/>
  <property name="hackyApp_Template.src.dir" location="${hackyApp_Template.local.basedir}/src"/>

  <!-- The modules that must be available and whose targets this module's targets will depend upon. -->
  <property name="hackyApp_Template.required.modules"  value="hackyCore_Kernel"/>

  <path id="hackyApp_Template.classpath">
    <pathelement location="${install.war.web-inf.classes.dir}"/>
    <pathelement location="${tomcat.servlet.jar}"/>
    <fileset dir="${install.war.web-inf.lib.dir}">
      <include name="*.jar"/>
    </fileset>
  </path>

Project Name and default target. The top line names the Ant project. To avoid name conflicts, name your project <module>.local, and provide a default target called <module>.default, which can do nothing. (If you do not supply a <module>.default target in your local.build.xml file, the AutoConfig system will define this target for you in modules.build.xml.)

Description. Please provide a short sentence or two describing the capabilities provided your module in the <description> element. The AutoConfig system extracts this text when parsing the local.build.xml file and uses it to generate documentation on the set of modules in the system.

Local basedir. The Hackystat build system uses the Ant "import" construct to create the equivalent of a single large build file at runtime. Because of this, it is important that each module define a property that points to the local directory in which each module's implementation resides. The line defining the <module>.local.basedir property is the standard way to accomplish this in Ant.

Java source dir. Many Hackystat module targets operate on the subdirectory containing the Java sources associated with the module. The <module>.src.dir property provides the location of this directory. It is typically named "src". This property must always be defined in order to support implicitly defined targets, although it will not cause problems if no Java sources are found within the directory.

Required modules. A key feature of build systems including Ant is the notion of "dependencies": when target X "depends" upon target Y, then the build system will guarantee that Y will be invoked prior to X. The Hackystat build system provides this concept, but at the higher level of entire modules. If you declare that module X "requires" module Y, then the build system will guarantee that module Y exists and is available if module X exists and is available. Furthermore, it will order the "all" targets of a particular type according to the resulting dependency tree. So, for example, in the all.compile target, Y.compile will execute before Y.compile. In the all.install.pre-sensorshell target, Y.install.pre-sensorshell will execute before X.install.pre-sensorshell.

The declaration of module-level dependencies is accomplished with the <module>.required.modules property. You can supply a single required module name as the value of this property, or a comma-separated list of required module names. The example property definition declares that hackyApp_Template requires hackyCore_Kernel. This means that the build system will fail if hackyApp_Template is specified as available but hackyCore_Kernel is not. It also means, for example, that the hackyApp_Template.compile target can assume that it will be run after the hackyCore_Kernel.compile target.

The <module>.required.modules property is transitive, just like the "depends" attribute in Ant. In other words, if module A "requires" module B, and module B "requires" module C, then module A implicitly "requires" module C. This means you don't need to specify every single required module in this property definition, as long as the chain of required modules eventually includes all of the ones you need.

Classpath. Several java-related targets (such as for compilation and javadoc) require a classpath pointing to all of the code required for the Java sources in this module to be compiled (or javadoc'd). The <module>.classpath path declaration provides the classpath specification for this module. This property must always be defined, though the path can be declared empty.

The classpath declaration provides an immediate example of the use of the required.modules property. Assume that module hackyApp_Template defines some Java code that invokes Java code defined in the hackyCore_Kernel module. During compilation, how does the hackyApp_Template module guarantee itself access to the most recently compiled Java code from hackyCore_Kernel? The answer is as follows: by convention, all modules use the hackyCore_Build.javac task to compile their Java code, which places the compiled code into the directory ${install.war.web-inf.classes.dir}. By including this directory in the classpath, and by specifying that hackyApp_Template requires hackyCore_Kernel, the hackyApp_Template module guarantees that by the time in the build process that its code is compiled, the Java code from hackyCore_Kernel will have already been compiled and the .class files placed into a directory on its classpath.

The example classpath path declaration refers to a couple of other useful properties. The property ${tomcat.servlet.jar} will always point to the servlet.jar file in the local installation of Tomcat, which is useful for modules that employ servlet code. The property ${install.war.web-inf.lib.dir} points to a directory into which third party libraries needed by modules at runtime are placed. So, for example, since the module hackyCore_Kernel uses the JDOM XML package, it will copy the jdom.jar library to ${install.war.web-inf.lib.dir} as part of its compilation target. This means that any modules that require hackyCore_Kernel can simply point to ${install.war.web-inf.lib.dir} and obtain access to this library.

13.8.3. The compile target

The <compile>.compile target has two primary responsibilities: (1) to compile your Java classes by calling the hackyCore_Build.javac target, and (2) to copy any third party jar files your module code will need at run-time in the Hackystat server to ${install.war.web-inf.lib.dir}. If your module is a sensor definition module, then the compile target may do other things as well that are specific to the tool being instrumented.

Here is a simple example of the compile target:

  <target name="hackyApp_Template.compile" if="hackyApp_Template.available"
    depends="hackyCore_Kernel.compile"
    description="Compiles changed classes to WEB-INF/classes directory.">   
    <hackyCore_Build.javac srcdir="${hackyApp_Template.src.dir}">
      <classpath refid="hackyApp_Template.classpath"/>           
    </hackyCore_Build.javac>
  </target>

The compile target must be named <module>.compile, and (like all targets in the local.build.xml) must provide an "if" attribute that conditionalizes invocation on the existence of the <module>.available property. This is the way that the build system enables the contents of the hackystat.build.properties file to control the set of modules involved in the build.

In order to support stand-alone compilation of your module, you should include a "depends" clause that lists the compile targets associated with all of the modules listed in your required.modules property. In this example, hackyApp_Template has listed a single required module, hackyCore_Kernel, and so the compile target depends upon hackyCore_Kernel.compile.

The hackyCore_Kernel.javac task is an Ant presetdef that sets certain properties of the <javac> task, including: where the compiled classes go, that debugging information should be inserted into the .class files, and that the bytecodes should be Java 1.4 compatible.

13.8.3.1. Typical modification: copying files to the web-inf/lib directory

The hackyCore_Kernel.compile task illustrates a slightly more complicated version of the compile target, which copies a set of jar files to the ${install.war.web-inf.lib.jar.dir} directory so that they are available at run time and also accessable during compilation.

  <target name="hackyCore_Kernel.compile" if="hackyCore_Kernel.available"
    description="Updates WEB-INF/lib with library jars, then compiles changed classes to WEB-INF/classes directory.">
    <!-- Copy third party jars needed for compilation and runtime to web-inf/lib, thus making available to higher level modules. -->
    <copy todir="${install.war.web-inf.lib.dir}" preservelastmodified="true">
      <fileset dir="${hackyCore_Kernel.lib.jar.dir}">
        <include name="*.jar"/>
      </fileset>
     </copy>
    <!-- Now do standard hackystat javac. -->
    <hackyCore_Build.javac srcdir="${hackyCore_Kernel.src.dir}">
       <classpath refid="hackyCore_Kernel.classpath"/>           
    </hackyCore_Build.javac>
  </target>

A common compilation problem is failure due to a ClassNotFound error. Two things to check are: (1) did you specify all of the appropriate dependencies for your module? In other words, is your module being compiled out of order? (2) If your module depends upon a third party jar, is it specified in this classpath correctly? Specifying it correctly might mean adding it to this module's classpath property, and if it needed at runtime in the server, adding it to the ${install.war.web-inf.lib.dir} directory.

If you do not perform any compilation in your module, then you can omit this target from your local.build.xml file. The AutoConfig system will generate a default implementation of this target (which does nothing) in the modules.build.xml file.

13.8.4. Typical modification: using Java 5 constructs

Currently, the hackyCore_Build presetdef defaults the source code to Java 1.4 compliance. If you want to use Java 5 constructs in your module, you need to override this default. Simply include source="1.5" in your compile target as illustrated below.

  <target name="hackyApp_Template.compile" if="hackyApp_Template.available"
    description="Compiles changed classes to WEB-INF/classes directory.">   
    <hackyCore_Build.javac srcdir="${hackyApp_Template.src.dir}" source="1.5">
      <classpath refid="hackyApp_Template.classpath"/>           
    </hackyCore_Build.javac>
  </target>

13.8.5. The install.pre-sensorshell target

The <module>.install.pre-sensorshell targets are run after compilation, but before creation of the sensorshell.jar file. The basic purpose of this target is to allow a module copy their local files over to appropriate places in the installation area. Which files are copied, and where they go, will usually depend upon whether the module defines a sensor, a sensor data type, or an application.

The following subsections describe some of the common copying scenarios and the idioms used in these situations.

13.8.5.1. Typical modification: Updating standard webapp content

Most modules define one or more of the standard Hackystat objects: sensors, sensor data types, commands, selectors, JSP or HTML pages, and so forth. These files needs to be copied over to various places in the build area. The makeUpdateWebAppContent macro does this update. Here's a sample invocation:

    <makeUpdateWebAppContent module.src.dir="${hackyApp_Template.src.dir}" />

13.8.5.2. Typical modification: Updating installer files

Modules that define new sensors must include a set of files providing code and declarations for use by HackyInstaller. These files must be copied over to an appropriate build area set up by the hackyInstaller module. The HackyCore_Installer module defines an Ant macro called hackyCore_Installer.installXmlDefinitions to do this. Here's a sample invocation:

    <hackyCore_Installer.installXmlDefinitions module.src.dir="${hackyApp_Template.src.dir}" />

Note that if you include this line in your target, but your module does not require hackyCore_Installer, then an error could occur during the build. So, only include this line if your module actually contains installer code!

13.8.5.3. Typical modification: Updating telemetry files

If your module includes telemetry-related definitions such as reduction functions, then you will need to copy your reduction function definition files to the build area. The hackyTelemetry module provides a macro to simplify this process. Here is an example:

    <hackyCore_Telemetry.installXmlDefinitions module.src.dir="${hackyApp_Template.src.dir}" />

Again, if you include this line, but your module does not depend upon hackyCore_Telemetry, then an error could result during the build. Only include this line if you actually have Telemetry definitions!

13.8.5.4. Typical modification: Updating test data

If your module contains test data, then you need to copy it over to the testdata directory in the build area. Here is an example invocation of the copy command to do so:

   <copy todir="${install.testdataset.dir}" preservelastmodified="true">
      <fileset dir="${hackyApp_Template.src.dir}/org/hackystat/app/template/dailyanalysis/testdataset" />
    </copy>

You will need to edit the <fileset> to include the correct directory hierarchy.

If you do not require any pre-sensorshell activities in your module, then you can omit this target from your local.build.xml file. The AutoConfig system will generate a default implementation of this target (which does nothing) in the modules.build.xml file.

13.8.6. The install.post-sensorshell target

The <module>.install.post-sensorshell targets are run after the sensorshell.jar file is constructed. Some modules, particularly sensor modules, may need to incorporate the completed sensorshell code and definitions into the executables that they build, and this target provides a way for them to do so.

13.8.6.1. Typical modification: building a jar using sensorshell information

HackyCore_Installer is an example of a module that needs to utilize the completed sensorshell code to perform its function. Here is what it's install.post-sensorshell target looks like:

  <target name="hackyCore_Installer.install.post-sensorshell" depends="hackyCore_Installer.unjarFiles" if="hackyCore_Installer.available" 
    description="Creates the hackyCore_Installer.jar file">

    <!-- Update installer directory with any changes in sensorshell files. -->
    <copy todir="${install.installer.classes.dir}" preservelastmodified="true">
      <fileset dir="${install.sensorshell.dir}/src" />
    </copy>

    <!-- Jar all the files -->
    <jar jarfile="${install.war.download.dir}/hackyInstaller.jar"
         basedir="${install.installer.classes.dir}">
   	   <manifest>
    	   <attribute name="Built-By" value="${user.name}" />
    	   <attribute name="Main-Class" value="org.hackystat.core.installer.controller.Control" />
    	   <attribute name="Specification-Title" value="Hackystat Installer" />
    	   <attribute name="Specification-Vendor"
    	              value="Collaborative Software Development Laboratory" />
    	   <attribute name="Specification-Version" value="${version}" />
    	   <attribute name="Implementation-Version" value="${buildtime}" />
    	  </manifest>  	
  	</jar>
  </target>

As you can see, it updates its own installation area with any changed files, and then builds itself.

If you do not require any post-sensorshell activities in your module, then you can omit this target from your local.build.xml file. The AutoConfig system will generate a default implementation of this target (which does nothing) in the modules.build.xml file.

13.8.7. The junit target

The JUnit target is where you place your unit testing invocation. The standard implementation of the junit target is quite straightforward:

  <target name="hackyApp_Template.junit" if="hackyApp_Template.available" description="Runs JUnit.">
    <makeJUnit module.name="hackyApp_Template" module.src.dir="${hackyApp_Template.src.dir}"/>
    <makeEcho message="Completed hackyApp_Template.junit." prefix="hackyApp_Template.junit" />
  </target>

Note the use of the macros makeJUnit and makeEcho to simplify the definition. The makeJUnit macro constructs the call to the standard JUnit Ant target, but supplies a significant amount of contextual information to support the testing process. The makeEcho target constructs a timestamped output line indicating that this target has completed. While most module-level targets should not produce command-line output during the build, the junit target is an exception: it is quite useful to see the progress of unit testing even with the -q option, and providing output at the completion of the target makes it easy to identify the module where failing tests reside.

13.8.7.1. Typical modification: supporting installer tests

Any module that runs hackyInstaller test cases will need to augment their junit target with code to set up a test directory for the installer and to add additional system properties. The hackySensor_CLI module contains an example of the kind of JUnit target required for sensor testing:

  <target name="hackySensor_Cli.junit" if="hackySensor_Cli.available" description="Runs JUnit.">
    <hackyCore_Installer.setupTestDir />
    <makeJUnit module.name="hackySensor_Cli"  module.package.prefix="org.hackystat.sensor.cli">
      <sysproperty.elements>
        <sysproperty key="installer.config.dir" value="${installer.config.dir}" />
        <sysproperty key="installer.test.dir" value="${hackyCore_Installer.test.tmp.dir}"/>
      </sysproperty.elements>
    </makeJUnit>
    <makeEcho message="Completed hackySensor_Cli.junit." prefix="hackySensor_Cli.junit" />
  </target>

Note the inserted call to hackyInstaller.setupTestDir, as well as the additional sysproperty.elements tag inside the call to makeJUnit.

13.8.7.2. Typical modification: adding entries to the JUnit classpath

Some modules will require the addition of classpath entries to JUnit for it to run correctly. The hackyInstaller.junit target shows how this is accomplished:

  <target name="hackyCore_Installer.junit" if="hackyCore_Installer.available" 
    description="Runs JUnit.">
    <hackyCore_Installer.setupTestDir/>
    <!-- By default, the GUI tests in swing/ are excluded. Override forkmode to 'perTest' and exclude to '' to enable GUI tests. -->
    <property name="hackyCore_Installer.junit.forkmode" value="once"/>
    <property name="hackyCore_Installer.exclude.string" value="**/swing/**"/>
    
    <!-- Now run the hackyCore_Installer unit tests. -->
    <makeJUnit module.name="hackyCore_Installer" module.package.prefix="org.hackystat.core.installer"
      junit.fork="on"
      junit.forkmode="${hackyCore_Installer.junit.forkmode}">
      <classpath.elements>
        <fileset dir="${hackyCore_Installer.local.basedir}">
          <include name="lib/jar/*.jar"/>
        </fileset>
      </classpath.elements>
      <sysproperty.elements>
        <sysproperty key="installer.config.dir" value="${installer.config.dir}" />
        <sysproperty key="installer.test.dir" value="${hackyCore_Installer.test.tmp.dir}"/>
      </sysproperty.elements>
      <fileset.elements>
        <exclude name="${hackyCore_Installer.exclude.string}"/>
      </fileset.elements>
    </makeJUnit>
    <makeEcho message="Completed hackyCore_Installer.junit." prefix="hackyCore_Installer.junit"/>
  </target>

Note the use of the classpath.elements tag to provide new entries on the classpath during the call to JUnit.

13.8.7.3. Typical modification: excluding directories/files from testing

In some cases, you may wish to exclude certain files or directories in your src directory from being searched for test code during the call to JUnit. As the hackyInstaller.junit target above illustrates, you can use the fileset.elements tag to customize the set of files return for JUnit testing.

13.8.7.4. JUnit and JUnitReport

In the Hackystat build system, JUnitReport is only invoked on those modules whose execution of JUnit resulted in at least one error or failure. This is because each invocation of JUnitReport is time-consuming (2-5 seconds), so running JUnitReport on every single module can result in the addition of minutes to the overall build when many modules are present.

If you do not require any unit testing activities in your module, then you can omit this target from your local.build.xml file. The AutoConfig system will generate a default implementation of this target (which does nothing) in the modules.build.xml file.

13.8.8. Implicitly defined targets

For each module, the build system requires an additional set of targets whose typical implementation is so simple that the AutoConfig system can provide it automatically in modules.build.xml. Because of this, you rarely need to provide these targets in your local.build.xml file.

In certain rare cases, you may need to implement the following targets in a manner differently than the default version generated by AutoConfig. In that case, supply the custom definition in your local.build.xml file and AutoConfig will not generate a default definition. Table 13.2, “Implicitly defined targets” lists these targets and describes their purpose.

Table 13.2. Implicitly defined targets

TargetPurpose
<module>.junitReport Invokes JUnitReport if a test case in this module fails.
<module>.cleanDeletes the local build/ directory.
<module>.checkstyle Runs checkstyle over the Java sources
<module>.javadocRuns JavaDoc on the Java sources
<module>.java2htmlRuns Java2Html on the Java sources
<module>.locc Runs LOCC over the Java sources
<module>.export Creates a zipped copy of this module in ../export/<module>.zip

Example 13.3, “Implicitly defined targets: default implementations” illustrates the default implementation of these implicitly defined targets.

Example 13.3. Implicitly defined targets: default implementations

  <target name="hackyApp_Template.junitReport" if="hackyApp_Template.junit.failed" 
     description="Runs JUnitReport if a test case failed.">
     <makeJUnitReport module.name="hackyApp_Template"/>
  </target>

  <target name="hackyApp_Template.clean" if="hackyApp_Template.available" 
    description="Deletes the hackyApp_Template build directory.">
    <delete dir="${hackyApp_Template.local.basedir}/build" quiet="yes"/>
  </target>

  <target name="hackyApp_Template.checkstyle" if="hackyApp_Template.available">
    <makeCheckstyle module.name="hackyApp_Template" module.src.dir="${hackyApp_Template.src.dir}" module.classpath.ref="hackyApp_Template.classpath"/>
  </target>

  <target name="hackyApp_Template.javadoc" if="hackyApp_Template.available">
    <makeJavaDoc module.name="hackyApp_Template" module.src.dir="${hackyApp_Template.src.dir}" module.classpath.ref="hackyApp_Template.classpath"/>
  </target>  

  <target name="hackyApp_Template.java2html" if="hackyApp_Template.available">
    <makeJava2Html module.name="hackyApp_Template" module.src.dir="${hackyApp_Template.src.dir}"/>
  </target>  
  
  <target name="hackyApp_Template.locc" if="hackyApp_Template.available">
    <makeLocc module.name="hackyApp_Template" module.src.dir="${hackyApp_Template.src.dir}"/>
    <makeLoccSensor module.name="hackyApp_Template"/>
  </target>    

  <target name="hackyApp_Template.export" if="hackyApp_Template.available" 
          description="Creates a .zip file containing this module in the ../export directory.">
    <delete dir="../export/hackyApp_Template" quiet="true" />
    <mkdir dir="../export/hackyApp_Template" />
    <copy todir="../export/hackyApp_Template">
      <fileset dir="${hackyApp_Template.local.basedir}">
        <exclude name="build/**" />
      </fileset>
    </copy>
    <zip destfile="../export/hackyApp_Template.zip">
      <zipfileset dir="../export/hackyApp_Template" prefix="hackyApp_Template" />
    </zip>
    <delete dir="../export/hackyApp_Template" quiet="true" />
  </target>

13.8.9. The local.build.xml file and AutoConfig

The beginning of this section overviewed the three essential components of the build system: the core targets, the local.build.xml file, and the AutoConfig system that glues the two core and module-specific parts together.

For the AutoConfig system to generate the "glue" (i.e. the modules.build.xml file), it must parse the contents of each local.build.xml file to determine the values of various required properties (such as the list of required modules) and determine which targets must be implicitly defined. Any time you modify the local.build.xml file, your changes may invalidate the targets generated previously for modules.build.xml.

To help ensure consistency between the core system and the module-specific definitions, the build system checks each time it runs to see if any local.build.xml files have a last modification time that is more recent than the last modification time associated with the modules.build.xml file. If there are newer versions of one or more local.build.xml files discovered, the build system generates a warning as follows:

     [echo] Warning: modules.build.xml file may be out of date. Regenerate with: 'ant -f autoconfig.build.xml'

Even if your changes don't actually affect the build process, it is easy to simply run the AutoConfig system once to regenerate the modules.build.xml file, which will remove the warning:

c:\svn\hackyCore_Build>ant -f autoconfig.build.xml
Buildfile: autoconfig.build.xml

run:
    [mkdir] Created dir: C:\svn\hackyCore_Build\build\autoconfig
     [echo] [AutoConfig] Generated modules.build.xml and sample.hackystat.build.properties.

autoconfig.build.default:

BUILD SUCCESSFUL
Total time: 1 second