Gradle and Java Dependency Library Management

Recently I learned that I couldn’t easily merge the encryption library I settled on into my main one without pain. As this is a sporadically visited hobby, I don’t have the free time to get to the bottom of it. As we’re dealing with a signed jar around security, it’s possible I’d be pushing s**t uphill anyway.

You might be aware by now that I like to solve the trusting of 3rd-party library problem by simply glueing those libraries into the final executable. If I can’t have that ideal, I want a model that’s as close as I can get to it.  I decided I’d get gradle to merge those dependency jars that are easy to merge, and ship the ones with issues as external dependencies.

I’m now as close to my ideal as I think I can push it, so below is some gory detail for fellow gradle fans who are especially interested in building Java applications. Gradle 1.11 was used for the scripting below.

First, I introduce some of my own properties into the project:


project.version = "0.2"

project.ext {
  title = "PersonalFinancier"
  libsDirectory = "libs"
  distributionDirectory = "dist"
}

Then I separate my dependencies into two lists. One list has the libraries I want merged. The other, the libraries I’ll ship separately.


List libsToMerge = [ 
  "com.jtattoo:JTattoo:1.6.10", 
  "com.cedarsoftware:json-io:2.5.2", 
  "de.erichseifert.gral:gral-core:0.9" 
]

List libsToShip = [ 
  "org.bouncycastle:bcprov-jdk15on:1.50" 
]

Then I introduce two new configurations, one for the merging of libraries, the other for shipping:


configurations {
  mergeLibs
  shipLibs
}

I then modify the dependencies to a) tie the relevant lists the their matching configurations, and b) ensure that the pre-supplied compile dependencies continue to rely on both sets of libraries:


dependencies {
  mergeLibs libsToMerge
  shipLibs libsToShip
  compile libsToMerge, libsToShip
}

I also modify the jar task. I first tell it to take the collection of merge libraries and zip them into the jar being produced. Then, as I build the manifest, I ensure that each of the libraries I need to ship are listed on the Class-Path of the manifest file. Finally, because I am developing in Linux, and would rather the build script do it, I’m waiting until the jar is cooked, then flagging the jar as executable.


jar {

  from {
    configurations.mergeLibs.collect { zipTree(it) }
  }

  manifest {
   attributes (
      'Implementation-Title': project.title, 
      'Implementation-Version': project.version,
      'Contact':  project.contact,
      'Built-With': "gradle-${project.getGradle().getGradleVersion()}, groovy-${GroovySystem.getVersion()}",
      'Created-By': System.getProperty('java.version') + ' (' + System.getProperty('java.vendor') + ')',
      'Main-Class' : mainClassName,
      'Class-Path' : configurations.shipLibs.collect{ it.getName() }.join(' '),
    )
  }
  
  doLast {
    file(jar.archivePath).setExecutable(true)
    println "$jar.archiveName created and is executable."
  }
}

Because I’m developing under Eclipse, I have a separate ‘libs’ directory for the Eclipse project to look in for dependencies. I make things easy for my Eclipse experience by introducing a task that takes all the project dependency libraries and syncs them to the directory that Eclipse knows to look in:


task syncLibs(type: Sync) {
  from configurations.compile
  into project.libsDirectory
  doLast {
    println "Synchronised library dependencies into directory ($project.libsDirectory)."
  }
}

Finally, I introduce a new task to distribute both the external libraries and the executable jar itself, all to to the distribution directory I specified as a project property:


task distribute(type: Copy) {
  dependsOn jar, syncLibs
  from jar 
  from configurations.shipLibs
  into project.distributionDirectory
  doLast {
    println "Distributed $project.title and support libraries to directory ($project.distributionDirectory)."
  }
}

It took me a while to figure out, but shipping an executable jar with a minimum of support libraries is something gradle handles gracefully.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s