One thing on my to-do list for a long time has been to switch GContracts  from Maven to Gradle. This blog post is about the basic concepts I learned from the migration and how parts of the resulting build.gradle look like.
GContracts is basically a Java library which provides Groovy AST transformations to annotate Groovy classes with pre-, post-conditions and class-invariants. So far, the build-process has been realized with Maven 2, thus with a pom.xml.
The overall build-process is pretty straight-forward:
compile the java classes with a dependency to the current Groovy version
compile the groovy classes with the same dependency (all tests are done in Groovy)
execute the test-suite
generate the javadoc files from the given source code
package the compiled class files to a separate Jar file
package the source files to a separate Jar file
package the javadoc files to a separate Jar file
sign all Jar files
upload all jar files to the OSS Sonatype Nexus staging repository
The pom.xml for GContracts build process can be found at github, but it mainly consists of plugins realizing the use-cases cited above.
Starting with a simple build.gradle
Gradle's equivalent to Maven's pom.xml is a file called build.gradle. But differently from Maven, it uses its own internal Groovy DSL called the Gradle Build Language .
Before I am going to start with the basic terminology it is important to notice that Gradle itself has been built by applying domain-driven design principles, leading to an important consequence: you'll find class representations of the terminology I use in this blog article in the Gradle API. This is a direct consequence of using a ubiquitous language (a language structured around the domain model and used by all team members to connect all the activities of the team with the software), a main principle of DDD.
A first-class citizen is the project. Each build.gradle file is directly linked to a project. If GContracts would consist of multiple sub-projects (core-module, spring-module, etc.) each sub-project would be assigned to a separate build.gradle file. For the sake of simplicity, let us ignore multi-project builds for this article (more about it can be found in the Gradle documentation ). As I've said previously, when taking a look at Gradle's API javadoc you'll find the Project class. It is exactly this Project class which will be instantiated by Gradle when starting a build process - after initialization build.gradle is used to configure the Project object instance.
There are various ways we can alter the Project instance with DSL statements in the build.gradle file. What we need to do first of all is to set some properties (although all of them would be optional and assigned to default values):
As you might see in the code snippet above, there is no reference to the actual Project object instance defined, like project.group = .... Since build.gradle syntax is a Groovy DSL, this can be done by altering the delegate - the object which receives method and property calls. In this case, every property we've specified will be delegated to the actual Project instance (if it is not a build script property/method, but let us ignore those details by now).
Gradle is based on a modularized structure, with each module being a plugin. Out-of-the-box Gradle comes with a bunch of officially supported plugins. In order to compile and package the Java source files we need to add the Gradle Java plugin . Applying a plugin to a build configuration file is done by calling the Project class' apply method
which imports all tasks defined by the Java plugin. When calling gradle tasks on the command-line we could see the tasks being imported by the Java plugin. As GContracts internally uses Groovy as the only dependency (the Java code executing the AST transformations needs to know the Groovy abstractions), an external dependency needs to be defined in our build.gradle file to let GContracts compile correctly:
This means that during compilation the dependency to Groovy 1.7.6 will automatically be resolved in a transitive way. In order to resolve all libraries we need to specify a Maven repository location to obtain the library files - I chose Maven central.
So far so good, next step is to setup the test environment.
Setting Up the Test Environment
Since GContracts test cases are entirely written in Groovy, we'll also need to apply the Groovy plugin . The plugin does not work out-of-the-box, it needs to know the exact Groovy version it should use. This can be done by specifying another dependency:
There is a little detail which might have caused your attention: the target of the newly introduced dependency is groovy.
Gradle introduces the concept of configurations. Multiple configurations can be added to the current project, each configuration will contain a certain distinct set of dependencies. When we applied the Groovy plugin, it added a groovy configuration to the current project. The groovy configuration does not default to a certain Groovy version, that is the reason we need to explicitly define one.
The Groovy plugin automatically applies the Java plugin, so we can remove this line. In addition, we can remove the first reference to Groovy for the compile configuration since the groovy configuration extends the compile configuration.
The next step is to add another dependency for executing JUnit test cases:
As I've already brought up, this is all valid Groovy code - we could use custom classes, custom plugins or custom Groovy functionality to adapt the build script for our (or the enterprise) needs.
This is it. With the following build.gradle we can already compile Java and Groovy source-code, create javadocs, test and package GContracts - only be specifying the plugins, a repository, dependencies and very little meta-data.
When running gradle tasks we can have a look at all tasks this build script offers for execution:
Of course there is a way to define custom tasks in Gradle, we will have a look at when we implement uploading to the Maven repository. If you want to know more about it in advance, take a look at .
It gets trickier
The next build tasks are to configure signing and uploading the archives to the OSS Sonatype Nexus staging repository. Signing is done via GPG and luckily there is a third party plugin for that use-case: the GPG Gradle plugin .
To use the GPG plugin we need to add a separate build script dependency:
with that dependency we can apply the third party plugin and thus import GPG signing functionality.
The PgpPlugin looks for the uploadArchives task (which will be part of the Gradle Maven plugin which is used for uploading the archive) and adds signing of all the affected artifacts before the deployment is executed. In order to let the plugin sign correctly, we need to specify some additional meta-data:
We could specify arbitrary configuration data in the gradle.properties file, located in the .gradle directory - this would be a better place for keyId and password.
The last requirement is uploading the signed artifacts to the OSS Sonatype Nexus staging repository. Therefore, we need to apply the Maven Gradle plugin  and specify additional data which will be part of the generated pom.xml.
Executing gradle uploadArchives now compiles, tests, packages, signs and deploys the GContracts jar to the staging repository.
Our last step is to deploy two more signed Jar files: sources and javadocs. This can be easily done by extending the archives configuration with two custom tasks:
As you can see in the code sample, the tasks are both of type Jar. A task type definition specifies custom properties and behavior which can be inherited for custom tasks. Finally, both Task object references are added to the artifacts of the current project.
Executing gradle uploadArchives now leads to the following result:
The source files, javadoc and binary are now signed and uploaded to the staging repository. This is exactly what we've been striving for. We're done for now.
Conclusion and Personal Opinion
To be fair, GContracts is just a plain Java library packaged as a Jar file. The complexity in the build process comes after the archives have been created. But up to signing and deploying, things were pretty easy and the build script was done in about 15 minutes. Searching for appropriate solutions for signing artifacts have taken some time but the mailinglist came to rescue as a very valuable information resource.
Personally, I really like the DDD approach Gradle has taken. The Gradle API is consistent, meaning it should be easy for beginners to quickly get into the build domain terminology and setup basic build scripts. If complexity raises, it is always possible to add arbitrary complex custom tasks with all the power Groovy provides. Furthermore, since Groovy is used as an internal DSL, you can use object-orientation for better build script code quality (encapsulation, inheritance, modularization, etc.) - time to say goodbye to Maven :-)