SlideShare a Scribd company logo
1 of 62
Download to read offline
Gr8Day Warsaw 2016
IDIOMATIC GRADLE
PLUGIN WRITING
Schalk W. Cronjé
ABOUT ME
Email:
Twitter / Ello : @ysb33r
ysb33r@gmail.com
Gradle plugins authored/contributed to: VFS, Asciidoctor,
JRuby family (base, jar, war etc.), GnuMake, Doxygen, Bintray
ABOUT THIS PRESENTATION
Written in Asciidoctor (1.5.3.2)
Styled by asciidoctor-revealjs extension
Built using:
Gradle
gradle-asciidoctor-plugin
gradle-vfs-plugin
GET YOUR DAILY GRADLE DOSE
@DailyGradle
#gradleTip
THE PROBLEM
There is no consistency in the way plugin authors craft extensions
to the Gradle DSL today
QUALITY ATTRIBUTES OF DSL
Readability
Consistency
Flexibility
Expressiveness
PROJECT LAYOUT
Figure 1. Plugin project file layout
BUILD SCRIPT
repositories {
jcenter()
}
apply plugin : 'groovy'
dependencies {
compile localGroovy()
compile gradleApi()
testCompile ("org.spockframework:spock-core:1.0-groovy-2.3") {
exclude module : 'groovy-all'
}
}
TRICK : SPOCK VERSION
ext {
spockGrVer = GroovySystem.version.replaceAll(/.d+$/,'')
}
dependencies {
testCompile ("org.spockframework:spock-core:1.0-${spockGrVer}") {
exclude module : 'groovy-all'
}
}
CREATE PLUGIN CLASS
package idiomatic.gradle.authoring
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyExamplePlugin implements Plugin<Project> {
void apply(Project project) {
}
}
CREATE PROPERTIES FILE
META-INF/gradle-
plugins/idiomatic.authored.example.properties
implementation-class=idiomatic.gradle.authoring.MyExamplePlugin
Name of file must match plugin identifier
CREATE TASK CLASS
package idiomatic.gradle.authoring
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class MyExampleTasks extends DefaultTask {
@TaskAction
void exec() {
}
}
FOR BEST COMPATIBILITY
Support same JDK range as Gradle
Gradle 1.x - mininum JDK5
Gradle 2.x - minimum JDK6
Build against Gradle 2.0
Only use later versions if specific new functionality is
required.
Suggested baseline at Gradle 2.10 (for new model)
JDK COMPATIBILITY
// build.gradle
targetCompatibility = 1.6
sourceCompatibility = 1.6
project.tasks.withType(JavaCompile) { task ->
task.sourceCompatibility = project.sourceCompatibility
task.targetCompatibility = project.targetCompatibility
}
project.tasks.withType(GroovyCompile) { task ->
task.sourceCompatibility = project.sourceCompatibility
task.targetCompatibility = project.targetCompatibility
}
GRADLE BUILD VERSION
// gradle/wrapper/gradle-wrapper.properties
distributionUrl=https://..../distributions/gradle-2.0-all.zip
HONOUR OFFLINE
gradle --offline
The build should operate without accessing
network resources.
HONOUR OFFLINE
Unset the enabled property, if build is offline
task VfsCopy extends DefaultTask {
VfsCopy() {
enabled = !project.gradle.startParameter.isOffline()
}
}
PREFER METHODS OVER PROPERTIES
( IOW To assign or not to assign )
Methods provide more flexibility
Tend to provide better readability
Assignment is better suited towards
One-shot attribute setting
Overriding default attributes
Non-lazy evaluation
HOW NOT 2 : COLLECTION OF FILES
Typical implementation …​
class MyTask extends DefaultTask {
@InputFiles
List<File> mySources
}
leads to ugly DSL
task myTask( type: MyTask ) {
myTask = [ file('foo/bar.txt'), new File( 'bar/foo.txt') ]
}
COLLECTION OF FILES
myTask {
mySources file( 'path/foobar' )
mySources new File( 'path2/foobar' )
mySources 'file3', 'file4'
mySources { "lazy evaluate file name later on" }
}
Allow ability to:
Use strings and other objects convertible to File
Append lists
Evaluate as late as possible
Reset default values
COLLECTION OF FILES
Ignore Groovy shortcut; use three methods
class MyTask extends DefaultTask {
@InputFiles
FileCollection getDocuments() {
project.files(this.documents) // magic API method
}
void setDocuments(Object... docs) {
this.documents.clear()
this.documents.addAll(docs as List)
}
void documents(Object... docs) {
this.documents.addAll(docs as List)
}
private List<Object> documents = []
}
STYLE : TASKS
Provide a default instantiation of your new task class
Keep in mind that user would want to create additional
tasks of same type
Make it easy for them!!
KNOW YOUR ANNOTATIONS
@Input
@InputFile
@InputFiles
@InputDirectory
@OutputFile
@OutputFiles
@OutputDirectory
@OutputDirectories
@Optional
COLLECTION OF STRINGS
import org.gradle.util.CollectionUtils
Ignore Groovy shortcut; use three methods
@Input
List<String> getScriptArgs() {
// stringize() is your next magic API method
CollectionUtils.stringize(this.scriptArgs)
}
void setScriptArgs(Object... args) {
this.scriptArgs.clear()
this.scriptArgs.addAll(args as List)
}
void scriptArgs(Object... args) {
this.scriptArgs.addAll(args as List)
}
private List<Object> scriptArgs = []
HOW NOT 2 : MAPS
Typical implementation …​
class MyTask extends DefaultTask {
@Input
Map myOptions
}
leads to ugly DSL
task myTask( type: MyTask ) {
myOptions = [ prop1 : 'foo/bar.txt', prop2 : 'bar/foo.txt' ]
}
MAPS
task myTask( type: MyTask ) {
myOptions prop1 : 'foo/bar.txt', prop2 : 'bar/foo.txt'
myOptions prop3 : 'add/another'
// Explicit reset
myOptions = [:]
}
MAPS
@Input
Map getMyOptions() {
this.attrs
}
void setMyOptions(Map m) {
this.attrs=m
}
void myOptions(Map m) {
this.attrs+=m
}
private Map attrs = [:]
COMPATIBILITY TESTING
How can a plugin author test a plugin against multiple Gradle
versions?
COMPATIBILITY TESTING
Gradle 2.7 added TestKit
Gradle 2.9 added multi-distribution testing
TestKit still falls short in ease-of-use
(Hopefully to be corrected over future releases)
What to do for Gradle 2.0 - 2.8?
COMPATIBILITY TESTING
GradleTest plugin to the rescue
buildscript {
dependencies {
classpath "org.ysb33r.gradle:gradletest:0.5.4"
}
}
apply plugin : 'org.ysb33r.gradletest'
http://bit.ly/1LfUUU4
COMPATIBILITY TESTING
Create src/gradleTest/NameOfTest folder.
Add build.gradle
Add task runGradleTest
Add project structure
COMPATIBILITY TESTING
Add versions to main build.gradle
gradleTest {
versions '2.0', '2.2', '2.4', '2.5', '2.9'
}
Run it!
./gradlew gradleTest
THANK YOU
Keep your DSL
extensions beautiful
Don’t spring surprising
behaviour on the user
Email:
Twitter / Ello : @ysb33r
#idiomaticgradle
ysb33r@gmail.com
http://bit.ly/1iJmdiP
ADVANCED CONCEPTS
NOMENCLATURE
Property: A public data member (A Groovy property)
Method: A standard Java/Groovy method
Attribute: A value, set or accessed via the Gradle DSL. Can
result in a public method call or property access.
User: Person authoring or executing a Gradle build script
@Input
String aProperty = 'stdValue'
@Input
void aValue(String s) { ... }
myTask {
aProperty = 'newValue'
aValue 'newValue'
}
USER OVERRIDE LIBRARY VERSION
Ship with prefered (and tested) version of dependent
library set as default
Allow user flexibility to try a different version of such
library
Dynamically load library when needed
Still use power of Gradle’s dependency resolution
USER OVERRIDE LIBRARY VERSION
Example DSL from Asciidoctor
asciidoctorj {
version = '1.6.0-SNAPSHOT'
}
Example DSL from JRuby Base
jruby {
execVersion = '1.7.12'
}
USER OVERRIDE LIBRARY VERSION
1. Create Extension
2. Add extension object in plugin apply
3. Create custom classloader
USER OVERRIDE LIBRARY VERSION
Step 1: Create project extension
class MyExtension {
// Set the default dependent library version
String version = '1.5.0'
MyExtension(Project proj) {
project= proj
}
@PackageScope
Project project
}
USER OVERRIDE LIBRARY VERSION
Step 2: Add extension object in plugin apply
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
// Create the extension & configuration
project.extensions.create('asciidoctorj',MyExtension,project)
project.configuration.maybeCreate( 'int_asciidoctorj' )
// Add dependency at the end of configuration phase
project.afterEvaluate {
project.dependencies {
int_asciidoctorj "org.asciidoctor:asciidoctorj" +
"${project.asciidoctorj.version}"
}
}
}
}
USER OVERRIDE LIBRARY VERSION (2.5+)
Step 2: Add extension object Gradle 2.5+
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
// Create the extension & configuration
project.extensions.create('asciidoctorj',MyExtension,project)
def conf = configurations.maybeCreate( 'int_asciidoctorj' )
conf.defaultDependencies { deps ->
deps.add( project.dependencies.create(
"org.asciidoctor:asciidoctorj:${asciidoctorj.version}")
)
}
}
}
USER OVERRIDE LIBRARY VERSION
Step 3: Custom classloader (usually loaded from task action)
// Get all of the files in the `asciidoctorj` configuration
def urls = project.configurations.int_asciidoctorj.files.collect {
it.toURI().toURL()
}
// Create the classloader for all those files
def classLoader = new URLClassLoader(urls as URL[],
Thread.currentThread().contextClassLoader)
// Load one or more classes as required
def instance = classLoader.loadClass(
'org.asciidoctor.Asciidoctor$Factory')
NEED 2 KNOW : 'AFTEREVALUATE'
afterEvaluate adds to a list of closures to be executed
at end of configuration phase
Execution order is FIFO
Plugin author has no control over the order
STYLE : PROJECT EXTENSIONS
Treat project extensions as you would for any kind of
global configuration.
With care!
Do not make the extension configuration block a task
configuration.
Task instantiation may read defaults from extension.
Do not force extension values onto tasks
NEED 2 KNOW : PLUGINS
Plugin author has no control over order in which plugins
will be applied
Handle both cases of related plugin applied before or after
yours
EXTEND EXISTING TASK
Task type extension by inheritance is not always best
solution
Adding behaviour to existing task type better in certain
contexts
Example: jruby-jar-plugin wants to semantically
describe bootstrap files rather than force user to use
standard Copy syntax
EXTEND EXISTING TASK
jruby-jar-plugin without extension
jrubyJavaBootstrap {
// User gets exposed (unnecessarily) to the underlying task type
// Has to craft too much glue code
from( {
// @#$$!!-ugly code goes here
} )
}
jruby-jar-plugin with extension
jrubyJavaBootstrap {
// Expressing intent & context.
jruby {
initScript = 'bin/asciidoctor'
}
}
EXTEND EXISTING TASK
1. Create extension class
2. Add extension to task
3. Link extension attributes to task attributes (for caching)
EXTEND EXISTING TASK
Create extension class
class MyExtension {
String initScript
MyExtension( Task t ) {
// TODO: Add Gradle caching support
// (See later slide)
}
}
EXTEND EXISTING TASK
Add extension class to task
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
Task stubTask = project.tasks.create
( name : 'jrubyJavaBootstrap', type : Copy )
stubTask.extensions.create(
'jruby',
MyExtension,
stubTask
)
}
EXTEND EXISTING TASK
Add Gradle caching support
class MyExtension {
String initScript
MyExtension( Task t ) {
// Tell the task the initScript is also a property
t.inputs.property 'jrubyInitScript' , { -> this.initScript }
}
}
NEED 2 KNOW : TASK EXTENSIONS
Good way extend existing tasks in composable way
Attributes on extensions are not cached
Changes will not cause a rebuild of the task
Do the extra work to cache and provide the user with a
better experience.
ADD GENERATED JVM SOURCE SETS
May need to generate code from template and add to
current sourceset(s)
Example: Older versions of jruby-jar-plugin added
a custom class file to JAR
Useful for separation of concerns in certain generative
programming environments
ADD GENERATED JVM SOURCE SETS
1. Create generator task using Copy task as transformer
2. Configure generator task
3. Update SourceSet
4. Add dependency between generation and compilation
ADD GENERATED JVM SOURCE SETS
Step1 : Add generator task
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
Task stubTask = project.tasks.create
( name : 'myGenerator', type : Copy )
configureGenerator(stubTask)
addGeneratedToSource(project)
addTaskDependencies(project)
}
void configureGenerator(Task t)
{ /* TODO: <-- See next slides */ }
void addGeneratedToSource(Project p)
{ /* TODO: <-- See next slides */ }
void addTaskDependencies(Project p)
{ /* TODO: <-- See next slides */ }
}
This example uses Java, but can apply to any kind of sourceset
that Gradle supports
ADD GENERATED JVM SOURCE SETS
Step 2 : Configure generator task
/* DONE: <-- See previous slide for apply() */
void configureGenerator(Task stubTask) {
project.configure(stubTask) {
group "Add to correct group"
description 'Generates a JRuby Java bootstrap class'
from('src/template/java') {
include '*.java.template'
}
into new File(project.buildDir,'generated/java')
rename '(.+).java.template','$1.java'
filter { String line ->
/* Do something in here to transform the code */ }
}
}
ADD GENERATED JVM SOURCE SETS
Step 3 : Add generated code to SourceSet
/* DONE: <-- See earlier slide for apply() */
void addGeneratedToSource(Project project) {
project.sourceSets.matching { it.name == "main" } .all {
it.java.srcDir new File(project.buildDir,'generated/java')
}
}
ADD GENERATED JVM SOURCE SETS
Step 4 : Add task dependencies
/* DONE: <-- See earlier slide for apply() */
void addTaskDependencies(Project project) {
try {
Task t = project.tasks.getByName('compileJava')
if( t instanceof JavaCompile) {
t.dependsOn 'myGenerator'
}
} catch(UnknownTaskException) {
project.tasks.whenTaskAdded { Task t ->
if (t.name == 'compileJava' && t instanceof JavaCompile) {
t.dependsOn 'myGenerator'
}
}
}
}
TRICK : SAFE FILENAMES
Ability to create safe filenames on all platforms from input
data
Example: Asciidoctor output directories based upon
backend names
// WARNING: Using a very useful internal API
import org.gradle.internal.FileUtils
File outputBackendDir(final File outputDir,
final String backend) {
// FileUtils.toSafeFileName is your magic method
new File(outputDir, FileUtils.toSafeFileName(backend))
}
TRICK : SELF-REFERENCING PLUGIN
New plugin depends on functionality in the plugin
Apply plugin direct in build.gradle
apply plugin: new GroovyScriptEngine(
['src/main/groovy','src/main/resources'].
collect{ file(it).absolutePath }
.toArray(new String[2]),
project.class.classLoader
).loadScriptByName('book/SelfReferencingPlugin.groovy')
TRICK : OPERATING SYSTEM
Sometimes customised work has to be done on a specific
O/S
Example: jruby-gradle-plugin needs to set TMP in
environment on Windows
// This is the public interface API
import org.gradle.nativeplatform.platform.OperatingSystem
// But to get an instance the internal API is needed instead
import org.gradle.internal.os.OperatingSystem
println "Are we on Windows? ${OperatingSystem.current().isWindows()}
THANK YOU
Keep your DSL
extensions beautiful
Don’t spring surprising
behaviour on the user
Email:
Twitter / Ello : @ysb33r
#idiomaticgradle
ysb33r@gmail.com
http://bit.ly/1iJmdiP

More Related Content

What's hot

Cool Jvm Tools to Help you Test - Aylesbury Testers Version
Cool Jvm Tools to Help you Test - Aylesbury Testers VersionCool Jvm Tools to Help you Test - Aylesbury Testers Version
Cool Jvm Tools to Help you Test - Aylesbury Testers VersionSchalk Cronjé
 
Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Gradle build tool that rocks with DSL JavaOne India 4th May 2012Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Gradle build tool that rocks with DSL JavaOne India 4th May 2012Rajmahendra Hegde
 
Managing dependencies with gradle
Managing dependencies with gradleManaging dependencies with gradle
Managing dependencies with gradleLiviu Tudor
 
Gradle - time for a new build
Gradle - time for a new buildGradle - time for a new build
Gradle - time for a new buildIgor Khotin
 
Idiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin WritingIdiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin WritingSchalk Cronjé
 
Idiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin WritingIdiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin WritingSchalk Cronjé
 
[Image Results] Java Build Tools: Part 2 - A Decision Maker's Guide Compariso...
[Image Results] Java Build Tools: Part 2 - A Decision Maker's Guide Compariso...[Image Results] Java Build Tools: Part 2 - A Decision Maker's Guide Compariso...
[Image Results] Java Build Tools: Part 2 - A Decision Maker's Guide Compariso...ZeroTurnaround
 
Gradle 3.0: Unleash the Daemon!
Gradle 3.0: Unleash the Daemon!Gradle 3.0: Unleash the Daemon!
Gradle 3.0: Unleash the Daemon!Eric Wendelin
 
Gradle - the Enterprise Automation Tool
Gradle  - the Enterprise Automation ToolGradle  - the Enterprise Automation Tool
Gradle - the Enterprise Automation ToolIzzet Mustafaiev
 
The world of gradle - an introduction for developers
The world of gradle  - an introduction for developersThe world of gradle  - an introduction for developers
The world of gradle - an introduction for developersTricode (part of Dept)
 
An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersKostas Saidis
 
うさぎ組 in G* WorkShop -うさみみの日常-
うさぎ組 in G* WorkShop -うさみみの日常-うさぎ組 in G* WorkShop -うさみみの日常-
うさぎ組 in G* WorkShop -うさみみの日常-kyon mm
 
Gradle: The Build System you have been waiting for!
Gradle: The Build System you have been waiting for!Gradle: The Build System you have been waiting for!
Gradle: The Build System you have been waiting for!Corneil du Plessis
 
Gradle in a Polyglot World
Gradle in a Polyglot WorldGradle in a Polyglot World
Gradle in a Polyglot WorldSchalk Cronjé
 

What's hot (20)

Cool Jvm Tools to Help you Test - Aylesbury Testers Version
Cool Jvm Tools to Help you Test - Aylesbury Testers VersionCool Jvm Tools to Help you Test - Aylesbury Testers Version
Cool Jvm Tools to Help you Test - Aylesbury Testers Version
 
Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Gradle build tool that rocks with DSL JavaOne India 4th May 2012Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Gradle build tool that rocks with DSL JavaOne India 4th May 2012
 
Managing dependencies with gradle
Managing dependencies with gradleManaging dependencies with gradle
Managing dependencies with gradle
 
Gradle - time for a new build
Gradle - time for a new buildGradle - time for a new build
Gradle - time for a new build
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
Idiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin WritingIdiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin Writing
 
Idiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin WritingIdiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin Writing
 
Gradle
GradleGradle
Gradle
 
[Image Results] Java Build Tools: Part 2 - A Decision Maker's Guide Compariso...
[Image Results] Java Build Tools: Part 2 - A Decision Maker's Guide Compariso...[Image Results] Java Build Tools: Part 2 - A Decision Maker's Guide Compariso...
[Image Results] Java Build Tools: Part 2 - A Decision Maker's Guide Compariso...
 
Gradle 3.0: Unleash the Daemon!
Gradle 3.0: Unleash the Daemon!Gradle 3.0: Unleash the Daemon!
Gradle 3.0: Unleash the Daemon!
 
Gradle - the Enterprise Automation Tool
Gradle  - the Enterprise Automation ToolGradle  - the Enterprise Automation Tool
Gradle - the Enterprise Automation Tool
 
The world of gradle - an introduction for developers
The world of gradle  - an introduction for developersThe world of gradle  - an introduction for developers
The world of gradle - an introduction for developers
 
An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java Developers
 
Gradle : An introduction
Gradle : An introduction Gradle : An introduction
Gradle : An introduction
 
Android presentation - Gradle ++
Android presentation - Gradle ++Android presentation - Gradle ++
Android presentation - Gradle ++
 
うさぎ組 in G* WorkShop -うさみみの日常-
うさぎ組 in G* WorkShop -うさみみの日常-うさぎ組 in G* WorkShop -うさみみの日常-
うさぎ組 in G* WorkShop -うさみみの日常-
 
Gradle: The Build System you have been waiting for!
Gradle: The Build System you have been waiting for!Gradle: The Build System you have been waiting for!
Gradle: The Build System you have been waiting for!
 
Gradle in a Polyglot World
Gradle in a Polyglot WorldGradle in a Polyglot World
Gradle in a Polyglot World
 
Gradle presentation
Gradle presentationGradle presentation
Gradle presentation
 
Introduction to gradle
Introduction to gradleIntroduction to gradle
Introduction to gradle
 

Similar to Basic Gradle Plugin Writing

Gradle plugin, take control of the build
Gradle plugin, take control of the buildGradle plugin, take control of the build
Gradle plugin, take control of the buildEyal Lezmy
 
Making the Most of Your Gradle Build
Making the Most of Your Gradle BuildMaking the Most of Your Gradle Build
Making the Most of Your Gradle BuildAndres Almiray
 
Anatomy of a Gradle plugin
Anatomy of a Gradle pluginAnatomy of a Gradle plugin
Anatomy of a Gradle pluginDmytro Zaitsev
 
Making the Most of Your Gradle Build
Making the Most of Your Gradle BuildMaking the Most of Your Gradle Build
Making the Most of Your Gradle BuildAndres Almiray
 
Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Tino Isnich
 
Asciidoctor, because documentation does not have to suck
Asciidoctor, because documentation does not have to suckAsciidoctor, because documentation does not have to suck
Asciidoctor, because documentation does not have to suckAndres Almiray
 
10 Cool Facts about Gradle
10 Cool Facts about Gradle10 Cool Facts about Gradle
10 Cool Facts about GradleEvgeny Goldin
 
DocuOps & Asciidoctor in a JVM World
DocuOps & Asciidoctor in a JVM WorldDocuOps & Asciidoctor in a JVM World
DocuOps & Asciidoctor in a JVM WorldSchalk Cronjé
 
Gradle - small introduction
Gradle - small introductionGradle - small introduction
Gradle - small introductionIgor Popov
 
Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017Andres Almiray
 
Andres Almiray - Gradle Ex Machina - Codemotion Rome 2019
Andres Almiray - Gradle Ex Machina - Codemotion Rome 2019Andres Almiray - Gradle Ex Machina - Codemotion Rome 2019
Andres Almiray - Gradle Ex Machina - Codemotion Rome 2019Codemotion
 
Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017Andres Almiray
 
containerit at useR!2017 conference, Brussels
containerit at useR!2017 conference, Brusselscontainerit at useR!2017 conference, Brussels
containerit at useR!2017 conference, BrusselsDaniel Nüst
 
Faster Java EE Builds with Gradle
Faster Java EE Builds with GradleFaster Java EE Builds with Gradle
Faster Java EE Builds with GradleRyan Cuprak
 
Faster Java EE Builds with Gradle
Faster Java EE Builds with GradleFaster Java EE Builds with Gradle
Faster Java EE Builds with GradleRyan Cuprak
 
Tips pour sécuriser ses conteneurs docker/podman
Tips pour sécuriser ses conteneurs docker/podmanTips pour sécuriser ses conteneurs docker/podman
Tips pour sécuriser ses conteneurs docker/podmanThierry Gayet
 

Similar to Basic Gradle Plugin Writing (20)

GradleFX
GradleFXGradleFX
GradleFX
 
Gradle plugin, take control of the build
Gradle plugin, take control of the buildGradle plugin, take control of the build
Gradle plugin, take control of the build
 
Making the Most of Your Gradle Build
Making the Most of Your Gradle BuildMaking the Most of Your Gradle Build
Making the Most of Your Gradle Build
 
Anatomy of a Gradle plugin
Anatomy of a Gradle pluginAnatomy of a Gradle plugin
Anatomy of a Gradle plugin
 
Making the Most of Your Gradle Build
Making the Most of Your Gradle BuildMaking the Most of Your Gradle Build
Making the Most of Your Gradle Build
 
Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01
 
Asciidoctor, because documentation does not have to suck
Asciidoctor, because documentation does not have to suckAsciidoctor, because documentation does not have to suck
Asciidoctor, because documentation does not have to suck
 
10 Cool Facts about Gradle
10 Cool Facts about Gradle10 Cool Facts about Gradle
10 Cool Facts about Gradle
 
DocuOps & Asciidoctor in a JVM World
DocuOps & Asciidoctor in a JVM WorldDocuOps & Asciidoctor in a JVM World
DocuOps & Asciidoctor in a JVM World
 
Gradle - small introduction
Gradle - small introductionGradle - small introduction
Gradle - small introduction
 
Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017
 
Andres Almiray - Gradle Ex Machina - Codemotion Rome 2019
Andres Almiray - Gradle Ex Machina - Codemotion Rome 2019Andres Almiray - Gradle Ex Machina - Codemotion Rome 2019
Andres Almiray - Gradle Ex Machina - Codemotion Rome 2019
 
Gradle ex-machina
Gradle ex-machinaGradle ex-machina
Gradle ex-machina
 
Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017
 
containerit at useR!2017 conference, Brussels
containerit at useR!2017 conference, Brusselscontainerit at useR!2017 conference, Brussels
containerit at useR!2017 conference, Brussels
 
Dart Workshop
Dart WorkshopDart Workshop
Dart Workshop
 
Faster Java EE Builds with Gradle
Faster Java EE Builds with GradleFaster Java EE Builds with Gradle
Faster Java EE Builds with Gradle
 
Faster Java EE Builds with Gradle
Faster Java EE Builds with GradleFaster Java EE Builds with Gradle
Faster Java EE Builds with Gradle
 
Tips pour sécuriser ses conteneurs docker/podman
Tips pour sécuriser ses conteneurs docker/podmanTips pour sécuriser ses conteneurs docker/podman
Tips pour sécuriser ses conteneurs docker/podman
 
Docker for dev
Docker for devDocker for dev
Docker for dev
 

More from Schalk Cronjé

What's new in Asciidoctor
What's new in AsciidoctorWhat's new in Asciidoctor
What's new in AsciidoctorSchalk Cronjé
 
Probability Management
Probability ManagementProbability Management
Probability ManagementSchalk Cronjé
 
Seeking Enligtenment - A journey of purpose rather than instruction
Seeking Enligtenment  - A journey of purpose rather than instructionSeeking Enligtenment  - A journey of purpose rather than instruction
Seeking Enligtenment - A journey of purpose rather than instructionSchalk Cronjé
 
Seeking Enligtenment - A journey of purpose rather tan instruction
Seeking Enligtenment - A journey of purpose rather tan instructionSeeking Enligtenment - A journey of purpose rather tan instruction
Seeking Enligtenment - A journey of purpose rather tan instructionSchalk Cronjé
 
Beyond Estimates - Probability Management
Beyond Estimates - Probability ManagementBeyond Estimates - Probability Management
Beyond Estimates - Probability ManagementSchalk Cronjé
 
Documentation An Engineering Problem Unsolved
Documentation  An Engineering Problem UnsolvedDocumentation  An Engineering Problem Unsolved
Documentation An Engineering Problem UnsolvedSchalk Cronjé
 
Death of Agile : Welcome to Value-focused Testing
Death of Agile : Welcome to Value-focused TestingDeath of Agile : Welcome to Value-focused Testing
Death of Agile : Welcome to Value-focused TestingSchalk Cronjé
 
Tree of Knowledge - About Philosophy, Unity & Testing
Tree of Knowledge - About Philosophy, Unity & TestingTree of Knowledge - About Philosophy, Unity & Testing
Tree of Knowledge - About Philosophy, Unity & TestingSchalk Cronjé
 
Beyond estimates - Overview at Agile:MK
Beyond estimates - Overview at Agile:MKBeyond estimates - Overview at Agile:MK
Beyond estimates - Overview at Agile:MKSchalk Cronjé
 
Groovy VFS (with 1.0 news)
Groovy VFS (with 1.0 news)Groovy VFS (with 1.0 news)
Groovy VFS (with 1.0 news)Schalk Cronjé
 
Beyond estimates - Reflection on the state of Agile Forecasting
Beyond estimates - Reflection on the state of Agile ForecastingBeyond estimates - Reflection on the state of Agile Forecasting
Beyond estimates - Reflection on the state of Agile ForecastingSchalk Cronjé
 
Seeking enligtenment - A journey of "Why?" rather than "How?"
Seeking enligtenment - A journey of "Why?" rather than "How?"Seeking enligtenment - A journey of "Why?" rather than "How?"
Seeking enligtenment - A journey of "Why?" rather than "How?"Schalk Cronjé
 
RfC2822 for Mere Mortals
RfC2822 for Mere MortalsRfC2822 for Mere Mortals
RfC2822 for Mere MortalsSchalk Cronjé
 
Prosperity-focused Agile Technology Leadership
Prosperity-focused Agile Technology LeadershipProsperity-focused Agile Technology Leadership
Prosperity-focused Agile Technology LeadershipSchalk Cronjé
 

More from Schalk Cronjé (18)

DocuOps & Asciidoctor
DocuOps & AsciidoctorDocuOps & Asciidoctor
DocuOps & Asciidoctor
 
What's new in Asciidoctor
What's new in AsciidoctorWhat's new in Asciidoctor
What's new in Asciidoctor
 
Probability Management
Probability ManagementProbability Management
Probability Management
 
Seeking Enligtenment - A journey of purpose rather than instruction
Seeking Enligtenment  - A journey of purpose rather than instructionSeeking Enligtenment  - A journey of purpose rather than instruction
Seeking Enligtenment - A journey of purpose rather than instruction
 
Seeking Enligtenment - A journey of purpose rather tan instruction
Seeking Enligtenment - A journey of purpose rather tan instructionSeeking Enligtenment - A journey of purpose rather tan instruction
Seeking Enligtenment - A journey of purpose rather tan instruction
 
Beyond Estimates - Probability Management
Beyond Estimates - Probability ManagementBeyond Estimates - Probability Management
Beyond Estimates - Probability Management
 
Documentation An Engineering Problem Unsolved
Documentation  An Engineering Problem UnsolvedDocumentation  An Engineering Problem Unsolved
Documentation An Engineering Problem Unsolved
 
Death of Agile : Welcome to Value-focused Testing
Death of Agile : Welcome to Value-focused TestingDeath of Agile : Welcome to Value-focused Testing
Death of Agile : Welcome to Value-focused Testing
 
Asciidoctor in 15min
Asciidoctor in 15minAsciidoctor in 15min
Asciidoctor in 15min
 
Tree of Knowledge - About Philosophy, Unity & Testing
Tree of Knowledge - About Philosophy, Unity & TestingTree of Knowledge - About Philosophy, Unity & Testing
Tree of Knowledge - About Philosophy, Unity & Testing
 
Beyond estimates - Overview at Agile:MK
Beyond estimates - Overview at Agile:MKBeyond estimates - Overview at Agile:MK
Beyond estimates - Overview at Agile:MK
 
Groovy VFS (with 1.0 news)
Groovy VFS (with 1.0 news)Groovy VFS (with 1.0 news)
Groovy VFS (with 1.0 news)
 
Beyond estimates - Reflection on the state of Agile Forecasting
Beyond estimates - Reflection on the state of Agile ForecastingBeyond estimates - Reflection on the state of Agile Forecasting
Beyond estimates - Reflection on the state of Agile Forecasting
 
Seeking enligtenment - A journey of "Why?" rather than "How?"
Seeking enligtenment - A journey of "Why?" rather than "How?"Seeking enligtenment - A journey of "Why?" rather than "How?"
Seeking enligtenment - A journey of "Why?" rather than "How?"
 
RfC2822 for Mere Mortals
RfC2822 for Mere MortalsRfC2822 for Mere Mortals
RfC2822 for Mere Mortals
 
Groovy VFS
Groovy VFSGroovy VFS
Groovy VFS
 
Prosperity-focused Agile Technology Leadership
Prosperity-focused Agile Technology LeadershipProsperity-focused Agile Technology Leadership
Prosperity-focused Agile Technology Leadership
 
Real World TDD
Real World TDDReal World TDD
Real World TDD
 

Recently uploaded

eSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolseSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolsosttopstonverter
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfDrew Moseley
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profileakrivarotava
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptxReal-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptxRTS corp
 
Effectively Troubleshoot 9 Types of OutOfMemoryError
Effectively Troubleshoot 9 Types of OutOfMemoryErrorEffectively Troubleshoot 9 Types of OutOfMemoryError
Effectively Troubleshoot 9 Types of OutOfMemoryErrorTier1 app
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZABSYZ Inc
 
SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?Alexandre Beguel
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Angel Borroy López
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identityteam-WIBU
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxThe Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxRTS corp
 
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonApplitools
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Developmentvyaparkranti
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 

Recently uploaded (20)

eSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolseSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration tools
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdf
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profile
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptxReal-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
 
Effectively Troubleshoot 9 Types of OutOfMemoryError
Effectively Troubleshoot 9 Types of OutOfMemoryErrorEffectively Troubleshoot 9 Types of OutOfMemoryError
Effectively Troubleshoot 9 Types of OutOfMemoryError
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZ
 
SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identity
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxThe Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
 
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Development
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 

Basic Gradle Plugin Writing

  • 1. Gr8Day Warsaw 2016 IDIOMATIC GRADLE PLUGIN WRITING Schalk W. Cronjé
  • 2. ABOUT ME Email: Twitter / Ello : @ysb33r ysb33r@gmail.com Gradle plugins authored/contributed to: VFS, Asciidoctor, JRuby family (base, jar, war etc.), GnuMake, Doxygen, Bintray
  • 3. ABOUT THIS PRESENTATION Written in Asciidoctor (1.5.3.2) Styled by asciidoctor-revealjs extension Built using: Gradle gradle-asciidoctor-plugin gradle-vfs-plugin
  • 4. GET YOUR DAILY GRADLE DOSE @DailyGradle #gradleTip
  • 5. THE PROBLEM There is no consistency in the way plugin authors craft extensions to the Gradle DSL today
  • 6. QUALITY ATTRIBUTES OF DSL Readability Consistency Flexibility Expressiveness
  • 7. PROJECT LAYOUT Figure 1. Plugin project file layout
  • 8. BUILD SCRIPT repositories { jcenter() } apply plugin : 'groovy' dependencies { compile localGroovy() compile gradleApi() testCompile ("org.spockframework:spock-core:1.0-groovy-2.3") { exclude module : 'groovy-all' } }
  • 9. TRICK : SPOCK VERSION ext { spockGrVer = GroovySystem.version.replaceAll(/.d+$/,'') } dependencies { testCompile ("org.spockframework:spock-core:1.0-${spockGrVer}") { exclude module : 'groovy-all' } }
  • 10. CREATE PLUGIN CLASS package idiomatic.gradle.authoring import org.gradle.api.Plugin import org.gradle.api.Project class MyExamplePlugin implements Plugin<Project> { void apply(Project project) { } }
  • 12. CREATE TASK CLASS package idiomatic.gradle.authoring import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction class MyExampleTasks extends DefaultTask { @TaskAction void exec() { } }
  • 13. FOR BEST COMPATIBILITY Support same JDK range as Gradle Gradle 1.x - mininum JDK5 Gradle 2.x - minimum JDK6 Build against Gradle 2.0 Only use later versions if specific new functionality is required. Suggested baseline at Gradle 2.10 (for new model)
  • 14. JDK COMPATIBILITY // build.gradle targetCompatibility = 1.6 sourceCompatibility = 1.6 project.tasks.withType(JavaCompile) { task -> task.sourceCompatibility = project.sourceCompatibility task.targetCompatibility = project.targetCompatibility } project.tasks.withType(GroovyCompile) { task -> task.sourceCompatibility = project.sourceCompatibility task.targetCompatibility = project.targetCompatibility }
  • 15. GRADLE BUILD VERSION // gradle/wrapper/gradle-wrapper.properties distributionUrl=https://..../distributions/gradle-2.0-all.zip
  • 16. HONOUR OFFLINE gradle --offline The build should operate without accessing network resources.
  • 17. HONOUR OFFLINE Unset the enabled property, if build is offline task VfsCopy extends DefaultTask { VfsCopy() { enabled = !project.gradle.startParameter.isOffline() } }
  • 18. PREFER METHODS OVER PROPERTIES ( IOW To assign or not to assign ) Methods provide more flexibility Tend to provide better readability Assignment is better suited towards One-shot attribute setting Overriding default attributes Non-lazy evaluation
  • 19. HOW NOT 2 : COLLECTION OF FILES Typical implementation …​ class MyTask extends DefaultTask { @InputFiles List<File> mySources } leads to ugly DSL task myTask( type: MyTask ) { myTask = [ file('foo/bar.txt'), new File( 'bar/foo.txt') ] }
  • 20. COLLECTION OF FILES myTask { mySources file( 'path/foobar' ) mySources new File( 'path2/foobar' ) mySources 'file3', 'file4' mySources { "lazy evaluate file name later on" } } Allow ability to: Use strings and other objects convertible to File Append lists Evaluate as late as possible Reset default values
  • 21. COLLECTION OF FILES Ignore Groovy shortcut; use three methods class MyTask extends DefaultTask { @InputFiles FileCollection getDocuments() { project.files(this.documents) // magic API method } void setDocuments(Object... docs) { this.documents.clear() this.documents.addAll(docs as List) } void documents(Object... docs) { this.documents.addAll(docs as List) } private List<Object> documents = [] }
  • 22. STYLE : TASKS Provide a default instantiation of your new task class Keep in mind that user would want to create additional tasks of same type Make it easy for them!!
  • 24. COLLECTION OF STRINGS import org.gradle.util.CollectionUtils Ignore Groovy shortcut; use three methods @Input List<String> getScriptArgs() { // stringize() is your next magic API method CollectionUtils.stringize(this.scriptArgs) } void setScriptArgs(Object... args) { this.scriptArgs.clear() this.scriptArgs.addAll(args as List) } void scriptArgs(Object... args) { this.scriptArgs.addAll(args as List) } private List<Object> scriptArgs = []
  • 25. HOW NOT 2 : MAPS Typical implementation …​ class MyTask extends DefaultTask { @Input Map myOptions } leads to ugly DSL task myTask( type: MyTask ) { myOptions = [ prop1 : 'foo/bar.txt', prop2 : 'bar/foo.txt' ] }
  • 26. MAPS task myTask( type: MyTask ) { myOptions prop1 : 'foo/bar.txt', prop2 : 'bar/foo.txt' myOptions prop3 : 'add/another' // Explicit reset myOptions = [:] }
  • 27. MAPS @Input Map getMyOptions() { this.attrs } void setMyOptions(Map m) { this.attrs=m } void myOptions(Map m) { this.attrs+=m } private Map attrs = [:]
  • 28. COMPATIBILITY TESTING How can a plugin author test a plugin against multiple Gradle versions?
  • 29. COMPATIBILITY TESTING Gradle 2.7 added TestKit Gradle 2.9 added multi-distribution testing TestKit still falls short in ease-of-use (Hopefully to be corrected over future releases) What to do for Gradle 2.0 - 2.8?
  • 30. COMPATIBILITY TESTING GradleTest plugin to the rescue buildscript { dependencies { classpath "org.ysb33r.gradle:gradletest:0.5.4" } } apply plugin : 'org.ysb33r.gradletest' http://bit.ly/1LfUUU4
  • 31. COMPATIBILITY TESTING Create src/gradleTest/NameOfTest folder. Add build.gradle Add task runGradleTest Add project structure
  • 32. COMPATIBILITY TESTING Add versions to main build.gradle gradleTest { versions '2.0', '2.2', '2.4', '2.5', '2.9' } Run it! ./gradlew gradleTest
  • 33. THANK YOU Keep your DSL extensions beautiful Don’t spring surprising behaviour on the user Email: Twitter / Ello : @ysb33r #idiomaticgradle ysb33r@gmail.com http://bit.ly/1iJmdiP
  • 35. NOMENCLATURE Property: A public data member (A Groovy property) Method: A standard Java/Groovy method Attribute: A value, set or accessed via the Gradle DSL. Can result in a public method call or property access. User: Person authoring or executing a Gradle build script @Input String aProperty = 'stdValue' @Input void aValue(String s) { ... } myTask { aProperty = 'newValue' aValue 'newValue' }
  • 36. USER OVERRIDE LIBRARY VERSION Ship with prefered (and tested) version of dependent library set as default Allow user flexibility to try a different version of such library Dynamically load library when needed Still use power of Gradle’s dependency resolution
  • 37. USER OVERRIDE LIBRARY VERSION Example DSL from Asciidoctor asciidoctorj { version = '1.6.0-SNAPSHOT' } Example DSL from JRuby Base jruby { execVersion = '1.7.12' }
  • 38. USER OVERRIDE LIBRARY VERSION 1. Create Extension 2. Add extension object in plugin apply 3. Create custom classloader
  • 39. USER OVERRIDE LIBRARY VERSION Step 1: Create project extension class MyExtension { // Set the default dependent library version String version = '1.5.0' MyExtension(Project proj) { project= proj } @PackageScope Project project }
  • 40. USER OVERRIDE LIBRARY VERSION Step 2: Add extension object in plugin apply class MyPlugin implements Plugin<Project> { void apply(Project project) { // Create the extension & configuration project.extensions.create('asciidoctorj',MyExtension,project) project.configuration.maybeCreate( 'int_asciidoctorj' ) // Add dependency at the end of configuration phase project.afterEvaluate { project.dependencies { int_asciidoctorj "org.asciidoctor:asciidoctorj" + "${project.asciidoctorj.version}" } } } }
  • 41. USER OVERRIDE LIBRARY VERSION (2.5+) Step 2: Add extension object Gradle 2.5+ class MyPlugin implements Plugin<Project> { void apply(Project project) { // Create the extension & configuration project.extensions.create('asciidoctorj',MyExtension,project) def conf = configurations.maybeCreate( 'int_asciidoctorj' ) conf.defaultDependencies { deps -> deps.add( project.dependencies.create( "org.asciidoctor:asciidoctorj:${asciidoctorj.version}") ) } } }
  • 42. USER OVERRIDE LIBRARY VERSION Step 3: Custom classloader (usually loaded from task action) // Get all of the files in the `asciidoctorj` configuration def urls = project.configurations.int_asciidoctorj.files.collect { it.toURI().toURL() } // Create the classloader for all those files def classLoader = new URLClassLoader(urls as URL[], Thread.currentThread().contextClassLoader) // Load one or more classes as required def instance = classLoader.loadClass( 'org.asciidoctor.Asciidoctor$Factory')
  • 43. NEED 2 KNOW : 'AFTEREVALUATE' afterEvaluate adds to a list of closures to be executed at end of configuration phase Execution order is FIFO Plugin author has no control over the order
  • 44. STYLE : PROJECT EXTENSIONS Treat project extensions as you would for any kind of global configuration. With care! Do not make the extension configuration block a task configuration. Task instantiation may read defaults from extension. Do not force extension values onto tasks
  • 45. NEED 2 KNOW : PLUGINS Plugin author has no control over order in which plugins will be applied Handle both cases of related plugin applied before or after yours
  • 46. EXTEND EXISTING TASK Task type extension by inheritance is not always best solution Adding behaviour to existing task type better in certain contexts Example: jruby-jar-plugin wants to semantically describe bootstrap files rather than force user to use standard Copy syntax
  • 47. EXTEND EXISTING TASK jruby-jar-plugin without extension jrubyJavaBootstrap { // User gets exposed (unnecessarily) to the underlying task type // Has to craft too much glue code from( { // @#$$!!-ugly code goes here } ) } jruby-jar-plugin with extension jrubyJavaBootstrap { // Expressing intent & context. jruby { initScript = 'bin/asciidoctor' } }
  • 48. EXTEND EXISTING TASK 1. Create extension class 2. Add extension to task 3. Link extension attributes to task attributes (for caching)
  • 49. EXTEND EXISTING TASK Create extension class class MyExtension { String initScript MyExtension( Task t ) { // TODO: Add Gradle caching support // (See later slide) } }
  • 50. EXTEND EXISTING TASK Add extension class to task class MyPlugin implements Plugin<Project> { void apply(Project project) { Task stubTask = project.tasks.create ( name : 'jrubyJavaBootstrap', type : Copy ) stubTask.extensions.create( 'jruby', MyExtension, stubTask ) }
  • 51. EXTEND EXISTING TASK Add Gradle caching support class MyExtension { String initScript MyExtension( Task t ) { // Tell the task the initScript is also a property t.inputs.property 'jrubyInitScript' , { -> this.initScript } } }
  • 52. NEED 2 KNOW : TASK EXTENSIONS Good way extend existing tasks in composable way Attributes on extensions are not cached Changes will not cause a rebuild of the task Do the extra work to cache and provide the user with a better experience.
  • 53. ADD GENERATED JVM SOURCE SETS May need to generate code from template and add to current sourceset(s) Example: Older versions of jruby-jar-plugin added a custom class file to JAR Useful for separation of concerns in certain generative programming environments
  • 54. ADD GENERATED JVM SOURCE SETS 1. Create generator task using Copy task as transformer 2. Configure generator task 3. Update SourceSet 4. Add dependency between generation and compilation
  • 55. ADD GENERATED JVM SOURCE SETS Step1 : Add generator task class MyPlugin implements Plugin<Project> { void apply(Project project) { Task stubTask = project.tasks.create ( name : 'myGenerator', type : Copy ) configureGenerator(stubTask) addGeneratedToSource(project) addTaskDependencies(project) } void configureGenerator(Task t) { /* TODO: <-- See next slides */ } void addGeneratedToSource(Project p) { /* TODO: <-- See next slides */ } void addTaskDependencies(Project p) { /* TODO: <-- See next slides */ } } This example uses Java, but can apply to any kind of sourceset that Gradle supports
  • 56. ADD GENERATED JVM SOURCE SETS Step 2 : Configure generator task /* DONE: <-- See previous slide for apply() */ void configureGenerator(Task stubTask) { project.configure(stubTask) { group "Add to correct group" description 'Generates a JRuby Java bootstrap class' from('src/template/java') { include '*.java.template' } into new File(project.buildDir,'generated/java') rename '(.+).java.template','$1.java' filter { String line -> /* Do something in here to transform the code */ } } }
  • 57. ADD GENERATED JVM SOURCE SETS Step 3 : Add generated code to SourceSet /* DONE: <-- See earlier slide for apply() */ void addGeneratedToSource(Project project) { project.sourceSets.matching { it.name == "main" } .all { it.java.srcDir new File(project.buildDir,'generated/java') } }
  • 58. ADD GENERATED JVM SOURCE SETS Step 4 : Add task dependencies /* DONE: <-- See earlier slide for apply() */ void addTaskDependencies(Project project) { try { Task t = project.tasks.getByName('compileJava') if( t instanceof JavaCompile) { t.dependsOn 'myGenerator' } } catch(UnknownTaskException) { project.tasks.whenTaskAdded { Task t -> if (t.name == 'compileJava' && t instanceof JavaCompile) { t.dependsOn 'myGenerator' } } } }
  • 59. TRICK : SAFE FILENAMES Ability to create safe filenames on all platforms from input data Example: Asciidoctor output directories based upon backend names // WARNING: Using a very useful internal API import org.gradle.internal.FileUtils File outputBackendDir(final File outputDir, final String backend) { // FileUtils.toSafeFileName is your magic method new File(outputDir, FileUtils.toSafeFileName(backend)) }
  • 60. TRICK : SELF-REFERENCING PLUGIN New plugin depends on functionality in the plugin Apply plugin direct in build.gradle apply plugin: new GroovyScriptEngine( ['src/main/groovy','src/main/resources']. collect{ file(it).absolutePath } .toArray(new String[2]), project.class.classLoader ).loadScriptByName('book/SelfReferencingPlugin.groovy')
  • 61. TRICK : OPERATING SYSTEM Sometimes customised work has to be done on a specific O/S Example: jruby-gradle-plugin needs to set TMP in environment on Windows // This is the public interface API import org.gradle.nativeplatform.platform.OperatingSystem // But to get an instance the internal API is needed instead import org.gradle.internal.os.OperatingSystem println "Are we on Windows? ${OperatingSystem.current().isWindows()}
  • 62. THANK YOU Keep your DSL extensions beautiful Don’t spring surprising behaviour on the user Email: Twitter / Ello : @ysb33r #idiomaticgradle ysb33r@gmail.com http://bit.ly/1iJmdiP