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

#AndroidBuilds at Twitter

#AndroidBuilds at Twitter

https://www.youtube.com/watch?v=rG6nzrRPMRI

As codebases become larger and more complex, build performance is impacted. At Twitter, we’ve seen the existing tools struggle in a project with more than 100 modules and 1 million lines of code. This has forced us to explore a number of solutions to maintain good build speeds, and minimize resource usage.

We’ll share strategies that we’ve employed that were successful, and things that weren’t - to avoid hurting developer productivity.

These tips include:

- Strategies to effectively break down larger modules
- How to best utilize build tooling
- Profiling techniques to help find bottlenecks

Michael Evans

April 21, 2022
Tweet

More Decks by Michael Evans

Other Decks in Technology

Transcript

  1. Don't think of them as Gradle build times. Think of

    them as job security for your Twitter account 13 72 2:41 PM - 17 Jun 2017 Lisa Wray @lisawrayz
  2. 54 Modules • Mostly inside large app module, and one

    large library module • AGP 2.2.2 • Gradle 3.1 (In October 2016) Our build Seconds 0 75 150 225 300 Clean Incremental
  3. "I always see you just staring at your screen. You

    Android devs must have it easy." I AM WAITING FOR GRADLE OKAY 66 144 3:03 AM - 28 Apr 2017 Zarah Dominguez 🦉 @zarahjutz
  4. Very excited about fi nally switching entirely to Buck soon

    and never going back to Gradle. Subsecond builds are coming 😂 1 6 2:31 PM - 4 Nov 2016 Felipe Lima @felipecsl
  5. Buck • Super fast • OkBuck plugin makes it super

    easy to get started • Exopackage is like Instant Run (but works)
  6. Buck • Less fl exible • Not o ff i

    cially supported • High maintenance cost • No Kotlin support
  7. Avoid Legacy Multidex Disable Multi-APK Disable PNG Crunching 1 2

    3 Some Tips from James Lau Google IO 2017
  8. Avoid Legacy Multidex Disable Multi-APK Disable PNG Crunching 1 2

    3 Avoid inadvertent changes 4 Some Tips from James Lau Google IO 2017
  9. Avoid Legacy Multidex Disable Multi-APK Disable PNG Crunching 1 2

    3 Avoid inadvertent changes Watch the memory 4 5 Some Tips from James Lau Google IO 2017
  10. Avoid Legacy Multidex Disable Multi-APK Disable PNG Crunching 1 2

    3 Avoid inadvertent changes Watch the memory Enable Gradle caching 4 5 6 Some Tips from James Lau Google IO 2017
  11. More simple improvements Other Tips • Enable Parallel Builds •

    --parallel • Con fi gure on demand • --configure-on-demand • org.gradle.configureondemand=true
  12. More simple improvements Other Tips • Enable Parallel Builds •

    --parallel • Con fi gure on demand • --configure-on-demand • org.gradle.configureondemand=true • Partial builds (-a) • Ignore all dependent projects and only execute tasks in the target project
  13. Leveraging Gradle features 3.0 Daemon 3.1 Faster Dependency Resolution 3.4

    Compile Avoidance Incremental Compilation 4.0 Build Cache
  14. 3.0 Daemon 3.1 Faster Dependency Resolution 3.4 Compile Avoidance Incremental

    Compilation 4.0 Build Cache Leveraging Gradle features
  15. 3.0 Daemon 3.1 Faster Dependency Resolution 3.4 Compile Avoidance Incremental

    Compilation 4.0 Build Cache Leveraging Gradle features
  16. Compiler avoidance In Java projects… • Replace java with java-library

    • Use the api and implementation configurations • Declare annotation processors in the processor classpath
  17. Compiler avoidance In Java projects… • Replace java with java-library

    • Use the api and implementation configurations • Declare annotation processors in the processor classpath
  18. Compiler avoidance In Java projects… • Replace java with java-library

    • Use the api and implementation configurations • Declare annotation processors in the processor classpath
  19. Compiler avoidance In Java projects… • Replace java with java-library

    • Use the api and implementation configurations • Declare annotation processors in the processor classpath
  20. Compiler avoidance In Java projects… configurations { // A configuration

    that collects annotation processing dependencies // to add to the annotation processsor path. annotationProcessor } compileJava { options.annotationProcessorPath = configurations.annotationProcessor } dependencies { annotationProcessor 'com.google.dagger:dagger-compiler:2.11' }
  21. Compiler avoidance In Java projects… configurations { // A configuration

    that collects annotation processing dependencies // to add to the annotation processsor path. annotationProcessor } compileJava { options.annotationProcessorPath = configurations.annotationProcessor } dependencies { annotationProcessor 'com.google.dagger:dagger-compiler:2.11' }
  22. Compiler avoidance In Java projects… configurations { // A configuration

    that collects annotation processing dependencies // to add to the annotation processsor path. annotationProcessor } compileJava { options.annotationProcessorPath = configurations.annotationProcessor } dependencies { annotationProcessor 'com.google.dagger:dagger-compiler:2.11' }
  23. Compiler avoidance In Java projects… configurations { // A configuration

    that collects annotation processing dependencies // to add to the annotation processsor path. annotationProcessor } compileJava { options.annotationProcessorPath = configurations.annotationProcessor } dependencies { annotationProcessor 'com.google.dagger:dagger-compiler:2.11' }
  24. 3.0 Daemon 3.1 Faster Dependency Resolution 3.4 Compile Avoidance Incremental

    Compilation 4.0 Build Cache Leveraging Gradle features
  25. 3.0 Daemon 3.1 Faster Dependency Resolution 3.4 Compile Avoidance Incremental

    Compilation 4.0 Build Cache Leveraging Gradle features
  26. Enabling the build cache Enable cache in gradle.properties org.gradle.caching=true Con

    fi gure cache in settings.gradle buildCache { local(DirectoryBuildCache) { directory = System.env.GRADLE_USER_HOME != null ? new File(System.env.GRADLE_USER_HOME, '.buildcache') : new File(rootDir, '.buildcache') enabled = true } remote(HttpBuildCache) { url = 'https://ge.twitter.biz/cache/' push = true enabled = true } }
  27. Enable cache in gradle.properties org.gradle.caching=true Con fi gure cache in

    settings.gradle buildCache { local(DirectoryBuildCache) { directory = System.env.GRADLE_USER_HOME != null ? new File(System.env.GRADLE_USER_HOME, '.buildcache') : new File(rootDir, '.buildcache') enabled = true } remote(HttpBuildCache) { url = 'https://ge.twitter.biz/cache/' push = true enabled = true } } Enabling the build cache
  28. Enabling the build cache Enable cache in gradle.properties org.gradle.caching=true Con

    fi gure cache in settings.gradle buildCache { local(DirectoryBuildCache) { directory = System.env.GRADLE_USER_HOME != null ? new File(System.env.GRADLE_USER_HOME, '.buildcache') : new File(rootDir, '.buildcache') enabled = true } remote(HttpBuildCache) { url = 'https://ge.twitter.biz/cache/' push = true enabled = true } }
  29. Give me six hours to chop down a tree and

    I will spend the fi rst four sharpening the axe 16 68 2:48 PM – 15 Jul 1860 Abraham Lincoln @POTUS16
  30. --profile is your friend • Generates a report in build/reports/profile

    • Lists timing for con fi guration and task execution • Indicates if tasks were skipped/did no work
  31. The Gradle profiler nonAbiChange { tasks = [":app:twitter:assembleDefaultDebug"] apply-non-abi-change-to =

    "lib/twitter/Example.java" gradle-args = ["-PminSdkVersion=23"] warm-ups = 3 } tfa.scenarios
  32. The Gradle profiler nonAbiChange { tasks = [":app:twitter:assembleDefaultDebug"] apply-non-abi-change-to =

    "lib/twitter/Example.java" gradle-args = ["-PminSdkVersion=23"] warm-ups = 3 } tfa.scenarios
  33. The Gradle profiler nonAbiChange { tasks = [":app:twitter:assembleDefaultDebug"] apply-non-abi-change-to =

    "lib/twitter/Example.java" gradle-args = ["-PminSdkVersion=23"] warm-ups = 3 } tfa.scenarios
  34. The Gradle profiler nonAbiChange { tasks = [":app:twitter:assembleDefaultDebug"] apply-non-abi-change-to =

    "lib/twitter/Example.java" gradle-args = ["-PminSdkVersion=23"] warm-ups = 3 } tfa.scenarios
  35. The Gradle profiler nonAbiChange { tasks = [":app:twitter:assembleDefaultDebug"] apply-non-abi-change-to =

    "lib/twitter/Example.java" gradle-args = ["-PminSdkVersion=23"] warm-ups = 3 } tfa.scenarios
  36. YourKit changed my life. 4:12 PM - 18 Apr 2017

    Gwen (Chen) Shapira @gwenshap
  37. YourKit Or use the Gradle profiler! ./gradle-profiler \
 --profile yourkit

    --yourkit-sampling \
 --project-dir ~/src/twitter-android/ \
 --scenario-file tfa.scenarios \
 nonAbiChange
  38. YourKit Or use the Gradle profiler! ./gradle-profiler \
 --profile yourkit

    --yourkit-sampling \
 --project-dir ~/src/twitter-android/ \
 --scenario-file tfa.scenarios \
 nonAbiChange
  39. Use the logs! Why is your compilation not doing the

    right thing? ➡ check out the command-line parameters and the classpath ./gradlew assemble --info
  40. Use the logs! Why is your compilation not doing the

    right thing? ➡ check out the command-line parameters and the classpath Compiler arguments: -source 1.7 -target 1.7 -d /Users/ mevans/src/twitter-android/lib/core/database/lru/build/ intermediates/classes/release -encoding UTF-8 -bootclasspath /opt/twitter/Caskroom/android-sdk/25.2.3/ platforms/android-25/android.jar -g -sourcepath -processorpath /Users/mevans/.gradle/caches/modules-2/ files-2.1/javax.inject/javax.inject/ 1/6975da39a7040257bd51d21a231b76c915872d38/ javax.inject-1.jar… -classpath /Users/mevans/src/twitter-android/lib/core/util/ android/common/build/intermediates/bundles/default/ classes.jar…
  41. Use the logs! Why is your compilation not doing the

    right thing? ➡ check out the command-line parameters and the classpath Compiler arguments: -source 1.7 -target 1.7 -d /Users/ mevans/src/twitter-android/lib/core/database/lru/build/ intermediates/classes/release -encoding UTF-8 -bootclasspath /opt/twitter/Caskroom/android-sdk/25.2.3/ platforms/android-25/android.jar -g -sourcepath -processorpath /Users/mevans/.gradle/caches/modules-2/ files-2.1/javax.inject/javax.inject/ 1/6975da39a7040257bd51d21a231b76c915872d38/ javax.inject-1.jar… -classpath /Users/mevans/src/twitter-android/lib/core/util/ android/common/build/intermediates/bundles/default/ classes.jar…
  42. Android Gradle Plugin optimizations 2.2 Pre-dexing 2.3 Gradle Build Cache

    3.0 Compile Avoidance Variant-aware dependency Management …
  43. Android Gradle Plugin optimizations 2.2 Pre-dexing 2.3 Gradle Build Cache

    3.0 Compile Avoidance Variant-aware dependency Management …
  44. AGP 3.0 Trying the plugin early • Enable with a

    flag • Define flavor dimensions • Disable aapt2 and new resource processing • Define configurations for 2.3
  45. AGP 3.0 Trying the plugin early • Enable with a

    flag • Define flavor dimensions • Disable aapt2 and new resource processing • Define configurations for 2.3
  46. AGP 3.0 Trying the plugin early ext.isNewBuild = System.properties['build.new'] !=

    null buildscript { repositories { google() } dependencies { classpath isNewBuild ? 'com.android.tools.build:gradle:3.0.0-alpha4' : 'com.android.tools.build:gradle:2.3.0' } }
  47. AGP 3.0 Trying the plugin early ext.isNewBuild = System.properties['build.new'] !=

    null buildscript { repositories { google() } dependencies { classpath isNewBuild ? 'com.android.tools.build:gradle:3.0.0-alpha4' : 'com.android.tools.build:gradle:2.3.0' } }
  48. AGP 3.0 Trying the plugin early ext.isNewBuild = System.properties['build.new'] !=

    null buildscript { repositories { google() } dependencies { classpath isNewBuild ? 'com.android.tools.build:gradle:3.0.0-alpha4' : 'com.android.tools.build:gradle:2.3.0' } }
  49. AGP 3.0 Trying the plugin early • Enable with a

    flag • Define flavor dimensions • Disable aapt2 and new resource processing • Define configurations for 2.3
  50. AGP 3.0 Trying the plugin early android { if (isNewBuild)

    { flavorDimensions 'type' } productFlavors { 'default' { if (isNewBuild) { dimension 'type' } } } }
  51. AGP 3.0 Trying the plugin early • Enable with a

    flag • Define flavor dimensions • Disable aapt2 and new resource processing • Define configurations for 2.3
  52. AGP 3.0 Trying the plugin early Disable aapt2 and resource

    processing in gradle.properties android.enableNewResourceProcessing=false android.enableAapt2=false
  53. AGP 3.0 Trying the plugin early • Enable with a

    flag • Define flavor dimensions • Disable aapt2 and new resource processing • Define configurations for 2.3
  54. AGP 3.0 if (!isNewBuild) { configurations { // Aliases for

    the new configurations in 3.0.0. api implementation compile.extendsFrom(api, implementation) debugImplementation debugCompile.extendsFrom(debugImplementation) dogfoodImplementation dogfoodCompile.extendsFrom(dogfoodImplementation) testImplementation testCompile.extendsFrom(testImplementation) androidTestImplementation androidTestCompile.extendsFrom(androidTestImplementation) compileOnly provided.extendsFrom(compileOnly) } } Trying the plugin early
  55. AGP 3.0 if (!isNewBuild) { configurations { // Aliases for

    the new configurations in 3.0.0. api implementation compile.extendsFrom(api, implementation) debugImplementation debugCompile.extendsFrom(debugImplementation) dogfoodImplementation dogfoodCompile.extendsFrom(dogfoodImplementation) testImplementation testCompile.extendsFrom(testImplementation) androidTestImplementation androidTestCompile.extendsFrom(androidTestImplementation) compileOnly provided.extendsFrom(compileOnly) } } Trying the plugin early
  56. AGP 3.0 if (!isNewBuild) { configurations { // Aliases for

    the new configurations in 3.0.0. api implementation compile.extendsFrom(api, implementation) debugImplementation debugCompile.extendsFrom(debugImplementation) dogfoodImplementation dogfoodCompile.extendsFrom(dogfoodImplementation) testImplementation testCompile.extendsFrom(testImplementation) androidTestImplementation androidTestCompile.extendsFrom(androidTestImplementation) compileOnly provided.extendsFrom(compileOnly) } } Trying the plugin early
  57. Up-to-date check issues SkipUpToDateTaskExecuter in the logs Determining if task

    ':lib:core:util:android:common:extractReleaseAnnotations' is up-to-date Executing task ':lib:core:util:android:common:extractReleaseAnnotations' (up-to-date check took 0.012 secs) due to: Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v14/preference/R$id.class has changed. Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v7/appcompat/R$id.class has changed. Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v7/preference/R$id.class has changed. Determining if task ':lib:core:util:android:common:mergeReleaseAssets' is up-to-date Skipping task ':lib:core:util:android:common:mergeReleaseAssets' as it is up-to-date (took 0.003 secs). Determining if task ':lib:core:util:android:common:packageReleaseResources' is up-to-date Executing task ':lib:core:util:android:common:packageReleaseResources' (up-to-date check took 0.035 secs) due to: Value of input property 'minSdk' has changed for task ':lib:core:util:android:common:packageReleaseResources'
  58. Up-to-date check issues SkipUpToDateTaskExecuter in the logs Determining if task

    ':lib:core:util:android:common:extractReleaseAnnotations' is up-to-date Executing task ':lib:core:util:android:common:extractReleaseAnnotations' (up-to-date check took 0.012 secs) due to: Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v14/preference/R$id.class has changed. Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v7/appcompat/R$id.class has changed. Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v7/preference/R$id.class has changed. Determining if task ':lib:core:util:android:common:mergeReleaseAssets' is up-to-date Skipping task ':lib:core:util:android:common:mergeReleaseAssets' as it is up-to-date (took 0.003 secs). Determining if task ':lib:core:util:android:common:packageReleaseResources' is up-to-date Executing task ':lib:core:util:android:common:packageReleaseResources' (up-to-date check took 0.035 secs) due to: Value of input property 'minSdk' has changed for task ':lib:core:util:android:common:packageReleaseResources'
  59. Up-to-date check issues SkipUpToDateTaskExecuter in the logs Determining if task

    ':lib:core:util:android:common:extractReleaseAnnotations' is up-to-date Executing task ':lib:core:util:android:common:extractReleaseAnnotations' (up-to-date check took 0.012 secs) due to: Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v14/preference/R$id.class has changed. Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v7/appcompat/R$id.class has changed. Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v7/preference/R$id.class has changed. Determining if task ':lib:core:util:android:common:mergeReleaseAssets' is up-to-date Skipping task ':lib:core:util:android:common:mergeReleaseAssets' as it is up-to-date (took 0.003 secs). Determining if task ':lib:core:util:android:common:packageReleaseResources' is up-to-date Executing task ':lib:core:util:android:common:packageReleaseResources' (up-to-date check took 0.035 secs) due to: Value of input property 'minSdk' has changed for task ':lib:core:util:android:common:packageReleaseResources'
  60. Up-to-date check issues SkipUpToDateTaskExecuter in the logs Determining if task

    ':lib:core:util:android:common:extractReleaseAnnotations' is up-to-date Executing task ':lib:core:util:android:common:extractReleaseAnnotations' (up-to-date check took 0.012 secs) due to: Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v14/preference/R$id.class has changed. Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v7/appcompat/R$id.class has changed. Input property 'classDir' file /Users/cpuerta/workspace/twitter-android/lib/core/util/android/common/ build/intermediates/classes/release/android/support/v7/preference/R$id.class has changed. Determining if task ':lib:core:util:android:common:mergeReleaseAssets' is up-to-date Skipping task ':lib:core:util:android:common:mergeReleaseAssets' as it is up-to-date (took 0.003 secs). Determining if task ':lib:core:util:android:common:packageReleaseResources' is up-to-date Executing task ':lib:core:util:android:common:packageReleaseResources' (up-to-date check took 0.035 secs) due to: Value of input property 'minSdk' has changed for task ':lib:core:util:android:common:packageReleaseResources'
  61. Up-to-date check issues • Android Studio invalidating caches • Annotation

    processors that don’t generate consistent code • Missing dependencies among tasks • MinSDK Some examples
  62. Up-to-date check issues • Android Studio invalidating caches • Annotation

    processors that don’t generate consistent code • Missing dependencies among tasks • MinSDK Some examples
  63. Up-to-date check issues buildscript { // Remove Android Studio's local

    Maven repository, since it pollutes // the classpath and causes rebuilds. allprojects { buildscript { repositories.clear() } } } Some examples
  64. Up-to-date check issues • Android Studio invalidating caches • Annotation

    processors that don’t generate consistent code • Missing dependencies among tasks • MinSDK Some examples
  65. Up-to-date check issues • Android Studio invalidating caches • Annotation

    processors that don’t generate consistent code • Missing dependencies among tasks • MinSDK Some examples
  66. Up-to-date check issues afterEvaluate { // Work around missing task

    dependency getProjectVariants(project).each { variant -> def safeguardTask = tasks.getByName( "incremental${variant.name.capitalize()}JavaCompilationSafeguard") variant.variantData.outputs.each { output -> safeguardTask.dependsOn(output.scope.processResourcesTask.name) } } } Some examples
  67. Up-to-date check issues • Android Studio invalidating caches • Annotation

    processors that don’t generate consistent code • Missing dependencies among tasks • MinSDK Some examples
  68. Addressing memory issues Adjust daemon heap size in gradle.properties org.gradle.jvmargs=-Xms512m

    -Xmx4g -Dfile.encoding=utf-8 Fork compilation to a separate process tasks.withType(JavaCompile) { options.fork = true } Leaks? org.gradle.jvmargs=-Xms512m -Xmx4g -Dfile.encoding=utf-8 \ -agentpath:/Applications/YourKit.app/Contents/Resources/bin/mac/libyjpagent.jnilib=sampling,onexit=memory org.gradle.daemon=false
  69. Addressing memory issues Adjust daemon heap size in gradle.properties org.gradle.jvmargs=-Xms512m

    -Xmx4g -Dfile.encoding=utf-8 Fork compilation to a separate process tasks.withType(JavaCompile) { options.fork = true } Leaks? org.gradle.jvmargs=-Xms512m -Xmx4g -Dfile.encoding=utf-8 \ -agentpath:/Applications/YourKit.app/Contents/Resources/bin/mac/libyjpagent.jnilib=sampling,onexit=memory org.gradle.daemon=false
  70. Addressing memory issues Adjust daemon heap size in gradle.properties org.gradle.jvmargs=-Xms512m

    -Xmx4g -Dfile.encoding=utf-8 Fork compilation to a separate process tasks.withType(JavaCompile) { options.fork = true } Leaks? org.gradle.jvmargs=-Xms512m -Xmx4g -Dfile.encoding=utf-8 \ -agentpath:/Applications/YourKit.app/Contents/Resources/bin/mac/libyjpagent.jnilib=sampling,onexit=memory org.gradle.daemon=false
  71. Addressing memory issues Adjust daemon heap size in gradle.properties org.gradle.jvmargs=-Xms512m

    -Xmx4g -Dfile.encoding=utf-8 Fork compilation to a separate process tasks.withType(JavaCompile) { options.fork = true } Leaks? org.gradle.jvmargs=-Xms512m -Xmx4g -Dfile.encoding=utf-8 \ -agentpath:/Applications/YourKit.app/Contents/Resources/bin/mac/libyjpagent.jnilib=sampling,onexit=memory org.gradle.daemon=false
  72. Addressing memory issues Adjust daemon heap size in gradle.properties org.gradle.jvmargs=-Xms512m

    -Xmx4g -Dfile.encoding=utf-8 Fork compilation to a separate process tasks.withType(JavaCompile) { options.fork = true } Leaks? org.gradle.jvmargs=-Xms512m -Xmx4g -Dfile.encoding=utf-8 \ -agentpath:/Applications/YourKit.app/Contents/Resources/bin/mac/libyjpagent.jnilib=sampling,onexit=memory org.gradle.daemon=false
  73. And “ fi xing” the leak Addressing memory issues tasks.withType(MergeResources).each

    { task -> task.doLast { Utils.setPrivateFieldValue(task, MergeResources, 'processedInputs', null) task.sourceFolderInputs = { [] } } } And report an issue!
  74. Why? • Improve compiler avoidance • Optimize caching of build

    artifacts • More e ffi cient parallelization • Better code architecture • Finer job granularity in CI
  75. Why? • Improve compiler avoidance • Optimize caching of build

    artifacts • More e ffi cient parallelization • Better code architecture • Finer job granularity in CI
  76. Why? • Improve compiler avoidance • Optimize caching of build

    artifacts • More e ffi cient parallelization • Better code architecture • Finer job granularity in CI
  77. Why? • Improve compiler avoidance • Optimize caching of build

    artifacts • More e ffi cient parallelization • Better code architecture • Finer job granularity in CI
  78. Why? • Improve compiler avoidance • Optimize caching of build

    artifacts • More e ffi cient parallelization • Better code architecture • Finer job granularity in CI
  79. Why? • Improve compiler avoidance • Optimize caching of build

    artifacts • More e ffi cient parallelization • Better code architecture • Finer job granularity in CI
  80. Configuring Gradle • Shared gradle con fi guration fi les

    • Making sense of build errors • Select projects to run your tasks on
  81. Configuring Gradle • Shared gradle con fi guration fi les

    • Making sense of build errors • Select projects to run your tasks on
  82. Configuring Gradle • common-properties.gradle • checkstyle.gradle • lint.gradle • java-library-config.gradle

    • java-annotation-processor-config.gradle • android-common-config.gradle • android-library-config.gradle • android-application-config.gradle
  83. Configuring Gradle • common-properties.gradle • checkstyle.gradle • lint.gradle • java-library-config.gradle

    • java-annotation-processor-config.gradle • android-common-config.gradle • android-library-config.gradle • android-application-config.gradle
  84. Configuring Gradle • common-properties.gradle • checkstyle.gradle • lint.gradle • java-library-config.gradle

    • java-annotation-processor-config.gradle • android-common-config.gradle • android-library-config.gradle • android-application-config.gradle
  85. Configuring Gradle apply plugin: 'checkstyle' apply from: "${project.rootDir}/config/gradle-common/common-properties.gradle" checkstyle {

    configProperties = [ configDir:"${rootDir}/config/checkstyle" ] configFile = file("${rootDir}/config/checkstyle/checkstyle.xml") toolVersion = checkstyleVersion showViolations = false } dependencies { checkstyle project(':tools:checkstyle-rules') } task checkstyle(type: Checkstyle) { source 'src' include '**/*.java' exclude '*/resources/**' classpath = files() group = 'Verification' description = 'Run all checkstyle tasks.' } checkstyle.gradle
  86. Configuring Gradle • common-properties.gradle • checkstyle.gradle • lint.gradle • java-library-config.gradle

    • java-annotation-processor-config.gradle • android-common-config.gradle • android-library-config.gradle • android-application-config.gradle
  87. Configuring Gradle • common-properties.gradle • checkstyle.gradle • lint.gradle • java-library-config.gradle

    • java-annotation-processor-config.gradle • android-common-config.gradle • android-library-config.gradle • android-application-config.gradle
  88. Configuring Gradle apply plugin: 'java-library' apply from: "${project.rootDir}/config/gradle-common/common-properties.gradle" apply from:

    "${project.rootDir}/config/gradle-common/checkstyle.gradle" apply from: "${project.rootDir}/config/gradle-common/robolectric-test-config.gradle" sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 dependencies { api project.jetbrainsAnnotations } configurations { // A configuration that collects annotation processing dependencies to add to the // annotation processsor path. annotationProcessor } compileJava { options.annotationProcessorPath = configurations.annotationProcessor } java-library-config.gradle
  89. Configuring Gradle apply plugin: 'java-library' apply from: "${project.rootDir}/config/gradle-common/common-properties.gradle" apply from:

    "${project.rootDir}/config/gradle-common/checkstyle.gradle" apply from: "${project.rootDir}/config/gradle-common/robolectric-test-config.gradle" sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 dependencies { api project.jetbrainsAnnotations } configurations { // A configuration that collects annotation processing dependencies to add to the // annotation processsor path. annotationProcessor } compileJava { options.annotationProcessorPath = configurations.annotationProcessor } java-library-config.gradle
  90. Configuring Gradle apply plugin: 'java-library' apply from: "${project.rootDir}/config/gradle-common/common-properties.gradle" apply from:

    "${project.rootDir}/config/gradle-common/checkstyle.gradle" apply from: "${project.rootDir}/config/gradle-common/robolectric-test-config.gradle" sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 dependencies { api project.jetbrainsAnnotations } configurations { // A configuration that collects annotation processing dependencies to add to the // annotation processsor path. annotationProcessor } compileJava { options.annotationProcessorPath = configurations.annotationProcessor } java-library-config.gradle
  91. Configuring Gradle • common-properties.gradle • checkstyle.gradle • lint.gradle • java-library-config.gradle

    • java-annotation-processor-config.gradle • android-common-config.gradle • android-library-config.gradle • android-application-config.gradle
  92. Configuring Gradle • common-properties.gradle • checkstyle.gradle • lint.gradle • java-library-config.gradle

    • java-annotation-processor-config.gradle • android-common-config.gradle • android-library-config.gradle • android-application-config.gradle
  93. Configuring Gradle • common-properties.gradle • checkstyle.gradle • lint.gradle • java-library-config.gradle

    • java-annotation-processor-config.gradle • android-common-config.gradle • android-library-config.gradle • android-application-config.gradle
  94. Configuring Gradle apply plugin: 'com.android.library' apply from: "${project.rootDir}/config/gradle-common/common-properties.gradle" apply from:

    "${project.rootDir}/config/gradle-common/android-common-config.gradle" apply from: "${project.rootDir}/config/gradle-common/checkstyle.gradle" apply from: "${project.rootDir}/config/gradle-common/lint.gradle" android-library-config.gradle
  95. Configuring Gradle • common-properties.gradle • checkstyle.gradle • lint.gradle • java-library-config.gradle

    • java-annotation-processor-config.gradle • android-common-config.gradle • android-library-config.gradle • android-application-config.gradle
  96. Configuring Gradle apply from: "${project.rootDir}/config/gradle-common/android-library-config.gradle" dependencies { api 'com.twitter.android:media:1.22.1' api

    "com.android.support:exifinterface:${supportLibraryVersion}" implementation project(':lib:core:async:common') implementation project(':lib:core:util:android:io') } /lib/core/media/common/build.gradle
  97. Configuring Gradle apply from: "${project.rootDir}/config/gradle-common/android-library-config.gradle" dependencies { api 'com.twitter.android:media:1.22.1' api

    "com.android.support:exifinterface:${supportLibraryVersion}" implementation project(':lib:core:async:common') implementation project(':lib:core:util:android:io') } /lib/core/media/common/build.gradle
  98. Configuring Gradle apply from: "${project.rootDir}/config/gradle-common/android-library-config.gradle" dependencies { api 'com.twitter.android:media:1.22.1' api

    "com.android.support:exifinterface:${supportLibraryVersion}" implementation project(':lib:core:async:common') implementation project(':lib:core:util:android:io') } /lib/core/media/common/build.gradle
  99. Configuring Gradle • Shared gradle con fi guration fi les

    • Making sense of build errors • Select projects to run your tasks on
  100. Configuring gradle • Shared gradle con fi guration fi les

    • Making sense of build errors • Select projects to run your tasks on
  101. Some advice • Use api dependencies sparingly • Isolate annotation

    processing • Remove bottlenecks in your graph • Use Java modules when possible • Use the new refactors in Android Studio to move resources
  102. • Use api dependencies sparingly • Isolate annotation processing •

    Remove bottlenecks in your graph • Use Java modules when possible • Use the new refactors in Android Studio to move resources Some advice
  103. • Use api dependencies sparingly • Isolate annotation processing •

    Remove bottlenecks in your graph • Use Java modules when possible • Use the new refactors in Android Studio to move resources Some advice
  104. • Use api dependencies sparingly • Isolate annotation processing •

    Remove bottlenecks in your graph • Use Java modules when possible • Use the new refactors in Android Studio to move resources Some advice
  105. • Use api dependencies sparingly • Isolate annotation processing •

    Remove bottlenecks in your graph • Use Java modules when possible • Use the new refactors in Android Studio to move resources Some advice
  106. • Use api dependencies sparingly • Isolate annotation processing •

    Remove bottlenecks in your graph • Use Java modules when possible • Use the new refactors in Android Studio to move resources Some advice
  107. 54 Modules • Mostly inside large app module, and one

    large library module • AGP 2.2.2 • Gradle 3.1 (In October 2016) Our build
  108. 93 Modules • Still have 2 larger modules, but considerably

    smaller than months ago • AGP 2.3.3 - using 3.0.0 also • Gradle 4.0 (In June 2017) Our build