Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Gradle Plugin Goodness
@BRWNGRLDEV
@BRWNGRLDEV
apply plugin: 'checkstyle'

apply plugin: 'findbugs'

apply plugin: 'pmd'



task checkstyle(type: Checkstyle) {

descripti...
apply plugin: 'checkstyle'

apply plugin: 'findbugs'

apply plugin: 'pmd'



task checkstyle(type: Checkstyle) {

descripti...
OVERVIEW
▸Plugin Skeleton
▸Dependencies
▸Plugin.groovy
▸CustomTask.groovy
▸Publishing
@BRWNGRLDEV
PLUGIN SKELETON
@BRWNGRLDEV
PLUGIN SKELETON
@BRWNGRLDEV
PLUGIN SKELETON
@BRWNGRLDEV
PLUGIN SKELETON
How Gradle finds the Plugin Implementation
@BRWNGRLDEV
PLUGIN SKELETON
implementation-class=info.adavis.qualitychecks.QualityChecksPlugin
@BRWNGRLDEV
DEPENDENCIES
@BRWNGRLDEV
DEPENDENCIES
apply plugin: ‘groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
@BRWNGRLDEV
DEPENDENCIES
dependencies {
…
testCompile 'junit:junit:4.12'

testCompile ('org.spockframework:spock-core:1.0-groovy-2.4')...
THE CODE
@BRWNGRLDEV
BUT FIRST…
@BRWNGRLDEV
GRADLE BUILD
@BRWNGRLDEV
PROJECT
TASK TASK TASK
BUILD
GRADLE BUILD
@BRWNGRLDEV
PROJECT
TASK TASK TASK
BUILD
PROJECT
TASK TASK TASK
PLUGIN.GROOVY
class CustomPlugin implements Plugin<Project> {
}
@BRWNGRLDEV
PLUGIN.GROOVY
class CustomPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
}
}
@BRWNGRLDEV
QUALITYCHECKSPLUGIN.GROOVY
@BRWNGRLDEV
APPLY METHOD
@BRWNGRLDEV
void apply(Project project) {

this.project = project



}
APPLY METHOD
@BRWNGRLDEV
void apply(Project project) {

this.project = project



project.extensions.create('qualityChecks...
APPLY METHOD
@BRWNGRLDEV
void apply(Project project) {

this.project = project



project.extensions.create('qualityChecks...
PROJECT EXTENSION
@BRWNGRLDEV
class QualityChecksExtension {



String pmdConfigFile = 'quality-checks/pmd-ruleset.xml'



...
PROJECT EXTENSION
@BRWNGRLDEV
class QualityChecksExtension {



String pmdConfigFile = 'quality-checks/pmd-ruleset.xml'



...
PROJECT EXTENSION
Back in the application’s build.gradle file…
@BRWNGRLDEV
qualityChecks {

pmdConfigFile = ‘checks/pmd.xml’...
PROJECT EXTENSION
Back in the application’s build.gradle file…
@BRWNGRLDEV
qualityChecks {

pmdConfigFile = ‘checks/pmd.xml’...
TASKS
@BRWNGRLDEV
CREATING TASKS
Give it a name and a type
@BRWNGRLDEV
CREATING TASKS
@BRWNGRLDEV
▸Build on existing task
▸Extend the DefaultTask
BUILD ON EXISTING TASK
@BRWNGRLDEV
BUILD ON EXISTING
@BRWNGRLDEV
EXTEND DEFAULT TASK
@BRWNGRLDEV
CUSTOMTASK.GROOVY
class CustomTask extends DefaultTask {
CustomTask() {
group: ‘verification’
}
}
@BRWNGRLDEV
CUSTOMTASK.GROOVY
class CustomTask extends DefaultTask {
CustomTask() {
group: ‘verification’
}
}
@BRWNGRLDEV
CUSTOMTASK.GROOVY
CustomTask() {
group: ‘verification’
onlyIf {
// skip under certain conditions
}
}
@BRWNGRLDEV
CUSTOMTASK.GROOVY
@TaskAction
def defaultAction() {
description: ‘What my task does’
}
@BRWNGRLDEV
CUSTOMTASK.GROOVY
@BRWNGRLDEV
@TaskAction
def defaultAction() {
description: ‘What my task does’
<do your cool stuff here>...
GRADLE TASK DOCUMENTATION
@BRWNGRLDEV
SO FAR…
▸Plugin Skeleton
▸Dependencies
▸Plugin.groovy
▸CustomTask.groovy
@BRWNGRLDEV
PUBLISHING
@BRWNGRLDEV
PUBLISHING
@BRWNGRLDEV
PUBLISHING
buildscript {
…
dependencies {
classpath "com.gradle.publish:plugin-publish-plugin:0.9.4"
}
}
apply plugin: 'co...
PUBLISHING
version = "0.1.3"
group = "info.adavis"
@BRWNGRLDEV
PUBLISHING
pluginBundle {
website = 'https://github.com/adavis/quality-checks'
vcsUrl = 'https://github.com/adavis/quality...
PUBLISHING
pluginBundle {
…
plugins {
qualityChecksPlugin {
id = 'info.adavis.qualitychecks'
displayName = 'Quality Checks...
@BRWNGRLDEV
@BRWNGRLDEV
WE’RE DONE…
WRONG!
@BRWNGRLDEV
WE’RE DONE…
TESTS
@BRWNGRLDEV
TESTING - WITH JUNIT
@Before

void setUp() {

projectDir = temporaryFolder.root

projectDir.mkdirs()





}
@BRWNGRLDEV
TESTING - WITH JUNIT
@Before

void setUp() {

projectDir = temporaryFolder.root

projectDir.mkdirs()



project = ProjectB...
TESTING - WITH JUNIT
@Test
void shouldBeAbleToCreateTask() {
assertTrue(task instanceof WriteConfigFileTask)
}
@BRWNGRLDEV
TESTING - WITH JUNIT
@Test
void pluginShouldBeApplied() {
project.apply(plugin: QualityChecksPlugin)
assertNotNull(project...
SPOCK
@BRWNGRLDEV
TESTING - WITH SPOCK
def createCheckstyleTask() {

given: "we have a project"

def project = ProjectBuilder.builder().buil...
TESTING - WITH SPOCK
def createCheckstyleTask() {



and: "we apply the extension"

project.extensions.create('qualityChec...
TESTING - WITH SPOCK
def createCheckstyleTask() {



when: "we create a checkstyle task"

def checkstyleTask = project.tas...
TESTING - WITH SPOCK
def createCheckstyleTask() {



then: "it should not replace our previous file"

checkstyleTask.configF...
TESTING - WITH SPOCK
@BRWNGRLDEV
TESTING - REPORT
@BRWNGRLDEV
TESTING - SPOCK-REPORT
dependencies {
…
testCompile( 'com.athaydes:spock-reports:1.2.12' ) {

transitive = false
}
}
@BRWN...
TESTING - SPOCK-REPORT
@BRWNGRLDEV
@BRWNGRLDEV
BONUS: README
@BRWNGRLDEV
BONUS: README
@BRWNGRLDEV
BONUS: README
@BRWNGRLDEV
SUMMARY
▸Helps avoid copy/paste horror
▸Simple project structure
▸Extending DefaultTask
▸Testing techniques
▸Easy to publi...
THANKS!
@brwngrldev
+AnnyceDavis
www.adavis.info
@BRWNGRLDEV
Upcoming SlideShare
Loading in …5
×

Creating Gradle Plugins - GR8Conf US

318 views

Published on

If you find yourself copying and pasting code from your build.gradle file every time you start a new project, then it's time you learn about Gradle Plugins. Come and learn what it takes to create a custom Gradle plugin for use in your Android/Java applications.

We're going to go over the following:

• Plugin Structure
• Key Components
• Testing
• Publishing

Walk away feeling empowered to create your own awesome plugins!

Published in: Software
  • Be the first to comment

Creating Gradle Plugins - GR8Conf US

  1. 1. Gradle Plugin Goodness
  2. 2. @BRWNGRLDEV
  3. 3. @BRWNGRLDEV
  4. 4. apply plugin: 'checkstyle'
 apply plugin: 'findbugs'
 apply plugin: 'pmd'
 
 task checkstyle(type: Checkstyle) {
 description 'Checks if the code is somewhat acceptable'
 group 'verification'
 
 configFile file('./qa-check/checkstyle.xml')
 source 'src'
 include '**/*.java'
 exclude '**/gen/**'
 
 classpath = files()
 ignoreFailures = false
 }
 
 task findbugs(type: FindBugs) {

  5. 5. apply plugin: 'checkstyle'
 apply plugin: 'findbugs'
 apply plugin: 'pmd'
 
 task checkstyle(type: Checkstyle) {
 description 'Checks if the code is somewhat acceptable'
 group 'verification'
 
 configFile file('./qa-check/checkstyle.xml')
 source 'src'
 include '**/*.java'
 exclude '**/gen/**'
 
 classpath = files()
 ignoreFailures = false
 }
 
 task findbugs(type: FindBugs) {
 description 'Run findbugs'
 group 'verification'
 
 classes = files("$project.buildDir/intermediates/classes")
 source 'src'
 classpath = files()
 
 effort 'max'
 excludeFilter file('./qa-check/findbugs-exclude.xml')
 
 reports {
 xml.enabled = true
 html.enabled = false
 }
 
 ignoreFailures = true
 }
 
 task pmd(type: Pmd) {
 description 'Run PMD'
 group 'verification'
 
 ruleSetFiles = files("./qa-check/pmd-ruleset.xml")
 ruleSets = []
 
 source 'src'
 include '**/*.java'
 exclude '**/gen/**'
 
 reports {
 xml.enabled = false
 html.enabled = true
 }
 
 ignoreFailures = true
 } apply plugin: 'info.adavis.qualitychecks'
  6. 6. OVERVIEW ▸Plugin Skeleton ▸Dependencies ▸Plugin.groovy ▸CustomTask.groovy ▸Publishing @BRWNGRLDEV
  7. 7. PLUGIN SKELETON @BRWNGRLDEV
  8. 8. PLUGIN SKELETON @BRWNGRLDEV
  9. 9. PLUGIN SKELETON @BRWNGRLDEV
  10. 10. PLUGIN SKELETON How Gradle finds the Plugin Implementation @BRWNGRLDEV
  11. 11. PLUGIN SKELETON implementation-class=info.adavis.qualitychecks.QualityChecksPlugin @BRWNGRLDEV
  12. 12. DEPENDENCIES @BRWNGRLDEV
  13. 13. DEPENDENCIES apply plugin: ‘groovy' dependencies { compile gradleApi() compile localGroovy() } @BRWNGRLDEV
  14. 14. DEPENDENCIES dependencies { … testCompile 'junit:junit:4.12'
 testCompile ('org.spockframework:spock-core:1.0-groovy-2.4') {
 exclude module: 'groovy-all'
 } } @BRWNGRLDEV
  15. 15. THE CODE @BRWNGRLDEV
  16. 16. BUT FIRST… @BRWNGRLDEV
  17. 17. GRADLE BUILD @BRWNGRLDEV PROJECT TASK TASK TASK BUILD
  18. 18. GRADLE BUILD @BRWNGRLDEV PROJECT TASK TASK TASK BUILD PROJECT TASK TASK TASK
  19. 19. PLUGIN.GROOVY class CustomPlugin implements Plugin<Project> { } @BRWNGRLDEV
  20. 20. PLUGIN.GROOVY class CustomPlugin implements Plugin<Project> { @Override void apply(Project project) { } } @BRWNGRLDEV
  21. 21. QUALITYCHECKSPLUGIN.GROOVY @BRWNGRLDEV
  22. 22. APPLY METHOD @BRWNGRLDEV void apply(Project project) {
 this.project = project
 
 }
  23. 23. APPLY METHOD @BRWNGRLDEV void apply(Project project) {
 this.project = project
 
 project.extensions.create('qualityChecks', QualityChecksExtension)
 
 }
  24. 24. APPLY METHOD @BRWNGRLDEV void apply(Project project) {
 this.project = project
 
 project.extensions.create('qualityChecks', QualityChecksExtension)
 
 createConfigFilesIfNeeded()
 createConfigFileTasks()
 createQualityChecksTasks()
 }
  25. 25. PROJECT EXTENSION @BRWNGRLDEV class QualityChecksExtension {
 
 String pmdConfigFile = 'quality-checks/pmd-ruleset.xml'
 
 String checkstyleConfigFile = 'quality-checks/checkstyle.xml'
 
 String findBugsExclusionFile = 'quality-checks/findbugs-exclude.xml'
 
 }
  26. 26. PROJECT EXTENSION @BRWNGRLDEV class QualityChecksExtension {
 
 String pmdConfigFile = 'quality-checks/pmd-ruleset.xml'
 
 String checkstyleConfigFile = 'quality-checks/checkstyle.xml'
 
 String findBugsExclusionFile = 'quality-checks/findbugs-exclude.xml'
 
 }
  27. 27. PROJECT EXTENSION Back in the application’s build.gradle file… @BRWNGRLDEV qualityChecks {
 pmdConfigFile = ‘checks/pmd.xml’ checkstyleConfigFile = ‘checks/checkstyle.xml’
 }
  28. 28. PROJECT EXTENSION Back in the application’s build.gradle file… @BRWNGRLDEV qualityChecks {
 pmdConfigFile = ‘checks/pmd.xml’ checkstyleConfigFile = ‘checks/checkstyle.xml’
 }
  29. 29. TASKS @BRWNGRLDEV
  30. 30. CREATING TASKS Give it a name and a type @BRWNGRLDEV
  31. 31. CREATING TASKS @BRWNGRLDEV ▸Build on existing task ▸Extend the DefaultTask
  32. 32. BUILD ON EXISTING TASK @BRWNGRLDEV
  33. 33. BUILD ON EXISTING @BRWNGRLDEV
  34. 34. EXTEND DEFAULT TASK @BRWNGRLDEV
  35. 35. CUSTOMTASK.GROOVY class CustomTask extends DefaultTask { CustomTask() { group: ‘verification’ } } @BRWNGRLDEV
  36. 36. CUSTOMTASK.GROOVY class CustomTask extends DefaultTask { CustomTask() { group: ‘verification’ } } @BRWNGRLDEV
  37. 37. CUSTOMTASK.GROOVY CustomTask() { group: ‘verification’ onlyIf { // skip under certain conditions } } @BRWNGRLDEV
  38. 38. CUSTOMTASK.GROOVY @TaskAction def defaultAction() { description: ‘What my task does’ } @BRWNGRLDEV
  39. 39. CUSTOMTASK.GROOVY @BRWNGRLDEV @TaskAction def defaultAction() { description: ‘What my task does’ <do your cool stuff here> }
  40. 40. GRADLE TASK DOCUMENTATION @BRWNGRLDEV
  41. 41. SO FAR… ▸Plugin Skeleton ▸Dependencies ▸Plugin.groovy ▸CustomTask.groovy @BRWNGRLDEV
  42. 42. PUBLISHING @BRWNGRLDEV
  43. 43. PUBLISHING @BRWNGRLDEV
  44. 44. PUBLISHING buildscript { … dependencies { classpath "com.gradle.publish:plugin-publish-plugin:0.9.4" } } apply plugin: 'com.gradle.plugin-publish' @BRWNGRLDEV
  45. 45. PUBLISHING version = "0.1.3" group = "info.adavis" @BRWNGRLDEV
  46. 46. PUBLISHING pluginBundle { website = 'https://github.com/adavis/quality-checks' vcsUrl = 'https://github.com/adavis/quality-checks.git' description = 'Gradle Plugin for…’ tags = ['Checkstyle', 'FindBugs', 'PMD'] } @BRWNGRLDEV
  47. 47. PUBLISHING pluginBundle { … plugins { qualityChecksPlugin { id = 'info.adavis.qualitychecks' displayName = 'Quality Checks Plugin' } } @BRWNGRLDEV
  48. 48. @BRWNGRLDEV
  49. 49. @BRWNGRLDEV
  50. 50. WE’RE DONE… WRONG! @BRWNGRLDEV
  51. 51. WE’RE DONE… TESTS @BRWNGRLDEV
  52. 52. TESTING - WITH JUNIT @Before
 void setUp() {
 projectDir = temporaryFolder.root
 projectDir.mkdirs()
 
 
 } @BRWNGRLDEV
  53. 53. TESTING - WITH JUNIT @Before
 void setUp() {
 projectDir = temporaryFolder.root
 projectDir.mkdirs()
 
 project = ProjectBuilder.builder().withProjectDir(projectDir).build()
 task = project.tasks.create('writeConfigFile', WriteConfigFileTask)
 } @BRWNGRLDEV
  54. 54. TESTING - WITH JUNIT @Test void shouldBeAbleToCreateTask() { assertTrue(task instanceof WriteConfigFileTask) } @BRWNGRLDEV
  55. 55. TESTING - WITH JUNIT @Test void pluginShouldBeApplied() { project.apply(plugin: QualityChecksPlugin) assertNotNull(project.tasks.findByName(‘mytask’)) } @BRWNGRLDEV
  56. 56. SPOCK @BRWNGRLDEV
  57. 57. TESTING - WITH SPOCK def createCheckstyleTask() {
 given: "we have a project"
 def project = ProjectBuilder.builder().build() 
 }
  58. 58. TESTING - WITH SPOCK def createCheckstyleTask() {
 
 and: "we apply the extension"
 project.extensions.create('qualityChecks', QualityChecksExtension)
 
 and: "we supply an existing checkstyle config file"
 project.qualityChecks.checkstyleConfigFile = File.createTempFile('temp', '.xml').path
 }
  59. 59. TESTING - WITH SPOCK def createCheckstyleTask() {
 
 when: "we create a checkstyle task"
 def checkstyleTask = project.tasks.create('checkstyle', CheckstyleTask)
 
 
 }
  60. 60. TESTING - WITH SPOCK def createCheckstyleTask() {
 
 then: "it should not replace our previous file"
 checkstyleTask.configFile.name.startsWith('temp')
 }
  61. 61. TESTING - WITH SPOCK @BRWNGRLDEV
  62. 62. TESTING - REPORT @BRWNGRLDEV
  63. 63. TESTING - SPOCK-REPORT dependencies { … testCompile( 'com.athaydes:spock-reports:1.2.12' ) {
 transitive = false } } @BRWNGRLDEV
  64. 64. TESTING - SPOCK-REPORT @BRWNGRLDEV
  65. 65. @BRWNGRLDEV
  66. 66. BONUS: README @BRWNGRLDEV
  67. 67. BONUS: README @BRWNGRLDEV
  68. 68. BONUS: README @BRWNGRLDEV
  69. 69. SUMMARY ▸Helps avoid copy/paste horror ▸Simple project structure ▸Extending DefaultTask ▸Testing techniques ▸Easy to publish @BRWNGRLDEV
  70. 70. THANKS! @brwngrldev +AnnyceDavis www.adavis.info @BRWNGRLDEV

×