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

A Healthy Dose of Gradle

A Healthy Dose of Gradle

Chicago Roboto 2017

Gradle is the official build system for Android but few ever go beyond editing dependencies or copy-pasting code from stackoverflow. However, it is possible to have a build that is both fast and tailored to your needs.

This session aims to make you a better developer by taking back control of your build.

We will be covering concepts like the build phases (Initialization, Configuration, Execution), the task graph and the plugins that are available for you. We will look at how to extend an existing build by creating custom tasks, developing our own simple plugins and learning how to debug and test them. And since no one likes a slow build, we will use the Gradle Profiler to troubleshoot performance problems and talk about the usual root causes for neverending builds.

Philippe Breault

April 21, 2017
Tweet

More Decks by Philippe Breault

Other Decks in Technology

Transcript

  1. Intro • Google Developer Expert Android • Software Engineer @

    American Express* • Android since 2010 * Opinions and views on products, services and/or resources expressed in this presentation are mine alone and do not necessarily reflect the views of my employer.
  2. Build from Eclipse •Needs a developer to build •Build process

    was in someone’s head •Environment can change
  3. Ant •Codify the build process •Execute sequentially at will •It

    could be run on CI <project> <target name="clean"> <delete dir="build"/> </target> <target name="compile"> <mkdir dir="build/classes"/> <javac srcdir="src" destdir="build/classes"/> </target> <target name="jar"> <mkdir dir="build/jar"/> <jar destfile="build/jar/HelloWorld.jar" basedir="build/classes"> <manifest> <attribute name="Main-Class" value="oata.HelloWorld"/> </manifest> </jar> </target> <target name="run"> <java jar="build/jar/HelloWorld.jar" fork="true"/> </target> </project>
  4. Build Lifecycle 1. Run settings.xml 2. Add build.gradle 3. Add

    app/build.gradle 4. Run build.gradle 5. Run app/build.gradle 6. Generates tasks 5. Run the task Init Configure Execute Root !"" app # $"" build.gradle !"" build.gradle $"" settings.gradle
  5. task hello { doLast { println "Hello World"} } $./gradlew

    hello :hello Hello World BUILD SUCCESSFUL Total time: 0.584 secs
  6. Groovy methods don’t need semi colons or parentheses System.out.println(“Hello World”);

    println(“Hello World”); println(“Hello World) println “Hello World”
  7. task hello { doLast { println “Hey there” } }

    task goodbye(dependsOn: hello) { doLast { println “See ya" } } $./gradlew goodbye :hello Hey There :goodbye See ya BUILD SUCCESSFUL Total time: 0.713 secs
  8. Map Literals def map = new HashMap() map.put(“firstName", "Philippe") map.put("lastName",

    "Breault") def map = [firstName: "Philippe", lastName:"Breault"]
  9. Named Arguments def printStuff(Map params) { println params.firstName println params.lastName

    } printStuff(firstName: "Philippe", lastName: "Breault") apply plugin: 'com.android.application'
  10. $ ./gradlew greet :greet Hello everyone! BUILD SUCCESSFUL Total time:

    0.731 secs task greet(type: GreeterTask) { whom = "everyone" }
  11. class GreeterTask extends DefaultTask { public String whom = "world"

    @TaskAction def greet() { println "Hello $whom!" } }
  12. // copy text files and upper case their content task

    uppercase(type: UpperCaseTask) { source = "src/main/resources" }
  13. class UpperCaseTask extends DefaultTask { def source = "src/main/resources" def

    destination = "${project.buildDir}/uppercased" @TaskAction def upperCaseAllTheThings() { project.file(source).listFiles().each { original -> def target = new File(project.file(destination), original.name) target.parentFile.mkdirs() target.text = original.text.toUpperCase() } }
  14. class UpperCaseTask extends DefaultTask { def source = "src/main/resources" @InputDirectory

    File getSource() { project.file(source) } def destination = "${project.buildDir}/capitalized" @OutputDirectory File getDestination() { project.file(destination) } @TaskAction def upperCaseAllTheThings() { getSource().listFiles().each { original -> def target = new File(getDestination(), original.name) target.text = original.text.toUpperCase() } } }
  15. class UpperCaseTask extends DefaultTask { def source = "src/main/resources" @InputDirectory

    File getSource() { project.file(source) } def destination = "${project.buildDir}/uppercased" @OutputDirectory File getDestination() { project.file(destination) } @TaskAction def execute(IncrementalTaskInputs input) { // …. } }
  16. @TaskAction def execute(IncrementalTaskInputs input) { input.outOfDate { changed -> println

    "${changed.file.name} is out of date" def targetFile = new File(getDestination(), changed.file.name) targetFile.text = changed.file.text.toUpperCase() } input.removed { removed -> println("${removed.file.name} was removed") new File(getDestination(), removed.file.name).delete() } }
  17. $./gradlew shout :shout YO YO YO WORLD! BUILD SUCCESSFUL Total

    time: 0.763 secs apply plugin: ShouterPlugin shouter { message = "yo yo yo world!" }
  18. class ShouterPlugin implements Plugin<Project>{ @Override void apply(Project project) { project.extensions.create("shouter",

    ShouterPluginExtension) project.task('shout') { doLast { println project.shouter.message.toUpperCase() } } } } class ShouterPluginExtension { public String message }
  19. class ShouterPlugin implements Plugin<Project>{ @Override void apply(Project project) { project.extensions.create("shouter",

    ShouterPluginExtension) project.task('shout') { doLast { println project.shouter.message.toUpperCase() } } } } class ShouterPluginExtension { public String message }
  20. # Scenarios are run in alphabetical order clean_build { tasks

    = [":app:assembleDemoDebug"] cleanup-tasks = ["clean"] } incremental_java_file_change { tasks = [":app:assembleDemoDebug"] apply-non-abi-change-to = "app/src/main/java/com/developerphil/androidapp/MainActivity.java" } incremental_parallel_java_file_change { tasks = [":app:assembleDemoDebug"] gradle-args = ["--parallel", "--offline"] apply-non-abi-change-to = "app/src/main/java/com/developerphil/androidapp/MainActivity.java" }
  21. Don’t set dynamic values in the build config object buildConfigField

    'String', 'GIT_SHA', "\"${readGitSha()}\"" buildConfigField 'String', 'BUILD_DATE', "\"${parseDate()}\""
  22. Don’t configure what you don’t need // ./gradlew assembleDebug -DfastBuild=true

    ext.fastBuild = (System.getProperty("fastBuild", "false").toBoolean()) if (!ext.fastBuild) { apply plugin: ShouterPlugin shouter { message = "yo yo yo world!" } }
  23. Keep it Clean • Use the buildSrc folder • Use

    plugins • Limit the imperative code in your build