基于子项目属性配置Gradle任务

4

我正在尝试基于子项目中的一个属性配置Zip任务,但是在配置任务时该属性尚不可访问。例如,我想从我的zip文件中排除所有具有 toexclude = true 的项目。因此,我要排除的子项目的build.gradle如下:

ext.toexclude = true;
...

我的主要build.gradle文件有以下任务:

task zipContent (type: Zip){
    def excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
    println excludedProjects
    destinationDir = "/some/path"
    baseName = "myFile.zip"
    exclude excludedProjects
    from "/some/other/path"
}

问题在于excludedProjects始终为空。实际上,当我执行任务时,我可以看到[]。我认为这是由于在配置任务时没有设置子项目build.gradle中设置的属性。作为证据,如果我将任务的第一行替换为以下内容:
def excludedProjects = allprojects.collect{it.name}

这个任务会打印出我项目的所有名称,而且压缩包里什么也没有(这意味着问题在于 p.toexclude == true)。

另外,如果我尝试这样做:

task zipContent (type: Zip){

    def excludedProjects = []
    doFirst{
        excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
        println "IN DOFIRST"
        println excludedProjects
    }

    println "IN TASK CONFIG"
    println excludedProjects
    destinationDir = "/some/path"
    baseName = "myFile.zip"
    exclude excludedProjects
    from "/some/other/path"
}

该任务会打印出IN TASK CONFIG后跟一个空数组,然后打印出IN DOFIRST,其中包含只有我设置了ext.toexclude == true的子项目。
那么,在配置时间是否有一种方法可以获取子项目的属性?
3个回答

6

关键问题是:在构建的哪个阶段可以获取到所有必要的信息?

由于我们想了解构建中的每个项目,在其中额外属性toexclude设置为true,并且可能(按设计)通过构建脚本设置该属性,因此需要评估每个构建脚本。

现在,我们有两个选项:

  1. By default, subprojects are evaluated after the parent (root) project. To ensure the evaluation of each project, we need to wait for the point of the build, where all projects are evaluated. Gradle provides a listener for that point:

    gradle.addListener(new BuildAdapter() {
        @Override
        void projectsEvaluated(Gradle gradle) {
            tasks.getByPath('zipContent').with {
                exclude allprojects.findAll { it.toexclude }.collect{ it.name }
            }
        }
    })
    
  2. Gradle provides the method evaluationDependsOnChildren(), to turn the evaluation order around. It may be possible to use your original approach by calling this method before querying the excluded projects. Since this method only applies on child projects, you may try to call evaluationDependsOn(String) for each project in the build to also apply for 'sibling' projects. Since this solution breaks Gradle default behavior, it may have undesired side effects.


我成功地从你的第一种方法中获得了正确的排除项目列表。现在,从projectsEvaluated方法中,有没有一种方法可以使用排除项目列表调用zip任务?理想情况下,当调用另一个任务时,我会调用zip任务。 - Cydrick Trudel
1
在Gradle中,您不直接调用任务,而是创建任务并定义它们之间的依赖关系。您只需要配置任务,基本上与以前的方式相同。您可以从任务上下文中注册侦听器,以便您可以直接从侦听器方法中配置任务(基本上将我的代码示例移动到任务配置闭包中)。另一个选项是全局注册侦听器并通过tasks.getByPath(String)搜索任务。 - Lukas Körfer
我编辑了代码示例,以展示你如何从监听器方法中配置任务。 - Lukas Körfer

1
您可以在根项目中调用evaluationDependsOnChildren(),以便在根项目之前评估子项目。
例如:
evaluationDependsOnChildren() 
task zipContent (type: Zip) { ... } 

另一种选择是使用 afterEvaluate { ... } 闭包来延迟评估。
例如:
afterEvaluate {
    task zipContent (type: Zip) { ... } 
} 

但是 afterEvaluate 闭包将在当前项目被评估时调用,而不是在可能的子项目或同级项目之后。由于他正在使用 allprojects,其中一些可能尚未被评估。 - Lukas Körfer
我不确定afterEvaluate{...}在什么阶段被调用。可能是在子项目评估之后触发此事件,可以轻松尝试并查看!否则,我的第一个建议将起作用。 - lance-java
Gradle提供了一个特殊的监听函数projectsEvaluated,用于在所有项目评估完成后进行构建。afterEvaluate闭包及其监听器对应项是针对特定项目的。 - Lukas Körfer
我同意你所说的一切。但这仍然有可能是所有项目首先进行正常评估,然后触发每个项目的 afterEvaluate{...}。就像我说的那样,我不确定时间,但完全有可能,值得测试。 - lance-java

1
只需在任务之外定义 excludedProjects
def excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}

task zipContent (type: Zip){
    destinationDir = file("/some/path")
    baseName = "myFile.zip"
    exclude excludedProjects
    from "/some/other/path"
}

我尝试了这个方法,但是数组仍然为空。我在设置数组后打印了它的内容,也在任务中打印了数组内容,两次都为空。不过,如果我在doFirst块内写下第一行并打印输出,那么我就可以得到想要的内容。 - Cydrick Trudel
很奇怪,因为我的解决方案应该是可行的(我已经测试过了)。也许你可以分享更多你的 build.gradle 文件? - ToYonos
excludedProjects的定义放在任务内部或外部应该没有任何区别,因为任务的配置闭包在创建任务时直接执行。 - Lukas Körfer
将def放在“afterEvaluate”块中。以下页面有一个示例,它循环遍历所有项目后,在处理具有测试的项目。您可以使用类似的方法来收集具有您属性的项目。https://docs.gradle.org/current/userguide/build_lifecycle.html向下滚动页面查找“示例:向每个设置了某些属性的项目添加测试任务”。 - John Elion

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接