In the Brain of Hans Dockter: Gradle

3,899 views

Published on

Gradle is a flexible general purpose build system with a build-by-convention framework a la Maven on top. It uses Apache Ivy under the hood for its dependency management. Its build scripts are written in Groovy.

Published in: Education, Technology
1 Comment
5 Likes
Statistics
Notes
  • Watch the presentation at

    http://skillsmatter.com/podcast/java-jee/gradle-a-new-build-system/zx-489
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
3,899
On SlideShare
0
From Embeds
0
Number of Embeds
332
Actions
Shares
0
Downloads
0
Comments
1
Likes
5
Embeds 0
No embeds

No notes for slide

In the Brain of Hans Dockter: Gradle

  1. 1. 1 In The Brain 2009 Gradle Hans Dockter Gradle Project Lead mail@dockter.biz
  2. 2. 2 About Me Founder and Project Lead of Gradle CEO of Gradle Inc. Trainer for Skills Matter (TTD, Patterns, DDD) In the old days: Committer to JBoss (Founder of JBoss-IDE)
  3. 3. 3 Gradle Overview 1 A flexible general purpose build tool Offers dependency based programming with a rich API Build-by-convention plugins on top Powerful multi-project support Powerful dependency management based on Apache Ivy Deep Integration with Ant
  4. 4. 4 Gradle Overview 2 Build Scripts are written in Groovy We get our general purpose elements from a full blown OO language The perfect base to provide a mix of: Small frameworks, toolsets and dependency based programming Rich interaction with Java Gradle is NOT a framework Gradle is mostly written in Java with a Groovy DSL layer on top Offers good documentation (150+ Pages user’s guide) Commiter -> Steve Appling, Hans Dockter, Tom Eyckmans, Adam Murdoch, Russel Winder
  5. 5. 5 Dependency Based Programming task hello << { println 'Hello world!' } task intro(dependsOn: hello) << { println "I'm Gradle" } 4.times { counter -> task "task_$counter" << { println "I'm task $counter" } } > gradle hello task1 Hello world! I’m task 1
  6. 6. 6 Rich API task hello println hello.name hello.dependsOn distZip hello << { println ‘Hello’ } task distZip(type: Zip) distZip.fileSet(dir: ‘somePath’) distZip.baseName = hello.name
  7. 7. 7 Very Rich API: Test Task Register listeners for test execution (this works even in forked mode) Get informed about a started test execution Get informed about a failing or succeeding test Provides an API to ask for execution results. Part of Gradle 0.8
  8. 8. 8 One way configuration Ant <target name="test" depends="compile-test"> <junit> <classpath refid="classpath.test" /> <formatter type="brief" usefile="false" /> <test name="${test.suite}"/> </junit> </target>
  9. 9. 9 One way configuration Maven <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.4.2</version> <configuration> <includes> <include>Sample.java</include> </includes> </configuration> </plugin>
  10. 10. 10 Java Plugin usePlugin(‘java’)
  11. 11. 10 Java Plugin usePlugin(‘java’) repositories { mavenCentral() } dependencies { compile "commons-lang:commons-lang:3.1", "org.hibernate:hibernate:3.2" runtime “mysql:mysql-connector-java:5.1.6” testCompile "junit:junit:4.4" }
  12. 12. 10 Java Plugin usePlugin(‘java’) repositories { mavenCentral() } dependencies { compile "commons-lang:commons-lang:3.1", "org.hibernate:hibernate:3.2" runtime “mysql:mysql-connector-java:5.1.6” testCompile "junit:junit:4.4" } test { exclude '**/Abstract*' } task preCompile << { // do something } compile.dependsOn preCompile task myDocs(type: Zip) { fileset(dir: "path/docs") }
  13. 13. 11 Custom Plugins public class CustomPlugin implements Plugin { public void use(final Project project, ProjectPluginsContainer plugins) { project.myProperty = ‘myValue’ plugins.withType(JavaPlugin).allPlugins { project.getTasks().add(‘someName’, SomeType.class) } plugins.withType(WarPlugin.class).allPlugins { // do something with the project } }
  14. 14. 12 Deep API tasks.whenTaskAdded { task -> task.description = ‘This is a new task’ } tasks.allTasks { task -> task.description = ‘I am new or old’ } tasks.withType(Zip).whenTaskAdded { task -> task.fileSet(dir: ‘someDir’) } liveLibs = tasks.withType(Jar) task someJar(type: Jar) println liveLibs.all // prints [‘someJar’]
  15. 15. 13 Rules tasks.addRule("Pattern: ping<ID>") { String taskName -> if (taskName.startsWith("ping")) { task(taskName) << { // add task println "Pinging: " + (taskName - 'ping') } } } task groupPing { dependsOn pingServer1, pingServer2 } > gradle pingServer545 Server545 > gradle groupPing Server1 Server2
  16. 16. 14 Dependency Management 1 usePlugin(‘java’) ... configurations { allJar.extends runtime } dependencies { compile "commons-lang:commons-lang:3.1", "org.hibernate:hibernate:3.2" runtime “mysql:mysql-connector-java:5.1.6” testCompile "junit:junit:4.4" allJar "commons-io:commons-io:1.4" } ... task jarAll(type: Jar) { merge(dependencies.allJar.resolve()) }
  17. 17. 15 Dependency Management 2 usePlugin(‘java’) ... dependencies { compile "commons-lang:commons-lang:3.1", "org.hibernate:hibernate:3.2" runtime “mysql:mysql-connector-java:5.1.6” testCompile "junit:junit:4.4" } ... task showDeps << { println(configurations.runtime.files { dep -> dep.group == ‘myGroup’ }) }
  18. 18. 16 Dependency Management 3 Excludes per configuration or dependency You can define rules for configuration and dependencies (as for tasks) Very flexible repository handling Retrieve and deploy from/to Maven repositories Dependencies can have dynamic properties And much more
  19. 19. 17 Using Ant Tasks ant { taskdef name: "groovyc", classname: "org.groovy.ant.Groovyc" groovyc srcdir: "src", destdir: "${webinf}/classes", { classpath { fileset dir: "lib" { include name: "*.jar" } pathelement path: "classes" } javac source: "1.5", target: "1.5", debug: "on" } }
  20. 20. 18 Deep Integration with Ant Builds <project> <target name="hello" depends="intro"> <echo>Hello, from Ant</echo> </target> </project> ant.importBuild 'build.xml' hello.doFirst { println 'Here comes Ant' } task intro << { println 'Hello, from Gradle'} > gradle hello Hello, from Gradle...Here comes Ant...[ant:echo] Hello, from Ant
  21. 21. 19 Smart and Configurable Execution
  22. 22. 20 Deep API usePlugin(‘java’) ... compile.doFirst { compileTask -> println build.taskGraph.allTasks if (build.taskGraph.hasTask(‘codeGeneration’)) { compileTask.exclude ‘com.mycomp.somePackage’ } } build.taskGraph.beforeTask { task -> println “I am executing now $task.name” } build.taskGraph.afterTask { task, exception -> if (task instanceof Jetty && exception != null) { // do something } }
  23. 23. 21 Smart Task Exclusion A C B E D In Gradle 0.7 you can execute: gradle A B!
  24. 24. 22 Smart Merging test resources compile clean > gradle clean compile test > gradle clean compile; gradle test
  25. 25. 23 Smart Skipping usePlugin(‘java’) ... task instrument(dependsOn: compile) { onlyIf { timestampChanged classesDir // or contentsChanged classesDir } // do something }
  26. 26. 24 Multi-Project Builds Arbitrary Multiproject Layout Configuration Injection Separate Config/Execution Hierarchy Partial builds
  27. 27. 25 Configuration Injection ultimateApp api webservice shared subprojects { usePlugin(‘java’) dependencies { compile "commons-lang:commons-lang:3.1" testCompile "junit:junit:4.4" } test { exclude '**/Abstract*' } }
  28. 28. 26 Separation of Config/Exec ultimateApp api webservice shared dependsOnChildren() subprojects { usePlugin(‘java’) dependencies { compile "commons-lang:commons-lang:3.1" testCompile "junit:junit:4.4" } } task dist(type: Zip) << { subprojects.each { subproject -> files(subproject.jar.archivePath) } }
  29. 29. 27 Dependencies and Partial Builds ultimateApp api webservice shared dependsOn webservice dependencies { compile "commons-lang:commons-lang:3.1", project(‘:shared’) }
  30. 30. 28 Maven
  31. 31. 29 Convention instead of Configuration
  32. 32. 30 Frameworkitis ... ... is the disease that a framework wants to do too much for you or it does it in a way that you don’t want but you can’t change it. It’s fun to get all this functionality for free, but it hurts when the free functionality gets in the way. But you are now tied into the framework. To get the desired behavior you start to fight against the framework. And at this point you often start to lose, because it’s difficult to bend the framework in a direction it didn’t anticipate. Toolkits do not attempt to take control for you and they therefore do not suffer from frameworkitis. (Erich Gamma)
  33. 33. 31 Solution Because the bigger the framework becomes, the greater the chances that it will want to do too much, the bigger the learning curves become, and the more difficult it becomes to maintain it. If you really want to take the risk of doing frameworks, you want to have small and focused frameworks that you can also probably make optional. If you really want to, you can use the framework, but you can also use the toolkit. That’s a good position that avoids this frameworkitis problem, where you get really frustrated because you have to use the framework. Ideally I’d like to have a toolbox of smaller frameworks where I can pick and choose, so that I can pay the framework costs as I go.(Erich Gamma)
  34. 34. 32 Build Language instead of Build Framework
  35. 35. 33 Organizing Build Logic No unnecessary indirections If build specific: Within the script Build Sources Otherwise: Jar
  36. 36. 34 Gradle Wrapper Use Gradle without having Gradle installed Useful for CI and open source projects
  37. 37. 35 Production Ready? YES! (If you don’t need a missing feature). There are already large enterprise builds migrating from Ant and Maven to Gradle Expect 1.0 in autumn Roadmap: see http://www.gradle.org/roadmap
  38. 38. 36 Questions
  39. 39. 37 The old bulls ...
  40. 40. 38 ANT Ant’s domain model in five words?
  41. 41. 39 Ant Properties Resources Targets Tasks Flexible Toolset via dependency based programming
  42. 42. 40 Ant, XML and DRY
  43. 43. 41 Build By Convention Multi-Project Builds
  44. 44. 42 Live Demo - Hello World
  45. 45. 43 Maven Build-By-Convention Framework Plugins Abstractions for Software Projects Dependency Management
  46. 46. 44 Live Demo - Java
  47. 47. 45 There are no simple builds
  48. 48. 46 Project Automation A build can do far more than just building the jar Often repetitive, time consuming, boring stuff is still done manually Many of those tasks are very company specific Maven & Ant are often not well suited for this
  49. 49. 47 The Gradle Build Gradle is build with Gradle Automatic release management Automatic user’s guide generation Automatic distribution Behavior depends on task execution graph
  50. 50. 48 Release Management The version number is automatically calculated The distribution is build and uploaded to codehaus For trunk releases, a new svn branch is created. A tag is created. A new version properties file is commited. The download links on the website are updated
  51. 51. 49 User’s Guide The user’s guide is written in DocBook and generated by our build The source code examples are mostly real tests and are automatically included The expected output of those tests is automatically included. The tests are run The current version is added to the title
  52. 52. 50 Uploading & Execution Graph Based on the task graph we set: Upload Destination Version Number
  53. 53. 51 Why Groovy scripts?
  54. 54. 51 Why Groovy scripts? [‘Maven’, ‘Ant’, ‘Gradle’].findAll { it.indexOf(‘G’) > -1 }
  55. 55. 51 Why Groovy scripts? [‘Maven’, ‘Ant’, ‘Gradle’].findAll { it.indexOf(‘G’) > -1 } Why not JRuby or Jython scripts?
  56. 56. 51 Why Groovy scripts? [‘Maven’, ‘Ant’, ‘Gradle’].findAll { it.indexOf(‘G’) > -1 } Why not JRuby or Jython scripts? Why a Java core?
  57. 57. 52 Java Plugin usePlugin(‘java’) manifest.mainAttributes([ 'Implementation-Title': 'Gradle', 'Implementation-Version': '0.1' ]) dependencies { compile "commons-lang:commons-lang:3.1" runtime “mysql:mysql-connector-java:5.1.6” testCompile "junit:junit:4.4" } sourceCompatibility = 1.5 targetCompatibility = 1.5 test { exclude '**/Abstract*' } task(type: Zip) { zip() { files(dependencies.runtime.resolve() // add dependencies to zip fileset(dir: "path/distributionFiles") } }

×