gradle user to addict sf - Meetupfiles.meetup.com/1715787/gradle_user_to_addict.pdf · build.gradle...

Post on 31-Jan-2018

238 views 0 download

Transcript of gradle user to addict sf - Meetupfiles.meetup.com/1715787/gradle_user_to_addict.pdf · build.gradle...

Gradle: From User to Addict

Simple Possible

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.+' }

I want to add a task!

build.gradle

apply plugin: ‘com.android.application’

build.gradle

android { compileSdkVersion 19 … }

build.gradle

dependencies { compile 'com.android.support:appcompat-v7:19.+' compile fileTree(dir: 'libs', include: ['*.jar']) }

build.gradle

dependencies { compile 'com.android.support:appcompat-v7:19.+' compile fileTree(dir: 'libs', include: ['*.jar']) }

build.gradle

dependencies { compile 'com.android.support:appcompat-v7:19.+' compile fileTree(dir: 'libs', include: ['*.jar']) }

What is the Gradle language really doing?

How do I use it?

• create tasks • make a multi-app project • write builds once, reuse

Simple PossibleSimpossible

dependencies { compile 'com.crashlytics.android:crashlytics:1.+' }

build.gradle

build.gradle

android { dependencies { compile 'com.crashlytics.android:crashlytics:1.+' } }

build.gradle

android { … }

dependencies { compile 'com.crashlytics.android:crashlytics:1.+' }

{ }

example.java

new Runnable() { public void run() { … } }

example.java

final int time = System.currentTimeMillis() new Runnable() { public void run() { … } }

example.java

final int time = System.currentTimeMillis() new Runnable() { public void run() { System.out.println(time); } }

example.java

final int time = System.currentTimeMillis()

{ System.out.println(time); }

build.gradle

android { … }

build.gradle

android({ … })

android() =

android.apply()

android.apply(Closure configuration)

build.gradle

android.apply({ … })

build.gradle

android.apply({ it })

build.gradle

println(android.getClass()) android { println(it.getClass()) }

command line output

class com.android.build.gradle.AppExtension_Decorated class com.android.build.gradle.AppExtension_Decorated

println(android.getClass()) android { println(it.getClass()) }

build.gradle

closure.delegate = this

build.gradle

android.apply({ it })

build.gradle

android { defaultConfig { minSdkVersion 8 } }

defaultConfig.apply(Closure configuration)

build.gradle

android { defaultConfig { minSdkVersion 8 } }

build.gradle

android { it.defaultConfig { minSdkVersion 8 } }

build.gradle

println(android.defaultConfig.getClass()) android { defaultConfig { println(it.getClass()) } }

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()) } }

build.gradle

android { defaultConfig { minSdkVersion 8 } }

build.gradle

android { defaultConfig { minSdkVersion 8 println(minSdkVersion) } }

build.gradle

android { defaultConfig { setMinSdkVersion(8) } }

build.gradle

android.defaultConfig.minSdkVersion = 8

build.gradle

android.defaultConfig.minSdkVersion

build.gradle

android { dependencies { compile 'com.crashlytics.android:crashlytics:1.+' } }

build.gradle

repositories { maven { url “example" } }

def maven(Closure closure)

MavenArtifactRepository maven(Closure closure)

build.gradle

repositories { println maven { url "example" }.url }

command line output

example

MavenArtifactRepository maven(Closure closure)

mutation

build.gradle

repositories { maven { url “example" maven { url "example2" } } }

build.gradle (with annotation)

repositories { SEARCHED NEXT maven { SEARCHED FIRST } }

build.gradle

android { dependencies { compile 'com.crashlytics.android:crashlytics:1.+' } }

build.gradle

SEARCHED SECOND android { SEARCHED FIRST

dependencies { compile 'com.crashlytics.android:crashlytics:1.+' } }

Adding a task

Adding a task: Cache each build.gradle with a

timestamp

build.gradle

task copyTask(type: Copy)

build.gradle

task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

build.gradle

task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

build.gradle

task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

build.gradle

task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

command line output

:app:copyTask UP-TO-DATE

BUILD SUCCESSFUL

ideal directory listing

buildCache/ build.gradle1399906750071 build.gradle1401281449000 build.gradle1401290623000

command line output

> ls app/buildCache …

build.gradle

task copyTask

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.

build.gradle

task copyTask

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.

build.gradle

task copyTask

build.gradle

task(“copyTask”)

build.gradle

task copyTask(type: Copy)

build.gradle

task(“copyTask”, [“type” : “Copy”])

groovy map

Task

task(“copyTask”, [“type” : “Copy”])

build.gradle

def apply(Closure closure)

build.gradle

task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

command line output

:app:copyTask

command line output

> ls app/buildCache

build.gradle1399906750071

build.gradle

task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

build.gradle

task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

<< { }

.doLast({ })

Executes?

command line

./gradlew assemble

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)

build.gradle

task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

build.gradle

task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

build.gradle

task copyTask(type: Copy) { … } << { println(“Execution”) }

build.gradle

task copyTask(type: Copy) { … } << { println(“Execution”) }

build.gradle

task copyTask << { // Roll my own }

build.gradle

Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis()))

build.gradle

Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis()))

build.gradle

Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis()))

build.gradle

task copyTask << { Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis())) }

TASK

TASKTASK

TASK

@Input @InputDirectory @InputFile @InputFiles

TASK

@OutputDirectories @OutputDirectory @OutputFile @OutputFiles

TASK

TASK

TASK

TASK

TASKTASK

TASK TASK EXECUTED

TASKTASK

TASK TASK EXECUTED

TASKTASK

TASK UP-TO-DATE

build.gradle

task copyTask { inputs.file new File("build.gradle") // outputs } << { // Java file copy code }

build.gradle

task copyTask { inputs.file new File("build.gradle") // outputs } << { // Java file copy code }

build.gradle

task copyTask { inputs.file new File("build.gradle"), // outputs } << { // Java file copy code }

build.gradle

task example

HAS NO INPUTS YET, ALWAYS RUNS

build.gradle

task copyTask(type: Copy)

COPY TASK HAS EMPTY SET OF THINGS TO COPY

build.gradle

task copyTask(type: Copy) << {

}

COPY TASK HAS EMPTY SET OF THINGS TO COPY

// CODE WILL NOT EXECUTE

build.gradle

task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

Good

build.gradle

task copyTask(type: Copy) << { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

BAD

build.gradle

task copyTask << { Files.copy( new File("build.gradle"), new File(“${project.projectDir}/buildCache/build.gradle" + System.currentTimeMillis())) }

Weird

command line

./gradlew tasks

command line output

Build tasks ----------- assemble assembleDebug assembleDebugTest assembleRelease build buildDependents buildNeeded clean

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

Compile

Dependency Graph

PreBuild Assemble

call?

Compile Assemble

Compile Assemble

assemble.dependsOn(task)task.dependsOn(compile)

AssembleCompile

task.onlyIf { condition }

Compile Task

Dependency GraphCrashlytics Task

Crashlytics Task

Multi-Project Builds

opensource/library/

day-job/app/

development/app/library/

command line

cd development/app/

command line

ant build

development/app/library/

App

Project Root Library

root/app/library/build.gradle

development/

command line

cd development/root

command line

./gradlew app:assemble

build.gradle

dependencies { compile project(‘:library’) }

include ‘library’ include ‘app’

settings.gradle

opensource/library/

day-job/app/

AppLibrary

Installs Into Depends

OnLocal Maven Cache

build.gradle

repositories { mavenLocal() }

app library

ExecutionC

onfiguration./gradlew customBuild

Tasks Executed

Tasks Executed

app library Maven Local

ExecutionC

onfigurationEXEC: ./gradlew :library:install

Tasks Executed

app library Maven Local

ExecutionC

onfiguration

Tasks Executed

EXEC: ./gradlew :app:install

AppProject Root

Library

app library

ExecutionC

onfiguration

Tasks Executed

EXEC: ./gradlew :app:install

Tasks Executed

-P

./gradlew :app:assemble -PrunOnCi=false

if (runOnCi) { … }

build.gradle

App

App2

Project Root

Library

./gradlew :app:assemble -PrunOnCi=false

app library

ExecutionC

onfiguration

Tasks Executed

EXEC: ./gradlew :app:assemble -PrunOnCi=false

Tasks Executed

app2

BUILD SUCCESSFUL

App

App2

Project Root

Library

./gradlew :app2:assemble

app library

ExecutionC

onfigurationEXEC: ./gradlew :app2:assemble

app2

BUILD FAILED

BUILD FAILED

app library

ExecutionC

onfigurationEXEC: ./gradlew :app2:assemble

app2

BUILD FAILED

if (runOnCi)

if (hasProperty(runOnCi)) { … }

org.gradle.configureondemand=true

gradle.properties

app library

ExecutionC

onfigurationEXEC: ./gradlew :app2:assemble

Tasks Executed

app2

Tasks Executed

Code Reuse

build.gradle

task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

Good

build.gradle

task copyTask(type: Copy) { from ‘build.gradle’ into ‘buildCache’ rename {it + System.currentTimeMillis()} }

Good?

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()} }

build.gradle

class CacheBuild extends Copy {

}

build.gradle

class CacheBuild extends Copy { { … } }

plain ol’ java initializer

build.gradle

class CacheBuild extends Copy { { from('build.gradle'); into('buildCache'); rename({ it + System.currentTimeMillis() }) } }

build.gradle

task cache(type:CacheBuild)

AppProject Root

Library

App

Project Root

Library

App

Project Root

Library

Custom Code

App

Project Root

Library

App

Project Root

Library

buildsrc/src/main/groovy/CacheBuild.groovy

Custom Code

App

Project Root

Library

buildsrc/src/main/groovy/CacheBuild.groovy

App

Project Root

Library

buildsrc/src/main/groovy/CacheBuild.groovy

day-job/app

AppCustom Task

Installs Into Depends

OnLocal Maven Cache

build.gradle

assemble.dependsOn(‘cache’)

build.gradle

apply plugin: 'BuildCache'

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.

build.gradle

apply([‘plugin’ : ‘BuildCache’])

User’s Build Scriptapply plugin: ‘BuildCache’

plugin codevoid apply(Project project)

calls

build.gradle

class BuildCache implements Plugin<Project> {

void apply(Project project) {

Inject into dependency graph

compile.dependsOn(buildCache)

build.gradle

buildCache = … android.applicationVariants.all { assemble.dependsOn(buildCache) }

build.gradle

ExampleActivity.onClick()a.b()com.crashlytics.obfuscation.

Building on top of Android

Pay attention to Android Gradle updates

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"

java.lang.OutOfMemoryError

Resource Resource Resource Resource

Merge Resources TaskAndroid Gradle v0.4

Resource Resource Resource Resource

Merge Resources TaskAndroid Gradle v0.5.4

MergedResource

Use extra properties to configure on a per-flavor

basis

build.gradle

android { productFlavors { paid { ext.crashlyticsEnabled = true } free { ext.crashlyticsEnabled = false } } }

build.gradle

android.productFlavors.each { ext.crashlyticsEnabled }

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’

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 }

plugin code.groovy

afterEvaluate { // READ PACKAGE NAME }afterEvaluate { // CHANGE PACKAGE NAME }

apply plugin: ‘com.android.application’

android { productFlavors { paid { } } }

apply plugin: ‘io.fabric’

android.applicationVariants.all { variant -> // Rename package name }

Crashlytics used HttpClient 4.3

Android Gradle used HttpClient 4.1.1

command line

./gradlew assemble

[ERROR] Caused by: java.lang.NoSuchMethodError:

org.apache.http.entity.mime.content.StringBody.<init>

Use the Shadow Plugin: Bytecode Manipulation

apply plugin: ‘shadow’

org.apache -> com.crashlytics.org.apache

shadow { relocation { … } }

shadow { relocation { pattern = 'org.apache' shadedPattern = ‘com.crashlytics’ + pattern } }

Showed what Gradle is really doing.

• created tasks • made a multi-app project • wrote a plugin

Simple PossiblePossimple

Jake Ouellette @jakeout

Senior Software Engineer Twitter

Gradle Plugin Syntax has growing pains

buildscript { … }

buildscript { repositories { … } }

def crashlytics = … buildscript { … }

No such property: crashlytics

./gradlew assemble -Dcrashlytics=…

buildscript { repositories { maven { url System.getProperty(“crashlytics”) } } }

repositories { maven { url System.getProperty(“crashlytics”) } }

buildScript { ext.crashlytics = … }

buildscript.ext.crashlytics