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
  1. :name = The name of your AntProject. The default is ''.
  2. :basedir = The location of your project base directory. The default is File.pwd.
  3. :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.
  4. :logger = The Logger to use. The default id Logger.new(STDOUT)
  5. :loglevel = The log level. Default is Logger::Error
  6. :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


@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