如何将模块中的排除项汇总到根排除项?

问题描述 投票:1回答:3

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

在模块中,我们运行JUnit并使用Jacoco生成coverage。在根源,我们汇总覆盖范围。所有这些都可以,但是目前我们必须输入两次排除项:

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() {
    return rootDir.modules.excludes.withPrefixes("**/")
}

或:

def aggregatedAnalysisExcludes() {
    def excludes = []
    for (Module m : rootDir.modules) {
        excludes << m.excludes.withPrefixes("**/")
    }
    return excludes
}

我们如何编写真实的代码?

注意:假设有一种方法可以执行我们想要的操作,则结果列表将包含src / generated,src / test和wrapper / dists的重复项。我们尝试创建具有重复项的列表,以查看是否重要,而无关紧要。即使有重复项,排除项也可以正常工作。但是,如果有一种干净的声明性方式删除重复项,那将是对解决方案的不错补充。

gradle groovy multi-module
3个回答
0
投票

我知道您要确保Jacoco排除在子项目级别上所做的更改会自动传播到聚合级别。

这可以通过确保不对文件树进行过早评估,当然还要询问JacocoReport任务有关它们的配置方式来实现。我在解决方案中找到了this Gist,并编写了一个小插件来创建一个汇总报告,称为gradle-jacoco-log(在内部查找source code which explicitly performs the aggregation,包括排除项)。

您基本上将添加到您的build.gradle

plugins {
    id 'org.barfuin.gradle.jacocolog' version '1.1.0'
}

然后运行

gradle jacocoAggregatedReport

这里是full example project,其中的子项目带有JacocoReport排除项,这些子项目会自动聚合。


0
投票

我想要一个更具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()
}

(String[])强制转换很烦人,但是如果没有它,则分配排除类型时类型将不匹配:稍后。更改为new HashSet<String>()没有帮助,并且.toArray(String[]::new)会出现编译错误。


0
投票

排除聚合器的清理版本:

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] })

}

© www.soinside.com 2019 - 2024. All rights reserved.