
Gradle: Tasks Are Code
In the previous topic, I tried to briefly tell what Gradle is and what ideas it is built on. The concept of Source Sets and the functionality associated with it were also highlighted.
Now I would like to talk about what Gradle personally hooked me on. We will talk about ways to work with tasks. The task in Gradle is a close analogue of Ant Target. And, so as not to get confused in terms, the task (or task) hereinafter will always mean the Gradle Task. If we are talking about an entity from Ant, then this will be indicated explicitly: Ant task.
So, tasks in Gradle are created using a special dsl (domain specific language) based on Groovy. And the possibilities that this dsl provides, in my opinion, are almost endless compared to ant or maven.
Let's start with the traditional Hello World programmer. Suppose we have an empty file
Launch Bingo! But not impressive. Let's try something else:
We start That is the arbitrary definition on Groovy can be in a task definition . And the tasks themselves are a complete Groovy object. And this means that they have properties and methods that allow them to be controlled. For example, adding new actions. Let's look at a more interesting example. Suppose we have a small java project. Here is its build.gradle:
and directory structure
Nothing complicated: version, Maven Central repository, two dependencies for compilation, several packages. When you run the command, the archive will be collected
in the directory . All in full accordance with the agreements. Maven would do the exact same thing. But over time, requirements are known to change. Change the requirements in the example. Suppose we needed to collect Spring-related code into separate archives, assemble a separate jar with unit test classes, and another jar with source codes. In Gradle, this is solved as follows:
We start
And we see: What happened:
I wonder how to do it in Maven?
But life does not stand still, and our demands continue to change. One fine morning, the Spring Foundation demanded to add to the manifest every jar that is related to Spring and is published on Habré, the demo attribute with the value habr.ru. It sounds strange, but we still need to implement them. Add:
Run: Please note that many tasks have been marked UP-TO-DATE. This is another highlight of Gradle - incremental assembly. But about her another time. Now, if you are not too lazy and look at the contents of the archive manifests, you can find the necessary Bingo line in the Spring related ones ! But the requirements of the Spring Foundation continue to change. And now you need to put its checksum next to each jar :) Licensed cleanliness is a serious matter, and we are forced to obey. Unfortunately, Gradle does not have native support for the MD5 calculation operation. But it is in Ant. Well, let's use it. Change the last fragment as follows:
And this time we will collect only the ill-fated spring-related archives: Let's see what happened. Completing the task required minimal effort. Perhaps the requirements used in the example seem artificial and a bit naive, but the tasks that have to be solved in reality are surprisingly diverse. In this article, we were able, with the help of a small amount of Groovy code, to adapt to changing requirements and to perform several tasks that would be difficult to cope with Ant or Maven. Using a flexible programming language instead of xml frees you up and lets you decide how you want to complete your task. To be continued.
Now I would like to talk about what Gradle personally hooked me on. We will talk about ways to work with tasks. The task in Gradle is a close analogue of Ant Target. And, so as not to get confused in terms, the task (or task) hereinafter will always mean the Gradle Task. If we are talking about an entity from Ant, then this will be indicated explicitly: Ant task.
So, tasks in Gradle are created using a special dsl (domain specific language) based on Groovy. And the possibilities that this dsl provides, in my opinion, are almost endless compared to ant or maven.
Let's start with the traditional Hello World programmer. Suppose we have an empty file
build.gradle
. We write:task hello << {
println 'Hello world!'
}
Launch Bingo! But not impressive. Let's try something else:
>gradle -q hello
Hello world!
task upper << {
String someString = 'mY_nAmE'
println "Original: " + someString
println "Upper case: " + someString.toUpperCase()
4.times { print "$it " }
}
We start That is the arbitrary definition on Groovy can be in a task definition . And the tasks themselves are a complete Groovy object. And this means that they have properties and methods that allow them to be controlled. For example, adding new actions. Let's look at a more interesting example. Suppose we have a small java project. Here is its build.gradle:
>gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME
0 1 2 3
apply plugin: 'java'
version = '1.0'
repositories {
mavenCentral()
}
dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.7'
}
and directory structure
/ projectAlpha / src / test / main / java / my / own / code / spring / db / plugin / auth
Nothing complicated: version, Maven Central repository, two dependencies for compilation, several packages. When you run the command, the archive will be collected
>gradle build
in the directory . All in full accordance with the agreements. Maven would do the exact same thing. But over time, requirements are known to change. Change the requirements in the example. Suppose we needed to collect Spring-related code into separate archives, assemble a separate jar with unit test classes, and another jar with source codes. In Gradle, this is solved as follows:
projectAlpha/build/libs
projectAlpha-1.0.jar
task sourcesJar(type: Jar) {
appendix = 'sources'
from sourceSets.main.allJava
}
task testJar(type: Jar) {
appendix = 'test'
from sourceSets.test.classes
}
jar {
exclude 'my/spring/**'
}
task springDbJar(type: Jar) {
appendix = 'spring-db'
from sourceSets.main.classes
include 'my/spring/db/**'
}
task springAuthJar(type: Jar) {
appendix = 'spring-auth'
from sourceSets.main.classes
include 'my/spring/auth/**'
}
task springPluginJar(type: Jar) {
appendix = 'spring-plugin'
from sourceSets.main.classes
include 'my/spring/plugin/**'
}
We start
>gradle assemble
And we see: What happened:
projectAlpha>dir /b build\libs
projectAlpha-1.0.jar
projectAlpha-sources-1.0.jar
projectAlpha-spring-auth-1.0.jar
projectAlpha-spring-db-1.0.jar
projectAlpha-spring-plugin-1.0.jar
projectAlpha-test-1.0.jar
- We defined two new tasks with the Jar type:
sourcesJar
andtestJar
. To describe the contents of the archive, source sets already familiar to you are used. Another attribute is setappendix
, which, as you might guess, will be included in the archive name after the version. - We changed the default task
jar
(it is defined in the plugin) so that classes from specific packages do not fall into the main archive. - We defined 3 more tasks for assembling three separate archives with modules for Spring. When the task was called,
assemble
the build system independently selected all the tasks forming the archives (Zip, Jar ..), and completed them. Having preliminarily processed the dependencies on source sets and compiled the necessary classes, as in the previous article.
I wonder how to do it in Maven?
But life does not stand still, and our demands continue to change. One fine morning, the Spring Foundation demanded to add to the manifest every jar that is related to Spring and is published on Habré, the demo attribute with the value habr.ru. It sounds strange, but we still need to implement them. Add:
tasks.withType(Jar).matching { task -> task.archiveName.contains('spring') }.allObjects { task ->
task.manifest {
attributes demo: 'habr.ru'
}
}
Run: Please note that many tasks have been marked UP-TO-DATE. This is another highlight of Gradle - incremental assembly. But about her another time. Now, if you are not too lazy and look at the contents of the archive manifests, you can find the necessary Bingo line in the Spring related ones ! But the requirements of the Spring Foundation continue to change. And now you need to put its checksum next to each jar :) Licensed cleanliness is a serious matter, and we are forced to obey. Unfortunately, Gradle does not have native support for the MD5 calculation operation. But it is in Ant. Well, let's use it. Change the last fragment as follows:
projectAlpha>gradle assemble
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:sourcesJar UP-TO-DATE
:springAuthJar
:springDbJar
:springPluginJar
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:testJar UP-TO-DATE
:assemble
Manifest-Version: 1.0
demo: habr.ru
def allSpringJars = tasks.withType(Jar).matching { task -> task.archiveName.contains('spring') }
allSpringJars.allObjects { task ->
configure(task) {
manifest {
attributes demo: 'habr.ru'
}
doLast {
ant.checksum(file: archivePath, todir: archivePath.parentFile)
}
}
}
task springJars(dependsOn: allSpringJars)
And this time we will collect only the ill-fated spring-related archives: Let's see what happened. Completing the task required minimal effort. Perhaps the requirements used in the example seem artificial and a bit naive, but the tasks that have to be solved in reality are surprisingly diverse. In this article, we were able, with the help of a small amount of Groovy code, to adapt to changing requirements and to perform several tasks that would be difficult to cope with Ant or Maven. Using a flexible programming language instead of xml frees you up and lets you decide how you want to complete your task. To be continued.
projectAlpha>gradle clean springJars
:clean
:compileJava
:processResources UP-TO-DATE
:classes
:springAuthJar
:springDbJar
:springPluginJar
:springJars
BUILD SUCCESSFUL
Total time: 5.015 secs
c:\Work\Gradle\tasksAreCode\projectAlpha>dir /b build\libs
projectAlpha-spring-auth-1.0.jar
projectAlpha-spring-auth-1.0.jar.MD5
projectAlpha-spring-db-1.0.jar
projectAlpha-spring-db-1.0.jar.MD5
projectAlpha-spring-plugin-1.0.jar
projectAlpha-spring-plugin-1.0.jar.MD5