The Shadow Knows Gradle

Someone recently complained on Twitter that the so-called Shadow plugin for Gradle, written by the inestimable John Engleman, no longer worked on Gradle 2.11 or 2.12. Commenters were quick to point out that the latest version (1.2.3) of the plugin did work. I thought I’d put together this quick blog post to demonstrate that it works fine.

As a overview, the Shadow plugin creates what’s called a “fat” jar, including all the dependencies, so you can deliver a runnable final product without installing anything other than Java.

I made a trivial Java library project using the Gradle Init plugin, using Spock testing (because I couldn’t help using Spock). Fortunately, this time I remembered to create a folder to hold it in ahead of time.

> md temp/shadow_demo
> cd temp/shadow_demo
> gradle init --type java-library --test-framework spock

That created a typical Java project, with folders src/main/java and src/test/groovy in it, along with a source class called Library.java and a test called LibraryTest.groovy.

(First minor complaint: the test case should be called LibrarySpec.groovy. The compiler doesn’t require you to end your Spock tests with the word Spec, but it’s a standard idiom by now. Plus, it helps me keep straight which of my tests are Spock tests and which are JUnit tests.)

I then opened the build.gradle file in the root of the project in order to add the shadow plugin. Here’s how it looked when I started:

/*
 * This build file was auto generated by running the Gradle 'init' task
 * by 'kousen' at '3/14/16 5:20 PM' with Gradle 2.12
 *
 * This generated file contains a sample Java project to get you started.
 * For more details take a look at the Java Quickstart chapter in the Gradle
 * user guide available at https://docs.gradle.org/2.12/userguide/tutorial_java_projects.html
 */

// Apply the java plugin to add support for Java
apply plugin: 'java'

// Apply the groovy plugin to also add support for Groovy (needed for Spock)
apply plugin: 'groovy'

// In this section you declare where to find the dependencies of your project
repositories {
    // Use 'jcenter' for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

// In this section you declare the dependencies for your production and test code
dependencies {
    // The production code uses the SLF4J logging API at compile time
    compile 'org.slf4j:slf4j-api:1.7.18'

    // We use the latest groovy 2.x version for Spock testing
    compile 'org.codehaus.groovy:groovy-all:2.4.6'

    // Use the awesome Spock testing and specification framework even with Java
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'junit:junit:4.12'
}

(Second minor complaint: the build file adds both the java and groovy plugins. That’s redundant. The groovy plugin already includes all the needed java functionality.

I removed the comments and cleaned up the build file a bit, adding the shadow plugin.

plugins {
    id 'groovy'
    id 'com.github.johnrengelman.shadow' version '1.2.3'
}

repositories {
    jcenter()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.18'

    compile 'org.codehaus.groovy:groovy-all:2.4.6'

    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'junit:junit:4.12'
}

I’m using the newer plugins block syntax instead of the conventional `buildscript` approach. The page at the Gradle plugins site for the Shadow plugin (https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) shows the details. Note that the section at the bottom of that page labelled “About the new plugin mechanism…” gives additional details on the newer approach.

If you run the gradle tasks command at this point, you’ll see a lot of output, some of which is relevant for this demo.

> gradle tasks
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Documentation tasks
-------------------
groovydoc - Generates Groovydoc API documentation for the main source code.
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'shadow_demo'.
components - Displays the components produced by root project 'shadow_demo'. [incubating]
dependencies - Displays all dependencies declared in root project 'shadow_demo'.
dependencyInsight - Displays the insight into a specific dependency in root project 'shadow_demo'.
help - Displays a help message.
model - Displays the configuration model of root project 'shadow_demo'. [incubating]
projects - Displays the sub-projects of root project 'shadow_demo'.
properties - Displays the properties of root project 'shadow_demo'.
tasks - Displays the tasks runnable from root project 'shadow_demo'.

Shadow tasks
------------
knows - Do you know who knows?
shadowJar - Create a combined JAR of project and runtime dependencies

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 9.466 secs

Note the existence of the shadowJar task, which creates the fat jar.

My trivial app doesn’t have a Java class with a main method in it, however, so creating a shadow jar wouldn’t actually accomplish much. Therefore, after renaming src/main/java to src/main/groovy, I added a tiny groovy script called src/main/groovy/demo.groovy:

println 'What up, World?'

Then I added the application plugin to the Gradle build and set the main class to my script, giving me the final form of the build file:

plugins {
    id 'groovy'
    id 'application'
    id 'com.github.johnrengelman.shadow' version '1.2.3'
}

mainClassName = 'demo'

repositories {
    jcenter()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.18'
    compile 'org.codehaus.groovy:groovy-all:2.4.6'

    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'junit:junit:4.12'
}

The (built in) application plugin adds the run and shadowRun tasks to Gradle. Executing those is easy enough, too:

> gradle run
:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes
:run
What up, World?

BUILD SUCCESSFUL

Total time: 2.688 secs

> gradle runShadow
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:shadowJar
:startShadowScripts
:installShadowApp
:runShadow
What up, World?

BUILD SUCCESSFUL

Total time: 2.503 secs

The first runs the app without the shadow jar, and the second uses it. Everything looks good. The GitHub page for the shadow plugin describes lots of ways to customize it, but I didn’t need any of that for my demo.

Of course, there’s no way you can talk about the shadow plugin without running the additional task it adds, namely knows:

> gradle knows
:knows

No, The Shadow Knows....

                                        .
                                .MMMMMO      .M
                              .MMMMMMMMM. MMMM.
                              .MMMMMMMMMMMMMMM.
                               .MMMMMMMMMMMMM
                               .MMMMMMMMMMMM
                            .+MMMMMMMMM,ZMMM.
                          ...7MM8D8MM.ZMMMMM.
       ..                      MMZ..MZZNMMMMM
      ....                  MMMMMMMZZZ.MMMMMMOOOOOO..
      ...                7MMMMMMMMMZZZMIMMMMOOOOOMMMM..
      .. .~.                .MMMMMOMZZZMZMMOOOOOMMMM MM.
         .MMMMM             ..MMM.7DOMOMOOOOOOOMM MMMMM Z
      ..  MMMMMMM..  .     ...MMMMMMMMMOOOOOOMMMMMMMMMM
       .    .MMMMMMM.       .MMMMM MMMMMOMOMMMMMMMMMMM
             MMMMMMMMM    .MMM.MMMMMMMMMMMOMMMMMMMMMMM
             .MMMMMMMM   $MMMM MMMMMMMMMMMMMMMMMM MMM
              MMMMMMNMMMMMMMM M.MMMMMM.MMMMMMMM MMMMMM
             ..MMMMMMMMMMMMMMMMMMMMMMMMMMMM.MNMMMMMMM .
              ...MMMMMMMMMMM MMMMMMMMMMMMM.MMMMMMMM.
                 MMMMMMMMMM.MMMMMMMMMMMMMDMMMMMMMM.
                ..MMMMMMMMMMMMMMMMMMM M,MMMMMMMMMMMMMMMZMMMMM  +D            ,
                     .:DMM.M. MMMMMMM.MMMMMMMMMMMMMMI:MMMMM      :MMO
                        . MMMMMMMMMMMMMMMMMMMM.MMMMM8   NMMMN
                       ..MMMMMMMMMMMMMMMMMMMMM  MMMMN.
                       .MMMMMMMMMMMMMMMM. MMM7  ,      . =.
                       MMMMMMMMMMMM.$MM  M   .   MM7
                      MMMMMMMMM=MI:M8  . MNOM     M
                     MMMMMMMMMM.      .
                    MMMMMM .
                   +MM



BUILD SUCCESSFUL

Total time: 1.043 secs

That, of course, is worth the whole demo. 🙂

About Ken Kousen
I teach software development training courses. I specialize in all areas of Java and XML, from EJB3 to web services to open source projects like Spring, Hibernate, Groovy, and Grails. Find me on Google+ I am the author of "Making Java Groovy", a Java / Groovy integration book published by Manning in the Fall of 2013, and "Gradle Recipes for Android", published by O'Reilly in 2015.

2 Responses to The Shadow Knows Gradle

  1. Of all the places i searched for an answer to my problem, I was shown the light here. The blog was very informative. Thank you so much sir.

  2. VISWANATHA BASAVALINGAPPA says:

    where is the Def of task “shadowJar” here ?

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

%d bloggers like this: