terp ANT Tasks
A quick overview
terp supplies a number of ANT tasks that we have found useful in our own development efforts. ANT has traditionally been most popular in the pure-Java world because it has always come with many built-in Java tool abstractions. It has been less successful in C++ or .NET use cases because it does not integrate these tool chains very well.
Codemesh has been working at the intersection of all these technologies for a decade now and it has become clear to us that we
- like ANT for its Java centricity and platform portability.
- hate ANT for its Java centricity and how awkward it is to conditionalize builds based on different compilers, operating systems, processor architectures, etc.
Mind you, it is not impossible to conditionalize ANT builds to account for all these build environment differences, it's just not very easy and it does not feel elegant to do it. If you build a product containing C++ components on six different operating systems, each supporting 32- and 64-bit versions, some of them supporting different processor types, all of them supporting multiple compilers, ... you get the picture, it becomes quite ugly to maintain.
The terp ANT tasks were developed to account for real-life build requirements. They fall into the following major categories:
- Full-featured expression language
You can use the terp expression language within ANT's property expansions, thereby gaining access to all of Java's features from ANT.
- Build conditionalization
All terp ANT tasks support optional if and unless attributes whose values are interpreted as terp expressions. You can express arbitrarily complex conditions directly in the attribute, rather than having to declare a property via a <condition>. You can for example write if="os.family=='windows' && procarch.bits==32" to execute a task only in a 32-bit Windows context.
- ANT enhancements
Property values support the full terp expression language. You can reference object fields, call object methods, etc., all from within a property value. See the <terp.property> task for some examples.
We also supply a <terp.echo> task that supports terp templates, a <terp.exec> task that is specificly geared towards the execution of terp executors, and a <terp.fail> task for ANT unit testing.
When you need to build multiple versions of the same thing, it comes in handy to have some type of iterator task available. It's even handier to have such an iterator that ties seamlessly into the ANT and terp property scheme. See the <terp.foreach> task for more information.
- C++ compilers
Wouldn't it be nice to have a C++ compiler abstraction that's really smart? One that can locate C++ compilers by type, by version, or by processor architecture? One that can dynamically generate source files or resource files and then build them? One that can clean up behind itself? One that builds into different directories for different build configurations? One that understands 32- and 64-bit builds? I think you can see where this is going. Check out the <terp.cpp> task for more information.
- Boundschecker integration
Wouldn't it be nice to a) build intrumented C++ code and b) run the instrumented code under BoundsChecker to find memory leaks and other errors? You can do that with <terp.cpp> and <terp.boundschecker>.
terp tasks require ANT 1.7.0 or higher.
How do I add terp ANT tasks to my build?
ANT supports multiple ways of adding tasks to a project. You need to make the task implementations available to ANT in some way. The traditional way is to either
- copy the jar files into your ANT distribution's
- invoke ant with a
-lib pathoption, where
pathis the directory containing the terp jar files.
Once the implementation files are available to ANT, you need to import the tasks you wish to use into your project. You use the ANT
<taskdef> element for that purpose. Every terp ANT task specifies the required
<taskdef> element that is listed in its documentation page. In general, this looks something like:
<taskdef name="terp.cpp" classname="com.codemesh.terp.ant.compiler.cpp.CppCompilerImpl" />
You can also use the resource flavor of an ANT
ant-terp.jar file includes a properties file that lists the contained tasks with their default names. To introduce all terp tasks under their default names to your build, you can simply write:
<taskdef resource="tasks.properties" classpathref="terp.path" />
terp.path reference is expected to contain the terp distribution's jar files. Unfortunately, several popular development tools (eclipse, IntelliJ IDEA) seem to have trouble with this second way of importing tasks into your build script. For that reason, you might favor the former, more clumsy approach.
Some technical points
Many of terp's power features rely on terp's expression evaluation engine. To make terp expressions available in ANT, terp has to replace the
PropertyHelper used by ANT. terp does this automatically so you don't have to invoke ANT with additional, cryptic command line arguments, but this has a number of consequences:
- terp properties become redefinable.
If this causes problems in other parts of the build, you might want to segregate your terp tasks into a separate build script that you invoke from your more traditional ANT script.
- this might cause problems with other ANT extensions that play the same game. We are currently not aware of this problem occuring in practice, but please let us know if you encounter it.