Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

GetSocial SDK is available on three major platforms

Slide 5

Slide 5 text

#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

Slide 6

Slide 6 text

#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

Slide 7

Slide 7 text

#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

Slide 8

Slide 8 text

#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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

#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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

#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

Slide 13

Slide 13 text

#devfestnl So what we're going to do?

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

#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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

#devfestnl Looks like we have a plan

Slide 18

Slide 18 text

Gradle Unity Build Iteration 1. Time to rock

Slide 19

Slide 19 text

Bunch of Gradle tasks we had at the end of iteration

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

#devfestnl Local variables def releaseDir = "$buildDir/release" task copyReadme(type: Copy) {
 from "$rootDir/README.md"
 into releaseDir
 } build.gradle Declare and use variables

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

#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.

Slide 24

Slide 24 text

#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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

#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

Slide 27

Slide 27 text

#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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

#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

Slide 30

Slide 30 text

#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

Slide 31

Slide 31 text

#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

Slide 32

Slide 32 text

Gradle Unity Build Iteration 2. We can do better

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

#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

Slide 38

Slide 38 text

#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

Slide 39

Slide 39 text

#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

Slide 40

Slide 40 text

Gradle Unity Build Iteration 3. In progress

Slide 41

Slide 41 text

#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

Slide 42

Slide 42 text

Defined Unity Gradle plugin DSL

Slide 43

Slide 43 text

#devfestnl class UnityPlugin implements Plugin {
 void apply(Project project) {
 ...
 }
 } Define plugins UnityPlugin.groovy apply plugin: UnityPlugin build.grade

Slide 44

Slide 44 text

#devfestnl class UnityPluginExtension {
 String logOutput = 'build/build-log.txt'
 } Add extensions UnityPlugin.groovy void apply(Project project) {
 project.extensions
 .create('unity', UnityPluginExtension)
 } UnityPlugin.groovy

Slide 45

Slide 45 text

#devfestnl Add extensions unity {
 logOutput = 'demo-app-build-log.txt'
 } build.grade void apply(Project project) {
 project.extensions
 .create('unity', UnityPluginExtension, project)
 } UnityPlugin.groovy

Slide 46

Slide 46 text

Takeaways

Slide 47

Slide 47 text

#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

Slide 48

Slide 48 text

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