Introduction to the

In previous articles, we talked about how to use Gradle to create simple tasks, how tasks depend on each other, and even how to use programs to create tasks. In this article, we’ll take a closer look at Tasks in Gradle.

Define the task

There are several ways to define a task, such as using a string as the task name:

task('hello') {
    doLast {
        println "hello"
    }
}

task('copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}
Copy the code

Tasks can also be created using the Tasks container:

tasks.create('hello') {
    doLast {
        println "hello"
    }
}

tasks.create('copy', Copy) {
    from(file('srcDir'))
    into(buildDir)
}
Copy the code

In the above example, we use the tasks.create method to add the newly created task to the Tasks collection.

We can also define a task using Groovy’s unique syntax:

task(hello) {
    doLast {
        println "hello"
    }
}

task(copy, type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}
Copy the code

The tasks collection class

The Tasks collection class is used to create tasks.

In fact, the Tasks collection class is a very useful utility class that we can use to do a lot of things.

Using Tasks directly in the build file actually references an instance object of TaskContainer. We can also use project.gettasks () to get the instance object.

Let’s take a look at the TaskContainer definition:

public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task> 
Copy the code

By definition, TaskContainer is a collection of tasks and domain objects.

There are four types of methods that are very important in taskContainer:

The first type is the method that locates the task, which is findByPath and getByPath. The difference between the two methods is that findByPath returns null if it’s not found, and getByPath throws UnknownTaskException if it’s not found.

See how to use it:

task hello

println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
Copy the code

Output:

:hello
:hello
Copy the code

The second type is the create task method. The create method has multiple implementations, and you can create a task by name:

task('hello') {
    doLast {
        println "hello"}}Copy the code

You can also create a specific type of task:

task('copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}
Copy the code

You can also create a task with an argument constructor:

class CustomTask extends DefaultTask {
    final String message
    final int number

    @Inject
    CustomTask(String message, int number) {
        this.message = message
        this.number = number
    }
}
Copy the code

We created a constructor for CustomTask with parameters. Note that we need to annotate @javax.inject.Inject to indicate that we can pass parameters to the constructor later.

We can use it like this:

tasks.create('myTask', CustomTask, 'hello'.42)
Copy the code

It can also be used like this:

task myTask(type: CustomTask, constructorArgs: ['hello'.42])
Copy the code

The third type is register, which is also used to create new tasks, but registers perform deferred creation. That is, tasks are created only when they are needed.

Let’s look at the definition of a register method:

TaskProvider<Task> register​(String name,
                            Action<? super Task> configurationAction)
                     throws InvalidUserDataException 
Copy the code

The Register returns a TaskProvider, similar to the Callable in Java multithreading, which is created when we call provider.get () to get the task value.

Or when we call TaskCollection.getByName(java.lang.string), the corresponding task will be created.

The last type is the replace method:

Task replace​(String name)
<T extends Task> T replace​(String name,
                           Class<T> type)
Copy the code

Replace creates a new task and replaces an old task with the same name.

Dependencies between tasks

Dependencies between tasks are determined by task name. We can do dependencies between tasks in the same project:

task hello {
    doLast {
        println 'Hello www.flydean.com! '
    }
}
task intro {
    dependsOn hello
    doLast {
        println "I'm flydean"}}Copy the code

Task dependencies can also be made across projects. If the task dependencies are made across projects, the path of the task needs to be specified:

project('project-a') {
    task taskX {
        dependsOn ':project-b:taskY'
        doLast {
            println 'taskX'
        }
    }
}

project('project-b') {
    task taskY {
        doLast {
            println 'taskY'}}}Copy the code

Or we can deal with dependencies between tasks after we have defined them:

task taskX {
    doLast {
        println 'taskX'
    }
}

task taskY {
    doLast {
        println 'taskY'}}Copy the code

Dependencies can also be added dynamically:

task taskX {
    doLast {
        println 'taskX'}}// Using a Groovy Closure
taskX.dependsOn {
    tasks.findAll { task -> task.name.startsWith('lib') }
}

task lib1 {
    doLast {
        println 'lib1'
    }
}

task lib2 {
    doLast {
        println 'lib2'
    }
}

task notALib {
    doLast {
        println 'notALib'}}Copy the code

Define the order between tasks

Sometimes our tasks have an order of execution. This is called ordering tasks.

Take a look at the difference between ordering and dependency. Dependency represents a strong dependency. If taskA depends on taskB, taskB must be implemented before taskA.

The ordering is not too strong. Indicates that taskA needs to be executed after taskB, but taskB can be executed without it.

Gradle has two orders: must run after and should run after.

TaskA. MustRunAfter (taskB) indicates a sequence relationship that must be followed, while taskA. ShouldRunAfter (taskB) is not required and can be ignored in two cases: The first case is if shouldRunAfter introduces an order loop.

The second case is if all the dependencies of the task are satisfied in parallel execution, then the order is ignored.

Let’s see how it works:

task taskX {
    doLast {
        println 'flydean.com'
    }
}
task taskY {
    doLast {
        println 'hello'
    }
}
taskY.mustRunAfter taskX
//taskY.shouldRunAfter taskX
Copy the code

Give task some description

We can give the task some description so that when we execute Gradle Tasks, we can see:

task copy(type: Copy) {
   description 'Copies the resource directory to the target directory.'
   from 'resources'
   into 'target'
   include('**/*.txt'.'**/*.xml'.'**/*.properties')}Copy the code

Conditional execution of task

Sometimes we need to determine whether to execute a particular task based on certain attributes in the build file. We can use onlyIf:

task hello {
    doLast {
        println 'www.flydean.com'} } hello.onlyIf { ! project.hasProperty('skipHello')}Copy the code

Or we can throw a StopExecutionException, which, if encountered, will not execute subsequent tasks:

task compile {
    doLast {
        println 'We are doing the compile.'
    }
}

compile.doFirst {
    if (true) { throw new StopExecutionException() }
}
task myTask {
    dependsOn('compile')
    doLast {
        println 'I am not affected'}}Copy the code

We can also start and disable tasks:

myTask.enabled = false
Copy the code

Finally, we can make the task time out, and when it does, the thread executing the task will be interrupted and the task will be marked failed.

If we want to continue, we can use –continue.

Note that timeout is useful only for tasks that can respond to interrupts.

task hangingTask(a) {
    doLast {
        Thread.sleep(100000)
    }
    timeout = Duration.ofMillis(500)}Copy the code

task rule

If we want to define rules for certain tasks, we can use tasks.addrule:

tasks.addRule("Pattern: ping<ID>") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) {
            doLast {
                println "Pinging: " + (taskName - 'ping')}}}}Copy the code

Above we define a rule that will output if the taskName starts with a ping.

Take a look at the results:

> gradle -q pingServer1
Pinging: Server1
Copy the code

I can also introduce these rules as dependencies:

task groupPing {
    dependsOn pingServer1, pingServer2
}
Copy the code

Finalizer tasks

Like finally in Java, task can also specify the corresponding Finalize Task:

task taskX {
    doLast {
        println 'taskX'
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}

taskX.finalizedBy taskY

> gradle -q taskX
taskX
taskY
Copy the code

Finalize Task must be executed, even if taskX above throws an exception.

conclusion

Gradle task is a gradle task.

This article is available at www.flydean.com/gradle-task…

The most popular interpretation, the most profound dry goods, the most concise tutorial, many tips you didn’t know waiting for you to discover!

Welcome to pay attention to my public number: “procedures those things”, understand technology, more understand you!