📜 ⬆️ ⬇️

Gradle: Tasks Are Code

In the previous topic, I tried to briefly describe what Gradle is and on what ideas it is built. The concept of Source Sets and the functionality associated with it were also highlighted.

Now I would like to talk about what Gradle hooked me personally. It's about how to work with tasks. The task in Gradle is a close analogue of Ant Target. And, so as not to be confused in terms, the task (or task) hereinafter will always mean the Gradle Task. If we are talking about an entity from Ant, 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 limitless compared to ant or maven.
')


Let's start with the traditional “Hello World” programmers. Suppose we have an empty build.gradle file. We write:

 task hello << { println 'Hello world!' } 

Run

>gradle -q hello
Hello world!

Bingo! But not impressive. Let's try something else:
 task upper << { String someString = 'mY_nAmE' println "Original: " + someString println "Upper case: " + someString.toUpperCase() 4.times { print "$it " } } 

Run

>gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME
0 1 2 3

That is, an arbitrary Groovy code may be inside the task definition. And the tasks themselves are a full-fledged Groovy object. And this means that they have properties and methods that allow them to be managed. 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:

 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 to compile, several packages. When running a command

>gradle build

The projectAlpha/build/libs directory will projectAlpha-1.0.jar . All in full accordance with the agreements. Maven would do the same.

But over time, the requirements, as we know, change. Let's change the requirements in the example. Suppose we needed to assemble the Spring-related code into separate archives, assemble a separate jar with the classes of the unit-tests, and another jar with the source codes. In Gradle, this is solved as follows:

 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/**' } 

Run
>gradle assemble

And we see:
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


What happened:

I wonder how to do this 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 manifesto of each jar that is related to Spring and published on Habré, the demo attribute with the value habr.ru. It sounds weird, 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:

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


Note that many tasks have been marked UP-TO-DATE. This is another highlight of Gradle - incremental build. But about her another time. Now, if you don’t be lazy and look at the contents of the manifest archives, then you can find the necessary line in Spring-related files.

Manifest-Version: 1.0
demo: habr.ru

Bingo!

But the requirements of the Spring Foundation continue to change. And now you need to put his checksum next to each jar :) License purity is a serious matter, and we have to comply. Unfortunately, Gradle does not have built-in support for the MD5 calculation operation. But it is in Ant. So let's get it and use it. Let's change the last fragment as follows:

 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 unfortunate spring-related archives:

projectAlpha>gradle clean springJars
:clean
:compileJava
:processResources UP-TO-DATE
:classes
:springAuthJar
:springDbJar
:springPluginJar
:springJars

BUILD SUCCESSFUL

Total time: 5.015 secs


Let's see what happened.
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


Completing the task required minimal effort.
Perhaps the requirements used in the example seem artificial and a little naive, but the tasks that have to be solved in reality are surprisingly diverse.

In this article, we were able to adapt to changing requirements with a small amount of Groovy code and perform several tasks that would be difficult to cope with using Ant or Maven. Using a flexible programming language instead of xml gives you a free hand and allows you to decide for yourself how you want to accomplish your task.

To be continued.

Source: https://habr.com/ru/post/107558/


All Articles