Gradle 如何将模块中的排除项聚合到根排除项中?

Gradle 如何将模块中的排除项聚合到根排除项中?,gradle,groovy,multi-module,Gradle,Groovy,Multi Module,我们有一个多模块的Gradle项目。所有模块都是根项目目录的直接子目录 在模块中,我们运行JUnit并使用Jacoco生成覆盖率。在根中,我们聚合覆盖率。这一切都有效,但目前我们必须输入两次排除: MODULE1: apply plugin: "jacoco" jacoco { toolVersion = "0.8.5" } def static analysisExcludes() { return [ "com/ourcompany/module1/*Co

我们有一个多模块的Gradle项目。所有模块都是根项目目录的直接子目录

在模块中,我们运行JUnit并使用Jacoco生成覆盖率。在根中,我们聚合覆盖率。这一切都有效,但目前我们必须输入两次排除:

MODULE1:

apply plugin: "jacoco"
jacoco {
    toolVersion = "0.8.5"
}

def static analysisExcludes() {
    return [
        "com/ourcompany/module1/*Config*",
        "com/ourcompany/module1/endpoint/exception/**",
        "com/ourcompany/module1/v2/**",
        "com/ourcompany/module1/v3/**",
        "src/generated/**",
        "src/*test*/**",
        "wrapper/dists/**"
    ]
}

def execData() {
    return files(fileTree(buildDir).include("jacoco/*.exec"))
}

jacocoTestReport {
    getExecutionData().setFrom(execData())
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: analysisExcludes())
        }))
    }
}

jacocoTestCoverageVerification {
    getExecutionData().setFrom(execData());
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: analysisExcludes())
        }))
    }
    violationRules {
        rule {
            limit {
                minimum = 0.93
            }
        }
    }
}

MODULE2:

apply plugin: "jacoco"
jacoco {
    toolVersion = "0.8.5"
}

def static analysisExcludes() {
    return [
        "com/ourcompany/module2/*IgnoreMe*"
        "src/generated/**",
        "src/*test*/**",
        "wrapper/dists/**"
    ]
}

def execData() {
    return files(fileTree(buildDir).include("jacoco/*.exec"))
}

jacocoTestReport {
    getExecutionData().setFrom(execData())
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: analysisExcludes())
        }))
    }
}

jacocoTestCoverageVerification {
    getExecutionData().setFrom(execData());
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: analysisExcludes())
        }))
    }
    violationRules {
        rule {
            limit {
                minimum = 0.87
            }
        }
    }
}

ROOT:

apply plugin: "jacoco"
jacoco {
    toolVersion = "0.8.5"
}

// TODO: Eliminate double-entry bookkeeping by getting the excludes from the modules.
def static aggregatedAnalysisExcludes() {
    return [
        "**/com/ourcompany/module1/*Config*",
        "**/com/ourcompany/module1/endpoint/exception/**",
        "**/com/ourcompany/module1/v2/**",
        "**/com/ourcompany/module1/v3/**",
        "**/com/ourcompany/module2/*IgnoreMe*"
        "**/src/generated/**",
        "**/src/*test*/**",
        "**/wrapper/dists/**"
    ]
}

def execData() {
    return files(fileTree(rootDir).include("**/build/jacoco/*.exec"))
}

def includedClasses() {
    return files(fileTree(dir: rootDir, include: "**/build/classes/**", exclude: aggregatedAnalysisExcludes()))
}

task jacocoAggregateReport(type: JacocoReport) {
    getExecutionData().setFrom(execData());
    afterEvaluate {
        classDirectories.setFrom(includedClasses())
    }
}

task jacocoAggregateTestCoverageVerification(type: JacocoCoverageVerification) {
    getExecutionData().setFrom(execData());
    afterEvaluate {
        classDirectories.setFrom(includedClasses())
    }
    violationRules {
        rule {
            limit {
                minimum = 0.91
            }
        }
    }
}

task mergeJacocoExecData(type: JacocoMerge) {
    setExecutionData(execData());
    setDestinationFile(new File("build/jacoco/all.exec"))
}

apply plugin: "org.sonarqube"

def junitResultsDirs() {
    def dirs = []
    rootDir.eachDirRecurse { dir ->
        if (dir.absolutePath.matches("^.*/build/test-results/(test|(component|integration)Test)\$")) {
            dirs << dir
        }
    }
    return dirs;
}

sonarqube {
    properties {
        property "sonar.projectName", "Multimodule Repo"
        property "sonar.projectKey", "multimodule-repo"
        property "sonar.java.coveragePlugin", "jacoco"
        property "sonar.jacoco.reportPath", "build/jacoco/all.exec"
        property "sonar.junit.reportsPath", junitResultsDirs()
        property "sonar.issuesReport.html.enable", true
        property "sonar.issuesReport.console.enable", true
        property 'sonar.exclusions', aggregatedAnalysisExcludes()
    }
}
或:

def aggregatedAnalysisExcludes(){
def excludes=[]
for(模块m:rootDir.modules){

excludes我知道您希望确保在子项目级别创建的Jacoco excludes自动传播到聚合级别

这可以通过确保文件树不会过早评估来实现,当然,还可以询问JacoReport任务它们是如何配置的。我找到了解决方案,并编写了一个小插件,创建了一个聚合报告,名为(查看内部以查找,包括排除)

基本上,您可以将以下内容添加到build.gradle中:

然后跑

gradle jacocoAggregatedReport

这是一个具有自动聚合的JacoReport排除的子项目。

我想要一个更具Groovy风格的解决方案(声明性、闭包),多亏了@barfuin,我终于能够实现这一目标

在模块中,在定义排除后添加:

ext {
    analysisExcludes = analysisExcludes()
}
在根目录中,添加以下内容:

def aggregatedAnalysisExcludes() {
    evaluationDependsOnChildren()
    def excludes = new HashSet<>()
    subprojects.each {
        subproject -> subproject.property("analysisExcludes").each {
            exclude -> excludes << "**/" + exclude;
        }
    }
    return (String[])excludes.stream().sorted().toArray()
}
def aggregatedAnalysisExcludes(){
evaluationDependsOnChildren()
def excludes=newhashset()
各子项目{
子项目->子项目属性(“analysisExcludes”)。每个{

排除->排除排除聚合器的清理版本:

def aggregatedAnalysisExcludes() {
evaluationDependsOnChildren()
def excludes = new HashSet<>()
subprojects.each {
    subproject ->
        subproject.findProperty("analysisExcludes").each {
            exclude -> excludes << "**/" + exclude
        }
}
return excludes.stream().sorted().toArray({ length -> new String[length] })
def aggregatedAnalysisExcludes(){
evaluationDependsOnChildren()
def excludes=newhashset()
各子项目{
子项目->
子项目。findProperty(“analysisExcludes”)。每个{
排除->排除新字符串[长度]})

}

我一直在用Java重新实现我们的自定义插件,以使使用它们更容易处理(例如,调试器实际工作,自动完成和导航工作,等等)。我以为只有我一个人。关于你的解决方案,你能解释一下如何/在哪里使用排除项吗?你的解决方案没有提到排除项。我在寻找一种方法来引用子项目中任务中的排除项,但不知道如何访问它们。假定根项目对变量和函数具有完全可见性s在子项目中声明,但我无法使其工作。对,不需要直接引用它们。在链接的代码中,我们调用
JacocoReport.getAllClassDirs()
,注意不要解决懒惰的问题。这样,它可以在稍后提供文件时更新(例如,在配置阶段还不存在类!)。只有在所有文件(和配置)都正确显示的情况下,才会在执行阶段进行解决。谢谢。对我来说,它似乎工作得很好。
ext {
    analysisExcludes = analysisExcludes()
}
def aggregatedAnalysisExcludes() {
    evaluationDependsOnChildren()
    def excludes = new HashSet<>()
    subprojects.each {
        subproject -> subproject.property("analysisExcludes").each {
            exclude -> excludes << "**/" + exclude;
        }
    }
    return (String[])excludes.stream().sorted().toArray()
}
def aggregatedAnalysisExcludes() {
evaluationDependsOnChildren()
def excludes = new HashSet<>()
subprojects.each {
    subproject ->
        subproject.findProperty("analysisExcludes").each {
            exclude -> excludes << "**/" + exclude
        }
}
return excludes.stream().sorted().toArray({ length -> new String[length] })