I’ve been using Gradle for sometime at the workplace and I find it to be a good build tool. In the past I’ve SBT, Maven and Nant as well as, well, MSBuild. It was only MSBuild that I grew much familiar with back in my .net days.
This is a minimal Gradle post for whoever wants to get started with it with little/no fuss. Let’s get right into it.
What’s Gradle used for — It’s a build management tool. What this means is that you can use Gradle to manage building your projects, manage dependencies within projects and external to them, manage how tests are run, write pre/post build tasks/targets, and so on. You get the idea.
Two main ideas in Gradle — Projects and tasks
A gradle build can consist of one or more projects and each project can have one more more tasks. Tasks are nothing but units of work need to be done. A gradle project needs to have at least one build.gradle file. If there is only one such file it needs to be present in the root directory of the project. But you can additionally have build.gradle for each project in your project. Gradle build files contain DSL code and can also contain Groovy or Kotlin code. The DSL is basically a declarative kind of language.
At this point, let’s dive into code and see a few things first hand. I will be using IntelliJ IDEA to set up a Gradle project. You are free to use any editor of your choice as long as it has support for Gradle.
Download IntelliJ, install Gradle plugin (it would be checked by default), Setup a new ‘Gradle’ project. You will see a build.gradle file setup for you as well as a settings.gradle file.
Note that you will also see a few more things — gradlewrapper, gradlew.sh and gradlew.bat files
So, what’s gradlewrapper?
Say you run multiple projects on your laptop. Some might be using SBT, some Maven, some Gradle, etc. Gradlewrapper let’s you use Gradle local to your project by providing Gradle jars that don’t need to be available system wide. Meaning that you won’t need to install Gradle on your system. You can use the Gradle jars and scripts that come with Gradlewrapper and that’s the end of story. To use Gradle through Gradlewrapper you will need to submit your Gradle commands to the scripts that ship with Gradlewrapper — gradlew.bat for windows and gradlew.sh for *nix, much the same way as you would use the ‘gradle’ command from command line.
so, for example a normal gradle command could look something like
gradle
but if you don’t want to install Gradle on your system and rather just use Gradlewrapper, you will write something like
./gradlew
Project listing goes in settings.gradle
settings.gradle will contain at least one project name — the root project, by default it’s the name of the directory containing the main/root project of your setup. You can of course, change this name to anything of your liking. Over time, if you have more projects, their names also go in settings.gradle file.
The Gradle system is rich with plugins. All plugins available can be seen at this url. Plugins provide additional functionality to your Gradle setup. Plugins might have pre-written tasks that you might not want/have to write yourself, apart from lot more functionalities.
For example, if you want Scala to be the language for your project and you’d like Gradle to make src/test directories for the same, you can use the ‘scala’ plugin and ‘refresh’ your build.gradle and you will see the scala related directories in your project structure.
(By the way, you can also do a ‘refresh’ by executing gradle –refresh-dependencies task from command line)
For instance, my build.gradle file for this exercise looks like this —
group 'org.practice'
version '1.0-SNAPSHOT' //when version is set you will have a jar file with
//name like mylibName-1.0-SNAPSHOT.jar, in this case
apply plugin: 'scala'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
//example on how to target jars in a specific local directory
//compile files('/home/vaibhav/spark/spark-2.1.0/core/target/spark-core_2.11-2.1.0.jar')
compile group: 'org.apache.spark',name: 'spark-core_2.11', version: '2.1.0'
testCompile group: 'junit', name: 'junit', version: '4.11'
}
Couple of things to note above -
1. You can see how to apply a plugin.
2. When you might have setup your sample project(as mentioned above) as a gradle project, you would have been asked to supply groupId and artifactId. GroupId, ArtifactId and Version, also called GAV, is a pretty standard way in the mvn world to identify a library. Gradle follows the same mvn convention and in fact, the gradle project structure by convention, you will find, is much like mvn project structure.
src/main/scala
src/test/scala
3. There would be a sourceCompatibility option meaning that you compile your code to be compatible with a certain version of java run time.
4. Under ‘repositories’, you can list the repos Gradle will hit to search for the dependencies mentioned under ‘dependencies’. You can mention any number of repos by providing a url. MavenCentral is available by default.
You can also specify local file system path to load dependencies from, something like
runtime files(‘lib/project1.jar’,‘lib2/project2.jar’)
There are many ways to specify how to pick, from where to pick, and what to exclude. I am sure you can find the right syntax when you need it.
5. You can see that // works for comments, as well as /* .… */
6. When mentioning what dependencies to download, you provide their GAV. By the way, if you don’t tell gradle where to download your dependencies, it will do so by default at ~/.gradle/caches/
7. You can group your dependencies — above you can see that I don’t need junit in my main project but I need it in my test project. So I use a pre-built group ‘testCompile’. Similarly, for the ‘compile’ group.
If you’d rather not use gradlewrapper, you can install gradle using brew in Mac or apt-get install on Ubuntu, etc. You might need to set GRADLE_HOME if the installation does not already does that for you.
gradle.properties
You can specify your gradle specific settings like what jvm to use, whether to keep gradle daemon alive(recommended setting), etc. in a gradle.properties file. If you are using gradlewrapper, you should already have gradle-wrapper.properties file for you.
Gradle Tasks
So far, we’ve talked about setting up gradle and about projects. Let’s take a look at tasks now.
A build.gradle file can have many tasks, these tasks can be grouped or not grouped. When not grouped, a task is considered a ‘private’ task and will show up under ‘Other tasks’ when seeing the output of command ‘gradle tasks’, which lists all tasks. Here’s an example of a grouped task and an ungrouped task-
task calledOnce {
println("I will get printed first")
}
task helloworld {
group 'some group name'
description 'this is desc for task helloworld'
doFirst {
println("hello world task begins")
}
doLast {
println("hello world task ends")
}
}
> gradle helloworld
> I will get printed first
> hello world task begins
> hello world task ends
doFirst, doLast are phases within a task. Statements in task closure not under any phase by default go into configuration phase and are executed once before any other phases, no matter whether you call that particular task or not.
Tasks can depend on each other
This is a normal procedure for most build scripts. Execution order of tasks depends on which tasks depend on which ones.
example,
defaultTasks 'compile' //since a default task has been specified, on the command line it will suffice to just type 'gradle'
apply plugin: 'scala'
task compile(dependsOn: 'compileScala') {
doLast {
println("compile task")
}
}
//shorter syntax for defining doLast, doFirst
compileScala.doLast(dependsOn: 'clean') {
println("compileScala task given by scala plugin")
}
task clean {
doLast {
println("clean task")
}
}
Note that you can also create your own tasks by extending the DefaultTask class, but this is not something we are going to try in this post.
Multi-project build structure
Usually you would have a few sub-projects or modules in your project and they also need to be built, tests executed for them and there might be dependencies among these sub-projects. Of course, gradle gives you option here. Let’s say you have project structure like this —
root_project
— Web
— Core
— Util
In this case, your settings.gradle would contain ‘include’ something like this —
include 'Core', 'Web', 'Util' //telling gradle we have sub projects
Now, the build.gradle file needs to cater for these sub-projects as well. There will be some tasks that are specific to sub-projects, some tasks common for all, etc. Take a look —
//will be called once for each project, including root
allprojects {
//groupId: ....
//version: ....
}
//does not apply to root
subprojects {
//apply specific plugins, etc.
}
//root_project specific dependencies
project(':org.practice.root_project').dependencies {
compile project(':org.practice.Core'), project(':Util')
compile 'some lib'
}
or,
allprojects {
...
}
subprojects {
...
}
//Core specific stuff
project(':Core') {
dependencies {
...
}
}
//Util specific stuff
project(':Util') {
...
}
//Web specific stuff
project(':Web') {
...
}
Note that sub-project specific stuff could also go under their own separate build.gradle files.
Publishing artifacts
Published files are called artifacts. Publishing is usually done with the help of some plugin, like maven.
apply plugin: 'maven'
uploadArchives { //maybe some nexus repo or local
repositories {
mavenDeployer {
repository(url:"some url")
}
}
}
Hope the above gives you necessary information to get going with gradle! Thanks for reading!