Building and Testing with Gradle筆記2——Gradle Tasks
宣告一個Task
task hello
執行gradle tasks
輸出當前Project中所有task
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]
Help tasks
----------
components - Displays the components produced by root project 'gradle-test-2'. [incubating]
dependencies - Displays all dependencies declared in root project 'gradle-test-2'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gradle-test-2'.
help - Displays a help message.
model - Displays the configuration model of root project 'gradle-test-2' . [incubating]
projects - Displays the sub-projects of root project 'gradle-test-2'.
properties - Displays the properties of root project 'gradle-test-2'.
tasks - Displays the tasks runnable from root project 'gradle-test-2'.
Other tasks
-----------
hello
Gradle每當開始執行一個Task,都會輸出一個冒號加上該task的名字,如:tasks
新增Task要執行的操作
聲明後的Task可以作為一個可程式設計的物件,使用左移操作符給其新增要執行的操作
task hello
hello << {
print 'hello, '
}
hello << {
println 'world'
}
配置Task
task initializeDatabase
initializeDatabase << { println 'connect to database' }
initializeDatabase << { println 'update database schema' }
initializeDatabase { println 'configuring database connection' }
執行gradle -b scratch.gradle initializeDatabase
輸出:
configuring database connection
:initializeDatabase
connect to database
update database schema
首先,-b
選項可以執行gradle要執行的build檔案。
其次,沒有使用左移操作符的程式碼塊是指定task的配置部分。在Gradle的宣告週期的配置階段執行,早於task的執行階段。比配置階段更早的是初始化階段。
配置程式碼塊同樣可以追加,如下:
task initializeDatabase
initializeDatabase << { println 'connect to database' }
initializeDatabase << { println 'update database schema' }
initializeDatabase { print 'configuring ' }
initializeDatabase { println 'database connection' }
Task是物件
Task物件的預設型別是DefaultTask,Gradle中的所有Task都是派生自該型別。正如Java中的所有物件都派生自java.lang.Object
。
DefaultTask的方法
dependsOn(task)
// Declare that world depends on hello
// Preserves any previously defined dependencies as well
task loadTestData {
dependsOn createSchema
}
// An alternate way to express the same dependency
task loadTestData {
dependsOn << createSchema
}
// Do the same using single quotes (which are usually optional)
task loadTestData {
dependsOn 'createSchema'
}
// Explicitly call the method on the task object
task loadTestData
loadTestData.dependsOn createSchema
// A shortcut for declaring dependencies
task loadTestData(dependsOn: createSchema)
如果一個Task有多個依賴,如下:
// Declare dependencies one at a time
task loadTestData {
dependsOn << compileTestClasses
dependsOn << createSchema
}
// Pass dependencies as a variable-length list
task world {
dependsOn compileTestClasses, createSchema
}
// Explicitly call the method on the task object
task world
world.dependsOn compileTestClasses, createSchema
// A shortcut for dependencies only
// Note the Groovy list syntax
task world(dependsOn: [ compileTestClasses, createSchema ])
doFirst(closure)
傳遞一個closure在指定Task執行之前執行,注意和Task配置區分。
task setupDatabaseTests << {
// This is the task's existing action
println 'load test data'
}
setupDatabaseTests.doFirst {
println 'create schema'
}
執行gradle setupDatabaseTests
輸出,
:setupDatabaseTests
create schema
load test data
當然,這個closure可以在Task配置塊中指定:
task setupDatabaseTests << {
println 'load test data'
}
setupDatabaseTests {
doFirst {
println 'create schema'
}
}
doFirst也可以追加closure:
task setupDatabaseTests << {
println 'load test data'
}
setupDatabaseTests.doFirst {
println 'create database schema'
}
setupDatabaseTests.doFirst {
println 'drop database schema'
}
輸出如下:
:setupDatabaseTests
drop database schema
create database schema
load test data
doLast(closure)
示例如下:
task setupDatabaseTests << {
println 'create database schema'
}
setupDatabaseTests.doLast {
println 'load test data'
}
setupDatabaseTests.doLast {
println 'update version table'
}
本質上,<<
操作符等同於doLast方法。
onlyIf(closure)
在Task執行以前,進行判斷,是否要執行該Task。
task createSchema << {
println 'create database schema'
}
task loadTestData(dependsOn: createSchema) << {
println 'load test data'
}
loadTestData.onlyIf {
System.properties['load.data'] == 'true'
}
直接執行build loadTestData
輸出:
:createSchema
create database schema
:loadTestData SKIPPED
指定一個系統屬性並執行gradle -Dload.data=true loadTestData
,輸出:
:createSchema
create database schema
:loadTestData
load test data
DefaultTask的屬性
didWork
判斷一個Task是否執行成功
apply plugin: 'java'
task emailMe(dependsOn: compileJava) << {
if(tasks.compileJava.didWork) {
println 'SEND EMAIL ANNOUNCING SUCCESS'
}
}
enabled
是否啟用Task
task templates << {
println 'process email templates'
}
task sendEmails(dependsOn: templates) << {
println 'send emails'
}
sendEmails.enabled = false
執行gradle -b enabled.gradle sendEmails
,輸出:
:templates
process email templates
:sendEmails SKIPPED
path
task echoMyPath << {
println "THIS TASK'S PATH IS ${path}"
}
執行gradle -b path.gradle echoMyPath
,輸出:
THIS TASK'S PATH IS :echoMyPath
這表示該Task位於頂層Project,如果是子專案subProject的Task,那麼path應該是:subProject:echoMyPath
。
logger
Gradle內部的logger引用,看程式碼:
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 ' '
}
}
總而言之,logger的級別優先順序,從低到高。
logging
description
設定一個Task的描述資訊:
task helloWorld(description: 'Says hello to the world') << {
println 'hello, world'
}
或者
task helloWorld << {
println 'hello, world'
}
helloWorld {
description = 'Says hello to the world'
}
又或者:
task helloWorld << {
println 'hello, world'
}
helloWorld.description = 'Says hello to the world'
temporaryDir
以File物件的形式返回屬於當前build檔案的一個臨時目錄
動態屬性
每個Task除了上述固有屬性外還可以任意新增屬性,本質上就是新增鍵值對。
task copyFiles {
// Find files from wherever, copy them
// (then hardcode a list of files for illustration)
fileManifest = [ 'data.csv', 'config.json' ]
}
task createArtifact(dependsOn: copyFiles) << {
println "FILES IN MANIFEST: ${copyFiles.fileManifest}"
}
執行gradle -b dynamic.gradle createArtifact
,輸出:
FILES IN MANIFEST: [data.csv, config.json]
Task型別
除了DefaultTask,還有一些用於複製,打包,執行程式的Task型別。宣告一個Task型別就好像在面向物件程式設計中擴充套件一個類。
Copy
task copyFiles(type: Copy) {
from 'resources'
into 'target'
include '**/*.xml', '**/*.txt', '**/*.properties'
}
from,into,和include方法都是繼承自Copy。
Jar
這是java外掛中引入的一個Task,Jar用於將原始碼打包成一個jar檔案。我們同樣可以擴充套件這個Task。
apply plugin: 'java'
task customJar(type: Jar) {
manifest {
attributes firstKey: 'firstValue', secondKey: 'secondValue'
}
archiveName = 'hello.jar'
destinationDir = file("${buildDir}/jars")
from sourceSets.main.classes
}
jar包的清單檔案屬性可以輕易的使用Groovy的map字面量建立。file方法用於從字串構造一個java.io.File物件。from方法是Jar從Copy繼承而來。
JavaExec
該Task用於執行一個帶有main方法的Java類
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
runtime 'commons-codec:commons-codec:1.5'
}
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.classesDir
classpath configurations.runtime
}
自定義Task型別
在build檔案中建立自定義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 [email protected] 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
}
}
}
上面定義的每個Task都用於執行一條sql語句。
在原始碼樹中建立自定義Task型別
如果自定的Task較為複雜,應該將它們儲存為單獨的Groovy檔案。上面的例子還可以寫成這樣,將build檔案中的class移到一個單獨的檔案中MySqlTask.groovy
,專案的結構如下:
|---build.gradle
\---buildSrc
\---src
\---main
\---groovy
\---org
\---gradle
\---example
\---task
\---MySqlTask.groovy