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

Gradle beyond Android. Or going crazy with build automation

Gradle beyond Android. Or going crazy with build automation

Nowadays almost all Android projects are built with Gradle but do you know that Gradle is not only for Android? You can go pretty crazy with it's possibilities.

During the talk, Vitaliy will share his experience with adopting Gradle to manage complex GetSocial SDK build process with Android, iOS and Unity 3D subprojects.

As a bonus, he'll run quick walk through Gradle plugin creation process and tell how you can share it with the world.

Vitaliy Zasadnyy

October 10, 2015
Tweet

More Decks by Vitaliy Zasadnyy

Other Decks in Programming

Transcript

  1. Gradle beyond Android
    Or going crazy with build automation
    Vitaliy Zasadnyy
    Head of Mobile @ GetSocial
    Founder @ GDG Lviv

    View Slide

  2. #devfestnl
    Gradle is a first class build
    automation citizen on Android
    This presentation is about Gradle
    out of the scope of Android

    View Slide

  3. At GetSocial we make SDK to add
    social features to your app/game

    View Slide

  4. GetSocial SDK is available on three
    major platforms

    View Slide

  5. #devfestnl
    GetSocial SDK architecture
    Android
    SDK
    iOS
    SDK
    iOS Bridge Android Bridge
    C# API
    Unity SDK
    Java Objective C C#
    Public interfaces are
    exposed on: Android -
    Java, iOS - ObjectiveC,
    Unity - C#
    Unity SDK forwards calls
    to native SDKs via thin
    bridge layer

    View Slide

  6. #devfestnl
    Tasks to automate
    Build test apps
    Run Unit Tests
    Run Integration Tests
    Build Android SDK
    Build iOS SDK
    Build Unity SDK
    Generate Docs
    Deploy to Hokeyapp

    View Slide

  7. #devfestnl
    Prehistory of build automation at GetSocial
    • Android: Gradle + Eclipse
    • iOS: Xcode shell build scripts
    • Unity: bash scripts
    When I joined GetSocial automation
    was set up properly on Android and
    iOS
    On Unity - it was written long time
    ago => outdated and not working
    Main problem: 3 platforms - 3
    different approaches to automation

    View Slide

  8. #devfestnl
    Requirements for new build pipeline
    • Unified
    • Crossplatform
    • Concise, clear and pragmatic
    • Powerful
    Unified: one tool for all platforms
    Crossplatform: work on Windows
    and OSX
    No way for XML or similar
    Powerful: it should be easy to
    extend automation logic

    View Slide

  9. #devfestnl
    Available options
    Ant? No, just kidding :)

    View Slide

  10. #devfestnl
    Available options
    Bash pros: applicable to all our
    platforms; with Cygwin runs on
    Windows; pragmatic but not object
    oriented; powerful enough
    Bash cons: on Windows kind of
    magic, no

    View Slide

  11. #devfestnl
    Available options
    Grade looks like a dream comparing
    to Bash and Ant or Maven

    View Slide

  12. #devfestnl
    Decisions made, let's start the journey
    • Android: out of the box
    • iOS: easy, openbackery gradle-xcodePlugin
    • Unity: no plugins
    On Unity there were no plugins, no
    code samples, even no questions on
    Stackoverflow

    View Slide

  13. #devfestnl
    So what we're going to do?

    View Slide

  14. –Me
    If command line interface is available,
    we can automate
    We are going to create our own Unity
    Gradle plugin!

    View Slide

  15. #devfestnl
    Gradle Unity build
    > $UNITY_HOME/Unity -quit -batchmode
    -executeMethod GradleBuildHelper.PerformBuild
    COMMAND LINE
    Unity expose very simple command
    line tool for automation. Main feature:
    invoke any public static method on
    C# side

    View Slide

  16. #devfestnl
    Gradle Unity build
    > $UNITY_HOME/Unity -quit -batchmode
    -executeMethod GradleBuildHelper.PerformBuild
    COMMAND LINE
    There is full control on build process
    from C#

    View Slide

  17. #devfestnl
    Looks like we have a plan

    View Slide

  18. Gradle Unity Build
    Iteration 1. Time to rock

    View Slide

  19. Bunch of Gradle tasks we had at the
    end of iteration

    View Slide

  20. #devfestnl
    Iteration 1. Result
    Automated
    • Build
    • Unit / Integration tests
    • Beta Distribution
    build.grade
    600+ lines

    View Slide

  21. #devfestnl
    Local variables
    def releaseDir = "$buildDir/release"
    task copyReadme(type: Copy) {

    from "$rootDir/README.md"

    into releaseDir

    }
    build.gradle
    Declare and use variables

    View Slide

  22. #devfestnl
    Local variables
    def releaseDir = "$buildDir/release"
    build.gradle
    • Feature of the underlying Groovy language
    • Visible only in declaration scope

    View Slide

  23. #devfestnl
    Extra properties
    project.ext {

    unityProjectDir = "$rootDir/unity-src"

    sdkVersion = "4.0.0"

    targetPlatform = "Android"

    }
    build.gradle
    Gradle objects like projects, tasks,
    and source sets can hold extra user-
    defined properties.

    View Slide

  24. #devfestnl
    Extra properties
    project.ext {

    unityProjectDir = "$rootDir/unity-src"

    }
    build.gradle
    • Part of Gradle domain model
    • Available for projects, tasks, source sets, etc.
    • Accessible via the owning object's ext property

    View Slide

  25. #devfestnl
    Local
    variables
    Extra
    properties
    vs.
    part of scope
    for calculations
    local accessibility
    part of project/task/… DSL
    configuration
    globally available

    View Slide

  26. #devfestnl
    Tasks
    task helloWorld << {

    println "Hello world!"

    }
    build.gradle
    > gradle helloWorld

    Hello world!
    COMMAND LINE
    Tasks create a public API of the build
    script, available via command line
    Task is a part of the work to do in
    automation process

    View Slide

  27. #devfestnl
    Task dependencies
    build.gradle
    task cleanAll(dependsOn: [‘cleanAndroid', 'cleanRelease'])
    build.gradle
    task buildJars(dependsOn: 'android:assemble') {

    ext.getSocialEnvironment = 'master'

    }
    build.gradle
    Gradle do not preserve defined order
    Tasks can be chained

    View Slide

  28. #devfestnl
    Task dependencies
    • dependsOn do not preserve defined order
    • but you can order with mustRunAfter
    task cleanAll(dependsOn: [‘cleanAndroid', 'cleanRelease'])
    build.gradle

    View Slide

  29. #devfestnl
    dependsOnOrdered
    static dependsOnOrdered(Task task, Task... others) {

    task.dependsOn(others)

    for (int i = 0; i < others.size() - 1; i++) {

    others[i + 1].mustRunAfter(others[i])

    }

    }
    build.gradle
    task cleanAll {

    dependsOnOrdered(cleanAll, *[cleanAndroid, cleanRelease,])

    }
    build.gradle
    Any missing behaviour can be easily
    implemented with Groovy

    View Slide

  30. #devfestnl
    task buildUnity(type: Exec) << {

    commandLine "$unityExecutableDir/Unity",

    "-platform=$targetPlatform",

    "-sdkVersion=$sdkVersion",

    '-quit'

    }
    Extending existing tasks
    build.gradle
    • Other: JavaCompile, Jar, Javadoc, Delete…
    • You can create Tasks to extend

    View Slide

  31. #devfestnl
    extensions.buildTypes = new BuildTypes(project)

    buildTypes {

    ciDevelop 'ci', getSocialEnvironment: 'testing'

    ciMaster 'ci', getSocialEnvironment: 'master'

    }
    Build Types
    build.gradle
    • Custom implementation of Build Types
    • Switch to native in near future

    View Slide

  32. Gradle Unity Build
    Iteration 2. We can do better

    View Slide

  33. #devfestnl
    Iteration 1. Problems
    • Big scripts hard to maintain
    • Not DRY enough
    • No separation of concerns

    View Slide

  34. This iteration is only about
    refactoring, set of tasks will stay the
    same

    View Slide

  35. Build script was separated to 6 files
    + few Groovy utility classes

    View Slide

  36. #devfestnl
    Split into several files
    apply from: 'gradle/ci.gradle'
    build.gradle
    task updateVersionInsideUnityProject() { ... }
    task switchBranchAndroid(type: Exec) { ... }
    ci.gradle

    View Slide

  37. #devfestnl
    Split into several files
    • Splited into 6 *.gradle files
    • Separated logic
    • DRAWBACK: can’t share common tasks
    apply from: 'gradle/ci.gradle'
    build.gradle

    View Slide

  38. #devfestnl
    package im.getsocial.unity


    class GetSocialUtils {

    static createDirIfNotExist(dirPath) { ... }

    }
    Move common tasks to Groovy
    GetSocialUtils.groovy
    • Sources location: buildSrc/src/main/groovy/
    • Groovy syntax 95% Java compatible
    Use Groovy, it has tons of syntax
    sugar, code will be much more
    readable

    View Slide

  39. #devfestnl
    Move common tasks to Groovy
    import im.getsocial.unity


    createDirIfNotExist(“$rootDir/build”)
    build.gradle
    package im.getsocial.unity


    class GetSocialUtils {

    static createDirIfNotExist(dirPath) { ... }

    }
    GetSocialUtils.groovy

    View Slide

  40. Gradle Unity Build
    Iteration 3. In progress

    View Slide

  41. #devfestnl
    Requirements
    • Add support for Build Variants
    • Expose simple configuration DSL
    • Encapsulate logic in plugins
    DSL should be as sexy as Android
    Gradle Plugin DSL
    Besides having debug/release builds
    we also need internal/public test
    apps
    Build logic should be encapsulated
    inside

    View Slide

  42. Defined Unity Gradle plugin DSL

    View Slide

  43. #devfestnl
    class UnityPlugin implements Plugin {

    void apply(Project project) {

    ...

    }

    }
    Define plugins
    UnityPlugin.groovy
    apply plugin: UnityPlugin
    build.grade

    View Slide

  44. #devfestnl
    class UnityPluginExtension {

    String logOutput = 'build/build-log.txt'

    }
    Add extensions
    UnityPlugin.groovy
    void apply(Project project) {

    project.extensions

    .create('unity', UnityPluginExtension)

    }
    UnityPlugin.groovy

    View Slide

  45. #devfestnl
    Add extensions
    unity {

    logOutput = 'demo-app-build-log.txt'

    }
    build.grade
    void apply(Project project) {

    project.extensions

    .create('unity', UnityPluginExtension, project)

    }
    UnityPlugin.groovy

    View Slide

  46. Takeaways

    View Slide

  47. #devfestnl
    Takeaways
    • Automate all the things with Gradle
    • Keep it DRY with Groovy / Java util classes
    • Separate concerns with plugins
    • UnityGradle plugin will be open sourced

    View Slide

  48. Thank you!
    Questions?
    Vitaliy Zasadnyy
    @zasadnyy
    Presentation will be available at:
    v.zasadnyy.com/slides/

    View Slide