在我们的 Gradle(使用 Gradle 6.4.1(项目中,启用了按需配置。这意味着在项目的配置阶段不会配置任务。例如,这意味着subproject/build.gradle
println(project.tasks)
语句可能仅打印子项目中存在的某些任务。
我想以编程方式在所有项目中按名称查找所有任务。我当前的代码只是迭代allprojects
:
def foobar(Project rootProject, String name) {
Collection<String> paths = rootProject.allprojects.findResults { Project p ->
return p.tasks.findByName(name)?.getPath()
}
return paths
}
方法foobar()
由自定义 Gradle 插件调用,以设置复杂的依赖关系图。此图包括:"taskFoobar
应dependOn
allprojects
中名为name
的所有任务"。这是插件的行为类似于 Gradle 本身的 CLI 行为方式所必需的:
您还可以使用仅包含任务名称的任务选择器为所有子项目运行任务。例如,当从根项目目录调用时,这将对所有子项目运行"test"任务:
$ gradle test
这就是为什么需要这种程序化迭代的原因。请注意,foobar()
是在rootProject.afterEvaluate
块中调用的。
但是,由于按需配置功能,某些子项目中的某些任务在集合paths
中丢失,因为它们尚未"配置",仅"已注册"。
在调查期间,我研究了内置任务tasks
的工作原理。类TaskReportTask
使用内部 Gradle API,称为ProjectTaskLister
,其实现DefaultProjectTaskLister
调用另一个内部 API,即接口TaskContainerInternal
的方法realize()
:
/**
* Force the task graph to come into existence.
*/
void realize();
如何确保所有"已注册"的任务都可以在不使用这个内部 Gradle API 的情况下进行迭代?
如果不需要设置新的依赖项,则可以改用rootProject.getGradle().projectsEvaluated()
。这将确保在调用foobar()
时评估所有项目,而不仅仅是rootProject
。
方法 foobar(( 由自定义 Gradle 插件调用,以设置复杂的依赖关系图。
如果在评估完所有项目后确实需要更改依赖项图,很遗憾,您不能使用rootProject.getGradle().projectsEvaluated()
.此挂钩在构建的生命周期中执行得太晚 - 此时所有依赖项都是固定的。
但是,您可以像这样滚动自己的听众:
static void whenAllProjectsEvaluated(Project rootProject, Closure action) {
final AtomicInteger counter = new AtomicInteger()
final int allProjectsCount = rootProject.getAllprojects().size()
rootProject.allprojects { p ->
p.afterEvaluate {
final int currentEvaluatedCount = counter.incrementAndGet()
rootProject.logger.lifecycle("${p.name} - afterEvaluate (${currentEvaluatedCount}/$allProjectsCount")
if (currentEvaluatedCount == allProjectsCount) {
action.call()
}
}
}
}
需要AtomicInteger
才能允许并行生成。然后,此方法可以调用为:
whenAllProjectsEvaluated(project) {
foobar(...)
}