Gradle Task

Task는 ANT의 target과 유사하며, project 객체의 하위 객체입니다. 태스크를 호출하면 서브 프로젝트에 동일한 태스크가 있는 경우 서브 프로젝트의 태스크도 함께 호출됩니다.

Task 정의하기

기본 형식

task hello {
    doLast {
        println 'tutorialspoint'
    }
}

Deprecated 방식 (« 연산자)

// In Groovy, << is the left shift operator to append elements to a list:
// this is deprecated.
task hello << {
    println 'tutorialspoint'
}

문자열 이름으로 정의

task "hello" {
}

프로그래밍 방식으로 태스크 추가

project.tasks.create("copy", CopyTask.class)

여러 액션 추가하기

등록된 모든 액션이 호출됩니다.

task sampleTask2 {
    println "This is sampleTask2 configuration statements"

    doFirst {
        println "Task getProjectDetailsTask properties are: " + sampleTask1.taskDetail
    }
}

sampleTask2.doFirst { println "Actions added separately" }
sampleTask2.doLast { println " More Actions added " }
sampleTask2.doFirst { println "Actions added separately2" }
sampleTask2.doLast { println " More Actions added 2" }

Task Properties

Property Description
tasks 태스크 목록
name 태스크 이름
path 태스크 경로
description 태스크 설명 (gradle tasks 명령어에서 표시됨)
task copy(type: Copy) {
    description 'Copies the resource directory to the target directory.'
    from 'resources'
    into 'target'
    include('**/*.txt', '**/*.xml', '**/*.properties')
    println("description applied")
}

Task Methods

getByPath

tasks.getByPath('projectA:hello').path

Task Dependencies (의존성)

기본 의존성 정의

task taskX << {
    println 'taskX'
}

task taskY(dependsOn: 'taskX') << {
    println "taskY"
}

task task1(dependsOn: [task2, task3])

실행:

gradle -q taskY

나중에 의존성 추가

task taskY << {
    println 'taskY'
}

task taskX << {
    println 'taskX'
}

taskY.dependsOn taskX
taskY.dependsOn taskX, taskZ

// exclusively override existing dependencies
// this ignores compileJava task.
classes {
    dependsOn = [task1, task2]
}

동적 의존성

taskX.dependsOn {
    tasks.findAll {
        task -> task.name.startsWith('lib')
    }
}

Task 건너뛰기

enabled 속성

sampleTask12.enabled = false  // configuration scope

조건부 건너뛰기

ext {
    environment = 'prod'
    // can set this value from property file or command line using -Pname=value option
}

task prodTask << {
    println 'Executing prod tasks ' + environment
}
prodTask.onlyIf { project.hasProperty('environment') && project.environment == 'prod' }

task qaTask << {
    println 'Executing qa tasks ' + environment
}
qaTask.onlyIf { project.hasProperty('environment') && project.environment == 'qa' }

closure로 조건 지정

// #1st approach - closure returning true, if the task should be executed, false if not.
eclipse.onlyIf {
    project.hasProperty('usingEclipse')
}

// #2nd approach - alternatively throw an StopExecutionException() like this
// this stops only the task.
eclipse.doFirst {
    if (!usingEclipse) {
        throw new StopExecutionException()
    }
}

Task Optimization

입력과 출력이 변경되지 않으면 태스크는 ‘UP-TO-DATE’로 표시되어 실행되지 않습니다. 입력과 출력이 있는 경우에만 작동합니다.

출력이 없는 경우 TaskOutputs.upToDateWhen() 또는 outputs.upToDateWhen을 사용합니다.

task updateExample {
    ext {
        propXml = file('PropDetails.xml')
    }

    File envFile = file('envproperty.txt')
    File sysFile = file('sysproperty.txt')

    inputs.file propXml
    outputs.files(envFile, sysFile)

    doLast {
        println "Generating Properties files"
        def properties = new XmlParser().parse(propXml)
        properties.property.each { property ->
            def fileName = property.filedetail[0].name[0].text()
            def key = property.filedetail[0].key[0].text()
            def value = property.filedetail[0].value[0].text()
            def destFile = new File("${fileName}")
            destFile.text = "$key = ${value}\n"
        }
    }
}

Task Order (순서)

  • finalizedBy: 태스크 실행 후 다른 태스크를 실행합니다.
  • mustRunAfter: 실행할 태스크가 있으면 해당 태스크 이후에 실행됩니다. 없으면 실행되지 않습니다.
  • shouldRunAfter: mustRunAfter와 동일하지만, 순환 의존성이 있으면 이 순서를 무시합니다.
(1..6).each {
    task "sampleTask$it" << {
        println "Executing $name"
    }
}

sampleTask1.dependsOn sampleTask2
sampleTask3.dependsOn sampleTask2
sampleTask5.finalizedBy sampleTask6
sampleTask5.mustRunAfter sampleTask4

Rule: Dynamic Task

태스크가 없을 때 rule을 호출하고, rule이 태스크를 정의하여 호출합니다. 변수 값이 있을 때, 태스크 이름 뒤에 값을 넣는 방식으로 사용됩니다.

tasks.addRule("Pattern: sync<repoServer>") { String taskName ->
    if (taskName.startsWith("sync")) {
        task(taskName) << {
            println "Syncing from repository: " + (taskName - 'sync')
        }
    }
}
gradle -b build_rule.gradle tasks

출력:

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.
Pattern: sync<repoServer>

Task 구조

모든 태스크의 configuration이 먼저 실행되고, 그 다음 doFirst -> doLast 순서로 실행됩니다.

task A {
    println 'A config'
    doFirst {
        println 'A do First'
        throw new StopExecutionException()
    }
    doLast {
        println 'A do Last'
    }
}

task B(dependsOn: 'A') {
    println 'B config'
    doFirst {
        println 'B do First'
    }
    doLast {
        println 'B do Last'
    }
}

실행 결과:

A config
B config
A do First
A do Last
B do First
B do Last

Custom Task (커스텀 태스크)

@TaskAction

doFirst -> TaskAction -> doLast 순서로 실행됩니다.

class SampleTask extends DefaultTask {
    String systemName = "DefaultMachineName"
    String systemGroup = "DefaultSystemGroup"

    @TaskAction
    def action1() {
        println "System Name is " + systemName + " and group is " + systemGroup
    }

    @TaskAction
    def action2() {
        println 'Adding multiple actions for refactoring'
    }
}

task hello(type: SampleTask)

hello {
    systemName = 'MyDevelopmentMachine'
    systemGroup = 'Development'
}

hello.doFirst { println "Executing first statement " }
hello.doLast { println "Executing last statement " }

buildSrc: 별도 파일에 커스텀 태스크 정의

buildSrc는 gradle 빌드용 소스입니다. 같은 프로젝트나 서브프로젝트에서만 사용할 수 있습니다.

{projectDir}/buildSrc/src/main/groovy/ch3/SampleTask.groovy:

package ch3

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

class SampleTask extends DefaultTask {
    String systemName = "DefaultMachineName"
    String systemGroup = "DefaultSystemGroup"

    @TaskAction
    def action1() {
        println "System Name is " + systemName + " and group is " + systemGroup
    }

    @TaskAction
    def action2() {
        println 'Adding multiple actions for refactoring'
    }
}

{projectDir}/build.gradle:

task hello(type: ch3.SampleTask)

hello {
    systemName = 'MyDevelopmentMachine'
    systemGroup = 'Development'
}

hello.doFirst { println "Executing first statement " }
hello.doLast { println "Executing last statement " }

독립 태스크 (다른 프로젝트에서 사용)

build.gradle:

apply plugin: 'groovy'
version = 1.0

dependencies {
    compile gradleApi()
    compile localGroovy()
}

빌드 후 다른 프로젝트에서 사용:

buildscript {
    repositories {
        // relative path of sampleTaskProject jar file
        flatDir { dirs "../SampleTaskProj/build/libs" }
    }
    dependencies {
        classpath group: 'ch3', name: 'SampleTaskProj', version: '1.0'
    }
}

task hello(type: ch3.SampleTask)

Task Types (태스크 타입)

from, into 등은 모두 클래스 내부의 메서드입니다. 따라서 액션 전에 configuration을 설정합니다.

Copy

task copyTask(type: Copy) {
    from "."
    into "abc"
    include('employees.xml')
}

이름 변경

task copyWithRename(type: Copy) {
    from "."
    into "dir1"
    include('employees.xml')
    rename { String fileName ->
        fileName.replace("employees", "abc")
    }
}

Zip

task zipTask(type: Zip) {
    File destDir = file("dest")
    archiveName "sample.zip"
    from "src"
    destinationDir destDir
}

Delete

task clean(type: Delete) {
    delete rootProject.buildDir
}

태스크 내부에서 copy 사용

if (variant.getBuildType().isMinifyEnabled()) {
    variant.assemble.doLast {
        copy {  // clear project하면 사라지니까 복사
            from variant.mappingFile
            into "${rootDir}/mappingFiles"
            rename { String fileName ->
                "mapping-${variant.name}${data}.txt"
            }
        }
    }
}