πŸ“œ ⬆️ ⬇️

Details about the tasks of Gradle



Translation of the second chapter of the free book Building and Testing with Gradle

A task is the main component of the build process in the Gradle build file. Tasks are named sets of build instructions that Gradle launches when building an application. When compared with other build systems, tasks may seem like a familiar abstraction. However, Gradle provides a more advanced model, unlike the one that you may already be familiar with. Compared to the traditional capabilities of declaring dependency-related build operations, Gradle tasks are full-featured objects that you can programmatically manage if you wish.
')
Let's consider in what ways we can define a task, two key approaches to defining tasks and a program interface that we can use for flexible configuration.

Declaring tasks


There is an easy way to create a task. All you need is to specify the name of the task:

Example 1. Declaring a task by name only

task hello 

Having gradle hello command, we get the result:

Example 2. Gradle report on a new task.

 ------------------------------------------------------------ All tasks runnable from root project ------------------------------------------------------------ Build Setup tasks ----------------- init - Initializes a new Gradle build. [incubating] wrapper - Generates Gradle wrapper files. [incubating] Help tasks ---------- dependencies - Displays all dependencies declared in root project '__project'. dependencyInsight - Displays the insight into a specific dependency in root project '__project'. help - Displays a help message projects - Displays the sub-projects of root project '__project'. properties - Displays the properties of root project '__project'. tasks - Displays the tasks runnable from root project '__project'. Other tasks ----------- hello 


Task Action


Performing our task with the gradle hello command gradle hello still not produce any result, since we have not assigned a single operation ( action ) to it. The operation can be assigned using the left shift operator:

Example 3. Adding the simplest operation

 task hello << { println 'hello, world' } 

Operators such as << (β€œleft-shift” from Java) can be overloaded in Groovy to change behavior depending on the objects they work with. In this case, << is overloaded in Gradle to add a block of code to the list of operations that the task performs. Shifting to the left is equivalent to the doLast() method, which we will discuss below.

Now we have a flexible possibility to add an operation code in an additive way, referring to the task object that we created:

Example 4. Sequential addition of task operations one by one

 task hello hello << { print 'hello, ' } hello << { println 'world' } 

Now we again received the familiar build result:

Example 5. The result of the build with operations added one by one

 d:\project>gradle hello :hello hello, world BUILD SUCCESSFUL Total time: 1.916 secs 

This behavior is trivial, but it reveals an important principle: tasks are not static immutable declarations of build operations; Tasks are full-featured objects of the Gradle software environment. In addition to adding operations to them in an additive way in arbitrary places in the build file, we also have other options. Let's see what.

Task configuration


New Gradle users are usually confused with the configuration syntax when trying to define task operations. Continuing the previous example, we can expand it by adding a configuration block:

Example 6. Combining configuration and task operations

 task initializeDatabase initializeDatabase << { println 'connect to database' } initializeDatabase << { println 'update database schema' } initializeDatabase { println 'configuring database connection' } 

By running this build file, we will get a result that seems illogical to us:

Example 7. The result of executing the build file created above

 d:\project>gradle initializeDatabase configuring database connection :initializeDatabase connect to database update database schema BUILD SUCCESSFUL Total time: 3.088 secs 

To denote a block of code between a pair of curly brackets, Groovy uses the term "closed expression" or "closure" (closure). Functions-closures are similar to objects that can be passed to a method as a parameter or assigned to a variable, with the possibility of subsequent execution. They will be ubiquitous in Gradle, because they are highly suited as blocks, where you can define the configuration code and the build code for the build operation.

The last closed statement looks like a regular block of the build operation, and we expect that the output of his message will be the last, but not the first. It turns out the closure added to the task name without an operator. Left shift does not add a new operation at all. Instead, a configuration block was added. The task configuration block is executed during the configuration phase of the Gradle life cycle, which precedes the execution phase, during which the task operations are performed.

Each time Gradle starts a build, the process goes through three phases of its life cycle: initialization, configuration, and execution. Execution is a phase during which build tasks are performed in the order specified in the settings of their dependencies. Configuration - the phase in which task objects are collected into an internal object model, usually called a directed acyclic graph . Initialization is the phase in which Gradle decides which objects will take part in the build. The last phase is important in multi-project builds.

Note per.
From the glossary: ​​Gradle DAG - Directed Acyclic Graph ( directed acyclic graph ) - directed, not containing cycles, graph. The vertices of the graph are represented by the Gradle tasks to be performed. The dependsOn method, which establishes the dependence of this task on another, adds another task as a new vertex (if it is not already present in the graph) and creates a directed edge between two vertices. Any connection created with dependsOn is checked for the presence of loops. There should not be such a way in which the exit from a certain vertex, passing a sequence of edges of the graph, leads to the original vertex.

Configuration closures are additive in the same way as closures operations. Therefore, we can write the code of the build file for the previous example using the method below, while the result of the execution will be the same as before:

Example 8. Adding configuration blocks

 task initializeDatabase initializeDatabase << { println 'connect to database' } initializeDatabase << { println 'update database schema' } initializeDatabase { print 'configuring ' } initializeDatabase { println 'database connection' } 


The configuration block is a suitable place to assign values ​​of variables and data structures that are used by the task operation later when it starts in the build (if it starts up). The configuration structure gives you the opportunity to turn the tasks of your build into the essence of a developed object model, filled with information about the build. This is what makes Gradle tasks different from a simple set of build operations that are performed in a certain sequence. Without such a difference between configuration and operation, it would be necessary to complicate dependency settings, which would lead to a loss of reliability and a decrease in the expressiveness of the means for linking the main data structures of the build.

When the Gradle file is launched, the build configuration code is executed completely, regardless of whether any of the tasks are launched in the execution phase.

Tasks are objects


At this point, you might already have a guess that Gradle, before doing the build, creates its internal object model. That's exactly what happens. Each task you declare actually becomes a task object within the entire build project. The task object, like any other object, has properties and methods. And we can also control the type of each object-task, referring to the functionality defined in it. A few examples will help us figure it out.

By default, each new task is assigned the DefaultTask type. Just as each class is inherited from java.lang.Object in Java, in Gradle each task is inherited from a given type β€” even those tasks that extend the capabilities of DefaultTask by creating a new type. In fact, the DefaultTask tasks do nothing specific, like compiling code or copying files. However, they contain the functionality that is required to interact with the Gradle project program model. Consider the methods and properties that each task in Gradle has.

DefaultTask methods


dependsOn (task)

For the calling task, add a dependency task. Task dependency is always launched before a task that depends on it. The method can be invoked in several ways. The code example below shows how we can determine the loadTestData task’s loadTestData on createSchema :

Example 9. Different ways of calling the dependsOn method

 task createSchema //   'loadTestData'  'createSchema' //  ,  ,   task loadTestData { dependsOn createSchema } //       task loadTestData { dependsOn << createSchema } //    ,    (   ) task loadTestData { dependsOn 'createSchema' } //    - task loadTestData loadTestData.dependsOn createSchema //      task loadTestData(dependsOn: createSchema) 

A task may depend on several tasks. If the loadTestData task depends on the createSchema and compileTestClasses , we write the code like this:

Example 10. Various ways to call the dependsOn method for multiple dependencies

 task compileTestClasses task createSchema //     task loadTestData { dependsOn << compileTestClasses dependsOn << createSchema } //  ,     task loadTestData { dependsOn compileTestClasses, createSchema } //    - task loadTestData loadTestData.dependsOn compileTestClasses, createSchema //      //      Groovy task loadTestData(dependsOn: [ compileTestClasses, createSchema ]) 

doFirst (closure)

Adds a block of executable code to the beginning of the task operation. During the execution phase, the operation block of each task participating in the build is started. The doFirst method allows you to add pieces of logic to the beginning of an existing operation, even if this operation is already defined in the build file or an external module (plug-in) to which you do not have access. Multiple calls to doFirst add new blocks with an doFirst to the beginning of the task sequence.

The doFirst method can be called directly for a task object, passing a closure to it, which contains the code that will be executed before the current task operation.

As we said before, a closure is a Groovy block of code enclosed between a pair of curly braces. A closure can be passed to a method like any other object. The ability to transfer closed expressions to methods is a Groovy style feature.

Example 11. Calling the doFirst method

 task setupDatabaseTests << { //      println 'load test data' } setupDatabaseTests.doFirst { println 'create schema' } 

Example 12. The result of the execution of the previous build file

 d:\project>gradle setupDatabaseTests :setupDatabaseTests create schema load test data BUILD SUCCESSFUL Total time: 1.935 secs 

doFirst can also be called in the configuration block of the task. As we already said, the configuration block is the part of the executable code that runs during the build build phase before the task operations are performed. When we looked at the above configuration of tasks, you might have a question: where can I use configuration blocks? The following example will show you how you can call task methods inside the configuration block, which in the future makes the syntax of the task behavior change format very expressive:

Example 13. Calling the doFirst method inside the task configuration block

 task setupDatabaseTests << { println 'load test data' } setupDatabaseTests { doFirst { println 'create schema' } } 

doFirst callbacks doFirst additive. The operation code of each previous call is saved, and the new closure is added to the top of the list, ready to be executed in the appropriate order. For example, if we need to set up a database for integration testing (by breaking up the configuration steps in parts), we can use the following code:

Example 14. Repeated calls to doFirst have the additivity property

 task setupDatabaseTests << { println 'load test data' } setupDatabaseTests.doFirst { println 'create database schema' } setupDatabaseTests.doFirst { println 'drop database schema' } 

Example 15. The result of the previous example

 d:\project>gradle setupDatabaseTests :setupDatabaseTests drop database schema create database schema load test data BUILD SUCCESSFUL Total time: 3.126 secs 

In the previous example, splitting initialization into three separate closures with a call to doFirst() was, of course, a somewhat artificial step. However, there are cases when the source code of the task is not available. For example, a task is defined in another build file that cannot be modified or impractical. The considered method of program modification of inaccessible logic opens up wide possibilities.

So far, our examples have used a very simple syntax, which reveals the principles of Gradle operation due to multiple additions of closures. Most likely, in a real build we will organize the task as follows (all the same, instead of real test operations we use println operators):

Example 16. DoFirst repeated calls after refactoring

 //    (    ) task setupDatabaseTests << { println 'load test data' } //    ( ,     ) setupDatabaseTests { doFirst { println 'create database schema' } doFirst { println 'drop database schema' } } 

Notice that we have put together several doFirst calls within the same configuration block after the initial operation has already been added to the setupDatabaseTests task.

doLast (closure)

The doLast method doLast very similar to the doFirst method, with the only difference being that it adds behavior to the end of the operation, not to the beginning. If you need to run a block of code after a task completes, you can do the following:

Example 17. Using the doLast method

 task setupDatabaseTests << { println 'create database schema' } setupDatabaseTests.doLast { println 'load test data' } 

Example 18. DoLast callbacks are additive.

 task setupDatabaseTests << { println 'create database schema' } setupDatabaseTests.doLast { println 'load test data' } setupDatabaseTests.doLast { println 'update version table' } 

As mentioned earlier, in the Task Operation section, the << operator is another way to call the doLast() method.

onlyIf (closure)

The onlyIf method is a predicate that determines whether the task will be executed. The predicate value is the value returned by the closure. Using this method, you can deactivate the execution of a task that would otherwise start in the usual order of launching a build dependency sequence.

In Groovy, the last expression inside a closure determines its return value, even if the return missing. The Groovy method, in which only one expression is defined, is a function that returns the value of this expression.

Example 19. A build file using onlyIf method

 task createSchema << { println 'create database schema' } task loadTestData(dependsOn: createSchema) << { println 'load test data' } loadTestData.onlyIf { System.properties['load.data'] == 'true' } 

Example 20. Two options for running the build file. Note the difference in results

 d:\project>gradle loadTestData :createSchema create database schema :loadTestData SKIPPED BUILD SUCCESSFUL Total time: 4.361 secs d:\project>gradle -Dload.data=true loadTestData :createSchema create database schema :loadTestData load test data BUILD SUCCESSFUL Total time: 2.005 secs 

Using the onlyIf method onlyIf you can enable and disable individual tasks using the logic expressed by the Groovy code, which is not limited to just checking the simple System property that we used in the example. You have the ability to open files for reading, call Web services, check logins, passwords and do much more that can be done in the code.

DefaultTask properties


didWork

A boolean property indicating whether the task completed successfully. Not all tasks set the value of didWork to completion. However, some tasks, such as Compile , Copy and Delete , set the value of this property to communicate that their operations were completed, either successfully or with errors. The calculation of the value indicating that the task has already been completed is specific to different tasks. You can set the didWork value in your task to reflect the results of the build code you created:

Example 21. Sending an email for the case when the compilation was successful

 apply plugin: 'java' task emailMe(dependsOn: compileJava) << { if (tasks.compileJava.didWork) { println 'SEND E-MAIL ANNOUNCING SUCCESS' } } 

Example 22. The results of the build with the use of didWork

 d:\project>gradle emailMe :compileJava :emailMe SEND E-MAIL ANNOUNCING SUCCESS BUILD SUCCESSFUL Total time: 4.232 secs 

enabled

A boolean property indicating whether a task will be executed. You can disable task execution by setting the enabled property to false . Task dependencies are executed in the same order as if the task were not disabled.

Example 23. Disabling a task

 task templates << { println 'process email templates' } task sendEmails(dependsOn: templates) << { println 'send emails' } sendEmails.enabled = false 

Example 24. Build with a disabled task. Note that the dependency still starts.

 d:\project>gradle -b enabled.gradle sendEmails :templates process email templates :sendEmails SKIPPED BUILD SUCCESSFUL Total time: 3.271 secs 

The -b command line parameter indicates Gradle to a non-default build file. By default, Gradle searches for a file named build.gradle , but this command line parameter allows us to specify a different build file.

path

A string type property containing the full path of the task. By default, the path of the task is the name of the task with a colon in front.

Example 25. A single-level build file that displays the path of a single task defined in it

 task echoMyPath << { println "THIS TASK'S PATH IS ${path}" } 

Example 26. Results of executing the previous build file

 d:\project>gradle echoMyPath :echoMyPath THIS TASK'S PATH IS :echoMyPath BUILD SUCCESSFUL Total time: 3.135 secs 

A colon in front indicates that the task is defined at the top level of the build file. The location of tasks at the top level, however, is not mandatory. Gradle supports dependent subprojects, or nested builds. If the task is defined in a nested build with the name subProject , the path will be :subProject:echoMyPath .

logger

A reference to the internal Gradle logger object. In Gradle, the logger implements the org.slf4j.Logger interface with several additional logging levels. The log levels supported by the logger object are described below. Setting the logging level to one of the values ​​below includes logging at all subsequent levels, except WARN and QUIET :

Example 27. The task demonstrates the effect of applying all levels of logging. Groovy's somewhat more complex code sets the logging level for each of the possible options defined in the list. Thus, each time messages are displayed at each of the logging levels.

 task logLevel << { def levels = ['DEBUG', 'INFO', 'LIFECYCLE', 'QUIET', 'WARN', 'ERROR'] levels.each { level -> logging.level = level def logMessage = "SETTING LogLevel=${level}" logger.error logMessage logger.error '-' * logMessage.size() logger.debug 'DEBUG ENABLED' logger.info 'INFO ENABLED' logger.lifecycle 'LIFECYCLE ENABLED' logger.warn 'WARN ENABLED' logger.quiet 'QUIET ENABLED' logger.error 'ERROR ENABLED' println 'THIS IS println OUTPUT' logger.error ' ' } } 

Example 28. The result of the execution of the previous build file

 d:\project>gradle logLevel 19:35:44.677 [LIFECYCLE] [class org.gradle.TaskExecutionLogger] 19:35:44.699 [ERROR] [org.gradle.api.Task] SETTING LogLevel=DEBU 19:35:44.732 [ERROR] [org.gradle.api.Task] --------------------- 19:35:44.747 [DEBUG] [org.gradle.api.Task] DEBUG ENABLED 19:35:44.760 [INFO] [org.gradle.api.Task] INFO ENABLED 19:35:44.775 [LIFECYCLE] [org.gradle.api.Task] LIFECYCLE ENABLED 19:35:44.788 [WARN] [org.gradle.api.Task] WARN ENABLED 19:35:44.801 [QUIET] [org.gradle.api.Task] QUIET ENABLED 19:35:44.812 [ERROR] [org.gradle.api.Task] ERROR ENABLED 19:35:44.857 [QUIET] [system.out] THIS IS println OUTPUT 19:35:44.868 [ERROR] [org.gradle.api.Task] SETTING LogLevel=INFO --------------------- INFO ENABLED LIFECYCLE ENABLED WARN ENABLED QUIET ENABLED ERROR ENABLED THIS IS println OUTPUT SETTING LogLevel=LIFECYCLE -------------------------- LIFECYCLE ENABLED WARN ENABLED QUIET ENABLED ERROR ENABLED THIS IS println OUTPUT SETTING LogLevel=QUIET ---------------------- QUIET ENABLED ERROR ENABLED THIS IS println OUTPUT SETTING LogLevel=WARN --------------------- WARN ENABLED QUIET ENABLED ERROR ENABLED THIS IS println OUTPUT SETTING LogLevel=ERROR ---------------------- ERROR ENABLED BUILD SUCCESSFUL Total time: 2.184 secs 


logging

This property loggingallows us to control the logging level. As already shown in the example for the propertylogger , the logging level of the build can be obtained and changed using the property logging.level.

description

The property description, as the name implies, describes the purpose of the task with a small amount of metadata available for understanding the person. The value descriptioncan be specified in several ways:

Example 29. At the same time set the task description and behavior

 task helloWorld(description: 'Says hello to the world') << { println 'hello, world' } 

Example 30. Two ways to declare a task’s behavior and set a description

 task helloWorld << { println 'hello, world' } helloWorld { description 'Says hello to the world' } //    helloWorld.description 'Says hello to the world' 


temporaryDir

The property temporaryDirreturns an object Filethat refers to the temporary directory of the current build file. Such a directory is created for temporary storage of intermediate results of the task, or for organizing the files that the task will process.

Dynamic properties


, , Gradle. , . - -, , ( , ).

: createArtifact copyFiles . copyFiles β€” , createArtifact . , , , . :

31. -,

 task copyFiles { //    ,   // (     ) fileManifest = [ 'data.csv', 'config.json' ] } task createArtifact(dependsOn: copyFiles) << { println "FILES IN MANIFEST: ${copyFiles.fileManifest}" } 

32.

 d:\project>gradle -q createArtifact FILES IN MANIFEST: [data.csv, config.json] 


As we said earlier, in the paragraph, Tasks are objects , each task has a type. In addition to the type DefaultTask, there are other types of tasks for copying, archiving, running programs and other actions. Declaring a task type is a lot like inheriting from a base class in an object-oriented language. Thus, by implementing inheritance, you immediately receive certain properties and methods in your task. This syntax significantly shortens the definition of tasks, while the possibilities are still great.

Consideration of the complete documentation of the tasks is beyond the scope of the material presented here, but we still consider several important types with examples of use.

Copy


A task Copycopies files from one place to another. In the simplest case, it copies files from one directory to another, with some additional restrictions on including or excluding files using name masks:

Example 33. The simplest example of using a task Copy

 task copyFiles(type: Copy) { from 'resources' into 'target' include '**/*.xml', '**/*.txt', '**/*.properties' } 

Jar


The task Jarcreates a jar file from resource files. The task of this type with a known name is Jardefined in the module 'java'. The task packs * .class-files and resources into a Jar-file with the name of the project, while using the usual manifest. The result is saved to the directory build/libs. This task is highly flexible.

Example 34. The simplest example of using a task Jar

 apply plugin: 'java' task customJar(type: Jar) { manifest { attributes firstKey: 'firstValue', secondKey: 'secondValue' } archiveName = 'hello.jar' destinationDir = file("${buildDir}/jars") from sourceSets.main.output } 

Please note - the name of the archive and the target folder are easily configured. In the same way, you can change the values ​​of the manifest file using the simple syntax of the Groovy dictionaries. The content of a JAR file is defined by a string from sourceSets.main.outputthat includes .class files. The method fromis identical to the method used in the example CopyTask, which reveals one interesting detail: the task is Jarinherited from the task Copy. Knowing this feature, you can still not looking into the documentation to draw some conclusions about the wide possibilities and structure of the classes underlying the task Jar.

destinationDirassigned a very simple expression. It would be more natural if the property were destinationDirassigned a string. But the property works with objects java.io.File. Method comes to the rescuefile()which is always available in the build code of the Gradle file. It converts a string to an object File.

Remember, you can always find documentation docs/dsl/index.htmlthat describes the standard features of Gradle, such as a task Jar. The description of all the possibilities of the task Jaris beyond the scope of our chapter.

JavaExec


The task JavaExecruns a Java class with a method main(). Running a console Java application can be a nuisance. However, this task eliminates the inconvenience by integrating console Java applications into your build:

Example 35. The Gradle task launches a console Java application (example is taken from the javaexec-task )

 apply plugin: 'java' repositories { mavenCentral() } dependencies { compile 'commons-codec:commons-codec:1.6' runtime 'commons-codec:commons-codec:1.6' } task encode(type: JavaExec, dependsOn: classes) { main = 'org.gradle.example.commandline.MetaphoneEncoder' args = "The rain in Spain falls mainly in the plain".split().toList() classpath sourceSets.main.output.classesDir classpath configurations.runtime } 

 package org.gradle.example.commandline; import org.apache.commons.codec.language.Metaphone; public class MetaphoneEncoder { public static void main(String args[]) { Metaphone codec = new Metaphone(); String phrase = ""; String encoded = ""; for (String s : args) { phrase += s.toUpperCase() + " "; encoded += codec.encode(s) + " "; } System.out.println("PHRASE =" + phrase); System.out.println("ENCODED=" + encoded); } } 

encode classpath configurations.runtime . configurations , . runtime , . , ( compile ), , , , , . configurations Gradle , , .

Apache Commons Codec. Java-, , .class JAR-. , main() ( org.gradle.example.commandline.MetaphoneEncoder ), , classpath . , sourceSets , , runtime . , .


Sometimes the capabilities of the built-in tasks Gradle may not be enough to solve your problem. Then creating a custom task will be the most expressive way that you can apply when designing your build. Gradle allows you to do this in several ways. We consider the two most common.

Defining a custom task type in the build file


Let's say in your build you need to perform various queries to the MySQL database. In Gradle, this problem is solved in several ways, but you have come to the conclusion that creating a custom task will be the most expressive solution. The easiest way to create a task is to declare it as shown in the example below:

Example 36. Custom task for executing queries in a MySQL database (from the example custom-task

 task createDatabase(type: MySqlTask) { sql = 'CREATE DATABASE IF NOT EXISTS example' } task createUser(type: MySqlTask, dependsOn: createDatabase) { sql = "GRANT ALL PRIVILEGES ON example.* TO exampleuser@localhost IDENTIFIED BY 'passw0rd'" } task createTable(type: MySqlTask, dependsOn: createUser) { username = 'exampleuser' password = 'passw0rd' database = 'example' sql = 'CREATE TABLE IF NOT EXISTS users (id BIGINT PRIMARY KEY, username VARCHAR(100))' } class MySqlTask extends DefaultTask { def hostname = 'localhost' def port = 3306 def sql def database def username = 'root' def password = 'password' @TaskAction def runQuery() { def cmd if(database) { cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} ${database} -e " } else { cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} -e " } project.exec { commandLine = cmd.split().toList() + sql } } } 

MySqlTask DefaultTask . DefaultTask , . ( , DefaultTask . . , .) Groovy, ( hostname , database , sql ..). runQuery() , @TaskAction . runQuery() .

, , MySqlTask . , . ( username password , , ), , , . createDatabase createUser SQL-, .

createTable username , password database . , , . , , Gradle.


If the user task is very large, its code can significantly complicate the build file. As was shown in the example above, a task may consist of several lines of simple code. However, at a certain stage a task may develop into its own class hierarchy with dependencies on the external API and the need to apply automated testing. A build is a code, and a complex build code must be considered as a full inhabitant of the world of code development. Such a problem in Gradle is solved simply.

When the logic of a user task grows within the reasonable limits of the build file, we can transfer it to the directory buildSrcthat is in the root of the project. This directory is automatically compiled and added to the classpath of the build. We will change the previous example in which we will use buildSrc:

37. - ,

 task createDatabase(type: MySqlTask) { sql = 'CREATE DATABASE IF NOT EXISTS example' } task createUser(type: MySqlTask, dependsOn: createDatabase) { sql = "GRANT ALL PRIVILEGES ON example.* TO exampleuser@localhost IDENTIFIED BY 'passw0rd'" } task createTable(type: MySqlTask, dependsOn: createUser) { username = 'exampleuser' password = 'passw0rd' database = 'example' sql = 'CREATE TABLE IF NOT EXISTS users (id BIGINT PRIMARY KEY, username VARCHAR(100))' } 

38. buildSrc

 import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction class MySqlTask extends DefaultTask { def hostname = 'localhost' def port = 3306 def sql def database def username = 'root' def password = 'password' @TaskAction def runQuery() { def cmd if(database) { cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} ${database} -e " } else { cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} -e " } project.exec { commandLine = cmd.split().toList() + sql } } } 

, buildSrc , . , , , , , .

, - Gradle. β€” , -, . β€” buildSrc , . β€” - -. β€” , Java Goovy. Gradle β€” , .

39. Gradle, , buildSrc

 . β”œβ”€β”€ build.gradle β”œβ”€β”€ buildSrc β”‚ └── src β”‚ └── main β”‚ └── groovy β”‚ └── org β”‚ └── gradle β”‚ └── example β”‚ └── task β”‚ └── MySqlTask.groovy 



So far, we have created tasks by directly writing code in the Gradle build scripts, or in a directory buildSrcin the form of Groovy code. This approach is good for studying tasks, as it gives a detailed overview of all their features. However, most of the tasks that you will use will not be written by you. They will be imported from external modules.

In the simplest example of building a console application HelloWorldin Java, the build file looks like this:

 apply plugin: 'java' 

Java, - , . , doFirst() doLast() , . Gradle . Gradle , , Gradle DSL (DSL β€” Domain Specific Language), Groovy.

, Gradle , tasks properties . . Gradle DSL.

Conclusion


. , , , Gradle . , Groovy API. API , . , .

, . Gradle , . . Gradle β€” - Groovy. , .

In Gradle, tasks are the fundamental units of the build process. Much more can be said about them than the introduction can cover. Still, having the knowledge from this chapter in your arsenal, you are already prepared enough to start using tasks and continue to study them further.

If you have ideas for improving the text,
as well as those who have found errors,
you are welcome on GitHub .


For finding typos special thanks to the reader Nikita_Rogatnev .

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


All Articles