I released the starter package of my Test With Spring course. Take a look at the course >>

Getting Started With Gradle: Creating a Binary Distribution

Yellow easy way and hard way sign

After we have created a useful application, the odds are that we want to share it with other people. One way to do this is to create a binary distribution that can be downloaded from our website.

This blog post describes how we can build a binary distribution that fulfils the following requirements:

  • Our binary distribution must not use so called “fat jar” approach. In other words, the dependencies of our application must not be packaged into the same jar file than our application.
  • Our binary distribution must contain startup scripts for *nix and Windows operating systems.
  • The root directory of our binary distribution must contain the license of our application.

Let’s get started.

Additional reading:

If you are not familiar with Gradle, you should read the following blog posts before you continue reading this blog post:

Creating a Binary Distribution

The application plugin is a Gradle plugin that allows us to run our application, install it, and create a binary distribution that doesn’t use the “fat jar” approach.

We can create a binary distribution by making the following changes to the build.gradle file of the example application that we created during the previous part of my Getting Started with Gradle tutorial:

  1. Remove the configuration of the jar task.
  2. Apply the application plugin to our project.
  3. Configure the main class of our application by setting the value of the mainClassName property.

After we have made these changes to our build.gradle file, it looks as follows (the relevant parts are highlighted):

apply plugin: 'application'
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    compile 'log4j:log4j:1.2.17'
    testCompile 'junit:junit:4.11'
}

mainClassName = 'net.petrikainulainen.gradle.HelloWorld'

The application plugin adds five tasks to our project:

  • The run task starts the application.
  • The startScripts task creates startup scripts to the build/scripts directory. This tasks creates startup scripts for Windows and *nix operating systems.
  • The installApp task installs the application into the build/install/[project name] directory.
  • The distZip task creates the binary distribution and packages it into a zip file that is found from the build/distributions directory.
  • The distTar task creates the binary distribution and packages it into a tar file that is found from the build/distributions directory.

We can create a binary distribution by running one of the following commands in the root directory of our project: gradle distZip or gradle distTar. If we create a binary distribution that is packaged to a zip file, see the following output:

> gradle distZip
:compileJava
:processResources
:classes
:jar
:startScripts
:distZip

BUILD SUCCESSFUL

Total time: 4.679 secs

If we unpackage the created binary distribution created by the application plugin, we get the following directory structure:

  • The bin directory contains the startup scripts.
  • The lib directory contains the jar file of our application and its dependencies.

We can now create a binary distribution that fulfils almost all of our requirements. However, we still need to add the license of our application to the root directory of our binary distribution. Let’s move on and find out how we can do it.

Adding the License File of Our Application to the Binary Distribution

We can add the license of our application to our binary distribution by following these steps:

  1. Create a task that copies the license file from the root directory of our project to the build directory.
  2. Add the license file to the root directory of the created binary distribution.

Let’s move on and take a closer look at these steps.

Copying the License File to the Build Directory

The name of the file that contains the license of our application is LICENSE, and it is found from the root directory of our project.

We can copy the license file to the build directory by following these steps:

  1. Create a new Copy task called the copyLicense.
  2. Configure the source file by using the from() method of the CopySpec interface. Pass the string ‘LICENSE’ as a method parameter.
  3. Configure the target directory by using the into() method of the CopySpec interface. Pass the value of the $buildDir property as a method parameter.

After we have followed these steps, our build.gradle file looks as follows (the relevant part is highlighted):

apply plugin: 'application'
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    compile 'log4j:log4j:1.2.17'
    testCompile 'junit:junit:4.11'
}

mainClassName = 'net.petrikainulainen.gradle.HelloWorld'

task copyLicense(type: Copy) {
	from "LICENSE"
	into "$buildDir"
}

We have now created a task that copies the LICENSE file from the root directory of our project to the build directory. However, when we run the command gradle distZip in the root directory of our project, we see the following output:

> gradle distZip
:compileJava
:processResources
:classes
:jar
:startScripts
:distZip

BUILD SUCCESSFUL

Total time: 4.679 secs

In other words, our new task is not invoked and this naturally means that the license file is not included in our binary distribution. Let’s fix this problem.

Adding the License File to the Binary Distribution

We can add the license file to the created binary distribution by following these steps:

  1. Transform the copyLicense task from a Copy task to a “regular” Gradle task by removing the string ‘(type: Copy)’ from its declaration.
  2. Modify the implementation of the copyLicense task by following these steps:
    1. Configure the output of the copyLicense task. Create a new File object that points to the license file found from the build directory and set it as the value of the outputs.file property.
    2. Copy the license file from the root directory of our project to the build directory.
  3. The application plugin sets a CopySpec property called the applicationDistribution to our project. We can use it to include the license file to the created binary distribution. We can do this by following these steps:
    1. Configure the location of the license file by using the from() method of the CopySpec interface and pass the output of the copyLicense task as method parameter.
    2. Configure the target directory by using the into() method of the CopySpec interface and pass an empty String as a method parameter.

After we have followed these steps, our build.gradle file looks as follows (the relevant part is highlighted):

apply plugin: 'application'
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    compile 'log4j:log4j:1.2.17'
    testCompile 'junit:junit:4.11'
}

mainClassName = 'net.petrikainulainen.gradle.HelloWorld'

task copyLicense {
    outputs.file new File("$buildDir/LICENSE")
    doLast {
        copy {
            from "LICENSE"
            into "$buildDir"
        }
    }
}

applicationDistribution.from(copyLicense) {
    into ""
}

When we run the command gradle distZip in the root directory of our project, we see the following output:

> gradle distZip
:copyLicense
:compileJava
:processResources
:classes
:jar
:startScripts
:distZip

BUILD SUCCESSFUL

Total time: 5.594 secs

As we can see, the copyLicense task is now invoked and if we unpackage our binary distribution, we notice that the LICENSE file is found from its root directory.

Let’s move on summarize what we have learned from this blog post.

Summary

This blog post taught us three things:

  • We learned that we can create a binary distribution by using the application plugin.
  • We learned how we can copy a file from the source directory to the target directory by using the Copy task.
  • We learned how we can add files to the binary distribution that is created by the application plugin.

If you want play around with the example application of this blog post, you can get it from Github.

If you want to learn how to use Gradle, you should take a look at my Gradle tutorial.

About the Author

Petri Kainulainen is passionate about software development and continuous improvement. He is specialized in software development with the Spring Framework and is the author of Spring Data book.

About Petri Kainulainen →

5 comments… add one
  • all i can say is thank you very much for this series of tutorials. they are very informative and extremely helpful for us beginners! hope you will make more tutorials like this in the future!

    Reply
    • You are welcome! I am happy to hear that this blog post (and my other blog posts) were useful to you.

      Reply
  • hi! I use the same approach, but Google Chrome started marking such distributives with scripts inside as “not common”. I raised a question to the google chrome team: https://productforums.google.com/forum/#!topic/webmasters/zRqnyUPzPWo;context-place=forum/webmasters

    Reply
  • I “know” that task copyLicense{} copies the file but it would be good to know why the outputs.file property and applicationDistribution.from() method are implemented. I know that this is a beginners guide but I think that adding a sentence or two explaining this wouldn’t hurt.

    Reply
    • Hi,

      You are right. I will take a closer look at this blog post and update the sections that aren’t as clear as they should be.

      Reply

Leave a Comment