Wednesday, August 19, 2009

Building your Griffon app with Gradle

In this post, I'm going to show how to build your Griffon apps with Gradle. Griffon already comes with a command line tool to build and interact with your app, so why would you want to do this? I can think of a few reasons:
  • you have an existing Gradle build you want to integrate with
  • you have a complex build or packaging scenario
  • you want to take advantage of Gradle's dependency management for your app
Prerequisites
  1. Griffon
  2. Gradle
The build.gradle file
Below is a generic Gradle build file for Griffon applications. Either copy and paste the code below or download it and place it in a build.gradle in your Griffon application folder.
// standard configuration
project.captureStandardOutput(LogLevel.INFO)
configurations { compile }

// add additional repositories here if you need them
repositories {
//mavenRepo(urls:'http://download.java.net/maven/2/')
mavenCentral()
}

// add any dependencies you want downloaded as part of the bootstrap process
dependencies {
//compile 'com.google.collections:google-collections:1.0-rc2'
}

// this should be run once for the project
createTask('bootstrap') << {
// download any dependencies and put them in lib/
configurations['compile']?.files?.findAll { it.absolutePath.indexOf('unspecified') < 0 }.each { dep ->
ant.copy(toDir: new File('lib'), file: dep)
}

// try to install any plugins
new File('application.properties').eachLine { line ->
if (line.startsWith("plugins")) {
def plugin = line[8..-1].split('=')
griffon "install-plugin ${plugin[0]} ${plugin[1]}"
}
}
}

// mimic the tasks provided by the Java plugin
createTask('clean') << { griffon 'clean' }
createTask('compile') << { griffon 'compile' }
createTask('libs') << { griffon 'package' }
createTask('test') << { griffon 'test-app' }
createTask('dists') << { griffon 'prod package' }

// additional run-related tasks
createTask('run-app') << { griffon 'run-app' }
createTask('debug-app') << { griffon 'run-app -debug' }
createTask('run-webstart') << { griffon 'run-webstart' }
createTask('run-applet') << { griffon 'run-applet' }

// call out to the griffon command
def griffon(target) {
if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
ant.exec(executable: 'griffon.bat', dir: projectDir, failonerror: true) {
arg(value: target)
}
} else {
ant.exec(executable: 'griffon', dir: projectDir, failonerror: true) {
arg(value: target)
}
}
}

// if you want to use the gradle wrapper
createTask('wrapper', type: Wrapper).configure {
gradleVersion = '0.7'
}
To verify that everything is working, let's try running our app from Gradle (substitute gradle.bat if you are on Windows):
gradle run-app

Your Griffon app should run. If it doesn't, try invoking the Gradle command with the '-i' flag:
gradle -i run-app
to see what the problem is.

Multi-project Builds
So far it may seem like "What's the point?" We could already run our Griffon app with the griffon command. Where Gradle really starts to shine is when you start wanting to perform complex builds, such as building multiple Griffon apps at once or integrating with an existing build.

Gradle has good support for multi-project builds. Assuming you are using a directory layout like this:
settings.gradle
griffonapp1/
build.gradle
...
griffonapp2/
build.gradle
...
Building these multiple Griffon apps is as simple as listing them in the settings.gradle file:
include 'griffonapp1', 'griffonapp2'
Similarly, integrating with an existing build should be as simple as adding the name of your Griffon app to your existing settings.gradle file.

Complex Build/Packaging Scenarios
Griffon offers several packaging options with the Installer plugin. Sometimes you need more flexibility. For my PSICAT build, I wanted to package two Griffon apps together in a specific directory structure. The nice thing about Gradle build files is that they are just Groovy code and offer great integration with Ant. Below is a snippet from the PSICAT build that illustrates using Ant within Gradle:
// griffon create-all-launchers for PSICAT and SchemeEditor
griffon(psicat, 'create-all-launchers')
griffon(schemeEditor, 'create-all-launchers')

// build linux package
ant.copy(toDir: new File(linux, 'PSICAT')) {
fileset(dir: new File(psicat, 'installer/linux/dist')) {
include(name: 'bin/**')
include(name: 'lib/**')
}
}
ant.copy(toDir: new File(linux, 'PSICAT/tools/SchemeEditor')) {
fileset(dir: new File(schemeEditor, 'installer/linux/dist')) {
include(name: 'bin/**')
include(name: 'lib/**')
}
}
ant.chmod(perm: "+x", dir: new File(linux, 'PSICAT/bin'), includes: '*')
ant.chmod(perm: "+x", dir: new File(linux, 'PSICAT/tools/SchemeEditor/bin'), includes: '*')
ant.tar(destFile: new File(linux, "PSICAT-linux-${version}.tar.gz"), compression: 'gzip', basedir: linux, includes: 'PSICAT/**')
...
(if you're curious, the whole build.gradle file is here)

Cool Gradle Tricks
One cool feature of Gradle is that you can generate a 'wrapper' script that will download and bootstrap Gradle itself so that developers who download your source don't have to have Gradle installed to build your project.
The build.gradle file above is already setup to create this wrapper. You simply have to run the command:
gradle wrapper
and it will create four files in your project:
gradle-wrapper.jar
gradle-wrapper.properties
gradlew
gradlew.bat
If you include these files when you send someone your project, they can use the gradlew or gradlew.bat scripts as if they had Gradle installed:
./gradlew run-app

The other neat trick with Gradle is you can use its ability to pull from Maven repositories to bootstrap the dependencies of your Griffon app. This means you don't need to send someone your Griffon app with a bunch of JARs in the lib directory. Instead you can send them a Gradle-ized version of your app and tell them to run the command:
./gradlew bootstrap

This will download all of the dependencies you've listed in the build.gradle file and put them in the lib directory of your Griffon app. Specifying dependencies is as simple as adding a few lines to the dependencies section of the build file:
dependencies {
compile 'com.google.collections:google-collections:1.0-rc2'
compile 'org.slf4j:slf4j-api:1.5.8'
}
Conclusion
Hopefully this has given you a taste of using Gradle with Griffon. Both are awesome tools that really show off the power of the Groovy language. If there's enough interest in using Gradle with Griffon, we can make build.gradle a standard artifact created by the griffon create-app task.

3 comments:

Chrigel said...

Interessing.

I am currently pondering how to create multiple installer binaries (install with full dependencies, only with application specific depenencies).

Would this be a good use case for gradle?

Josh Reed said...

Hi Chrigel,

I think what you describe would be easily accomplished in Gradle as you can code up custom tasks in Groovy. Are you building Griffon apps or thinking more general? If you are building Griffon apps you might check out the Installer Plugin as well.

Cheers,
Josh

Anonymous said...

Nice post as for me. It would be great to read something more concerning that topic.
BTW look at the design I've made myself High class escort