SlideShare a Scribd company logo
Why Gradle?
Peter Ledbrook
e: peter@cacoethes.co.uk
w: http://www.cacoethes.co.uk
t: @pledbrook
When did it all begin?
• Programmable
machines have a long
history
• Mechanised
instruments
• Looms
Late 18th/early 19th centuries
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
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
Magnetism saves the day!
Finally, R/W data and code. The dawn of text editors.
C/C++
Multi-platform
builds!
Compiler
Linker
Source
.so/.dll & .exe
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
Make
*.c: *.o
cc …
myapp: app.o mylib.o …
ld …
What’s this?
+ manual custom dependencies
Tabs were the bane of Make file writers
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.
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?
What will builds look like in 5
years time?
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
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)
But we don’t want to set up all
those steps!
Gradle Conventions
apply	 plugin:	 "java"

group	 =	 "org.example"

version	 =	 "0.7.1"

sourceCompatibility	 =	 "1.7"

targetCompatibility	 =	 "1.7"

Java build conventions
provided by plugin
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
Not all builds are the same
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
Custom build
publishPdf
publishGuide
apiDocs
fetchGrailsSource
grails-doc
project
expensive,
so optional
build
A build to generate the Grails user guide
Automation of all steps
No standard tasks
Task graph is a suitable model
Mixed build
C/C++
DLL/SO
Java
Debian packages
Executable
Build barrier
Build barrier
I’ve worked with or know of several projects that have native + Java parts
Each transfer between different build systems is like a baton handover - with the potential for the baton to be dropped.
The build tool should not
dictate the build process
Goals of a build
What do we want out of our build processes?
Automation
People are fallible
Muscle memory can help
e.g. tying shoe laces
Not usually applicable in software development
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
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
Does your build have manual
steps? If so, why?
For example, copying files around, entering parameter values, running external scripts, etc.
Custom tasks in Gradle
task	 publishGuide	 <<	 {

	 	 	 	 //	 Generate	 HTML	 from	 .gdoc

}

build.dependsOn	 publishGuide
buildSrc/src/groovy/pkg/PublishGuideTask.groovy
pkg.PublishGuideTask in a JAR
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
Repeatability
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
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
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
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`
Gradle cache
• Origin checks
• Artifact checksums
• Concurrency safe
• Avoid mavenLocal()!
Dependency cache can be a big impediment to repeatability
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
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
Efficiency
Some tasks take significant amounts of time to execute
Why run them each time?
Think Ant/Maven incremental compilation
Incremental build
:lazybones-app:compileJava

:lazybones-app:compileGroovy	 UP-TO-DATE

:lazybones-app:processResources	 UP-TO-DATE

:lazybones-app:classes	 UP-TO-DATE

:lazybones-app:jar	 UP-TO-DATE

:lazybones-app:startScripts	 UP-TO-DATE

:lazybones-app:installApp	 UP-TO-DATE

BUILD	 SUCCESSFUL

Total	 time:	 2.178	 secs
UP-TO-DATE
Don’t execute tasks you don’t have to
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
A build should be…
Automated
Repeatable
Efficient
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
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
Thank you!
e: peter@cacoethes.co.uk
w: http://www.cacoethes.co.uk
t: @pledbrook

More Related Content

Why Gradle?

  • 1. Why Gradle? Peter Ledbrook e: peter@cacoethes.co.uk w: http://www.cacoethes.co.uk t: @pledbrook
  • 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
  • 5. Magnetism saves the day! Finally, R/W data and code. The dawn of text editors.
  • 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?
  • 11. What will builds look like in 5 years time?
  • 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)
  • 14. But we don’t want to set up all those steps!
  • 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
  • 17. Not all builds are the same
  • 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
  • 19. Custom build publishPdf publishGuide apiDocs fetchGrailsSource grails-doc project expensive, so optional build A build to generate the Grails user guide Automation of all steps No standard tasks Task graph is a suitable model
  • 20. Mixed build C/C++ DLL/SO Java Debian packages Executable Build barrier Build barrier I’ve worked with or know of several projects that have native + Java parts
  • 21. Each transfer between different build systems is like a baton handover - with the potential for the baton to be dropped.
  • 22. The build tool should not dictate the build process
  • 23. Goals of a build What do we want out of our build processes?
  • 24. Automation People are fallible Muscle memory can help e.g. tying shoe laces Not usually applicable in software development
  • 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
  • 39. Incremental build :lazybones-app:compileJava :lazybones-app:compileGroovy UP-TO-DATE :lazybones-app:processResources UP-TO-DATE :lazybones-app:classes UP-TO-DATE :lazybones-app:jar UP-TO-DATE :lazybones-app:startScripts UP-TO-DATE :lazybones-app:installApp UP-TO-DATE BUILD SUCCESSFUL Total time: 2.178 secs UP-TO-DATE Don’t execute tasks you don’t have to
  • 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
  • 41. A build should be… Automated Repeatable Efficient
  • 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
  • 44. Thank you! e: peter@cacoethes.co.uk w: http://www.cacoethes.co.uk t: @pledbrook