VirtualJUG
Extremely fast builds with Gradle 3
Cédric Champeau
2016

Cédric Champeau (@CedricChampeau), Gradle

Who am I

speaker {
    name 'Cédric Champeau'
    company 'Gradle Inc'
    oss 'Apache Groovy committer',
    successes 'Static type checker',
                    'Static compilation',
                    'Traits',
                    'Markup template engine',
                    'DSLs'
        failures Stream.of(bugs),
        twitter '@CedricChampeau',
        github 'melix',
        extraDescription '''Groovy in Action 2 co-author
Misc OSS contribs (Gradle plugins, deck2pdf, jlangdetect, ...)'''
}

Agenda

  • The Gradle Daemon

  • Profiling

  • Incremental builds

  • Incremental compilation

  • Continuous builds

  • Composite builds

  • Build cache

What is Gradle?

  • A build tool

  • Cloud Services

The Gradle daemon

  • Long-lived background process

  • Listens and executes build actions

  • Faster startup / execution

  • Enabled by default since 3.0

The Gradle daemon

Cold daemon

daemon keynote cold

Warm daemon

daemon keynote warm

Profiling a build

gradle --profile

  • Generates a report in the build directory

  • Limited insight

  • Not easily shareable

  • But can already give precious information

Build scans

  • First member of the Cloud Services family

  • Insights into your build

  • View and share via URL

  • Debug, optimize and refine

  • Analyze all of your builds

  • Available for free

build scans

Build scan demo

Creating a build scan

Apply the build scan plugin

plugins {
    id 'com.gradle.build-scan' version '1.3'
}

buildScan {
    licenseAgreementUrl = 'https://gradle.com/terms-of-service'
    licenseAgree = 'yes'
}

Run the build

./gradlew -Dscan build

Incremental builds

  • Gradle is meant for incremental builds

  • clean is a waste of time

  • Prepare your builds for incrementalness

Example: building a shaded jar

task shadedJar(type: ShadedJar) {
   jarFile = file("$buildDir/libs/shaded.jar")
   classpath = configurations.runtime
   mapping = ['org.apache': 'shaded.org.apache']
}
  • What are the task inputs?

  • What are the task outputs?

  • What if one of them changes?

Declaring inputs

@InputFiles
FileCollection getClasspath() { ... }

@Input
Map<String, String> getMapping() { ... }

Declaring outputs

@OutputFile
File getJarFile() { ... }

Incremental compilation

  • Given a set of source files

  • Only compile the files which have changed…​

  • and their dependencies

  • Language specific

Gradle has support for incremental compilation of Java

compileJava {
    //enable incremental compilation
    options.incremental = true
}

Continuous builds

  • Gradle watches for changes in task inputs

  • Re-executes tasks as changes occur

  • Enabled with -t

gradle -t asciidoctor

Composite builds

  • Compose various projects as if there were one

    • Each project can live in its own repository

    • Each project has its own Gradle build

    • Composition unites them through dependency resolution

  • Split monolithic projects

    • For large multiproject builds, allows splitting them into several pieces

    • Each piece can be versioned independently

    • Developers can choose what subprojects they care about

Composite builds demo

What’s next?

Compile avoidance

Usage

import com.acme.model.Person;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

...

public Set<String> getNames(Set<Foo> persons) {
   return ImmutableSet.copyOf(Iterables.transform(persons, TO_NAME))
}

Declaring dependencies

dependencies {
   compile project(':model')
   compile 'com.google.guava:guava:18.0'
}

But…​

import com.acme.model.Person; // exported dependency
import com.google.common.collect.ImmutableSet; // internal dependency
import com.google.common.collect.Iterables; // internal dependency

...

public Set<String> getNames(Set<Foo> persons) {
   return ImmutableSet.copyOf(Iterables.transform(persons, TO_NAME))
}

Separating API from implementation

dependencies {
   api project(':model')
   implementation 'com.google.guava:guava:18.0'
}

Benefit

  • No more compile classpath leakage

  • Downstream dependencies not recompiled when internal dependency changes

Build cache

  • Avoid doing work even after clean

  • Share binaries between projects on a single machine

  • Share binaries between projects on a network

  • Backend agnostic

Build cache use cases

  • Long compile tasks

  • Bisecting

  • Continuous integration

  • Green Earth ;)

Task output cache demo

Performance guide

gradle vs maven clean build

Thank you!

Learn more at www.gradle.org