Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GDG Devfest Hamburg

Anton
October 14, 2016

GDG Devfest Hamburg

Level up your Gradle build!

Anton

October 14, 2016
Tweet

More Decks by Anton

Other Decks in Programming

Transcript

  1. 2147 tasks in 68 projects -> 32 tasks per project

    IOSCHED 2015 app Some numbers.. Gradle source -> 42 tasks
  2. > created by the user, executed by Gradle > have

    one primary action > ordered on a DAG (directed acyclic graph) Tasks compileJava processResources classes javadoc
  3. > Gradle offers several basic task types to extend from

    > Copy > Exec > Zip > … Framework tasks task('copy', type: Copy) { from(file('srcDir')) into(buildDir) }
  4. > write your own task type by extending DefaultTask >

    declare task action with @TaskAction annotation Custom Tasks class MyTask extends DefaultTask { @TaskAction void sayHello() { println 'Moin Moin!' } } task hello(type: MyTask)
  5. > Most common use is to take input to generate

    some output Custom Tasks TASK Input Output TaskInputs > File(s) > Directories > Properties > TaskOutputs > File(s) > Directories > Action(s)
  6. > a task can be skipped when: > inputs did

    not change > outputs are still there and did not change > shown as UP-TO-DATE > change detection via > snapshot of input & output files > hash of contents of each file > snapshots persisted until next execution Incremental Builds
  7. > Create typed fields or properties (via getter methods) >

    Add annotation to each field or getter method Incremental Builds @Input // for binary inputs, only path check @InputFile //checks path and file contents @InputFiles @InputDirectory @OutputFile @OutputFiles @OutputDirectory
  8. public class ZipAlign extends DefaultTask { // ----- PUBLIC TASK

    API ----- @InputFile File inputFile @OutputFile File outputFile // ----- PRIVATE TASK API ----- @InputFile File zipAlignExe @TaskAction void zipAlign() { project.exec { executable = getZipAlignExe() args '-f', '4' args getInputFile() args getOutputFile() } } } ZipAlign inputFile outputFile zipAlignExe
  9. > Common solution: use dependsOn task('myJavadoc', type: Javadoc) { source

    sourceSets.main.allJava destinationDir file("${project.docsDir}/myjavadoc") } task('zipJavadoc', type: Zip){ from "${project.docsDir}/myjavadoc" dependsOn myJavadoc } Chain tasks
  10. > Use outputs of a task as inputs for another

    task: inferred dependency (instead of using dependsOn) Chain tasks task('myJavadoc', type: Javadoc) { source sourceSets.main.allJava destinationDir file("${project.docsDir}/myjavadoc") } task('zipJavadoc', type: Zip){ from myJavadoc.outputs } Outputs are exposed as FileCollection and can be directly used as an input because from accepts tasks as input (uses project.files() for evaluation)
  11. > Problems can arise if you want to connect @OutputDirectory

    to @InputFiles -> type mismatch > Solution A: use TaskOutputs.files as input Chain custom tasks task('myJavadoc', type: Javadoc) { source sourceSets.main.allJava destinationDir file("${project.docsDir}/myjavadoc") } task('randomTask', type: SomeRandomType ){ inputDir = myJavadoc.outputs.files }
  12. > Solution B: let Gradle evaluate the FileCollection from output

    Chain custom tasks task('myJavadoc', type: Javadoc) { source sourceSets.main.allJava destinationDir file("${project.docsDir}/myjavadoc") } task('randomTask', type: SomeRandomType ){ inputDir = files(myJavadoc) } task('randomTask2', type: SomeRandomType ){ inputDir = files('myJavadoc'){ builtBy 'myJavadoc' } } Reduce amount of files to set generated by distinct task(s)
  13. > Enable/disable tasks with enabled property Skipping tasks task('expensiveTests') {

    ... } expensiveTests.enabled = false expensiveTests.onlyIf { System.getEnv('I_AM_JENKINS') } > Skip tasks with onlyIf method e.g. when on CI/dev > Evaluates the given closure:
  14. > Use a StopExecutionException to exit the current task >

    Build will continue normally and marked as SUCCESSFUL Skipping tasks task('expensiveTests') { doFirst{ if (skipThisTask) { throw new StopExecutionException() } } ... }
  15. > upToDateWhen can be used to implement custom up-to-date checks

    > Force a task to run every time Skipping tasks task('runAlways') { ... outputs.upToDateWhen { false } } task('storeToDB') { ... outputs.upToDateWhen { checkMyDatabase() } }
  16. > Continous checks by Gradle when inputs/outputs of tasks change

    > Affected tasks are executed then > Activate via --continous or –t > Note: still incubating -> could be instable or buggy Continous build
  17. > typical use cases > extend basic Gradle model >

    add new tasks, extend specific model > configure your project/tasks Plugins
  18. > types of Plugins > Script Plugins (local or remote)

    > Binary Plugins Plugins apply from: "myscriptplugin.gradle" // "http://www.xyz.com/myscriptplugin.gradle" build.gradle task taskFromPlugin() { doLast { println "added by a script plugin!" } } myscriptplugin.gradle
  19. > Binary plugins are implementations of the Plugin interface >

    typically compiled and reused via JARs Binary Plugins package my.org class MyPlugin implements Plugin<Project> { void apply(Project project) { Task myTask = project.tasks.add("myTask") myTask.doLast { println "added by a binary plugin!" } } }
  20. > apply via the class object or plugin id >

    plugin id defined in .properties file Binary Plugins apply plugin: my.org.MyPlugin apply plugin: "my-plugin" implementation-class=my.org.MyPlugin META-INF/gradle-plugins/my-plugin.properties
  21. Plugin Extension // Extensions are just plain objects, there is

    no interface/type class MyExtension { String foo MyExtension(String foo) { this.foo = foo } } // Add new extensions via the extension container project.extensions.create('custom', MyExtension) //apply within project custom { foo "bar" }