Gradle Deep Dive

Gradle Deep Dive

All about Gradle!

108056ccba92f98fdbbabad534537573?s=128

Bryan Herbst

October 03, 2017
Tweet

Transcript

  1. Gradle deep dive Bryan Herbst Android Engineer @ Target

  2. The toolchain and you

  3. Your Code

  4. Your Code (it’s just text)

  5. Build tools

  6. Gradle

  7. IDE

  8. Your Code Build tools Gradle IDE

  9. Why Gradle?

  10. Reproducible builds !

  11. Dependency Management "

  12. Extensibility #

  13. Faster builds $

  14. Task automation %

  15. Other options Bazel and Buck

  16. Gradle intro

  17. Projects Every build contains 1+ projects

  18. Projects Every build contains 1+ projects compile ':projectB:childC'

  19. Tasks Every project contains 1+ tasks

  20. Build lifecycle Initialization Configuration Execution

  21. Gradle Wrapper It isn’t that special

  22. if (gradleIsNotInstalled) { installGradle() } invokeGradle()

  23. Why?

  24. Why? For consistent builds across machines

  25. Tasks Things you can do

  26. Android Tasks • androidDependencies - Displays the Android dependencies of

    the project. • signingReport - Displays the signing info for each variant. • sourceSets - Prints out all the source sets defined in this project. • assemble - Assembles all variants of all applications and secondary packages. • assembleAndroidTest - Assembles all the Test applications. • assembleDebug - Assembles all Debug builds. • assembleRelease - Assembles all Release builds. • 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. • clean - Deletes the build directory. • cleanBuildCache - Deletes the build cache directory. • compileDebugAndroidTestSources • compileDebugSources • compileDebugUnitTestSources • compileReleaseSources • compileReleaseUnitTestSources • mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests. • init - Initializes a new Gradle build. • wrapper - Generates Gradle wrapper files. • buildEnvironment - Displays all buildscript dependencies declared in root project 'HelloWorld'. • components - Displays the components produced by root project 'HelloWorld'. [incubating] • dependencies - Displays all dependencies declared in root project 'HelloWorld'. • dependencyInsight - Displays the insight into a specific dependency in root project 'HelloWorld'. • dependentComponents - Displays the dependent components of components in root project 'HelloWorld'. [incubating] • help - Displays a help message. • model - Displays the configuration model of root project 'HelloWorld'. [incubating] • projects - Displays the sub-projects of root project 'HelloWorld'. • properties - Displays the properties of root project 'HelloWorld'. • tasks - Displays the tasks runnable from root project 'HelloWorld' (some of the displayed tasks may belong to subprojects). • installDebug - Installs the Debug build. • installDebugAndroidTest - Installs the android (on device) tests for the Debug build. • uninstallAll - Uninstall all applications. • uninstallDebug - Uninstalls the Debug build. • uninstallDebugAndroidTest - Uninstalls the android (on device) tests for the Debug build. • uninstallRelease - Uninstalls the Release build. • check - Runs all checks. • connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected devices. • connectedCheck - Runs all device checks on currently connected devices. • connectedDebugAndroidTest - Installs and runs the tests for debug on connected devices. • deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers. • deviceCheck - Runs all device checks using Device Providers and Test Servers. • lint - Runs lint on all variants. • lintDebug - Runs lint on the Debug build. • lintRelease - Runs lint on the Release build. • test - Run unit tests for all variants. • testDebugUnitTest - Run unit tests for the debug build. • testReleaseUnitTest - Run unit tests for the release build. •
  27. (Some) Android Tasks • assembleDebug – Assembles all Debug builds.

    • assemble – Assembles all variants of all applications and secondary packages. • clean – Deletes the build directory
  28. (Some) Android Tasks • assembleDebug – Assembles all Debug builds.

    • assemble – Assembles all variants of all applications and secondary packages. • clean – Deletes the build directory
  29. (Some) Android Tasks • assembleDebug – Assembles all Debug builds.

    • assemble – Assembles all variants of all applications and secondary packages. • clean – Deletes the build directory
  30. task hello { doLast { println 'Hello world!' } }

  31. doFirst() doLast()

  32. doFirst() Add to the beginning of the action list doLast()

  33. doFirst() Add to the beginning of the action list doLast()

    Add to the end of the action list
  34. task hello { description 'Says hello' group 'Useless tasks' doLast

    { println 'Hello world!' } }
  35. Hot off the presses (Gradle 4.2) task hello { description

    'Says hello' group 'Useless tasks' doLast('Print greeting') { println 'Hello world!' } }
  36. task taskX(dependsOn: 'taskY') { doLast { println 'taskX' } }

    Will run first
  37. class GreetingTask: DefaultTask { @TaskAction fun greet() { println("Hello!") }

    }
  38. Shortcuts ./gradlew tDUT = testDebugUnitTest

  39. Shortcuts ./gradlew tDUT = testDebugUnitTest ./gradlew t = test

  40. Shortcuts ./gradlew tDUT = testDebugUnitTest ./gradlew t = test test?

    tasks?
  41. Shortcuts ./gradlew tDUT = testDebugUnitTest ./gradlew t = test ./gradlew

    te = test
  42. Task Outcomes • EXECUTED- Task was fully executed

  43. Task Outcomes • EXECUTED- Task was fully executed • UP-TO-DATE-

    Task inputs/outputs did not change
  44. Task Outcomes • EXECUTED- Task was fully executed • UP-TO-DATE-

    Task inputs/outputs did not change • FROM-CACHE- Output pulled from cache
  45. Task Outcomes • EXECUTED- Task was fully executed • UP-TO-DATE-

    Task inputs/outputs did not change • FROM-CACHE- Output pulled from cache • SKIPPED- Task was skipped
  46. Task Outcomes • EXECUTED- Task was fully executed • UP-TO-DATE-

    Task inputs/outputs did not change • FROM-CACHE- Output pulled from cache • SKIPPED- Task was skipped • NO-SOURCE- Task wasn’t needed
  47. Task Outcomes • EXECUTED - BAD • UP-TO-DATE - GOOD

    • FROM-CACHE - OKAY • SKIPPED - GOOD • NO-SOURCE - GOOD
  48. Plugins

  49. class GreetingPlugin implements Plugin<Project> { void apply(Project project) { project.task('hello')

    { doLast { println "Hello from the GreetingPlugin" } } } }
  50. class GreetingPlugin implements Plugin<Project> { void apply(Project project) { project.task('hello')

    { doLast { println "Hello from the GreetingPlugin" } } } }
  51. class GreetingPlugin implements Plugin<Project> { void apply(Project project) { project.task('hello')

    { doLast { println "Hello from the GreetingPlugin" } } } }
  52. Anatomy of a build script

  53. Anatomy of a Gradle build script buildscript { // ...

    } allprojects { // ... } apply plugin: 'kotlin' android { // ... } dependencies { // ... }
  54. buildscript { // ... } Configuration for the Gradle build

    script itself allprojects { // ... } apply plugin: 'kotlin' android { // ... } dependencies { // ... }
  55. buildscript { repositories { google() jcenter() } dependencies { classpath

    ’com.foo:bar:1.0.0' } } Dependency repos for the build script allprojects { // ... } apply plugin: 'kotlin' android { // ... } dependencies { // ... } Dependencies for the build script
  56. allprojects { // ... } Configuration for this and all

    child projects apply plugin: 'kotlin' android { // ... } dependencies { // ... } buildscript { // ... }
  57. allprojects { repositories { google() jcenter() } } Common dependency

    repo definition apply plugin: 'kotlin' android { // ... } dependencies { // ... } buildscript { // ... }
  58. apply plugin: 'kotlin' Apply plugins android { // ... }

    dependencies { // ... } buildscript { // ... } allprojects { // ... }
  59. android { compileSdkVersion 26 buildToolsVersion "26.0.1" } Plugin configuration DSL

    dependencies { // ... } buildscript { // ... } allprojects { // ... } apply plugin: 'kotlin'
  60. dependencies { compile 'com.foo:bar:2.1.3' testCompile 'junit:junit:4.12' } Project dependencies buildscript

    { // ... } allprojects { // ... } apply plugin: 'kotlin' android { // ... }
  61. android { compileSdkVersion 26 } android.compileSdkVersion 26 =

  62. Configurations Dependency configurations

  63. Configurations (Android) • debugCompile

  64. Configurations (Android) • debugCompile • releaseCompile

  65. Configurations (Android) • debugCompile • releaseCompile • testCompile

  66. Configurations (Android) • debugCompile • releaseCompile • testCompile • testDebugCompile

  67. Configurations (Android) • debugCompile • releaseCompile • testCompile • testDebugCompile

    • testDebugFreeBlueArmv7Compile
  68. Custom Configurations configurations { internalCompile debugCompile.extendsFrom(internalCompile) dogfoodCompile.extendsFrom(internalCompile) } dependencies {

    internalCompile '...' }
  69. Custom Configurations configurations { internalCompile debugCompile.extendsFrom(internalCompile) dogfoodCompile.extendsFrom(internalCompile) } dependencies {

    internalCompile '...' }
  70. Custom Configurations configurations { internalCompile debugCompile.extendsFrom(internalCompile) dogfoodCompile.extendsFrom(internalCompile) } dependencies {

    internalCompile '...' }
  71. Gradle Kotlin DSL

  72. Static typing defaultConfig { versionCode "abc" versionName 123 multiDexEnabled "10"

    }
  73. Static typing defaultConfig { versionCode "abc" versionName 123 multiDexEnabled "10"

    }
  74. Autocompletion + content assist

  75. And more! (but it is a little rough)

  76. Organizing scripts

  77. buildscript { // ... } ext.isCi = System.getenv().containsKey("IS_CI") task foo

    { doLast { if (isCi) { // ... } } }
  78. buildscript { // ... } ext.isCi = System.getenv().containsKey("IS_CI") task foo

    { doLast { if (isCi) { // ... } } }
  79. ext.supportLibVersion = "26.0.1" dependencies { compile "[…]:appcompat-v7:$supportLibVersion" compile "[…]:design:$supportLibVersion" }

  80. Organizing scripts- include versions.gradle ext.buildTools = "26.0.1" ext.compileVersion = 26

    build.gradle apply from: 'versions.gradle' android { buildToolsVersion buildTools // ... }
  81. buildSrc Re-usable and testable buildscript code

  82. {project}/buildSrc/ src/main/java - Plugins, tasks, etc.

  83. {project}/buildSrc/ src/main/java - Plugins, tasks, etc. build.gradle – Dependencies and

    configuration
  84. {project}/buildSrc/ src/main/java - Plugins, tasks, etc. build.gradle – Dependencies and

    configuration src/test/java – Tests!
  85. See Also: github.com/android/platform_frameworks_support

  86. Getting Build Information Knowledge is power

  87. apply plugin: 'project-report' ./gradlew projectReport Provides: • Task report •

    dependency report • property report
  88. apply plugin: 'build-dashboard' ./gradlew buildDashboard Provides: • All reports implementing

    the reporting interface
  89. ./gradlew [task] --profile Provides: • Build profile by step

  90. apply plugin: 'com.gradle.build-scan' ./gradlew assembleDebug --scan Provides: • Shareable, centralized

    build record
  91. Build Scans Demo

  92. Improving Build Performance

  93. def gitSha = 'git rev-parse --short HEAD’ .execute([], project.rootDir).text.trim()

  94. def gitSha = 'git rev-parse --short HEAD’ .execute([], project.rootDir).text.trim() timestamp

    = new Date().format('yyyyMMddHHmmss')
  95. def gitSha = 'git rev-parse --short HEAD’ .execute([], project.rootDir).text.trim() timestamp

    = new Date().format('yyyyMMddHHmmss') &
  96. def gitSha = 'git rev-parse --short HEAD’ .execute([], project.rootDir).text.trim() timestamp

    = new Date().format('yyyyMMddHHmmss') versionNameSuffix = new Date().format('yyyyMMddHHmmss')
  97. def gitSha = 'git rev-parse --short HEAD’ .execute([], project.rootDir).text.trim() timestamp

    = new Date().format('yyyyMMddHHmmss') versionNameSuffix = new Date().format('yyyyMMddHHmmss') '
  98. Gradle Daemon Enabled by default as of Gradle 3.0

  99. Parallel Builds

  100. Parallel Builds org.gradle.parallel=true

  101. Don’t compile if you don’t need to

  102. Don’t compile if you don’t need to taskX.onlyIf { !project.hasProperty('skipX')

    }
  103. Don’t compile if you don’t need to class TaskX extends

    DefaultTask { @Input def inputProperty }
  104. Don’t compile if you don’t need to class TaskX extends

    DefaultTask { @Input def inputProperty } Task is now incremental!
  105. Android Plugin 3.0.0 & Gradle 4.x Parallel & cacheable

  106. Configurations compile

  107. Configurations compile testCompile

  108. Configurations compile testCompile debugCompile

  109. Configurations compile testCompile debugCompile apk

  110. Configurations compile testCompile debugCompile apk provided

  111. Configurations compile testCompile debugCompile apk provided

  112. Configurations implementation Available at compile time, Only available to consumers

    at runtime
  113. Configurations implementation api Available at compile time and run time

  114. Configurations implementation api compileOnly Only available at compile time

  115. Configurations implementation api compileOnly runtimeOnly Only available at runtime

  116. Lib B Lib A App

  117. Lib B Lib A App Old configurations Change (recompile)

  118. Lib B Lib A App Old configurations Change (recompile) recompile

  119. Lib B Lib A App Old configurations Change (recompile) recompile

    recompile
  120. Lib B Lib A App New configurations Change (recompile) recompile

    implementation
  121. Plugin 2.2.0 Gradle 2.14.1 Plugin 3.0 Gradle 4.0 Configuration ~2

    mins ~2.5 s 1-line Java change ~2 mins 15 s ~6.4 s
  122. Build cache

  123. Build cache (not Android’s)

  124. Has input changed?

  125. Has input changed? UP-TO-DATE

  126. Has input changed? UP-TO-DATE In cache?

  127. Has input changed? UP-TO-DATE In cache? Yes FROM-CACHE

  128. Has input changed? UP-TO-DATE In cache? Yes FROM-CACHE Execute task

  129. Build cache org.gradle.caching=true Or ./gradlew [task] --build-cache

  130. Resources • google.github.io/android-gradle-dsl • github.com/android/platform_frameworks_support • gradle.org/docs