Why Gradle?
- 2. When did it all begin?
• Programmable
machines have a long
history
• Mechanised
instruments
• Looms
Late 18th/early 19th centuries
- 3. Punched
cards
First used for looms in 18th Century France - Jacquard Loom 1801
IBM introduced punched card format 1928 - 80 columns!
Text mode column width for DOS, Unix etc.
Imagine creating by hand
Keypunch machines (a little like typewriters) for creating them
Errors mean throwing away the card and doing it again
Fortran and Cobol started with punched cards
- 4. Fixing card decks
What if your cards are dropped on the floor?
This is an IBM 082 Sorter
Automation of a very error-prone manual process
- 7. Make
*.c: *.o
cc …
myapp: app.o mylib.o …
ld …
+ manual custom dependencies
Focus mainly on compilation and linking
Make has powerful model based on file dependencies
Maintenance was a lot of work
- 8. Make
*.c: *.o
cc …
myapp: app.o mylib.o …
ld …
What’s this?
+ manual custom dependencies
Tabs were the bane of Make file writers
- 9. Java
• Compiler handles .java to .class
dependencies
• JAR as ‘module’ unit
• Several standard deployment types
- Applet
- WAR
- Standalone app
Publish == prod server, installer, JAR, zip package, etc.
- 10. A standard build
Compile Test Package Publish?
A simplification from the days of C
Interesting point: does the packaging actually require the tests to run?
- 12. Our builds last a long time
You want a tool that can grow and adapt
One that handles all sorts of build processes
Even Java builds are evolving
- 13. Model as a graph
Step
Step Step
Step
Step
All build processes can be modeled as a Directed Acyclic Graph (DAG) of steps
This is the Gradle model (a task == a step)
- 15. Gradle Conventions
apply plugin: "java"
group = "org.example"
version = "0.7.1"
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
Java build conventions
provided by plugin
- 16. Gradle Conventions
• compileJava (src/main/java)
• compileTestJava (src/test/java)
• test (added as dependency of check)
- test reports in build/reports/test
• jar (added as dependency of assemble)
- created in build/libs
• javadoc
- 18. Java build evolved
Compile Test Package Publish?
Dev setup Run
QA
SCM Tags
Deployment
Docs
Generators
JS + CSS
Builds are incorporating more and more steps
Android is an example of a quite different Java build
- 21. Each transfer between different build systems is like a baton handover - with the potential for the baton to be dropped.
- 23. Goals of a build
What do we want out of our build processes?
- 25. Automation
• Humans are fallible
• Every manual step adds to the fragility of
the build
• Muscle memory can help, but rarely
applicable
People are fallible
Muscle memory can help
e.g. tying shoe laces
Not usually applicable in software development
- 26. Mistakes == Lost time
(and possibly money)
Errors made deploying to beta.grails.org and grails.org
Long path between updating source code and starting the server with changes
Stress and downtime
- 27. Does your build have manual
steps? If so, why?
For example, copying files around, entering parameter values, running external scripts, etc.
- 28. Custom tasks in Gradle
task publishGuide << {
// Generate HTML from .gdoc
}
build.dependsOn publishGuide
buildSrc/src/groovy/pkg/PublishGuideTask.groovy
pkg.PublishGuideTask in a JAR
- 29. Don’t reinvent the wheel
http://plugins.gradle.org/
Gradle Plugin Portal
This is searchable
Contains many (but not all) publicly available plugins
You can publish your own too
- 31. Same inputs should result in
same outputs
In other words, repeatable builds
Environment should not be a factor
Does the build behave differently on different platforms?
Consider “works for me” with local Maven cache
- 32. Depends on tasks
• Are tasks environment-dependent?
• Do system properties or environment
variables have an effect?
• What about number of cores?
• Order of tests?
Gradle can’t help much with this - except for task ordering
- 33. Task ordering
• mustRunAfter/shouldRunAfter
• No task dependencies
task integTest {
...
}
task packageReports {
mustRunAfter integTest
...
}
`packageReports` does not depend on `integTest`
But if both are executed, `packageReports` must come after `integTest`
shouldRunAfter is less strict - mostly to optimise feedback
- 34. Task ordering
• finalizedBy
• Ensures execution of the finalizer task
task integTest {
finalizedBy packageReports
...
}
task packageReports {
...
}
`packageReports` does not depend on `integTest`
If `integTest` runs, then `packageReports` will run too, always after `integTest`
- 35. Gradle cache
• Origin checks
• Artifact checksums
• Concurrency safe
• Avoid mavenLocal()!
Dependency cache can be a big impediment to repeatability
- 36. Resolution strategy
configurations.all {
resolutionStrategy {
failOnVersionConflict()
force 'asm:asm-all:3.3.1',
'commons-io:commons-io:1.4'
}
Override version to use
for specific dependencies
Fail the build if any
version conflicts
Defaults to ‘newest’ strategy
Fine-grained control over dependency versions
Automatic conflict resolution error-prone
- 37. Resolution strategy
configurations.all {
eachDependency { details ->
if (details.requested.name == 'groovy-all') {
details.useTarget(
group: details.requested.group,
name: 'groovy',
version: details.requested.version)
}
}
}
Control modules with
different names, same classes
A painful problem to debug
- 38. Efficiency
Some tasks take significant amounts of time to execute
Why run them each time?
Think Ant/Maven incremental compilation
- 40. Task inputs & outputs
class BintrayGenericUpload extends DefaultTask {
@InputFile File artifactFile
@Input String artifactUrlPath
@Optional
@Input String repositoryUrl
…⋯
}
Use of annotations makes task support incremental build
Inputs and outputs can be values, files, directories
- 42. On top of this, Gradle gives you
• a rich API for modelling your build process
• full dependency management
• Java ecosystem integration
- IDEs, CI, static analysis tools, etc.
• Flexible publishing (Maven, Ivy,AWS S3)
• Many useful plugins to save you work
- 43. Summary
• End-to-end process is the build
• 80-20 rule (80% standard 20% custom)
• Automate everything == save time
• Invest in build as if it’s part of your code
base
• Building software requires a rich model