- Aims of This Chapter
- Property Files
- Ant ClearCase Integration
- Project Support
- Summary
Property Files
From the perspective of Chapter 4, an Ant build.xml file is a single centralized build file that defines a repeatable process for bringing together an application, usually producing some form of executable output. Although a single build.xml file can be enough to drive the build process, in practice it can quickly become large and unwieldy. In software development in general, it is recommended that you separate the data from the processes so that configuration changes can be made simply by focusing on and changing data alone. In Ant this is achieved through the use of property files. This section describes different uses of these property files:
- Implementing a configurable build using default and build property files. These files contain properties that define the data for your build process, such as the compiler version, optimization settings, and so on.
- Automating build numbering using build number property files. These files can be used to automate the generation of build numbers for applying labels or baselines.
- Reusing prebuilt libraries using library property files. These files can be used to specify a set of library versions to build against.
I will start by looking at default and build property files.
The Configurable Build
One of the problems of having a single, centralized build file is that there may be times when you want to change things slightly to carry out different build scenarios. For example, developers might want to override the standard optimization or debug settings. Similarly, when you change the details of an existing project or create a new project, you might have to rework a build file significantly. One of the ways to address both of these issues is to maintain property files that define default and overrideable property values. One convention for this is to create a file called default.properties (in the same directory as the build.xml file). It defines default values for the set of properties that would be required as part of a "normal" build. For example, the default.properties file for our RatlBankModel project might look like Listing 5.1.
Example 5.1. default.properties File
# default properties value.compile.debug = false value.compile.fork = true value.compile.optimize = true name.compile.compiler = javac1.4 name.project-vob = \\RatlBankProjects name.project = RatlBankModel name.build.admin = ccadm name.build.branch = RatlBankModel_Int
In this file you can place both project build information (compiler settings) and ClearCase-related information (the name of the UCM project, the ClearCase admin user, the build branch, and so on). As with any other important software development asset, this file should be placed under version control in ClearCase. In the same location, you can also create a file called build.properties. This file should contain any of the properties from the default.properties file that a user wants to override. For example, a developer's build.properties file might look like Listing 5.2.
Example 5.2. build.properties File
# build properties value.compile.debug = true value.compile.optimize = off name.build.admin = fred name.build.branch = fred_RatlBankModel_Dev
This file should be created by each user as and when he or she needs to override particular values. Consequently, it should not be placed under version control. To make use of these two property files, you have to include a reference to them in the build file. You can do this by including the following lines at the top of the build.xml file (usually before the definition of any targets):
<property file="build.properties"/> <property file="default.properties"/>
The order is important here. Since in Ant all properties are immutable, they cannot be changed after they are set. Therefore, if a property value is defined in the private build.properties file, it cannot be changed by the value in the default.properties file. Given this scenario, it is therefore possible to write tasks that use these defined property values as follows:
<javac destdir="${dir.build}" debug="${value.compile.debug}" optimize="${value.compile.optimize}" fork="${value.compile.fork}" compiler="${value.compile.compiler}"> <src path="${dir.src}"/> <classpath refid="project.classpath"/> </javac>
Once these properties exist in a file, it is also possible for users to identify and override single entries from the command line. For example, to override the value.compile.debug setting in the preceding example, Ant could be called as follows:
>ant -Dvalue.compile.debug=false ...
Having this capability could be seen as an audit or security loophole. For example, if during a Release Build a particular property is overridden, where is the information on this override recorded? To avoid this, I recommend that as part of your Release Build you record the values of all the customizable properties that are used. You can use the <echoproperties> task, as follows:
<echoproperties prefix="name."/> <echoproperties prefix="value."/>
This command echoes all the properties that begin with the prefix name or value to the Ant log file.
Automated Baseline Numbering
One of the most important aspects of software build automation is the reliable and consistent generation of build or release baselines. Baselines should be generated in a standard format. You also should be able to determine the baseline's quality simply by inspecting it. As I discussed in Chapter 3, "Configuring Your SCM Environment," with Base ClearCase this can be achieved via the baseline's name or through an attribute, and with UCM this can be achieved by using UCM baseline promotion levels. If these baselines are also "built" into the application, such as into a help file or .jar file, they can be extremely useful in debugging test or live problems.
One recommended way to automatically generate Release Build baselines is to use Ant's <propertyfile> task to automatically increment a build number. Ant does this by creating and updating the entries in a properties file; by convention, this particular properties file is called buildinfo.properties. When you start making use of this property file, I recommend creating an entry (called name.build.info or something similar) in the default.properties file to refer to it. At the same time, create an entry (called name.build.referer or something similar) that refers to a (single) source file or a Web page that should be updated with the automatically generated baseline. For example, the additional entries that can be added to your default.properties would be as follows:
name.build.info = buildinfo.properties name.build.referer = src/com/ratlbank/model/Bank.java name.build.prefix = RATLBANKMODEL-
The name.build.prefix entry prefixes any labels or baselines that are created.
To insert the baseline into the file that the name.build.referer entry points to, some sort of marker must be placed in that file to be able to search for and replace it. If this file was a Java source file, one way to do this would be to create a Java static string called version:
private final static String version = "@(#)<label> (on:<date>)@";
The beginning @(#) and ending @ strings are simply tokens to be used for searching and replacing. This string could be displayed in a log file, in an About box, or wherever you need information about the version of the application that is running. To generate the build number, you would include a <propertyfile> task in the build.xml file:
<propertyfile file="${name.build.info}" comment="Build Information File - DO NOT CHANGE"> <entry key="build.num" type="int" default="0000" operation="+" pattern="0000"/> <entry key="build.date" type="date" value="now" pattern="dd.MM.yyyy HH:mm"/> </propertyfile>
This task writes (and increments) a build number into the buildinfo.properties file in the format 0000, 0001, and so on. It also writes the current date. Here's an example of the contents of a typical buildinfo.properties file:
#Build Information File - DO NOT CHANGE #Wed Apr 1 17:48:22 BST 2006 build.num=0006 build.date=01.04.2006 17\:48
I recommend adding this file to version control and subsequently checking it in or out when it is updated. This way, the last build number is always preserved.
Once this build number has been generated, it can be used as part of a baseline to be placed across all the build files. To actually write the baseline into the Java source file containing the version string described earlier, you can use Ant's <replaceregexp> task to carry out a search and replace:
<replaceregexp file="${name.build.referer}" match="@\(#\).*@" replace="@(#)${name.build.prefix}-${build.num} (on: ${build.date})@"/>
Note that the baseline is prefixed with the name.build.prefix entry from the build.properties file, so in this case the baseline would be something like RATLBANKMODEL-0006. In fact, this task searches for this expression:
@(#)<any text>@
and replaces < any text > with the following:
@(#)<build prefix>-<build number> (on: <build date>)@
For example:
@(#)RATLBANKMODEL-0006 (on: April 1st 2006)@
In case you are wondering why I chose the strange combination of tokens @(#), this is a bit of UNIX history. On certain UNIX systems, if a static string containing these tokens was built into a program, you could use the SCCS what command to extract its information—for example, what /bin/ls. This command was always extremely useful in debugging test and live problems!
Reusing Prebuilt Libraries
In Chapter 2, "Tools of the Trade," I stated that one of the biggest issues with Java development is keeping track and control of all the third-party libraries a build requires. One of the ways to accomplish this is to maintain a library properties file. In this file, which I normally call library.properties (and create at the same level as the build.xml file), you define the exact version numbers and locations of the third-party libraries that the build requires. A sample library.properties file is shown in Listing 5.3.
Example 5.3. Sample library.properties File
# Xalan - http://xml.apache.org/xalan-j/ xalan.version = 2.6.0 xalan.dir = ${dir.vendor.libs} xalan.jar = ${dir.vendor.libs}/xalan-j-${xalan.version}.jar # log4j - http://logging.apache.org/log4/ log4j.version = 1.2.9 log4j.dir = ${dir.vendor.libs} log4j.jar = ${dir.vendor.libs}/log4j-${log4j.version}.jar
This example specifies the exact versions of all the third-party libraries that the build uses and where to pick them up. By default they are picked up from the JavaTools libs directory. To make use of this particular properties file, you include it at the top of build.xml at the same time you include the other property files:
<property file="build.properties"/> <property file="default.properties"/> <property file="library.properties"/>
Then, when specifying the CLASSPATH for the build process, you explicitly include the .jar files as follows:
<path id="classpath"> ... <!-- include third party vendor libraries --> <pathelement location="${xalan.jar}"/> <pathelement location="${log4j.jar}"/> ... </path>
Since the location and names of the libraries being built against are based on property values, you have the flexibility to override them as before. For example, a developer trying out some new functionality (and versions of these libraries) could create the following entries in his private build.properties file:
xalan.dir = ${user.home}/libs xalan.version = 2.6.1
Again, I recommend recording the values of all the properties used during the build with the echoproperties task.