Quest for the Holy Gradle – Software Build Top-to-Bottom



Quest for the Holy Gradle – Software Build Top-to-Bottom

1 1


holygradle

Presentation on using Gradle for multi-language builds

On Github nastevens / holygradle

Quest for the Holy Gradle

Software Build Top-to-Bottom

Nick Stevens / @bitcurry

Who Am I?

  • Embedded Systems Programmer by Day
  • Programming Language Polyglot by Night
  • Vim user
  • Sickly Fascinated with Building Software
  • Embedded Systems Programmer by Day
  • Programming Language Polyglot by Night Bruce Tate - Seven Languages in Seven Weeks
  • Vim user
  • Sickly Fascinated with Building Software

What Is Gradle?

Build configuration tool for the JVM

Build configuration tool for multiple languages

Groovy-based domain-specific language for software builds

Accurate but incomplete. Gradle supports more than just JVM languages. A little better, but "configuration" isn't quite right A mouthful, but the general idea
  • Gradle is not configuration
  • Basic build handled by config
  • Groovy exists if needed - difficult to paint into a corner

Our Goals Today

  • Build a REST webservice using Java
  • Debug webservice on local HTTP server
  • Unit test the webservice using Groovy
  • Add static webpage content
  • Add JQuery dependency
  • Compile CoffeeScript to JavaScript
  • Collect and minify JavaScript
  • Combine all artifacts into a WAR file

All done in an environment with only the JDK installed*

Go from 'git clone' to running debug server in one command

Today I want to show you what Gradle is capable of

Demonstration

  • Build and run using 'gradle runDebug'
  • Show generation of production jar

Gradle Bootcamp

  • Basic Gradle structures: projects and tasks
  • Build begins with contents of build.gradle file
  • Gradle plugins provide conventions, tasks, and configurations
  • Plugins expose varying amounts of configuration
  • In the end though everything is code
  • Tasks similar to maven goals or ant tasks
  • Projects are collections of tasks
  • Conventions often just "make sense"
  • Everything is code - means everything can be modified

Building Java

// build.gradle
apply plugin: 'java'

What does that give us?

  • Convention for Java source in src/main/java
  • Convention for Java tests in src/test/java
  • Can build and run tests with gradle build
Create new directory Create source directory Create the following Java source file
package com.bitcurry.holynow;
public class Demo { public static void main(String... args) {
        System.out.println("Imagine Whirled Peas"); } }
mainClassName = 'com.bitcurry.holynow.HolyNow'

Getting Dependencies

// build.gradle
repositories {
    mavenCentral()
}

dependencies {
    compile group: 'org.glassfish.jersey.core',
            name: 'jersey-server',
            version: '2.7'
    compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.7'
    compile 'org.glassfish.jersey.media:jersey-media-moxy:2.7'
}
From here, going to jump into making small Java code files to implement our webservice

The Gradle Wrapper

// build.gradle
task wrapper(type: Wrapper) {
    gradleVersion = '1.9'
}
This will be the first time that closures and maps are introduced so I'll want to talk about the elements of this code

Groovy Bootcamp

Valid Java == Valid Groovy

  • Built-in Lists and Maps
  • Closures
  • GString
  • Greatly reduced syntax requirements

To the batmobile groovysh!

  • Show lists, maps, closures, GStrings in GroovySh
  • Show syntax reduction
    • apply plugin: 'java' is actually apply([plugin:'java'])
    • [ ] around maps and lists are often optional
    • Parens around function parameters not required
    • function(..., Closure closure) can be written function(...) { }

Expanded Dependencies

// repositories and dependencies are functions that takes a closure
Closure repositoryConfig = { mavenCentral() }
repositories(repositoryConfig)

dependencies({
    // compile is an overloaded function that works on a map or a string
    compile([group: 'org.glassfish.jersey.core',
             name: 'jersey-server', version: '2.7'])
    compile('org.glassfish.jersey.containers:jersey-container-servlet:2.7')
    compile('org.glassfish.jersey.media:jersey-media-moxy:2.7')
})

Deploy Webservice

https://github.com/Khoulaiz/gradle-jetty-eclipse-plugin
buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        compile 'com.sahlbach.gradle:gradle-jetty-eclipse-plugin:1.9.+'
    }
}

apply plugin: 'war'
apply plugin: 'jettyEclipse'

Run ./gradlew build war jettyEclipseRun

http://localhost:8080

Descriptive Task Name

task runDebug(dependsOn: [tasks.build, tasks.war]) {
    tasks.jettyEclipseRun.execute()
}

Testing with Groovy

apply plugin: 'groovy'

dependencies {
    testCompile 'org.glassfish.jersey.core:jersey-client:2.7'
    testCompile 'org.glassfish.jersey.test-framework.providers:' +
                'jersey-test-framework-provider-jetty:2.7'
                /* ^ developer paid by character in package name? */

    testCompile 'org.codehaus.groovy:groovy-all:2.2.2'
    testCompile 'junit:junit:4.11'
}
Jump over to code for groovy test after slide

Add Static Web Content

Put it in src/main/webapp

Have a beer!

Add JQuery Library

webjars.org

dependencies {
    runtime 'org.webjars:jquery:2.1.0-2'
}
<script src="webjars/jquery/2.1.0/jquery.min.js"></script>

Make sure to talk about jars getting auto-added to the classpath to make this work

Compiling CoffeeScript

  • Incubating CoffeeScript feature in Gradle core
  • Write a custom task around WRO4J
class CompileCoffeeScript extends DefaultTask {
    @InputDirectory
    def srcDir = "src/main/coffee"

    @OutputDirectory
    def destDir = "${buildDir}/js"

    @TaskAction
    void doCompile() {
        /* ... */
    }
}

CoffeeScript (cont)

// buildSrc/build.gradle
dependencies {
    compile 'ro.isdc.wro4j:wro4j-extensions:1.7.4'
}
// build.gradle
task compileCoffee(type: CompileCoffeeScript) {
    srcDir = file('src/main/coffee')
    destDir = new File($buildDir, 'js')
}

Combine/Minify JavaScript

// buildSrc/build.gradle
dependencies {
    compile 'com.eriwen:gradle-js-plugin:1.9.0'
}
// build.gradle
apply plugin: 'js'

combineJs {
    source = tasks.compileCoffee
    dest = new File(buildDir, 'all.js')
}

minifyJs {
    source = tasks.combineJs
    dest = new File(buildDir, 'all.min.js')
    sourceMap = new File(buildDir, 'all.sourcemap.json')
}

tasks.combineJs.dependsOn tasks.compileCoffee
tasks.minifyJs.dependsOn tasks.combineJs

Questions?

Links

gradle.org

reveal.js [http://github.com/hakimel/reveal.js]

https://github.com/nastevens/holygradle