gradle user to addict sf -...
TRANSCRIPT
![Page 1: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/1.jpg)
Gradle: From User to Addict
But my talk:I’m jake ouellette, senior software engineer working at twitterThis is my presentation, GradleFrom user to addict
![Page 2: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/2.jpg)
If you’re like me, you’ve been using gradle for a while (Or maybe just considering it?) I’ve been using it both for making applications and developing gradle plugins
![Page 3: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/3.jpg)
At Twitter, I work on the Fabric team to build tools for mobile developers. One tool we have is Crashlytics, a mobile crash reporting SDK that goes into android applications. I’ll talk about how the Gradle plugin works later.
![Page 4: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/4.jpg)
Simple Possible
What I’ve found from personal experience and when teaching others about Gradle* Can see what’s possible, but can’t reach it.
![Page 5: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/5.jpg)
* Load up android studio
![Page 6: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/6.jpg)
build.gradle
apply plugin: 'android'
android { compileSdkVersion 19 buildToolsVersion "19.0.3"
defaultConfig { minSdkVersion 8 targetSdkVersion 19 versionCode 1 versionName "1.0" } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } }
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:19.+' }
* Look at the default project build.gradle
![Page 7: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/7.jpg)
I want to add a task!
* Like “I want to add a task to this thing!”just look over the default build script and try and understand it.
![Page 8: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/8.jpg)
build.gradle
apply plugin: ‘com.android.application’
* Ok, so the first line adds lots of tasks!* Gradle is based on plugins, so it doesn’t do much on its own* Super important, not very informative on how to build grade scripts
![Page 9: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/9.jpg)
build.gradle
android { compileSdkVersion 19 … }
* The next line seems to set android properties* What if I have my own variables?* What if I want to change these depending on the build?
![Page 10: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/10.jpg)
build.gradle
dependencies { compile 'com.android.support:appcompat-v7:19.+' compile fileTree(dir: 'libs', include: ['*.jar']) }
* Ok, finally I get to a dependency block* Gradle is a dependency manager, which means it figures out what dependencies your projects has and downloads them, and their transitive
dependencies, from the web
![Page 11: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/11.jpg)
build.gradle
dependencies { compile 'com.android.support:appcompat-v7:19.+' compile fileTree(dir: 'libs', include: ['*.jar']) }
* As a naive person, it kind of reminds me of maven.
![Page 12: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/12.jpg)
build.gradle
dependencies { compile 'com.android.support:appcompat-v7:19.+' compile fileTree(dir: 'libs', include: ['*.jar']) }
* But wait, is fileTree executing code? * Maven doesn’t execute code, maven is just xml
![Page 13: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/13.jpg)
What is the Gradle language really doing?
How do I use it?
• create tasks • make a multi-app project • write builds once, reuse
![Page 14: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/14.jpg)
To do this, we’re going to dive under the hood in the Gradle Syntax understand what’s really going on
![Page 15: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/15.jpg)
Simple PossibleSimpossible
So what I’d like to do today, is bridge the gap between simple and possible
* Forming the simpossible
![Page 16: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/16.jpg)
dependencies { compile 'com.crashlytics.android:crashlytics:1.+' }
build.gradle
* We have directions on how to set up the crashlytics SDK
![Page 17: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/17.jpg)
build.gradle
android { dependencies { compile 'com.crashlytics.android:crashlytics:1.+' } }
* And for a while, in the android studio project, that was inside the android block!
![Page 18: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/18.jpg)
build.gradle
android { … }
dependencies { compile 'com.crashlytics.android:crashlytics:1.+' }
* But at one point, android studio switched to a different location, outside that block* I was super confused, because the old way didn’t break our build! Why did this work?
![Page 19: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/19.jpg)
{ }
* Realized didn’t understand how { } work
![Page 20: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/20.jpg)
example.java
new Runnable() { public void run() { … } }
* Take a look at anonymous classes in java
![Page 21: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/21.jpg)
example.java
final int time = System.currentTimeMillis() new Runnable() { public void run() { … } }
* If you declare a variable
![Page 22: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/22.jpg)
example.java
final int time = System.currentTimeMillis() new Runnable() { public void run() { System.out.println(time); } }
* You can reference it, it is in scope of the anonymous class* Outputting time when runnable was CREATED
![Page 23: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/23.jpg)
example.java
final int time = System.currentTimeMillis()
{ System.out.println(time); }
* Groovy Curly braces work a lot like anonymous classes
![Page 24: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/24.jpg)
build.gradle
android { … }
So when we look at this
![Page 25: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/25.jpg)
build.gradle
android({ … })
We should note, the curly braces really are creating a closure, and passing it into an android object / method
![Page 26: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/26.jpg)
android() =
android.apply()
* Calls without methods get delegated to an apply method
![Page 27: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/27.jpg)
android.apply(Closure configuration)
* android object actually has this method which takes a closure
![Page 28: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/28.jpg)
build.gradle
android.apply({ … })
* And that parameter is our curly braces block!
![Page 29: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/29.jpg)
build.gradle
android.apply({ it })
In gradle, “IT” is an object works kind of like this, but references the object curly braces are applied to
![Page 30: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/30.jpg)
build.gradle
println(android.getClass()) android { println(it.getClass()) }
We can see that by printing the class of it, comparing it against the class of the android object.
![Page 31: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/31.jpg)
command line output
class com.android.build.gradle.AppExtension_Decorated class com.android.build.gradle.AppExtension_Decorated
println(android.getClass()) android { println(it.getClass()) }
Same classes!
![Page 32: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/32.jpg)
build.gradle
closure.delegate = this
The reason why that works is that in groovy, you can set closures to delegate to specific objects.Internally, Gradle is delegating the closure to the applied object
![Page 33: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/33.jpg)
build.gradle
android.apply({ it })
changes what “it” is
![Page 34: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/34.jpg)
build.gradle
android { defaultConfig { minSdkVersion 8 } }
But inside that android object, we’re not just setting values of things, we’re also opening up new curly braces
![Page 35: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/35.jpg)
defaultConfig.apply(Closure configuration)
You can think of new curly braces as though the android object has a defaultConfig object with an apply method!
![Page 36: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/36.jpg)
build.gradle
android { defaultConfig { minSdkVersion 8 } }
So similarly, this
![Page 37: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/37.jpg)
build.gradle
android { it.defaultConfig { minSdkVersion 8 } }
can look like this
![Page 38: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/38.jpg)
build.gradle
println(android.defaultConfig.getClass()) android { defaultConfig { println(it.getClass()) } }
We can do the same thing of comparing it to an object
![Page 39: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/39.jpg)
command line output
class com.android.build.gradle.internal.dsl.ProductFlavorDsl_Decorated class com.android.build.gradle.internal.dsl.ProductFlavorDsl_Decorated
println(android.defaultConfig.getClass()) android { defaultConfig { println(it.getClass()) } }
And we get the same internal object in both cases
![Page 40: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/40.jpg)
build.gradle
android { defaultConfig { minSdkVersion 8 } }
minSdk doesn’t open up another closure, it sets a value of a property
![Page 41: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/41.jpg)
build.gradle
android { defaultConfig { minSdkVersion 8 println(minSdkVersion) } }
Adding a println would output 8
![Page 42: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/42.jpg)
build.gradle
android { defaultConfig { setMinSdkVersion(8) } }
You can actually call its setter like this,This is because gradle code is groovy codeand groovy POGOs translate to java classes with getters and setters
![Page 43: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/43.jpg)
build.gradle
android.defaultConfig.minSdkVersion = 8
So gradle also has a dot notation you’ve probably seen which allows both setting
![Page 44: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/44.jpg)
build.gradle
android.defaultConfig.minSdkVersion
and direct access
![Page 45: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/45.jpg)
build.gradle
android { dependencies { compile 'com.crashlytics.android:crashlytics:1.+' } }
So now, going back to the original question, why did a dependency block inside an android block work?
Well, we know compile is a method or field of the dependency object, shouldn’t dependencies be a method or field of the android object?
![Page 46: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/46.jpg)
build.gradle
repositories { maven { url “example" } }
Let’s explore why this is working by cooking up a weird test
Here’s a repository block
![Page 47: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/47.jpg)
def maven(Closure closure)
And the reason why that works is that maven is a method of repositories that takes a closure
![Page 48: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/48.jpg)
MavenArtifactRepository maven(Closure closure)
but it actually isn’t a void method. Not all closures objects return void methods.
![Page 49: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/49.jpg)
build.gradle
repositories { println maven { url "example" }.url }
![Page 50: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/50.jpg)
command line output
example
![Page 51: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/51.jpg)
Ok, are you ready to have your mind blown?
![Page 52: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/52.jpg)
MavenArtifactRepository maven(Closure closure)
mutation
You see, this call actually mutates the repositories block
![Page 53: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/53.jpg)
build.gradle
repositories { maven { url “example" maven { url "example2" } } }
![Page 54: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/54.jpg)
build.gradle (with annotation)
repositories { SEARCHED NEXT maven { SEARCHED FIRST } }
![Page 55: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/55.jpg)
build.gradle
android { dependencies { compile 'com.crashlytics.android:crashlytics:1.+' } }
So the original dependency block weirdness is now part of a pattern to us,
![Page 56: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/56.jpg)
build.gradle
SEARCHED SECOND android { SEARCHED FIRST
dependencies { compile 'com.crashlytics.android:crashlytics:1.+' } }
* gradle searches up parent scope, just like a closure block* It’s just that the scopes are weird because gradle delegated them
Answer: It always belonged out of the android curly braces, they moved it correctly
![Page 57: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/57.jpg)
Adding a task
Continuing to show you how Gradle works, let’s talk about the syntax for adding a task.
![Page 58: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/58.jpg)
Adding a task: Cache each build.gradle with a
timestamp
Specifically, let’s have our task be to cache each build.grade with a timestamp.
![Page 59: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/59.jpg)
build.gradle
task copyTask(type: Copy)
Ok, so I created a task called copy task
![Page 60: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/60.jpg)
build.gradle
task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
And I wrote it out like this
![Page 61: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/61.jpg)
build.gradle
task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
The idea is, it takes my build.gradle
![Page 62: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/62.jpg)
build.gradle
task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
Copies it into a build cache folder
![Page 63: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/63.jpg)
build.gradle
task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
And renames it based on the current system time.
![Page 64: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/64.jpg)
command line output
:app:copyTask UP-TO-DATE
BUILD SUCCESSFUL
Feeling clever, I ran it, and it looked OK, it read “UP-TO-DATE” which was strange… but the build completed…?
![Page 65: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/65.jpg)
ideal directory listing
buildCache/ build.gradle1399906750071 build.gradle1401281449000 build.gradle1401290623000
![Page 66: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/66.jpg)
command line output
> ls app/buildCache …
I later did an LS of that folder, and found nothing there. What did I do wrong??
![Page 67: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/67.jpg)
build.gradle
task copyTask
So let’s figure out what copy task is doing
![Page 68: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/68.jpg)
Gradle has great documentation
![Page 69: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/69.jpg)
org.gradle.api Interface Project
All Superinterfaces: Comparable<Project>, ExtensionAware, PluginAware
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware
This interface is the main API you use to interact with Gradle from your build file. From a Project, you have programmatic access to all of Gradle's features.
and really, what we want to look at is their API for PROJECT
![Page 70: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/70.jpg)
build.gradle
task copyTask
The reason we want to look at PROJECT is free floating lines in your build.gradle act on projects
Syntax like this is changing your project object by calling on it
![Page 71: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/71.jpg)
org.gradle.api Interface Project
All Superinterfaces: Comparable<Project>, ExtensionAware, PluginAware
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware
This interface is the main API you use to interact with Gradle from your build file. From a Project, you have programmatic access to all of Gradle's features.
So if we look at the interface for project, we find a bunch of methods named “TASK” in that documentation
![Page 72: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/72.jpg)
build.gradle
task copyTask
So putting the word task in
![Page 73: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/73.jpg)
build.gradle
task(“copyTask”)
is equivalent to a method called on the project object
![Page 74: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/74.jpg)
build.gradle
task copyTask(type: Copy)
and giving it a type
![Page 75: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/75.jpg)
build.gradle
task(“copyTask”, [“type” : “Copy”])
groovy map
Is equivalent to passing in a groovy map with type being copy.
![Page 76: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/76.jpg)
Task
task(“copyTask”, [“type” : “Copy”])
The output of this method is a Task
![Page 77: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/77.jpg)
build.gradle
def apply(Closure closure)
Like android and buildConfig from earlier, TASK has an apply method
![Page 78: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/78.jpg)
build.gradle
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
We can pass in a closure, and with it specify from, into, and rename
![Page 79: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/79.jpg)
command line output
:app:copyTask
So when you run it…
![Page 80: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/80.jpg)
command line output
> ls app/buildCache
build.gradle1399906750071
It runs OK! wait, so.. wait what?
![Page 81: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/81.jpg)
build.gradle
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
Here’s the script again, based on my investigation of the api
![Page 82: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/82.jpg)
build.gradle
task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
Here was my original script
![Page 83: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/83.jpg)
<< { }
So what does the double arrow do? I saw it in the gradle docs?
![Page 84: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/84.jpg)
.doLast({ })
Double arrow tells my project to do something after everything else executes
![Page 85: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/85.jpg)
Executes?
wait executes? What’s different between setting parameters for configuring, and adding code for execution?
![Page 86: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/86.jpg)
command line
./gradlew assemble
So when you gradle assemble
![Page 87: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/87.jpg)
configuration
apply plugin: ‘android’
android { minSdkVersion 8 }
task print << { println(“Hi!”) }
assemble.dependsOn(print)
apply plugin: ‘android’
android { minSdkVersion 8 }
task print << { println(“Hi!”) }
assemble.dependsOn(print)
executed
execution
apply plugin: ‘android’
android { minSdkVersion 8 }
task print << { println(“Hi!”) }
assemble.dependsOn(print)
First, all the code except execution steps are parsedThen all the execution steps are ordered and run
![Page 88: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/88.jpg)
build.gradle
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
So this is a method call to create a task
![Page 89: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/89.jpg)
build.gradle
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
And this immediately is applied to that task
![Page 90: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/90.jpg)
build.gradle
task copyTask(type: Copy) { … } << { println(“Execution”) }
Even the curly braces themselves can be thought of as configuration, creating an object and passing it into the task
![Page 91: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/91.jpg)
build.gradle
task copyTask(type: Copy) { … } << { println(“Execution”) }
It’s this last thing — the block of code that runs when you see your task running inside gradle.
You rarely see execution code in build.gradles because plugins tend to roll them up
During the execution step, you should not change properties (in fact, some don’t let you)
![Page 92: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/92.jpg)
build.gradle
task copyTask << { // Roll my own }
Lets look at what happens if I roll my own copy task
![Page 93: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/93.jpg)
build.gradle
Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis()))
Let’s roll our own copy task, here’s our from
![Page 94: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/94.jpg)
build.gradle
Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis()))
And here’s our into plus rename
![Page 95: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/95.jpg)
build.gradle
Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis()))
Just one thing I want to call out here, in this version, I have to figure out the project directory, copy tasks are always relative of the current project
![Page 96: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/96.jpg)
build.gradle
task copyTask << { Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis())) }
When I make a new task, and add double arrowDOWNSIDE:This task runs EVERY Time, because I haven’t told the task its inputs or outputs
![Page 97: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/97.jpg)
TASK
So a task
![Page 98: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/98.jpg)
TASKTASK
TASK
@Input @InputDirectory @InputFile @InputFiles
has inputs, you can declare them when making a task using annotations
![Page 99: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/99.jpg)
TASK
@OutputDirectories @OutputDirectory @OutputFile @OutputFiles
TASK
TASK
TASK
TASK
And outputs, which also are declared on the task using an annotation
![Page 100: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/100.jpg)
TASKTASK
TASK TASK EXECUTED
When the at least one input changes, then the task would be rerun
![Page 101: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/101.jpg)
TASKTASK
TASK TASK EXECUTED
Causing downstream tasks to get rerun too
![Page 102: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/102.jpg)
TASKTASK
TASK UP-TO-DATE
Otherwise, the task is up to date
![Page 103: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/103.jpg)
build.gradle
task copyTask { inputs.file new File("build.gradle") // outputs } << { // Java file copy code }
![Page 104: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/104.jpg)
build.gradle
task copyTask { inputs.file new File("build.gradle") // outputs } << { // Java file copy code }
You could specify your inputs
![Page 105: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/105.jpg)
build.gradle
task copyTask { inputs.file new File("build.gradle"), // outputs } << { // Java file copy code }
So then you’d put the java code back here now with inputs and outputs
![Page 106: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/106.jpg)
build.gradle
task example
HAS NO INPUTS YET, ALWAYS RUNS
If you just make a task without saying it is a copy type task, it has no inputs declared, so always runs
![Page 107: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/107.jpg)
build.gradle
task copyTask(type: Copy)
COPY TASK HAS EMPTY SET OF THINGS TO COPY
And if you don’t provide any configuration The task has a list of 0 things to copy, so it never runs, is UP-TO-DATE
![Page 108: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/108.jpg)
build.gradle
task copyTask(type: Copy) << {
}
COPY TASK HAS EMPTY SET OF THINGS TO COPY
// CODE WILL NOT EXECUTE
Copy task has an empty set of things, so if you add a do Last, it never executes, because the task is never executed.
![Page 109: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/109.jpg)
build.gradle
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
Good
So in summary,
![Page 110: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/110.jpg)
build.gradle
task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
BAD
This code doesn’t run because of no inputs or outputs, also, it’d be changing configuration properties if it did run during execution, which is bad.
![Page 111: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/111.jpg)
build.gradle
task copyTask << { Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis())) }
Weird
And I just don’t recommend doing this if you have a DSL to help you.
![Page 112: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/112.jpg)
command line
./gradlew tasks
Gradle tasks is a command that lists tasks without executing them
One of the things I really like about gradle is how the configuration step sets up your project’s structure
![Page 113: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/113.jpg)
command line output
Build tasks ----------- assemble assembleDebug assembleDebugTest assembleRelease build buildDependents buildNeeded clean
So you can see all the tasks that you can call here.
![Page 114: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/114.jpg)
configuration
apply plugin: ‘android’
android { minSdkVersion 8 }
task print << { println(“Hi!”) }
assemble.dependsOn(print)
apply plugin: ‘android’
android { minSdkVersion 8 }
task print << { println(“Hi!”) }
assemble.dependsOn(print)
not executed
Basically just configures the project, then outputs the configured tasks
![Page 115: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/115.jpg)
Compile
Dependency Graph
PreBuild Assemble
And during the configuration step, gradle figures out the dependencies between things
![Page 116: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/116.jpg)
call?
Compile Assemble
You might be familiar with ant, which has antcall,
and say “Hey wait, what if I decide I want to call a task from another task? Is there a language for that?”
![Page 117: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/117.jpg)
Compile Assemble
no
![Page 118: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/118.jpg)
assemble.dependsOn(task)task.dependsOn(compile)
AssembleCompile
“The answer is no. You should add a task dependency.
![Page 119: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/119.jpg)
task.onlyIf { condition }
You can switch the task on and off with conditions
![Page 120: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/120.jpg)
Compile Task
Dependency GraphCrashlytics Task
Crashlytics Task
The crashlytics plugin, for example, uses this to put our tasks between steps.
![Page 121: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/121.jpg)
Multi-Project Builds
![Page 122: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/122.jpg)
opensource/library/
day-job/app/
Alright, so hey, I had an open source project I wanted to reference it from an app I was working on
![Page 123: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/123.jpg)
development/app/library/
If you had a project with a library, you might have a folder structure like this
![Page 124: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/124.jpg)
command line
cd development/app/
In ant, you’d CD into the app’s folder
![Page 125: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/125.jpg)
command line
ant build
Ant build the app
![Page 126: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/126.jpg)
development/app/library/
During its build, the app would look at the library and maybe build it
![Page 127: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/127.jpg)
App
Project Root Library
In order for gradle to understand all the tasks during the configuration step, a build needs to see everything
![Page 128: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/128.jpg)
root/app/library/build.gradle
development/
So gradle makes your root folder have both projects, and a root build.gradle
![Page 129: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/129.jpg)
command line
cd development/root
and now, to build, first you cd into your root folder
![Page 130: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/130.jpg)
command line
./gradlew app:assemble
And build the specific app you want to build
![Page 131: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/131.jpg)
build.gradle
dependencies { compile project(‘:library’) }
To link apps, you use a special syntax called project, that requires the other project be visible at the root
![Page 132: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/132.jpg)
include ‘library’ include ‘app’
settings.gradle
You can make apps visible in the root by adding them to your settings.gradle
![Page 133: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/133.jpg)
opensource/library/
day-job/app/
So you want something like this
![Page 134: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/134.jpg)
AppLibrary
Installs Into Depends
OnLocal Maven Cache
So you install into your local maven cache
![Page 135: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/135.jpg)
build.gradle
repositories { mavenLocal() }
Then make your root project depend on maven local.
![Page 136: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/136.jpg)
app library
ExecutionC
onfiguration
./gradlew customBuild
Tasks Executed
Tasks Executed
Before you were doing this
![Page 137: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/137.jpg)
app library Maven Local
ExecutionC
onfiguration
EXEC: ./gradlew :library:install
Tasks Executed
But, in order to keep your open source project separate from your day job
![Page 138: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/138.jpg)
app library Maven Local
ExecutionC
onfigurationTasks
Executed
EXEC: ./gradlew :app:install
![Page 139: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/139.jpg)
AppProject Root
Library
So I had a project that looked like this
![Page 140: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/140.jpg)
app library
ExecutionC
onfiguration
Tasks Executed
EXEC: ./gradlew :app:install
Tasks Executed
And I’d compile it, so during the configuration step, gradle figured out the DAG, then executed compile tasks in the correct order
![Page 141: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/141.jpg)
-P
I was using a gradle -P syntax, which sets a property in your root project
![Page 142: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/142.jpg)
./gradlew :app:assemble -PrunOnCi=false
So I’d call my app like this
![Page 143: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/143.jpg)
if (runOnCi) { … }
build.gradle
And so I and this flag, runOnCi, that seemed to work when I build this project
![Page 144: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/144.jpg)
App
App2
Project Root
Library
./gradlew :app:assemble -PrunOnCi=false
but then I added a second project to the root and tried to compile the first one
![Page 145: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/145.jpg)
app library
ExecutionC
onfiguration
Tasks Executed
EXEC: ./gradlew :app:assemble -PrunOnCi=false
Tasks Executed
app2
So the project ran through the configure stepAnd the app2 didn’t need to be executed
![Page 146: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/146.jpg)
BUILD SUCCESSFUL
![Page 147: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/147.jpg)
App
App2
Project Root
Library
./gradlew :app2:assemble
so then I tried to compile the second projectThis project didn’t yet have different behavior on CI, so I didn’t pass that parameter in.
![Page 148: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/148.jpg)
app library
ExecutionC
onfiguration
EXEC: ./gradlew :app2:assemble
app2
BUILD FAILED
So the project ran through the configure stepAnd the app2 didn’t need to be executed
![Page 149: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/149.jpg)
BUILD FAILED
![Page 150: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/150.jpg)
app library
ExecutionC
onfiguration
EXEC: ./gradlew :app2:assemble
app2
BUILD FAILED
if (runOnCi)
What did the build fail on?
![Page 151: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/151.jpg)
if (hasProperty(runOnCi)) { … }
Can fix by adding hasProperty
![Page 152: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/152.jpg)
org.gradle.configureondemand=true
gradle.properties
Or you can speed up your build using gradle incubating configureondemand feature
![Page 153: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/153.jpg)
app library
ExecutionC
onfiguration
EXEC: ./gradlew :app2:assemble
Tasks Executed
app2
Tasks Executed
So once you set that, it only configures the parts of the app you want to configure
![Page 154: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/154.jpg)
Code Reuse
![Page 155: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/155.jpg)
build.gradle
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
Good
Earlier we had that build caching task that we said was good.
![Page 156: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/156.jpg)
build.gradle
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
Good?
but is it.. really?
![Page 157: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/157.jpg)
build.gradle
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }
because, I’m going to put this in every project that I have
![Page 158: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/158.jpg)
build.gradle
class CacheBuild extends Copy {
}
So let’s say we could extend the copy task and set intelligent defaults
![Page 159: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/159.jpg)
build.gradle
class CacheBuild extends Copy { { … } }
plain ol’ java initializer
One way we could do that is make a java initializer block Not a closure
![Page 160: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/160.jpg)
build.gradle
class CacheBuild extends Copy { { from('build.gradle'); into('buildCache'); rename({ it + System.currentTimeMillis() }) } }
Then we put our configuration code into that initializer
![Page 161: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/161.jpg)
build.gradle
task cache(type:CacheBuild)
And now we can create our task very easily.
![Page 162: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/162.jpg)
AppProject Root
Library
The questions becomes, where do we save that new gradle task?
![Page 163: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/163.jpg)
App
Project Root
Library
Imagining each of my projects is a build.gradle
![Page 164: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/164.jpg)
App
Project Root
Library
Custom Code
You can put the class declaration in any of them
![Page 165: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/165.jpg)
App
Project Root
Library
But then the class isn’t visible to the others
![Page 166: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/166.jpg)
App
Project Root
Library
buildsrc/src/main/groovy/CacheBuild.groovy
Custom Code
Or I could put it in a build src folder
![Page 167: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/167.jpg)
App
Project Root
Library
buildsrc/src/main/groovy/CacheBuild.groovy
buildsrc code is visible to all projects relative of a root
![Page 168: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/168.jpg)
App
Project Root
Library
buildsrc/src/main/groovy/CacheBuild.groovy
day-job/app
However, then you run into the issue that that app in another project root can’t see the cool code
![Page 169: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/169.jpg)
AppCustom Task
Installs Into Depends
OnLocal Maven Cache
So you can install build scripts (locally or not), and then use them elsewhere
![Page 170: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/170.jpg)
build.gradle
assemble.dependsOn(‘cache’)
Even still, we’d have to wire this shared code up in every build between two tasks, wouldn’t it be nice if we could automate usage?
![Page 171: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/171.jpg)
build.gradle
apply plugin: 'BuildCache'
Gradle has a solution to this though! Plugins.
![Page 172: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/172.jpg)
org.gradle.api Interface Project
All Superinterfaces: Comparable<Project>, ExtensionAware, PluginAware
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware
This interface is the main API you use to interact with Gradle from your build file. From a Project, you have programmatic access to all of Gradle's features.
![Page 173: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/173.jpg)
build.gradle
apply([‘plugin’ : ‘BuildCache’])
![Page 174: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/174.jpg)
User’s Build Scriptapply plugin: ‘BuildCache’
plugin codevoid apply(Project project)
calls
Code reuse behaviors here.
![Page 175: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/175.jpg)
build.gradle
class BuildCache implements Plugin<Project> {
void apply(Project project) {
And so you declare a plugin class, and that’s where you put this apply method
![Page 176: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/176.jpg)
Inject into dependency graph
compile.dependsOn(buildCache)
Inside apply, add your tasks and hook them onto user’s tasks
![Page 177: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/177.jpg)
build.gradle
buildCache = … android.applicationVariants.all { assemble.dependsOn(buildCache) }
On android, you can hook each build type and flavor using applicationVariants.all
![Page 178: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/178.jpg)
So this is actually how Crashlytics is integrated into gradle, it’s hooking onto Gradle tasks
![Page 179: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/179.jpg)
So as a reminder, crashlytics is a crash reporting tool that shows you stack traces of your mobile applications
![Page 180: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/180.jpg)
build.gradle
ExampleActivity.onClick()a.b()com.crashlytics.obfuscation.
If you’re using proguard and your app crashes, the crashes would be obfuscated
![Page 181: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/181.jpg)
Previously, the Crashlytics plugin was responsible for uploading these mapping files. I was part of the team that integrated Twitter & MoPub into the Crashlytics plugin, so you now can provision Twitter keys with the same tool.
![Page 182: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/182.jpg)
Fabric was actually announced last week, so more on that by Andrea Falconee: it’s pretty cool stuff.
![Page 183: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/183.jpg)
Building on top of Android
Anyway, like I said, we upload the mapping files via hooking onto tasks.
I’ve learned a lot building on top of the Android Gradle plugin,and just wanted to mention a couple tips to help people out
![Page 184: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/184.jpg)
Pay attention to Android Gradle updates
![Page 185: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/185.jpg)
0.3 * System requirements: * Gradle 1.3+ (tested on 1.3/1.4). Will not be compatible with 1.5. An update will be required. * Android Platform Tools 16.0.2+ * New Features: * Renderscript support. * Support for multi resource folders. See 'multires' sample. * PNG crunch is now done incrementally and in parallel. * Support for multi asset folders. * Support for asset folders in Library Projects. * Support for versionName suffix provided by the BuildType. * Testing * Default sourceset for tests now src/instrumentTest (instrumentTest<Name> for flavors) * Instrumentation tests now: * started from "deviceCheck" instead of "check" * run on all connected devices in parallel. * break the build if any test fails. * generate an HTML report for each flavor/project, but also aggregated. * New plugin 'android-reporting' to aggregate android test results across projects. See 'flavorlib' sample. * Improved DSL: * replaced android.target with android.compileSdkVersion to make it less confusing with targetSdkVersion * signing information now a SigningConfig object reusable across BuildType and ProductFlavor * ability to relocate a full sourceSet. See 'migrated' sample. * API to manipulate Build Variants. * Fixes: * Default Java compile target set to 1.6. * Fix generation of R classes in case libraries share same package name as the app project. 0.2 * Fixed support for windows. * Added support for customized sourceset. (http://tools.android.com/tech-docs/new-build-system/using-the-new-build-system#TOC-Working-with-and-Customizing-SourceSets) * Added support for dependency per configuration. * Fixed support for dependency on local jar files. * New samples "migrated" and "flavorlib"
![Page 186: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/186.jpg)
java.lang.OutOfMemoryError
Resource Resource Resource Resource
Merge Resources TaskAndroid Gradle v0.4
![Page 187: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/187.jpg)
Resource Resource Resource Resource
Merge Resources TaskAndroid Gradle v0.5.4
MergedResource
![Page 188: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/188.jpg)
Use extra properties to configure on a per-flavor
basis
![Page 189: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/189.jpg)
build.gradle
android { productFlavors { paid { ext.crashlyticsEnabled = true } free { ext.crashlyticsEnabled = false } } }
![Page 190: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/190.jpg)
build.gradle
android.productFlavors.each { ext.crashlyticsEnabled }
![Page 191: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/191.jpg)
build.gradle
apply plugin: ‘com.android.application’
android { productFlavors { paid { } } }
apply plugin: ‘io.fabric’apply plugin: ‘com.android.application’
android { productFlavors { paid { } } }
apply plugin: ‘io.fabric’apply plugin: ‘io.fabric’
Finally, a note on the timing of things.
When this build script runs, each line runs
crashlytics doesn’t look at what flavors you have until AFTER the android block runs
![Page 192: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/192.jpg)
build.gradle
apply plugin: ‘com.android.application’
android { productFlavors { paid { } } }
android.applicationVariants.all { variant -> // Rename package name }
apply plugin: ‘io.fabric’apply plugin: ‘com.android.application’
android { productFlavors { paid { } } }
apply plugin: ‘io.fabric’apply plugin: ‘io.fabric’
android.applicationVariants.allandroid.applicationVariants.all { variant -> // Rename package name }
Now, if you add a line of code that does something for each application variant, e.g., rename a package, does our plugin see it?
First, all the code runs, our plugin sets up a hook to do something after the android block runs.
Then, you set up a hook, the applicationsVariants call actually doesn’t run until after the entire build.grade script is configured
![Page 193: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/193.jpg)
plugin code.groovy
afterEvaluate { // READ PACKAGE NAME }afterEvaluate { // CHANGE PACKAGE NAME }
![Page 194: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/194.jpg)
apply plugin: ‘com.android.application’
android { productFlavors { paid { } } }
apply plugin: ‘io.fabric’
android.applicationVariants.all { variant -> // Rename package name }
![Page 195: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/195.jpg)
Crashlytics used HttpClient 4.3
![Page 196: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/196.jpg)
Android Gradle used HttpClient 4.1.1
![Page 197: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/197.jpg)
command line
./gradlew assemble
![Page 198: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/198.jpg)
[ERROR] Caused by: java.lang.NoSuchMethodError:
org.apache.http.entity.mime.content.StringBody.<init>
![Page 199: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/199.jpg)
Use the Shadow Plugin: Bytecode Manipulation
![Page 200: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/200.jpg)
apply plugin: ‘shadow’
Avail. on github john engelman
![Page 201: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/201.jpg)
org.apache -> com.crashlytics.org.apache
![Page 202: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/202.jpg)
shadow { relocation { … } }
![Page 203: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/203.jpg)
shadow { relocation { pattern = 'org.apache' shadedPattern = ‘com.crashlytics’ + pattern } }
![Page 204: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/204.jpg)
So here’s a screenshot inside our plugin JAR file, you can see apache commons at a redirected path
![Page 205: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/205.jpg)
Showed what Gradle is really doing.
• created tasks • made a multi-app project • wrote a plugin
Ok, so tonight I showed you what gradle is really doing.
We created tasks, made a multi-app project, and wrote a plugin
![Page 206: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/206.jpg)
Simple PossiblePossimple
Ok, so hopefully, by now, we’ve bridged the gap between simple and possible, if not forming simpossible, at least forming possimple?
![Page 207: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/207.jpg)
Jake Ouellette @jakeout
Senior Software Engineer Twitter
![Page 208: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/208.jpg)
![Page 209: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/209.jpg)
Gradle Plugin Syntax has growing pains
![Page 210: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/210.jpg)
buildscript { … }
![Page 211: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/211.jpg)
buildscript { repositories { … } }
We have a staging repository for our plugins I wanted to test against,
![Page 212: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/212.jpg)
def crashlytics = … buildscript { … }
So I thought maybe I could set a variable, visible by both.
![Page 213: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/213.jpg)
No such property: crashlytics
![Page 214: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/214.jpg)
Turns out the build script block is magic. Literally, what he means is it’s as though it’s executed as another file, configuring the build script.
![Page 215: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/215.jpg)
./gradlew assemble -Dcrashlytics=…
One easy workaround
![Page 216: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/216.jpg)
buildscript { repositories { maven { url System.getProperty(“crashlytics”) } } }
repositories { maven { url System.getProperty(“crashlytics”) } }
![Page 217: gradle user to addict sf - files.meetup.comfiles.meetup.com/1715787/gradle_user_to_addict_notes.pdf · build.gradle apply plugin: ‘com.android.application’ * Ok, so the first](https://reader033.vdocuments.site/reader033/viewer/2022052019/6032adc302842e0d2227c4dd/html5/thumbnails/217.jpg)
buildScript { ext.crashlytics = … }
buildscript.ext.crashlytics
Remember, build script is just an object like everything else