Gradle tip #3: Tasks ordering

简介:

I noticed that the quite often problem I face when I work with Gradle - is tasks ordering (either existing or my custom ones). Apparently my build works better when my tasks are executed at the right moment of the build process :)

我注意到我在使用Gradle的时候遇到的大多数问题都是和task的执行顺序有关的。很明显如果我的构建会工作的更好如果我的task都是在正确的时候执行。

So let's dig deeper into how can we change tasks execution order.

下面我们就深入了解一下如何更改task的执行顺序。

dependsOn

I believe the most obvious way of telling your task to execute after some other task - is to use dependsOn method.

我认为最直接的方式来说明的你task的执行时依赖别的task的方法就是使用dependsOn方法。 

Let's consider existing task A and we need to add task Bwhich executes only after task A is executed:

比如下面的场景,已经存在task A,我们要添加一个task B,它的执行必须要在A执行完之后: 

587773-20150919155606570-1846565365.png

This is probably the easiest thing you can do. Given that tasks Aand B are already defined:

这是一个很简单的场景,假定A和B的定义如下:

task A << {println 'Hello from A'}
task B << {println 'Hello from B'}

What you need to do - is just tell Gradle that task B depends on task A

B.dependsOn A

只需要简单的调用B.dependsOn A,就可以了。 

This means that whenever I try to execute task B - Gradle will take care of executing task A as well:

这意味着,只要我执行task B,task A都会先执行。

$ gradle B
:A
Hello from A
:B
Hello from B

Alternatively, you could declare such a dependency right inside task configuration section:

另外,你也可以在task的配置区中来声明它的依赖:

task A << {println 'Hello from A'}
task B {
    dependsOn A
    doLast {
        println 'Hello from B'  
    }
}

Result is the same.

But what if we want to insert our task inside already existing task graph?

如果我们想要在已经存在的task依赖中插入我们的task该怎么做呢? 

587773-20150919155626476-327691506.png

The process is pretty much the same:

过程和刚才类似。

original task graph:

task A << {println 'Hello from A'}
task B << {println 'Hello from B'}
task C << {println 'Hello from C'}

B.dependsOn A
C.dependsOn B

our new custom task:

task B1 << {println 'Hello from B1'}
B1.dependsOn B
C.dependsOn B1

output:

$ gradle C
:A
Hello from A
:B
Hello from B
:B1
Hello from B1
:C
Hello from C

Please note, that dependsOn adds task to the set of dependencies. Thus it is totally fine to be dependent on multiple tasks:

注意dependsOn把task添加到依赖的集合中,所以依赖多个task是没有问题的

587773-20150919155648836-1386302459.png

task B1 << {println 'Hello from B1'}
B1.dependsOn B
B1.dependsOn Q

output:

$ gradle B1
:A
Hello from A
:B
Hello from B
:Q
Hello from Q
:B1
Hello from B1

mustRunAfter

Now imagine that our task depends on 2 other tasks. For this example I decided to use more real-life case. Imagine I have one task for unit tests and another for UI tests. Also I have a task which executes both unit & UI tests:

现在假定我又一个task,它依赖于其他两个task。这里我使用一个真实的场景,我有两个task,一个单元测试的task,一个是UI测试的task。另外还有一个task是跑所有的测试的,它依赖于前面的两个task。

587773-20150919155707883-1205706384.png

task unit << {println 'Hello from unit tests'}
task ui << {println 'Hello from UI tests'}
task tests << {println 'Hello from all tests!'}

tests.dependsOn unit
tests.dependsOn ui

output:

$ gradle tests
:ui
Hello from UI tests
:unit
Hello from unit tests
:tests
Hello from all tests!

Even though tasks unit and UI tests will be executed beforetask tests, the order of execution for tasks ui and unit is not determined. Right now I believe they will be executed in alphabetical order, but this behavior is an implementation detail and you definitely should not rely on this fact.

尽管unitest和UI test会子啊test task之前执行,但是unit和ui这两个task的执行顺序是不能保证的。虽然现在来看是按照字母表的顺序执行,但这是依赖于Gradle的实现的,你的代码中绝对不能依赖这种顺序

Since UI tests are executing much longer than unit tests, I want my unit tests run first and only if everything OK - proceed to executing UI tests. So what should I do if I want my unit tests run before UI tests?

由于UI测试时间远比unit test时间长,因此我希望unit test先执行。

One way for solving this would be to make UI test task depend on unit test task:

一个解决办法就是让ui task依赖于unit task:

587773-20150919155728023-273163065.png

task unit << {println 'Hello from unit tests'}
task ui << {println 'Hello from UI tests'}
task tests << {println 'Hello from all tests!'}

tests.dependsOn unit
tests.dependsOn ui
ui.dependsOn unit // <-- I added this dependency

output

paveldudka$ gradle tests
:unit
Hello from unit tests
:ui
Hello from UI tests
:tests
Hello from all tests!

Now my unit tests are getting executed before UI tests! Great!

现在unit test会在ui test之前执行了。 

BUT! There is one really big fat nasty problem with this approach! My UI tests do not really depend on unit tests. I wanna be able to run my UI tests separately, but now every time I want to run my UI tests - my unit tests will be run as well!

但是这里有个很恶心的问题,我的ui测试其实并不依赖于unit test。我希望能够单独的执行ui test,但是这里每次我执行ui test,都会先执行unit test。 

That's where mustRunAfter method comes into play. It tells Gradle to run task after task specified as an argument. So essentially, we do not introduce dependency between our unit tests and UI tests, but instead we told Gradle to give unit tests priority if they are executed together, so unit tests are executedbefore our UI test suite:

这里就要用到mustRunAfter了。mustRunAfter并不会添加依赖,它只是告诉Gradle执行的优先级如果两个task同时存在。比如我们这里就可以指定ui.mustRunAfter unit,这样如果ui task和unit task同时存在,Gradle会先执行unit test,而如果只执行gradle ui,并不会去执行unit task。

task unit << {println 'Hello from unit tests'}
task ui << {println 'Hello from UI tests'}
task tests << {println 'Hello from all tests!'}

tests.dependsOn unit
tests.dependsOn ui
ui.mustRunAfter unit

output

paveldudka$ gradle tests
:unit
Hello from unit tests
:ui
Hello from UI tests
:tests
Hello from all tests!

And the dependency graph looks like: 

依赖关系如下图: 

587773-20150919155754539-1882627766.png

Notice that we lost explicit dependency between UI tests and unit tests! Now if I decide to run just UI tests - my unit tests won't be executed.

Please note that mustRunAfter is marked as "incubating" (as of Gradle 2.4) which means that this is an experimental feature and its behavior can be changed in future releases.(mustRunAfter在Gradle2.4中目前还是实验性的功能。)

finalizedBy

Now I have task which runs both UI and unit tests. Great! Let's say each of them produces test report. So I decided to create a task which merges 2 test reports into one:

现在我们已经有两个task,unit和ui,假定这两个task都会输出测试报告,现在我想把这两个测试报告合并成一个: 

587773-20150919155814133-832112147.png

task unit << {println 'Hello from unit tests'}
task ui << {println 'Hello from UI tests'}
task tests << {println 'Hello from all tests!'}
task mergeReports << {println 'Merging test reports'}

tests.dependsOn unit
tests.dependsOn ui
ui.mustRunAfter unit
mergeReports.dependsOn tests

Now if I want to get test report with both UI & unit tests - I execute mergeReports task:

现在如果我想获得ui和unit的测试报告,执行task mergeReports就可以了。

$ gradle mergeReports
:unit
Hello from unit tests
:ui
Hello from UI tests
:tests
Hello from all tests!
:mergeReports
Merging test reports

It works, but... it looks sloppy.. mergeReports task doesn't make a lot of sense from user (by user I mean developer :) ) perspective. I want to be able to execute tests and get merged report. Obviously, I could add merge logic inside teststask, but for the sake of this demo - I want to keep this logic in separate mergeReports task.

这个task是能工作,但是看起来好笨啊。mergeReports从用户的角度来看感觉不是特别好。我希望执行tests task就可以获得测试报告,而不必知道mergeReports的存在。当然我可以把merge的逻辑挪到tests task中,但我不想把tests task搞的太臃肿,我还是继续把merge的逻辑放在mergeReports task中。 

finalizedBy method come to the rescue. Its name is quite self-explanatory - it adds finalizer task to this task.

finalizeBy来救场了。顾名思义,finalizeBy就是在task执行完之后要执行的task。

So let's modify our script as follows:

修改我们的脚本如下:

task unit << {println 'Hello from unit tests'}
task ui << {println 'Hello from UI tests'}
task tests << {println 'Hello from all tests!'}
task mergeReports << {println 'Merging test reports'}

tests.dependsOn unit
tests.dependsOn ui
ui.mustRunAfter unit
mergeReports.dependsOn tests

tests.finalizedBy mergeReports

Now I'm able to execute tests task and I still get my merged test report:

现在执行tests task就可以拿到测试报告了:

$ gradle tests
:unit
Hello from unit tests
:ui
Hello from UI tests
:tests
Hello from all tests!
:mergeReports
Merging test reports

Please note that finalizedBy is marked as "incubating" (as of Gradle 2.4) which means that this is an experimental feature and its behavior can be changed in future releases.(注意,finalizedBy也是Gradle2.4的实验性功能)

This is pretty much it - with these 3 tools you can easily tune your build process!

Happy gradling!




本文转自我爱物联网博客园博客,原文链接:http://www.cnblogs.com/yydcdut/p/4821611.html如需转载请自行联系原作者

相关文章
|
Java API Kotlin
Gradle Authoring Tasks
在入门教程中,您学习了如何创建简单的任务。 稍后您还学习了如何向这些任务添加额外的行为,并学习了如何在任务之间创建依赖关系。 这一切都是关于简单的任务,但 Gradle 把任务的概念更进一步。 Gradle 支持增强型任务,这些任务具有自己的属性和方法。 这与您习惯使用 Ant 目标的情况大不相同。 这些强化的任务要么是你提供的,要么是内置在 Gradle 的。
86 0
|
6月前
|
SQL 人工智能 移动开发
Android Studio插件版本与Gradle 版本对应关系
Android Studio插件版本与Gradle 版本对应关系
Android Studio插件版本与Gradle 版本对应关系
|
7月前
|
存储 Java Android开发
Android 开发 - 充分利用Gradle
Android 开发 - 充分利用Gradle
112 2
|
缓存 Android开发
Android Studio中如何清理gradle缓存
Android Studio中如何清理gradle缓存
|
Android开发
Android Studio中修改gradle插件版本和Gradle版本
Android项目中,我们一般要设置gradle插件版本和gradle版本。 项目根目录下的build.gradle文件中,通过classpath可以指定gradle插件的版本。
|
4月前
|
开发工具 Android开发 开发者
Android 项目编译 Gradle 配置说明
Android 项目编译 Gradle 配置说明
151 0