task hello
gradle hello
command, we get the result: ------------------------------------------------------------ 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
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: 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.
task hello hello << { print 'hello, ' } hello << { println 'world' }
d:\project>gradle hello :hello hello, world BUILD SUCCESSFUL Total time: 1.916 secs
task initializeDatabase initializeDatabase << { println 'connect to database' } initializeDatabase << { println 'update database schema' } initializeDatabase { println 'configuring database connection' }
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.
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. ThedependsOn
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 withdependsOn
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.
task initializeDatabase initializeDatabase << { println 'connect to database' } initializeDatabase << { println 'update database schema' } initializeDatabase { print 'configuring ' } initializeDatabase { println 'database connection' }
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.
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.loadTestData
taskβs loadTestData
on createSchema
: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)
loadTestData
task depends on the createSchema
and compileTestClasses
, we write the code like this: 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
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.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.
task setupDatabaseTests << { // println 'load test data' } setupDatabaseTests.doFirst { println 'create schema' }
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: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:doFirst
have the additivity property task setupDatabaseTests << { println 'load test data' } setupDatabaseTests.doFirst { println 'create database schema' } setupDatabaseTests.doFirst { println 'drop database schema' }
d:\project>gradle setupDatabaseTests :setupDatabaseTests drop database schema create database schema load test data BUILD SUCCESSFUL Total time: 3.126 secs
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.println
operators): // ( ) task setupDatabaseTests << { println 'load test data' } // ( , ) setupDatabaseTests { doFirst { println 'create database schema' } doFirst { println 'drop database schema' } }
doFirst
calls within the same configuration block after the initial operation has already been added to the setupDatabaseTests
task.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: task setupDatabaseTests << { println 'create database schema' } setupDatabaseTests.doLast { println 'load test data' }
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
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.
onlyIf
method task createSchema << { println 'create database schema' } task loadTestData(dependsOn: createSchema) << { println 'load test data' } loadTestData.onlyIf { System.properties['load.data'] == 'true' }
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
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.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: apply plugin: 'java' task emailMe(dependsOn: compileJava) << { if (tasks.compileJava.didWork) { println 'SEND E-MAIL ANNOUNCING SUCCESS' } }
d:\project>gradle emailMe :compileJava :emailMe SEND E-MAIL ANNOUNCING SUCCESS BUILD SUCCESSFUL Total time: 4.232 secs
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. task templates << { println 'process email templates' } task sendEmails(dependsOn: templates) << { println 'send emails' } sendEmails.enabled = false
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.
task echoMyPath << { println "THIS TASK'S PATH IS ${path}" }
d:\project>gradle echoMyPath :echoMyPath THIS TASK'S PATH IS :echoMyPath BUILD SUCCESSFUL Total time: 3.135 secs
subProject
, the path will be :subProject:echoMyPath
.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
:DEBUG
. For detailed logging messages that are needed by the build developer, however, they should not be displayed when the build is executed in normal mode. If this level is selected, Gradle automatically uses the extended format, which in each message inserts a timestamp, logging level, and the name of the logging task. The remaining levels use a shorter message format.INFO
. Needed for less informative build messages that play a minor role during the execution of a build.LIFECYCLE
. Uninformative messages about changes in the life cycle of the build and the process of the tool itself that launched the project build. Usually generated by Gradle itself. This level is used by default when Gradle is run without the -q command line option. This logging level is assigned to messages printed by the println
operator.WARN
. , , . WARN
, QUIET
.QUIET
. , -q . ( -q QUIET
). , System.out.println
. QUIET
, WARN
.ERROR
. Rare, but important messages that are displayed at all levels of logging. Messages inform about the completion of the build with errors. If ERROR
- the current logging level, calls System.out.println
will not be displayed in the console window. 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 ' ' } }
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
allows 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
, as the name implies, describes the purpose of the task with a small amount of metadata available for understanding the person. The value description
can be specified in several ways: task helloWorld(description: 'Says hello to the world') << { println 'hello, world' }
task helloWorld << { println 'hello, world' } helloWorld { description 'Says hello to the world' } // helloWorld.description 'Says hello to the world'
temporaryDir
returns an object File
that 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.createArtifact
copyFiles
. copyFiles
β , createArtifact
. , , , . : task copyFiles { // , // ( ) fileManifest = [ 'data.csv', 'config.json' ] } task createArtifact(dependsOn: copyFiles) << { println "FILES IN MANIFEST: ${copyFiles.fileManifest}" }
d:\project>gradle -q createArtifact FILES IN MANIFEST: [data.csv, config.json]
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.Copy
copies 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:Copy
task copyFiles(type: Copy) { from 'resources' into 'target' include '**/*.xml', '**/*.txt', '**/*.properties' }
Jar
creates a jar file from resource files. The task of this type with a known name is Jar
defined 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.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 }
from sourceSets.main.output
that includes .class files. The method from
is identical to the method used in the example CopyTask
, which reveals one interesting detail: the task is Jar
inherited 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
.destinationDir
assigned a very simple expression. It would be more natural if the property were destinationDir
assigned 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 documentationdocs/dsl/index.html
that describes the standard features of Gradle, such as a taskJar
. The description of all the possibilities of the taskJar
is beyond the scope of our chapter.
JavaExec
runs 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: 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 , , .
main()
( org.gradle.example.commandline.MetaphoneEncoder
), , classpath
. , sourceSets
, , runtime
. , . 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.buildSrc
that 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
: 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))' }
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 β , .
buildSrc
. βββ build.gradle βββ buildSrc β βββ src β βββ main β βββ groovy β βββ org β βββ gradle β βββ example β βββ task β βββ MySqlTask.groovy
buildSrc
in 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.HelloWorld
in Java, the build file looks like this: apply plugin: 'java'
doFirst()
doLast()
, . Gradle . Gradle , , Gradle DSL (DSL β Domain Specific Language), Groovy.tasks
properties
. . Gradle DSL.Source: https://habr.com/ru/post/167227/
All Articles