Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Customizing and Refactoring Gradle Builds

Marc Philipp
September 24, 2019

Customizing and Refactoring Gradle Builds

Gradle is an open-source build automation tool focused on flexibility and performance. Gradle build scripts are written using a Groovy or Kotlin DSL, which makes it very easy to customize the build according to your needs. For example, adding additional tasks or configuration can be done directly in the build script.

However, as soon as such build logic starts to become complex it should be encapsulated in order to keep the build script declarative, to avoid duplication and to enable reuse. In this session, we will start with a quick and dirty build customization, watch it become increasingly complex, and eventually refactor it into a custom plugin step-by-step.

Marc Philipp

September 24, 2019
Tweet

More Decks by Marc Philipp

Other Decks in Programming

Transcript

  1. Customizing and Refactoring
    Gradle Builds
    Marc Philipp, Gradle Inc.

    View Slide

  2. Marc Philipp
    So ware Engineer at Gradle, Inc.
    JUnit 5 team lead
    Twi er:
    Web:
    @marcphilipp
    marcphilipp.de

    View Slide

  3. What is Gradle?

    View Slide

  4. What is Gradle?




    based on the Java Virtual Machine (JVM)
    implemented in Java
    focused on flexibility and performance
    100% open‑source (Apache 2.0) and free
    Gradle is an open‑source build
    automa on tool

    View Slide

  5. Versa le




    Java ecosystem: Java, Groovy, Kotlin, Scala, …
    Official build tool for Android
    Na ve projects: C, C++, Swi , …
    And more: Go, Asciidoctor, …

    View Slide

  6. Gradle Inc.






    Vision: Build Happiness
    Mission: Accelerate Developer Produc vity
    Products:
    Gradle Build Tool
    Gradle Enterprise
    more than 60 employees including over 40 engineers

    View Slide

  7. Agenda





    Basic concepts
    From Quick & Dirty to Safe & Sound
    dependency management
    custom tasks
    custom configura on

    View Slide


  8. Show of Hands

    View Slide

  9. Basic Concepts

    View Slide

  10. Tasks



    a Gradle build executes tasks
    tasks can depend on other tasks
    tasks have inputs and outputs

    View Slide

  11. Hello World
    tasks.register("helloWorld") { // in build.gradle
    doLast {
    println("Hello World!")
    }
    }
    $ gradle helloWorld
    > Task :helloWorld
    Hello World!
    BUILD SUCCESSFUL in 0s
    1 actionable task: 1 executed

    View Slide

  12. Build Scripts
    A Gradle project is configured in build scripts:


    settings.gradle[.kts]: configures the subprojects
    that comprise the build
    build.gradle[.kts]: configures the used plugins
    and tasks

    View Slide

  13. se ngs.gradle[.kts]
    rootProject.name = "new-project"
    include("subproject-a")
    include("subproject-b")

    View Slide

  14. build.gradle[.kts]
    plugins {
    java // to compile Java sources
    application // to generate startup scripts
    }
    repositories {
    jcenter() // to resolve dependencies
    }
    dependencies {
    implementation("com.google.guava:guava:28.0-jre")
    testImplementation("org.junit.jupiter:junit-jupiter:5.5.2")
    }
    application { // extension of the 'application' plugin
    mainClassName = "com.example.App"
    }

    View Slide

  15. Groovy vs. Kotlin DSL





    build scripts use a Domain Specific Language (DSL)
    ini ally Gradle only supported Groovy
    dynamically typed
    limited IDE support
    Kotlin DSL is stable since Gradle 5.0
    Build scripts should be declara ve –
    complex logic does not belong here.

    View Slide

  16. Gradle Wrapper
    ./gradlew instead of gradle




    execute builds with prior installa on of Gradle
    downloads required version
    caches already downloaded versions locally
    everyone uses the same version

    View Slide

  17. Anatomy of a Gradle project
    $ gradle init --dsl=kotlin --type=java-application \
    --test-framework=junit --package=com.example \
    --project-name=new-project
    BUILD SUCCESSFUL in 0s
    2 actionable tasks: 2 executed
    ├── build.gradle.kts // build script
    ├── gradle/wrapper // wrapper jar and configuration
    ├── gradlew // wrapper script for Linux/macOS
    ├── gradlew.bat // wrapper script for Windows
    ├── settings.gradle.kts // settings script
    └── src // Java source tree
    ├── main
    │ ├── java
    │ └── resources
    └── test
    ├── java
    └── resources

    View Slide

  18. Incremental Builds





    only execute tasks that are affected by changes in
    between two subsequent builds
    inputs have changed
    outputs are present and unchanged
    task implementa on has changed (e.g. different
    plugin version)
    keep outputs of all tasks that are up‑to‑date

    View Slide

  19. First Build
    $ ./gradlew --console=plain build
    > Task :compileJava
    > Task :processResources NO-SOURCE
    > Task :classes
    > Task :jar
    [...]
    > Task :compileTestJava
    > Task :testClasses
    > Task :test
    > Task :check
    > Task :build
    BUILD SUCCESSFUL in 5s
    7 actionable tasks: 7 executed

    View Slide

  20. Subsequent Build
    $ ./gradlew --console=plain build
    > Task :compileJava UP-TO-DATE
    > Task :processResources NO-SOURCE
    > Task :classes UP-TO-DATE
    > Task :jar UP-TO-DATE
    [...]
    > Task :compileTestJava UP-TO-DATE
    > Task :testClasses UP-TO-DATE
    > Task :test UP-TO-DATE
    > Task :check UP-TO-DATE
    > Task :build UP-TO-DATE
    BUILD SUCCESSFUL in 0s
    7 actionable tasks: 7 up-to-date

    View Slide

  21. Build Scans



    Accelerate debugging of build problems
    Private but shareable link
    Free to use on

    scans.gradle.com
    $ ./gradlew build --scan
    BUILD SUCCESSFUL in 1s
    7 actionable tasks: 5 executed, 2 up-to-date
    Publishing build scan...
    https://gradle.com/s/lu7dxy7quyoju
    h ps:/
    /gradle.com/s/lu7dxy7quyoju

    View Slide

  22. Build Cache


    allows reusing task outputs of any previous build
    local and remote cache
    $ git pull
    [...]
    185 files changed, 4320 insertions(+), 1755 deletions(-)
    $ ./gradlew --build-cache sanityCheck
    BUILD SUCCESSFUL in 1m 11s
    1338 actionable tasks: 238 executed, 1100 from cache

    View Slide

  23. Dependency Management

    View Slide

  24. Demo

    View Slide

  25. Recap




    Don’t duplicate dependency version
    Prefer api or implementation over compile
    Use buildSrc to collect dependency versions
    Use a java-platform plugin to streamline
    dependency management

    View Slide

  26. More on Dependency Management
    Free webinars:


    h ps:/
    /gradle.com/blog/dependency‑management‑
    fundamentals/
    h ps:/
    /gradle.com/blog/dependency‑management‑
    part‑2‑handling‑conflicts/

    View Slide

  27. Custom Tasks

    View Slide

  28. Demo

    View Slide

  29. Recap



    Don’t define complex tasks directly in the build script
    Define them in the buildSrc project
    Allows for tes ng and reuse in subprojects

    View Slide

  30. Custom Configura on

    View Slide

  31. Demo

    View Slide

  32. Recap



    Extract custom logic into separate build scripts
    Even be er: Extract your custom logic into a pre‑
    compiled script plugin in buildSrc
    Next step: Move it to a separate plugin to use it in
    independent projects

    View Slide

  33. Summary

    View Slide

  34. Summary


    Keep your build scripts declara ve
    Use buildSrc to share logic

    View Slide

  35. Links


    Demo code:
    My talks on Gradle and JUnit:
    h ps:/
    /github.com/marcphilipp/gradle‑refactorings
    h ps:/
    /www.marcphilipp.de/en/talks/

    View Slide

  36. Thank you!
    @marcphilipp

    View Slide