Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Kenneth A. Kousen - Making Java Groovy - 2014.pdf
Скачиваний:
50
Добавлен:
19.03.2016
Размер:
15.36 Mб
Скачать

Making Ant Groovy

97

Each task outputs its own name, followed by the included built-in Ant tasks indented underneath. The build completed successfully, though that can be misleading. The BUILD SUCCESSFUL statement at the end means that Ant finished all the tasks. The individual tasks may or may not have worked.

The tasks chosen here are typical, but there is no standard. Each organization (and even each developer) is free to choose their own. Reusing tasks between different builds also requires an import statement (or copy-and-paste reuse), plus some effort to make sure the tasks are not tied to a particular project structure.

Again, the benefit here is that this is all completely portable. The Ant build should work just as well on Mac OS X as it does on Windows or Linux. The downside is that this is a trivial Hello World application and the build file is already over 35 lines long. Once you add in the junit and junitreport tasks, to say nothing of customizing the classpath with third-party libraries, the size of this file will grow quickly. A more extensive build file, including the JUnit 4 libraries and a test case, can be found in the chapter source code.

Rather than do that here, however, let me show you how to introduce Groovy into this system.

5.3Making Ant Groovy

Ant is not as common in Java builds as it used to be, but switching build tools is a major decision for most organizations and not to be undertaken lightly. If you’re working with a large installed base of Ant builds, then Groovy can still contribute.

Four approaches are available:

Groovy scripting code can be added directly to an Ant build file.

Groovy scripts and classes can be compiled and executed in Ant builds using special Ant tasks for that purpose.

The Groovy standard library contains a special class called groovy.util.AntBuilder that can replace the XML build file with Groovy scripting code that does the same thing.

There’s a Groovy DSL available, called Gant, which provides an alternative to

AntBuilder.

ANTBUILDER Even if you don’t use Ant, the AntBuilder class is worth knowing about because it’s embedded in other build tools, like Gant and Gradle.

The following subsections will tackle each of these Groovy and Ant topics in turn.

5.3.1The <groovy> Ant task

Ant has two hooks that allow you to add Groovy to a standard build file. The <groovy> and <groovyc> tasks use the Groovy libraries to execute Groovy scripts and compile Groovy source files, respectively.

www.it-ebooks.info

98

CHAPTER 5 Build processes

Starting first with <groovy>, defining the associated task in an Ant build lets you write Groovy code directly into the build file. The following listing shows a trivial example.

Listing 5.3 A trivial Ant build that executes Groovy code in a task

<?xml version="1.0" encoding="UTF-8"?>

 

 

 

 

Access environment

<project name="Groovy Ant" basedir="." default="info">

<property environment="env" />

 

 

 

 

variables

 

 

 

 

 

<path id="groovy.classpath">

 

 

 

 

Use the groovy-all

<fileset dir="${env.GROOVY_HOME}/embeddable" />

 

 

 

 

 

JAR file

</path>

 

 

 

 

 

 

 

 

 

<taskdef name="groovy"

 

 

 

 

Define the

classname="org.codehaus.groovy.ant.Groovy"

groovy task

classpathref="groovy.classpath" />

 

 

 

 

 

<target name="info">

 

 

 

 

 

<groovy>

 

Use the

 

println 'Hello, World!'

 

groovy task

 

ant.echo 'Hello, World!' </groovy>

</target>

</project>

The environment property allows the build to access system properties in the operating system. Here the env variable is used to access the current value of GROOVY_HOME, the installation directory for Groovy. The <path> element assigns the groovy-all JAR file (found in the embeddable directory) to the groovy.classpath ID.

The <taskdef> element then defines the groovy task as a reference to the org.codehaus.groovy.ant.Groovy class, which is resolved in the groovy-all JAR file. Once the groovy task has been defined it can be used to execute arbitrary Groovy code. A straight print of “Hello, World!” is executed, and then the Ant echo task is also called.

It’s therefore easy enough to add Groovy code to an existing Ant build file, which can be useful if looping or conditional logic is needed in the build. It’s notoriously difficult to “program” in XML, and technologies that tend that direction (like Ant and XSLT) often result in awkward, complex build files. Adding Groovy scripting code might help the build file without modifying the underlying source code.

5.3.2The <groovyc> Ant task

Say you follow the advice in this book and decide to add Groovy modules to your implementation code. If you’re still going to build with Ant you’ll need a compilation task, similar to <javac>, for Groovy. That task is <groovyc>.

The basic <groovyc> task definition is simple enough:

<taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.classpath"/>

www.it-ebooks.info

Making Ant Groovy

99

The name of the task is <groovyc>, and it’s backed by the Groovyc class in the org.codehaus.groovy.ant package. This class is part of the Groovy Ant JARs referenced in the earlier build file.

The result of this task definition is that you can compile Groovy classes with <groovyc> while you compile Java classes with <javac>. This enforced separation of code bases can lead to difficulties, however, if there are cross dependencies. For example, a Groovy class may implement a Java interface and reference a Java class, which in turn uses a Groovy class, and so on.

A good way to resolve these issues is to use the joint-compilation approach. Ant lets you embed the <javac> task inside the <groovyc> task. The nested tag approach results in a <groovyc> task that looks like this:

<groovyc srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath">

<javac source="1.5" target="1.5" /> </groovyc>

The nested <javac> task doesn’t imply the Java compiler is running. As a child of the <groovyc> task it lets the Groovy joint compiler do all the work.

The source directory, destination directory, and classpath variables defined in the <groovyc> task are passed down to the nested <javac> task. The joint-compilation approach means that Groovy will compile the Groovy sources and create stubs for them, then call the Java compiler to do the same for the Java sources, and resume the compilation process with the Groovy compiler. The result is that you can mix Java and Groovy sources without a problem.

Therefore, to extend the Ant build file presented section 5.2 to include Groovy files, make the additions and changes shown in the next listing.

Listing 5.4 Extending the “Hello, World” build to mix Java and Groovy sources

<path id="groovy.classpath">

<fileset dir="${env.GROOVY_HOME}/embeddable" /> </path>

<path id="classpath">

<fileset dir="${lib.dir}" includes="**/*.jar" /> </path>

<taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.classpath" />

...

<target name="compile">

<mkdir dir="${classes.dir}" />

<groovyc srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath">

<javac source="1.5" target="1.5" /> </groovyc>

</target>

www.it-ebooks.info

100

CHAPTER 5 Build processes

The rest is the same as before.

If you’re committed to Ant builds using XML, that’s all there is to it. If, however, you’re willing to switch your build language to Groovy, there are a couple of other alternatives. The next two subsections use Groovy for the build language but are still fundamentally based on Ant.

5.3.3Writing your build in Groovy with AntBuilder

The standard Groovy library includes a class called groovy.util.AntBuilder. To use it you need to add the Java-based Ant JAR library files to your classpath, but once you do, AntBuilder lets you replace the XML syntax with Groovy.

Any task defined by Ant can be used through the AntBuilder class. For example, the following listing shows a simple script that makes a copy of its own source, verifies that it worked, and then deletes the copy.

Listing 5.5 antbuilder.groovy, which copies itself

def ant = new AntBuilder() String dir = 'src/main/groovy'

assert !(new File("$dir/antbuildercopy.groovy").exists())

ant.echo 'about to copy the source code' ant.copy file:"$dir/antbuilder.groovy",

tofile:"$dir/antbuildercopy.groovy"

assert (new File("$dir/antbuildercopy.groovy").exists())

ant.echo 'deleting the copied file' ant.delete file:"$dir/antbuildercopy.groovy"

Builder code and regular Groovy code are freely intermixed in this example. The Ant tasks used here are echo, copy, and delete, but it would be easy enough to use others like javac, junitreport, or even optional Ant tasks like mail. As long as the required Ant libraries are in the classpath, each will work.

There’s actually a simplification available. The with syntax is available as a part of Groovy’s metaprogramming capabilities. It can simplify the previous listing down to that shown in the next listing.

Listing 5.6 Simplifying the build script using the with method

ant.with

{

echo

'about to copy the source code'

copy

file:"$dir/antbuilder.groovy",

 

tofile:"$dir/antbuildercopy.groovy"

echo

'deleting the copied file'

delete file:"$dir/antbuildercopy.groovy"

}

The with method invokes the contained methods on the Ant builder.

www.it-ebooks.info

Making Ant Groovy

101

AntBuilder can be used to script entire build files. It’s useful for creating a build file quickly, especially if you already know the corresponding Ant tasks well. Because AntBuilder is part of the standard Groovy library it can be used wherever you need to do build-related tasks. Even better, Gradle build files include an instance of AntBuilder, making the migration path from Ant to Gradle much simpler.

A more interesting example is given in the next listing, which is a port of the original Ant build shown in listing 5.1.

Listing 5.7 A Groovy AntBuilder script port of the build.xml file from listing 5.1

AntBuilder ant = new AntBuilder()

String srcDir = 'src' String buildDir = 'build'

String classesDir = "${buildDir}/classes" String jarDir = "${buildDir}/jar"

String reportDir = "${buildDir}/reports" String libDir = 'lib'

Instantiate the builder

Port of <property> elements

ant.with { path(id:'classpath') {

fileset dir:libDir, includes:"**/*.jar"

}

Use builder as delegate for unrecognized methods in block

path id:'application', location:"$jarDir/HelloAntBuilder.jar"

delete dir:buildDir

mkdir dir:classesDir

Compile Java

javac(srcdir:srcDir, destDir:classesDir, includeantruntime:false, classpathref:'classpath')

mkdir dir:jarDir

 

 

 

 

jar(destfile:"${jarDir}/HelloAntBuilder.jar", basedir:classesDir) {

Build

manifest {

 

 

 

attribute name:'Main-Class', value:'mjg.HelloWorld'

JAR

}

 

 

 

 

}

 

 

 

 

mkdir dir:reportDir

 

 

 

 

 

 

 

 

junit(printsummary:'yes') {

 

 

 

 

classpath {

 

 

 

 

path refid:'classpath'

 

 

 

 

path refid:'application'

 

 

 

 

}

 

 

Run tests

 

formatter type:'xml'

 

 

 

 

batchtest(fork:'yes', todir:reportDir) {

 

 

 

 

fileset dir:srcDir, includes:"**/*Test.java"

 

 

 

 

}

 

 

 

 

}

 

 

 

 

junitreport(todir:reportDir) {

 

Generate

 

 

 

fileset dir:reportDir, includes:"TEST-*.xml"

 

 

report todir:reportDir

 

test report

 

}

 

 

 

 

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]