gradle plugins, take it to the next level

116
Gradle Plugins Take It To The Next Level

Upload: eyal-lezmy

Post on 17-Jan-2017

576 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Gradle plugins, take it to the next level

Gradle PluginsTake It To The Next Level

Page 3: Gradle plugins, take it to the next level

Agenda

01

Basics

02

Gradly DSL

03

Android Gradle Plugin

04

Test it

05

Publish it

{ }

Page 4: Gradle plugins, take it to the next level

BASICS01

Page 5: Gradle plugins, take it to the next level

Build scriptsYour build.gradle file

Script pluginsThe customization you start writing

Binary pluginsThe code I want you to write

BASICS

Gradle Plugins Types

Page 6: Gradle plugins, take it to the next level

Is a piece of work for a buildCompiling a class, generating javadoc, ...

Can be manipulateddoFirst, doLast

Can inherits from anothertype

Can depend on another taskdependsOn, finalizedBy

BASICS

The Gradle Task

Page 7: Gradle plugins, take it to the next level

Is a piece of work for a buildCompiling a class, generating javadoc, ...

Can be manipulateddoFirst, doLast

Can inherits from anothertype

Can depend on another taskdependsOn, finalizedBy

BASICS

The Gradle Task

A build = A task graph

Page 8: Gradle plugins, take it to the next level

Is a Gradle projectBasically, a Groovy project

It containsA build.gradleA plugin classA descriptorOne or several tasksAn extension

ExamplesJava, Groovy, Maven, Android plugin

BASICS

The Binary Plugin

Page 9: Gradle plugins, take it to the next level

BASICS

InitializationChoose project(s) to build

ConfigurationExecute build.gradleBuild task graph

ExecutionExecute task chain

Gradle build

lifecycle

Page 10: Gradle plugins, take it to the next level

BASICS

Project evaluationbeforeEvaluateafterEvaluate

Task GraphwhenTaskAddedwhenReadybeforeTaskafterTask

The lifecycleevents

Page 11: Gradle plugins, take it to the next level

GRADLY DSL02

Page 12: Gradle plugins, take it to the next level

EXTEND IT

ReadableThe user can easily understand

FlexibleExpress complex situations, on a simple way

IntuitiveThe user can easily configure

TalkativeHelp the user solve his problems

What makes a good

DSL

Page 13: Gradle plugins, take it to the next level

Use nested extensions

Make it readable

Page 14: Gradle plugins, take it to the next level

READABLE

//create the extensionproject.extensions.create(“myExtension”,MyExtension, arg1, arg2)

Plugin class

Page 15: Gradle plugins, take it to the next level

class MyExtension {

String myInfo List<String> myList

MyExtension(def arg1, def arg2) {

myInfo = “Default String” myList = [“default”, “list”]

}

}

READABLE

Extension class

Page 16: Gradle plugins, take it to the next level

READABLE

apply: “myPlugin”

...

myExtension {

myInfo “New String” myList [“new”, “list”]

}

build.gradle

Page 17: Gradle plugins, take it to the next level

READABLE

genymotion {

//configure genymotion configLicenseServer true configLicenseServerAddress “192.168.1.33” configSdkPath “/home/me/Android/sdk” configUseCustomSdk true

//launch devices device(“Nexus5”, “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920”) device(“Nexus4”, “Google Nexus 4 - 4.4.4 - API 19 - 768x1280”)}

build.gradle

Page 18: Gradle plugins, take it to the next level

READABLE

genymotion {

config { licenseServer true licenseServerAddress “192.168.1.33” sdkPath “/home/me/Android/sdk” useCustomSdk true }

//launch devices device(“Nexus5”, “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920”) device(“Nexus4”, “Google Nexus 4 - 4.4.4 - API 19 - 768x1280”)}

build.gradle

Page 19: Gradle plugins, take it to the next level

//create the extensionproject.extensions.create(“genymotion”,GenymotionExtension)//create the nested extensionproject.genymotion.extensions.create(“config”,GenymotionConfig)

READABLE

Plugin class

Page 20: Gradle plugins, take it to the next level

Use Containers

Make it flexible

Page 21: Gradle plugins, take it to the next level

FLEXIBLE

genymotion { //launch devices device(“Nexus5”, “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920”) device(“Nexus4”, “Google Nexus 4 - 4.4.4 - API 19 - 768x1280”)}

build.gradle

Page 22: Gradle plugins, take it to the next level

FLEXIBLE

genymotion {

device(“Nexus5”, “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920”, 1920, 1080, “xxhdpi”, ...) device(“Nexus4”, “Google Nexus 4 - 4.4.4 - API 19 - 768x1280”, 1280, 800, “xhdpi”, ...))}

build.gradle

Page 23: Gradle plugins, take it to the next level

FLEXIBLE

genymotion {

device(“Nexus5”, “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920”, 1920, 1080, “xxhdpi”, [“path/to/apk”, “path/to/apk2”], [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/database.db”:”/tmp/], true)

device(“Nexus4”, “Google Nexus 4 - 4.4.4 - API 19 - 768x1280”, 1280, 800, “xhdpi”, “path/to/apk”, [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”], true)}

build.gradle

Page 24: Gradle plugins, take it to the next level

FLEXIBLE

build.gradlegenymotion {

device(name: “Nexus5”, template: “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920”, width: 1920, height: 1080, density: “xxhdpi”, install: [“path/to/apk”, “path/to/apk2”], pullAfter: [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”], stopWhenFinished: true)

device(name: “Nexus4”, template: “Google Nexus 4 - 4.4.4 - API 19 - 768x1280”, ...)}

Page 25: Gradle plugins, take it to the next level

FLEXIBLE

genymotion {

devices { Nexus5 { template “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920” width 1920 height 1080 density “xxhdpi” install [“path/to/apk”, “path/to/apk2”] pullAfter [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”] stopWhenFinished true } Nexus4 { ... } }}

build.gradle

Page 26: Gradle plugins, take it to the next level

FLEXIBLE

genymotion {

devices { Nexus5 { template “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920” width 1920 height 1080 density “xxhdpi” install [“path/to/apk”, “path/to/apk2”] pullAfter [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”] stopWhenFinished true } Nexus4 { ... } }}

build.gradle

project.genymotion.devices

Page 27: Gradle plugins, take it to the next level

FLEXIBLE

genymotion {

devices { Nexus5 { template “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920” width 1920 height 1080 density “xxhdpi” install [“path/to/apk”, “path/to/apk2”] pullAfter [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”] stopWhenFinished true } Nexus4 { ... } }}

build.gradle

project.genymotion.devices(Closure c)

Page 28: Gradle plugins, take it to the next level

FLEXIBLE

genymotion {

devices { Nexus5 { template “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920” width 1920 height 1080 density “xxhdpi” install [“path/to/apk”, “path/to/apk2”] pullAfter [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”] stopWhenFinished true } Nexus4 { ... } }}

build.gradle

project.genymotion.devices(Closure c)

Add ‘Nexus4’Add ‘Nexus5’

Page 29: Gradle plugins, take it to the next level

FLEXIBLE

genymotion {

devices { Nexus5 { template “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920” width 1920 height 1080 density “xxhdpi” install [“path/to/apk”, “path/to/apk2”] pullAfter [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”] stopWhenFinished true } Nexus4 { ... } }}

build.gradle

project.genymotion.devices(Closure c)

Add ‘Nexus4’Add ‘Nexus5’

Container

Page 30: Gradle plugins, take it to the next level

FLEXIBLE

class GenymotionPlugin implements Plugin<Project> {

void apply(Project project) {

def instantiator = project.gradle.services.get(Instantiator) def deviceLaunches = project.container(DeviceLaunch,

new DeviceLaunchFactory(instantiator))

project.extensions.create(“genymotion”,GenymotionExtension, project, deviceLaunches)

project.afterEvaluate { project.genymotion.injectTasks() }

}}

The Plugin class

Page 31: Gradle plugins, take it to the next level

FLEXIBLE

class GenymotionPlugin implements Plugin<Project> {

void apply(Project project) {

def instantiator = project.gradle.services.get(Instantiator) def deviceLaunches = project.container(DeviceLaunch,

new DeviceLaunchFactory(instantiator))

project.extensions.create(“genymotion”,GenymotionExtension, project, deviceLaunches)

project.afterEvaluate { project.genymotion.injectTasks() }

}}

The Plugin class

Page 32: Gradle plugins, take it to the next level

FLEXIBLE

class GenymotionPlugin implements Plugin<Project> {

void apply(Project project) {

def instantiator = project.gradle.services.get(Instantiator) def deviceLaunches = project.container(DeviceLaunch,

new DeviceLaunchFactory(instantiator))

project.extensions.create(“genymotion”,GenymotionExtension, project, deviceLaunches)

project.afterEvaluate { project.genymotion.injectTasks() }

}}

The Plugin class

Create a container for DeviceLaunch

Page 33: Gradle plugins, take it to the next level

FLEXIBLE

class GenymotionPlugin implements Plugin<Project> {

void apply(Project project) {

def instantiator = project.gradle.services.get(Instantiator) def deviceLaunches = project.container(DeviceLaunch,

new DeviceLaunchFactory(instantiator))

project.extensions. create(“genymotion”,GenymotionExtension, project, deviceLaunches)

project.afterEvaluate { project.genymotion.injectTasks() }

}}

The Plugin class

Create the extension

Page 34: Gradle plugins, take it to the next level

FLEXIBLE

class GenymotionPlugin implements Plugin<Project> {

void apply(Project project) {

def instantiator = project.gradle.services.get(Instantiator) def deviceLaunches = project.container(DeviceLaunch,

new DeviceLaunchFactory(instantiator))

project.extensions.create(“genymotion”,GenymotionExtension, project, deviceLaunches)

project.afterEvaluate { project.genymotion.injectTasks() }

}}

The Plugin class

Add the DeviceLaunch container

Page 35: Gradle plugins, take it to the next level

FLEXIBLE

class GenymotionPlugin implements Plugin<Project> {

void apply(Project project) {

def instantiator = project.gradle.services.get(Instantiator) def deviceLaunches = project.container(DeviceLaunch,

new DeviceLaunchFactory(instantiator))

project.extensions.create(“genymotion”,GenymotionExtension, project, deviceLaunches)

project.afterEvaluate { project.genymotion.injectTasks() }

}}

The Plugin class

Page 36: Gradle plugins, take it to the next level

FLEXIBLE

The Extension classclass GenymotionExtension {

NamedDomainObjectContainer<DeviceLaunch> deviceLaunches

GenymotionExtension(Project project, deviceLaunches) { this.project = project this.deviceLaunches = deviceLaunches }

def devices(Closure closure) { deviceLaunches.configure(closure) }

...

Page 37: Gradle plugins, take it to the next level

FLEXIBLE

The Extension classclass GenymotionExtension {

NamedDomainObjectContainer<DeviceLaunch> deviceLaunches

GenymotionExtension(Project project, deviceLaunches) { this.project = project this.deviceLaunches = deviceLaunches }

def devices(Closure closure) { deviceLaunches.configure(closure) }

...

DeviceLaunch container

Page 38: Gradle plugins, take it to the next level

FLEXIBLE

The Extension classclass GenymotionExtension {

NamedDomainObjectContainer<DeviceLaunch> deviceLaunches

GenymotionExtension(Project project, deviceLaunches) { this.project = project this.deviceLaunches = deviceLaunches }

def devices(Closure closure) { deviceLaunches.configure(closure) }

...

We get it from plugin apply()

Page 39: Gradle plugins, take it to the next level

FLEXIBLE

The Extension classclass GenymotionExtension {

NamedDomainObjectContainer<DeviceLaunch> deviceLaunches

GenymotionExtension(Project project, deviceLaunches) { this.project = project this.deviceLaunches = deviceLaunches }

def devices(Closure closure) { deviceLaunches.configure(closure) }

... Create the syntax genymotion.devices{ }

Page 40: Gradle plugins, take it to the next level

FLEXIBLE

The Extension classclass GenymotionExtension {

NamedDomainObjectContainer<DeviceLaunch> deviceLaunches

GenymotionExtension(Project project, deviceLaunches) { this.project = project this.deviceLaunches = deviceLaunches }

def devices(Closure closure) { deviceLaunches.configure(closure) }

... Let Gradle add all the declared items

Page 41: Gradle plugins, take it to the next level

FLEXIBLE

The Extension classclass DeviceLaunchFactory implements NamedDomainObjectFactory<DeviceLaunch> {

final Instantiator instantiator

public DeviceLaunchFactory(Instantiator instantiator) { this.instantiator = instantiator }

@Override DeviceLaunch create(String name) { return instantiator.newInstance(DeviceLaunch.class, name) }}

Page 42: Gradle plugins, take it to the next level

FLEXIBLE

class DeviceLaunchFactory implements NamedDomainObjectFactory<DeviceLaunch> {

final Instantiator instantiator

public DeviceLaunchFactory(Instantiator instantiator) { this.instantiator = instantiator }

@Override DeviceLaunch create(String name) { return instantiator.newInstance(DeviceLaunch.class, name) }}

The Extension class

Page 43: Gradle plugins, take it to the next level

INTUITIVE

The modelclass DeviceLaunch {

String name

DeviceLaunch(String name) { this.name = name }

...

}

Page 44: Gradle plugins, take it to the next level

methods > properties

Make it intuitive

Page 45: Gradle plugins, take it to the next level

INTUITIVE

genymotion { devices {

Nexus5 { template “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920” width 1920 height 1080 density “xxhdpi” install [“path/to/apk”, “path/to/apk2”] pullAfter [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”] stopWhenFinished true }

}}

build.gradle

Page 46: Gradle plugins, take it to the next level

INTUITIVE

genymotion { devices {

Nexus5 { template “Google Nexus 5 - 5.0.0 - API 21 - 1080x1920” width 1920 height 1080 density “xxhdpi” install [“path/to/apk”, “path/to/apk2”] pullAfter [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”] stopWhenFinished true }

}}

build.gradle

Page 47: Gradle plugins, take it to the next level

INTUITIVE

build.gradle

install [“path/to/apk”, “path/to/apk2”]pullAfter [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”]

Page 48: Gradle plugins, take it to the next level

INTUITIVE

install “path/to/apk”, “path/to/apk2”, ...

pullAfter from:“/sdcard/prop.txt”, to:”/tmp/”pullAfter from:“/sdcard/data.db”, to:”/tmp/”

build.gradle

install [“path/to/apk”, “path/to/apk2”]pullAfter [“/sdcard/prop.txt”:”/tmp/”, “/sdcard/data.db”:”/tmp/”]

Page 49: Gradle plugins, take it to the next level

INTUITIVE

The Extension class (1/2)class GenymotionExtension {

private List<String> install = []

def install(String... paths) { install.addAll(paths) }

def setInstall(String... paths) { install.clear() install.addAll(paths) }

...

Page 50: Gradle plugins, take it to the next level

INTUITIVE

The Extension class (2/2)

...

private def pullAfter = [:]

def pullAfter(String from, String to) { pullAfter.put(from, to) }

...

}

Page 51: Gradle plugins, take it to the next level

Counterbalance the lack of autocompletion

Make it talkative

Page 52: Gradle plugins, take it to the next level

TALKATIVE

No suggestion in IDEs

No integrated documentation

No Discoverability

Page 53: Gradle plugins, take it to the next level

TALKATIVE

Log is your voiceBut respect the Gradle conventioned levels

Errors are part of documentationAnticipate the mistakes and deliver the appropriate explicit message

Be Talkative

Page 54: Gradle plugins, take it to the next level

DANCE WITH THE ANDROID GRADLE PLUGIN

03

Page 55: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

android.applicationVariantsOnly for the app plugin

android.libraryVariantsOnly for the library plugin

android.testVariantsFor both plugins

The entry points

Page 56: Gradle plugins, take it to the next level

android.applicationVariants.all { variant -> ....

}

ANDROID GRADLE PLUGIN

Page 57: Gradle plugins, take it to the next level

android.applicationVariants.all { variant -> ....

}

ANDROID GRADLE PLUGIN

Call it after the evaluation

Page 58: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

tools.android.com/tech-docs/new-build-system/user-guide

“Manipulating tasks” sectionDetails variant attributes

The documentation

Page 59: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

Not up-to-date~30% wrong information

But a good entry point

The documentation

Page 60: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

The source code100% accurateThe real

documentation

Page 61: Gradle plugins, take it to the next level

$ git clone https://android.googlesource.com/platform/tools/base

$ git checkout tags/gradle_1.3.1

ANDROID GRADLE PLUGIN

Page 62: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

Your debuggerBrowsing through the project on-the-fly

Integration tests...... are highly recommended

The real documentation

part 2

Page 63: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

Avoid using explicit values“connectedAndroidTest”, ...

Use dedicated properties

Internals are changing a lot

Page 64: Gradle plugins, take it to the next level

android.testVariants.all { variant -> Task testTask = variant.connectedAndroidTest

... }

ANDROID GRADLE PLUGIN

Page 65: Gradle plugins, take it to the next level

android.testVariants.all { variant -> Task testTask = variant.connectedAndroidTest

... }

ANDROID GRADLE PLUGIN

$ gradle test --stacktrace

Page 66: Gradle plugins, take it to the next level

android.testVariants.all { variant -> Task testTask = variant.connectedAndroidTest

... }

ANDROID GRADLE PLUGIN

$ gradle test --stacktrace

groovy.lang.MissingPropertyException: Could not find property 'connectedAndroidTest'

...BUILD FAILED

Page 67: Gradle plugins, take it to the next level

variant.variantData.connectedTestTask = "task ':connectedDebugAndroidTest'"

ANDROID GRADLE PLUGIN

Using the debugger

Page 68: Gradle plugins, take it to the next level

variant.variantData.connectedTestTask = "task ':connectedDebugAndroidTest'"

ANDROID GRADLE PLUGIN

@Overridepublic DefaultTask getConnectedInstrumentTest() { return variantData.connectedTestTask;}

Using the debugger

TestVariantImpl.java

Page 69: Gradle plugins, take it to the next level

variant.variantData.connectedTestTask = "task ':connectedDebugAndroidTest'"

ANDROID GRADLE PLUGIN

@Overridepublic DefaultTask getConnectedInstrumentTest() { return variantData.connectedTestTask;}

Using the debugger

TestVariantImpl.java

Task testTask = variant.connectedInstrumentTest

The good API

Page 70: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

connectedAndroidTest1.0.0 05/12/14

Page 71: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

connectedAndroidTest

connectedAndroidTestDebug

1.0.0

1.2.0

05/12/14

23/04/15

5 months

Page 72: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

connectedAndroidTest

connectedAndroidTestDebug

connectedDebugAndroidTest

1.0.0

1.2.0

1.3.0

05/12/14

23/04/15

01/07/15

5 months

3 months

Page 73: Gradle plugins, take it to the next level

ANDROID GRADLE PLUGIN

Do not depend on a specific release

Integration tests...... are highly recommended

Internals are changing a lot

Page 74: Gradle plugins, take it to the next level

TEST IT04

Page 75: Gradle plugins, take it to the next level

Very simpleAs simple as Groovy is

Groovy is your best friendVery easy to mock

Junit & coAs anybody knows

TEST IT

Gradle project testing

Page 76: Gradle plugins, take it to the next level

ProjectBuilderTo create a project stub

EvaluateTo execute your build script

TEST IT

A few specificities

Page 77: Gradle plugins, take it to the next level

TEST IT

Our buid.gradle

...

repositories { mavenCentral()}

dependencies { testCompile 'junit:junit:4.11'}

...

Page 78: Gradle plugins, take it to the next level

TEST IT

Our buid.gradle

...

repositories { mavenCentral()}

dependencies { testCompile 'junit:junit:4.11'}

...

Adding maven central repository

Page 79: Gradle plugins, take it to the next level

TEST IT

Our buid.gradle

...

repositories { mavenCentral()}

dependencies { testCompile 'junit:junit:4.11'}

...

Adding junit as testing dependency

Page 80: Gradle plugins, take it to the next level

Now, test the extension

Page 81: Gradle plugins, take it to the next level

TEST IT

Your first test!

class GenymotionPluginTest {

@Test public void canAddGenymotionExtension() { Project project = ProjectBuilder.builder().build() project.apply plugin: 'genymotion'

assert project.genymotion instanceof GenymotionExtension }}

Page 82: Gradle plugins, take it to the next level

TEST IT

Your first test!

class GenymotionPluginTest {

@Test public void canAddGenymotionExtension() { Project project = ProjectBuilder.builder().build() project.apply plugin: 'genymotion'

assert project.genymotion instanceof GenymotionExtension }}

Stub a Gradle project

Page 83: Gradle plugins, take it to the next level

TEST IT

Your first test!

class GenymotionPluginTest {

@Test public void canAddGenymotionExtension() { Project project = ProjectBuilder.builder().build() project.apply plugin: 'genymotion'

assert project.genymotion instanceof GenymotionExtension }}

Apply our plugin

Page 84: Gradle plugins, take it to the next level

TEST IT

Your first test!

class GenymotionPluginTest {

@Test public void canAddGenymotionExtension() { Project project = ProjectBuilder.builder().build() project.apply plugin: 'genymotion'

assert project.genymotion instanceof GenymotionExtension }}

Test our extension exists

Page 85: Gradle plugins, take it to the next level

Now, test the task

Page 86: Gradle plugins, take it to the next level

TEST IT

Your second test!

class GenymotionPluginTest {

@Test public void canAddGenymotionTask() { Project project = ProjectBuilder.builder().build() project.apply plugin: 'genymotion'

assert project.tasks.genymotionTask instanceof GenymotionTask }}

We initialize our project

Page 87: Gradle plugins, take it to the next level

TEST IT

Your second test!

class GenymotionPluginTest {

@Test public void canAddGenymotionTask() { Project project = ProjectBuilder.builder().build() project.apply plugin: 'genymotion'

assert project.tasks.genymotionTask instanceof GenymotionTask }}

We test the task

Page 88: Gradle plugins, take it to the next level

TEST IT

Run your second test$ gradle test --stacktrace --debug

Page 89: Gradle plugins, take it to the next level

TEST IT

Run your second test$ gradle test --stacktrace --debug

...

com.genymotion.GenymotionPluginTest > canAddGenymotionTask FAILEDMissingPropertyException:Could not find property 'genymotionTask' on task set

...

BUILD FAILED

Page 90: Gradle plugins, take it to the next level

TEST IT

Run your second test$ gradle test --stacktrace --debug

...

com.genymotion.GenymotionPluginTest > canAddGenymotionTask FAILEDMissingPropertyException: Could not find property 'genymotionTask' on task set

...

BUILD FAILED

Our task is not created

Page 91: Gradle plugins, take it to the next level

class GenymotionPlugin implements Plugin<Project> {

void apply(Project project) {

//create extensions ...

project.afterEvaluate { //create the tasks ... } }}

TEST IT

The Plugin class

Tasks are created after project.evaluate()

Page 92: Gradle plugins, take it to the next level

So, evaluate.

Page 93: Gradle plugins, take it to the next level

TEST IT

Your first test!

class GenymotionPluginTest {

@Test public void canAddGenymotionTask() { Project project = ProjectBuilder.builder().build() project.apply plugin: 'genymotion' project.evaluate()

assert project.tasks.genymotionTask instanceof GenymotionTask }} We launch evaluate() on the project

Page 94: Gradle plugins, take it to the next level

TEST IT

Run your second test$ gradle test --stacktrace --debug

Page 95: Gradle plugins, take it to the next level

TEST IT

Run your second test$ gradle test --stacktrace --debug

...

BUILD SUCCESSFUL

Page 96: Gradle plugins, take it to the next level

TEST IT

Run your second test$ gradle test --stacktrace --debug

...

BUILD SUCCESSFUL Yay!

Page 97: Gradle plugins, take it to the next level

TEST IT

LUKE DALEYGradleware Principal Engineer

GRADLE FORUM

You don't see this in the API docs for Project because it is an internal method and is therefore potentially subject to change in future releases.

There will be a supported mechanism for doing this kind of thing in the near future.

Page 98: Gradle plugins, take it to the next level

TEST IT

LUKE DALEYGradleware Principal Engineer

GRADLE FORUM

You don't see this in the API docs for Project because it is an internal method and is therefore potentially subject to change in future releases.

There will be a supported mechanism for doing this kind of thing in the near future.

June 2011

Page 99: Gradle plugins, take it to the next level

What about Android?

Page 100: Gradle plugins, take it to the next level

TEST IT

build.gradlerepositories { jcenter()}

dependencies { testCompile 'junit:junit:4.11' testCompile "com.android.tools.build:gradle:1.3.1"}

Page 101: Gradle plugins, take it to the next level

TEST IT

Project project = ProjectBuilder.builder() .withProjectDir(new File("res/test/android-app")) .build();

project.apply plugin: 'com.android.application'project.apply plugin: 'genymotion'

project.android { compileSdkVersion 21 buildToolsVersion "21.1.2"}

Test class

We create a project from a folder

Page 102: Gradle plugins, take it to the next level

TEST IT

Project project = ProjectBuilder.builder() .withProjectDir(new File("res/test/android-app")) .build();

project.apply plugin: 'com.android.application'project.apply plugin: 'genymotion'

project.android { compileSdkVersion 21 buildToolsVersion "21.1.2"}

Test class

We add the Android Gradle plugin

Page 103: Gradle plugins, take it to the next level

TEST IT

Project project = ProjectBuilder.builder() .withProjectDir(new File("res/test/android-app")) .build();

project.apply plugin: 'com.android.application'project.apply plugin: 'genymotion'

project.android { compileSdkVersion 21 buildToolsVersion "21.1.2"}

Test class

We declare the mandatory values

Page 104: Gradle plugins, take it to the next level

TEST IT

res/test/android-app

Page 105: Gradle plugins, take it to the next level

TEST IT

sdk.dir=/path/to/android/sdk

local.properties

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.genymotion.sample">

</manifest>

AndroidManifest.xml

Page 106: Gradle plugins, take it to the next level

TEST IT

@Test@Category(Android)public void canInjectToVariants() {

project = getAndroidProject()

project.android.productFlavors { flavor1 flavor2 } project.evaluate()

...

Test class (1/2) We annotate Android related tests

Page 107: Gradle plugins, take it to the next level

TEST IT

@Test@Category(Android)public void canInjectToVariants() {

project = getAndroidProject()

project.android.productFlavors { flavor1 flavor2 } project.evaluate()

...

Test class (1/2)

We add flavors to the project

Page 108: Gradle plugins, take it to the next level

TEST IT

...

project.android.testVariants.all { variant ->

Task connectedTask = variant.connectedInstrumentTest assert connectedTask.getTaskDependencies().getDependencies() .contains(genymotionTask) }}

Test class (2/2)

We test the dependency is done

Page 109: Gradle plugins, take it to the next level

Test with several Android plugin versions

Control Android plugin versionfrom outside the project

Use Gradle properties

TEST IT

Ensure compatibility

Page 110: Gradle plugins, take it to the next level

TEST IT

def androidVersion = "+"if (hasProperty("androidPluginVersion")) { androidVersion = androidPluginVersion}

dependencies { testCompile 'junit:junit:4.11' testCompile "com.android.tools.build:gradle: $androidVersion"}

build.gradle

./gradlew test -PandroidPluginVersion=1.3.1

cmd

Page 111: Gradle plugins, take it to the next level

Run Android integration tests daily On your CI

Test with the beta releasesUse jcenter()Set the default plugin version to “+”

TEST IT

Ensure compatibility

Page 112: Gradle plugins, take it to the next level

PUBLISH IT05

Page 113: Gradle plugins, take it to the next level

PUBLISH IT

Sharing with peopleBeing public

Easy embbedingOn the build.gradle

Why publishing

your plugin?

Page 114: Gradle plugins, take it to the next level

PUBLISH IT

Host code on githubOpen Source

Host binary on bintraybintray.com

Referenced on JCenterjcenter()

How to?The quick way, for free

Page 115: Gradle plugins, take it to the next level

PUBLISH IT

Gradle Plugin Devhttps://github.com/etiennestuder/gradle-plugindev-plugin

Publish automatically to Bintray

The good tool

Page 116: Gradle plugins, take it to the next level

Thank you!

eyal.fr

SLIDES bit.ly/gradle-plugin-next-level