Friday, April 22, 2011

Invoke Groovy script from Ant or Gant

Recently I tried to use groovy script to improve our build system. I found Groovy TemplateEngine is so powerful and very easy to use, quite similar to erb in Ruby. I want to use it to generate a text file. The following example shows how to generate a text file based on a template.

file test.template
Dear "$firstname $lastname",
So nice to meet you in <% out << (city == "New York" ? "\"The Big Apple\"" : city) %>.

file test.groovy
import groovy.text.SimpleTemplateEngine
import groovy.text.GStringTemplateEngine

def f = new File(this.args[0])
def binding = ["firstname":args[1], "lastname":args[2], "city":args[3]]
engine = new GStringTemplateEngine()
template = engine.createTemplate(f).make(binding)
println template.toString()


If in a terminal if I type:  groovy test.groovy Steve Zhang Toronto, and I will get output

Dear "Steve Zhang",
So nice to meet you in Toronto.

Invoke Groovy from Ant
I followed this article from Groovy site
Following is the ant script:
build.xml
<project>
 <taskdef name="groovy"
          classname="org.codehaus.groovy.ant.Groovy"
          classpath="lib/groovy-all-1.7.10.jar"/>
 
 <target name="helloGroovy">
    <groovy>
     println "hello world"
    </groovy>
 </target>
   
    <target name="testTemplate">
   <groovy src="test.groovy">
    <arg line="test.template"/>
    <arg line="Steve" />
    <arg line="Zhang"/>
    <arg line="Toronto"/>
    </groovy>
 </target> 
</project>
It pretty straight forward, first declare <taskdef>, you need to put groovy-all-version.jar in the class path, but it took me a while to figure out where the groovy-all-xxx.jar is, finally I got it: when you download the binary zip file, unzip it, find embeddable folder, inside this folder you will find the groovy-all-1.7.x.jar!
Then you can put any kind of groovy code inside <groovy>.

Invoke Groovy Script from Gant
Gant is a great tool to replace Ant, I can definitely convert the above build.xml to build.gant, but I realize it is a kind of weird, since Gant is already based on Groovy, it already depends on Groovy jars, why we still need to declare the dependency in groovy-all-xxx.jar? There must be a better solution. Through my exploration, I found two ways to call Groovy script from Gant:

A) Use GroovyShell
target( runGroovy:"run a groovy script") {
 def shell = new GroovyShell()
    shell.run(new File("test.groovy"),
               ["test.template","Steve","Zhang","Toronto"] as String[])
    
}

The above gant script also tells you how to invoke a groovy script from another Groovy script file. GroovyShell can do this for us.

B) Embed the groovy source code directly in the Gant script file
During the exploration, I have some feeling that Gant script might can be thought of Groovy code, what if we add the groovy source code directly into the Gant script, so I did this:
import groovy.text.GStringTemplateEngine

target( hello: "hello") {
  println "hello"
}

target( runGroovy:"run a groovy script") {
 def shell = new GroovyShell()
    shell.run(new File("test.groovy"),
               ["test.template","Steve","Zhang","Toronto"] as String[])
    
}

target(runTemplate :"run a groovy template") {
 def f = new File('test.template')
 def binding = ["firstname":"Steve", "lastname":"Zhang", "city":"Toronto"]
 engine = new GStringTemplateEngine()
 template = engine.createTemplate(f).make(binding)
    println template.toString()
}
The target runTemplate will have the same result output, when you look at the import statement at the top, and the groovy code inside the target, I realize that Gant script is actually Groovy code!, the Gant target can be regarded as Groovy method. This conclusion is so important, because it means you can easily integrate the Groovy(or Java) code in the build script, I am sure it will definitely improve developer's productivity.

No comments:

Post a Comment