Antwrap Introduction
Antwrap is a Ruby library that can be used to invoke Ant tasks. It is being
used in the
Buildr
project to execute Ant tasks in a Java project.
If you are tired of fighting with Ant or Maven XML files in your Java project,
take some time to check out Buildr!
Antwrap runs on both the native Ruby and the JRuby interpreters. Antwrap is
compatible with Ant versions 1.5.4, 1.6.5 and 1.7.0. For more information,
see the
Project
Info page.
Installing Antwrap
Installing Antwrap is done via the RubyGem gem command. In your OS shell type;
$ gem install antwrap
You will be prompted with the following options:
$Select which gem to install for your
platform (powerpc-darwin8.0)
1. Antwrap 0.6.0
(java)
2. Antwrap 0.6.0
(ruby)
3. Antwrap 0.5.4
(java)
4. Antwrap 0.5.4
(ruby)
If you are using the native Ruby interpreter or running the Buildr project,
then you want to select the
ruby option (in this case, #2).
If you are using Antwrap on the JRuby interpreter, select the
java
option. The native Ruby version of Antwrap depends on another
gem called
RJB
(RubyJavaBridge) and you will be prompted to install this as part of the
Antwrap installation. Do so. The RJB gem makes it possible
for a Ruby script to instantiate Java classes via the Java Native Interface.
If you chose the java gem, there are no further dependencies. Check the
RJB site for how to get RJB running (usually, it's just a matter of setting
the $JAVA_HOME and the $LD_LIBRARY_PATH environment variables).
Using Antwrap
The Antwrap library is pretty simple to use, and should look very familiar to
anyone who has written Ant tasks using XML.
You begin by instantiating an AntProject;
@ant = AntProject.new()
You can pass in a Hash of project options like so;
options = {:ant_home=>"/Users/fooman/tools/apache-ant-1.7.0", :name=>"FooProject", :basedir=> some_dir,
:declarative=> true, :logger=> Logger.new(STDOUT), :loglevel=> Logger::DEBUG
}
@ant = AntProject.new(options)
The default options for an AntProject are as follow
-
:name = The name of your AntProject. The default is ''.
:basedir = The location of your project base directory. The default is File.pwd.- :declarative
= If true, the AntProject will execute the task when you invoke it. If false, it
will return an instance of the task. Default value is true.
- :logger = The Logger to use. The default id Logger.new(STDOUT)
- :loglevel = The log level. Default is Logger::Error
- :ant_home
= The location of you Ant installation. If provided, Antwrap will
locate and load the Ant Jar files into the CLASSPATH. It will only do
this once per Ruby process, so even if you create multiple AntProject instances,
it only loads the required files once. If :ant_home is not provided, it is assumed
that you have added the Ant jar files to your CLASSPATH manually.
Once you have an AntProject instance, you can begin invoking tasks. To do so, you simply invoke the desired task on the AntProject. You pass in
task attributes via a Hash, and you pass in child tasks inside a block. For example;
@ant.path(:id => "other.class.path"){ |ant|
ant.pathelement(:location => "classes")
ant.pathelement(:location => "config")
}
@ant.path(:id => "common.class.path"){|ant|
ant.fileset(:dir => "${common.dir}/lib"){
ant.include(:name => "**/*.jar")
}
ant.pathelement(:location => "${common.classes}")
}
@ant.javac(:srcdir => "test", :destdir => "classes"){
|ant|
ant.classpath(:refid => "common.class.path")
ant.classpath(:refid => "foo.class.path")
}
Declarative Mode
By default, Antwrap runs in declarative mode. This means that the AntProject will execute the tasks as you declare them. Alternatively, you can declare your
Ant project to run in non-declarative mode, so that it only executes tasks upon the invocation of the execute() method (this is a more Object Oriented
approach, and may be useful in some circumstances). For example;
@ant = AntProject.new({:name=>"FooProject", :declarative=> false})
javac_task = @ant.javac(:srcdir => "test", :destdir => "classes"){
|ant|
ant.classpath(:refid => "common.class.path")
ant.classpath(:refid => "foo.class.path")
}
javac_task.execute
Reserved Words
If your Ant task conflicts with a Ruby reserved word, you can prep-end an underscore. For example, Ant-Contrib
tasks such as 'if' and 'else' conflict with the Ruby reserved words;
@ant._if(){|ant|
ant._equals(:arg1 => "${bar}", :arg2 => "bar")
ant._then(){
ant.echo(:message => "if 1 is equal")
}
ant._else(){
ant.echo(:message => "if 1 is not equal")
}
}
Under most circumstances, you won't need to use these tasks because the Ruby language (OK... any language) can handle
conditionals better than Ant. Indeed, the awkwardness of conditional operations in Ant scripts is likely one of the
reasons why you want to move to a build system such as Buildr.
Content Data
Content data is added via a 'pcdata' attribute:
@ant.echo(:pcdata => "<foo&bar>")
Changes
- Version 0.6.0: The Antwrap syntax has changed. Prior to version 0.6.0, any blocks passed in with a task were evaluated within the context of the task itself (using :instance_eval), so there was no AntProject instance passed to the block. For example, when the path task block was evaluated, it was evaluated within the context of the path task, so the pathelement children were assumed to be child tasks;
@ant.path(:id => "other.class.path"){
pathelement(:location => "classes")
pathelement(:location => "config")
}
This approach is pleasing syntactically because it looks like the XML syntax we are accustomed to. Unfortunately, it means that you can't execute arbitrary code in the block. For example, this would fail because the AntTask class does not contain a foobar() method;
def foobar
return "classes"
end
@ant.path(:id => "other.class.path"){
foo = foobar()
pathelement(:location => foo)
pathelement(:location => "config")
}
To rectify this, the AntProject now yields to the block and passes itself as a parameter. So calling your foobar() method will work;
def foobar
return "classes"
end
@ant.path(:id => "other.class.path"){ |ant|
foo = foobar()
ant.pathelement(:location => foo)
ant.pathelement(:location => "config")
}
While slightly more verbose, this changes make life easier when invoking Ant tasks, and is more typical of a Ruby library.
Comments or Questions? Contact caleb.powell@gmail.com