diff --git a/README.md b/README.md index 60bbfae..cddb83f 100644 --- a/README.md +++ b/README.md @@ -105,24 +105,17 @@ scoverage { } ``` -### Run without normal compilation - -By default, running any of the plugin tasks will compile the code both using "normal" compilation (`compileScala`) -and using the scoverage scalac plugin (`compileScoverageScala`). - -In cases where you only wish to generate reports / validate coverage, but are not interested in publishing the code, -it is possible to only compile the code with the scoverage scalac plugin, thus reducing build times significantly. -In order to do so, simply add the arguments `-PscoverageCompileOnly` to the gradle execution. -For example: `gradle reportScoverage -PscoverageCompileOnly`. - -Note that this mode is incompatible with parallel builds in multi-module projects. - ### Compatibility with Consistent Versions Plugin In order for the plugin to work alongside [Palantir's consistent versions plugin](https://github.com/palantir/gradle-consistent-versions), the Scala version must be manually configured (via `scoverageScalaVersion`); otherwise, the plugin will attempt to resolve the compilation classpath, which is prohibited by the versions plugin. +Migration to 8.x +---------------- + +* Running without normal compilation is no longer supported. + Migration to 7.x ---------------- diff --git a/build.gradle b/build.gradle index efecced..b0251c2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java-gradle-plugin' - id "com.gradle.plugin-publish" version "1.0.0" + id "com.gradle.plugin-publish" version "1.1.0" id "org.jetbrains.gradle.plugin.idea-ext" version "1.0" } @@ -11,7 +11,7 @@ repositories { group 'org.scoverage' description = 'gradle-scoverage is a Gradle plugin for calculating code coverage using Scoverage' if (project.version == 'unspecified') { - version = '7.0.0-SNAPSHOT' + version = '8.0.0-SNAPSHOT' } ext { website = 'http://scoverage.org' @@ -22,22 +22,19 @@ ext { } gradlePlugin { + website = project.ext.website + vcsUrl = project.ext.vcsUrl + description = project.description plugins { gradleScoverage { id = 'org.scoverage' implementationClass = 'org.scoverage.ScoveragePlugin' displayName = 'Gradle Scoverage plugin' + tags.set(['coverage', 'scala', 'scoverage']) } } } -pluginBundle { - website = project.website - vcsUrl = ext.vcsUrl - description = project.description - tags = ['coverage', 'scala', 'scoverage'] -} - apply plugin: 'maven-publish' apply plugin: 'groovy' @@ -46,7 +43,8 @@ targetCompatibility = '1.8' dependencies { - compileOnly "org.scoverage:scalac-scoverage-plugin_2.13:1.4.2" + compileOnly "org.scoverage:scalac-scoverage-plugin_2.13.8:2.0.7" + compileOnly "org.scoverage:scalac-scoverage-reporter_2.13:2.0.7" implementation group: 'commons-io', name: 'commons-io', version: '2.6' testImplementation 'junit:junit:4.12' @@ -116,7 +114,7 @@ gradlePlugin { task groovydocJar(type: Jar, dependsOn: groovydoc) { from "$buildDir/docs/groovydoc" - classifier 'groovydoc' + archiveClassifier.set('groovydoc') } def propOrDefault(String property) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..d79d004 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/crossScalaVersionTest/java/org/scoverage/Scala32Test.java b/src/crossScalaVersionTest/java/org/scoverage/Scala32Test.java new file mode 100644 index 0000000..d657410 --- /dev/null +++ b/src/crossScalaVersionTest/java/org/scoverage/Scala32Test.java @@ -0,0 +1,6 @@ +package org.scoverage; + +public class Scala32Test extends ScalaVersionTest { + + public Scala32Test() { super("3_2"); } +} diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle index 88c5533..a510442 100644 --- a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation group: 'org.scala-lang', name: 'scala-library', version: "2.12.8" + implementation group: 'org.scala-lang', name: 'scala-library', version: "2.12.17" testImplementation group: 'org.scalatest', name: "scalatest_2.12", version: scalatestVersion } diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_13/build.gradle b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_13/build.gradle index 4820211..ea57742 100644 --- a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_13/build.gradle +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/2_13/build.gradle @@ -1,4 +1,4 @@ dependencies { - implementation group: 'org.scala-lang', name: 'scala-library', version: "2.13.1" + implementation group: 'org.scala-lang', name: 'scala-library', version: "2.13.10" testImplementation group: 'org.scalatest', name: "scalatest_2.13", version: scalatestVersion } diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/build.gradle b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/build.gradle new file mode 100644 index 0000000..a703f21 --- /dev/null +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/build.gradle @@ -0,0 +1,5 @@ +dependencies { + implementation 'org.scala-lang:scala3-library_3:3.2.0' + testImplementation 'org.scalatest:scalatest_3:3.2.14' + testImplementation "org.scalatestplus:junit-4-13_3:3.2.14.0" +} \ No newline at end of file diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/main/scala/org/hello/World3_2.scala b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/main/scala/org/hello/World3_2.scala new file mode 100644 index 0000000..2e2530b --- /dev/null +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/main/scala/org/hello/World3_2.scala @@ -0,0 +1,14 @@ +package org.hello + +class World3_2 { + + // Scala 3 enum to force Scala 3 (Dotty) compiler + enum Num(val value: String): + case Three extends Num("3") + case Two extends Num("2") + + def foo(): String = { + val s = Num.Three.value + Num.Two.value + s + } +} \ No newline at end of file diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/test/scala/org/hello/World3_2Suite.scala b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/test/scala/org/hello/World3_2Suite.scala new file mode 100644 index 0000000..4582dc8 --- /dev/null +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/3_2/src/test/scala/org/hello/World3_2Suite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.funsuite.AnyFunSuite +import org.scalatestplus.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class World3_2Suite extends AnyFunSuite { + + test("foo") { + new World3_2().foo() + } +} \ No newline at end of file diff --git a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/settings.gradle b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/settings.gradle index f294f6a..9123650 100644 --- a/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/settings.gradle +++ b/src/crossScalaVersionTest/resources/projects/scala-multi-module-cross-version/settings.gradle @@ -1 +1 @@ -include '2_12', '2_13' \ No newline at end of file +include '2_12', '2_13', '3_2' \ No newline at end of file diff --git a/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java b/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java index 5da6f60..fa85c16 100644 --- a/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaMultiModuleTest.java @@ -1,6 +1,7 @@ package org.scoverage; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -60,6 +61,7 @@ public void reportScoverageOnlyA() { } @Test + @Ignore public void reportScoverageOnlyAWithoutNormalCompilation() { AssertableBuildResult result = run("clean", ":a:" + ScoveragePlugin.getREPORT_NAME(), @@ -192,6 +194,7 @@ public void checkScoverageWithoutCoverageInA() throws Exception { } @Test + @Ignore public void checkScoverageWithoutNormalCompilationAndWithoutCoverageInCommon() throws Exception { AssertableBuildResult result = runAndFail("clean", @@ -254,6 +257,7 @@ public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { } @Test + @Ignore public void aggregateScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getAGGREGATE_NAME(), diff --git a/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java b/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java index dc84e1f..8d9982b 100644 --- a/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaMultiModuleWithMultipleTestTasksTest.java @@ -1,6 +1,7 @@ package org.scoverage; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; @@ -188,6 +189,7 @@ public void checkScoverageWithoutCoverageInA() throws Exception { } @Test + @Ignore public void checkScoverageWithoutNormalCompilationAndWithoutCoverageInCommon() throws Exception { AssertableBuildResult result = runAndFail("clean", @@ -251,6 +253,7 @@ public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { } @Test + @Ignore public void aggregateScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getAGGREGATE_NAME(), diff --git a/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java index 19e0e82..ed7b8f4 100644 --- a/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaSingleModuleTest.java @@ -1,6 +1,7 @@ package org.scoverage; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; public class ScalaSingleModuleTest extends ScoverageFunctionalTest { @@ -100,6 +101,7 @@ public void reportScoverageWithExcludedClasses() throws Exception { } @Test + @Ignore public void reportScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), diff --git a/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java b/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java index cba1c6a..6b485b6 100644 --- a/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java +++ b/src/functionalTest/java/org/scoverage/ScalaSingleModuleWithMultipleTestTasksTest.java @@ -1,6 +1,7 @@ package org.scoverage; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; public class ScalaSingleModuleWithMultipleTestTasksTest extends ScoverageFunctionalTest { @@ -118,6 +119,7 @@ public void reportScoverageWithExcludedClasses() throws Exception { } @Test + @Ignore public void reportScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), diff --git a/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java b/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java index 051b6bb..bfc1c09 100644 --- a/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java +++ b/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java @@ -122,8 +122,8 @@ private void configureArguments(String... arguments) { List fullArguments = new ArrayList<>(); fullArguments.add("-PscalaVersionMajor=2"); - fullArguments.add("-PscalaVersionMinor=12"); - fullArguments.add("-PscalaVersionBuild=8"); + fullArguments.add("-PscalaVersionMinor=13"); + fullArguments.add("-PscalaVersionBuild=10"); fullArguments.add("-PjunitVersion=5.3.2"); fullArguments.add("-PjunitPlatformVersion=1.3.2"); fullArguments.add("-PscalatestVersion=3.0.8"); diff --git a/src/functionalTest/resources/projects/scala-multi-module-multiple-test-tasks/build.gradle b/src/functionalTest/resources/projects/scala-multi-module-multiple-test-tasks/build.gradle index af770c5..27fca57 100644 --- a/src/functionalTest/resources/projects/scala-multi-module-multiple-test-tasks/build.gradle +++ b/src/functionalTest/resources/projects/scala-multi-module-multiple-test-tasks/build.gradle @@ -25,36 +25,35 @@ allprojects { testImplementation group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion } - test { - useJUnitPlatform() - maxParallelForks = 1 - } - - configurations { - intTestImplementation.extendsFrom testImplementation - intTestRuntimeOnly.extendsFrom testRuntimeOnly - } - sourceSets { - intTest { - resources.srcDir file('src/intTest/resources') - scala { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir file("${projectDir}/src/intTest/scala") + testing { + suites { + configureEach { + useJUnit() + targets.configureEach { + testTask.configure { + maxParallelForks = 1 + } + } + } + intTest(JvmTestSuite) { + testType = TestSuiteType.INTEGRATION_TEST + // dependencies { ... } does not appear to work as advertised? + sources { + scala { + compileClasspath += sourceSets.test.compileClasspath + sourceSets.main.output + sourceSets.test.output + runtimeClasspath += sourceSets.test.runtimeClasspath + } + } + targets.configureEach { + testTask.configure{ + outputs.upToDateWhen { false } + mustRunAfter(test) + } + } } } } - - - task intTest(type: Test) { - testClassesDirs = sourceSets.intTest.output.classesDirs - classpath = sourceSets.intTest.runtimeClasspath - outputs.upToDateWhen { false } - - maxParallelForks = 1 - } - check.dependsOn(intTest) - intTest.mustRunAfter(test) + check.dependsOn(testing.suites.intTest) scoverage { minimumRate = 0.5 diff --git a/src/functionalTest/resources/projects/scala-single-module-multiple-test-tasks/build.gradle b/src/functionalTest/resources/projects/scala-single-module-multiple-test-tasks/build.gradle index a10ddbb..3f7ffd5 100644 --- a/src/functionalTest/resources/projects/scala-single-module-multiple-test-tasks/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module-multiple-test-tasks/build.gradle @@ -1,6 +1,7 @@ plugins { id 'io.spring.dependency-management' version "1.0.4.RELEASE" id 'org.scoverage' + id 'jvm-test-suite' } repositories { @@ -27,39 +28,35 @@ dependencies { testImplementation group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion } -test { - useJUnitPlatform() -} - - -configurations { - intTestImplementation.extendsFrom testImplementation - intTestRuntimeOnly.extendsFrom testRuntimeOnly -} -sourceSets { - intTest { - resources.srcDir file('src/intTest/resources') - scala { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir file("${projectDir}/src/intTest/scala") +testing { + suites { + configureEach { + useJUnit() + targets.configureEach { + testTask.configure { + maxParallelForks = 1 + } + } + } + intTest(JvmTestSuite) { + testType = TestSuiteType.INTEGRATION_TEST + // dependencies { ... } does not appear to work as advertised? + sources { + scala { + compileClasspath += sourceSets.test.compileClasspath + sourceSets.main.output + sourceSets.test.output + runtimeClasspath += sourceSets.test.runtimeClasspath + } + } + targets.configureEach { + testTask.configure{ + outputs.upToDateWhen { false } + mustRunAfter(test) + } + } } } } - -test { - maxParallelForks = 1 -} - -task intTest(type: Test) { - testClassesDirs = sourceSets.intTest.output.classesDirs - classpath = sourceSets.intTest.runtimeClasspath - outputs.upToDateWhen { false } - - maxParallelForks = 1 -} -check.dependsOn(intTest) -intTest.mustRunAfter(test) +check.dependsOn(testing.suites.intTest) scoverage { minimumRate = 0.6 diff --git a/src/main/groovy/org/scoverage/ScalaVersion.groovy b/src/main/groovy/org/scoverage/ScalaVersion.groovy new file mode 100644 index 0000000..7d76dc4 --- /dev/null +++ b/src/main/groovy/org/scoverage/ScalaVersion.groovy @@ -0,0 +1,31 @@ +package org.scoverage + +class ScalaVersion { + final String primaryVersion + final Optional secondaryVersion + final Integer majorVersion + final String scalacScoverageVersion + final String scalacScoveragePluginVersion + final String scalacScoverageRuntimeVersion + + ScalaVersion(primaryVersion) { + this(primaryVersion, Optional.empty()) + } + + ScalaVersion(String primaryVersion, Optional secondaryVersion) { + this.primaryVersion = primaryVersion + this.secondaryVersion = secondaryVersion + + this.majorVersion = primaryVersion.substring(0, primaryVersion.indexOf('.')).toInteger() + this.scalacScoverageVersion = this.majorVersion < 3 + ? primaryVersion.substring(0, primaryVersion.lastIndexOf('.')) + : this.majorVersion.toString() + this.scalacScoveragePluginVersion = secondaryVersion.orElse(primaryVersion) + this.scalacScoverageRuntimeVersion = scalacScoveragePluginVersion.substring(0, scalacScoveragePluginVersion.lastIndexOf('.')) + } + + @Override + String toString() { + return majorVersion < 3 ? primaryVersion : "$primaryVersion (${secondaryVersion.get()})" + } +} diff --git a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy index 9815dd1..f39ff98 100644 --- a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy +++ b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy @@ -10,7 +10,7 @@ import org.gradle.api.tasks.Nested import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.TaskAction -import scoverage.report.CoverageAggregator +import scoverage.reporter.CoverageAggregator import static org.gradle.api.tasks.PathSensitivity.RELATIVE @@ -57,7 +57,8 @@ class ScoverageAggregate extends DefaultTask { def dirs = [] dirs.addAll(dirsToAggregateFrom.get()) - def coverage = CoverageAggregator.aggregate(dirs.unique() as File[]) + def sourceRoot = getProject().getRootDir() + def coverage = CoverageAggregator.aggregate(dirs.unique() as File[], sourceRoot) if (coverage.nonEmpty()) { new ScoverageWriter(project.logger).write( diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index 39eb8e4..cb45b94 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -55,7 +55,7 @@ class ScoverageExtension { project.plugins.apply(ScalaPlugin.class) scoverageVersion = project.objects.property(String) - scoverageVersion.set('1.4.11') + scoverageVersion.set('2.0.8') scoverageScalaVersion = project.objects.property(String) diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 4f1ba18..ebcb5f6 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -62,15 +62,21 @@ class ScoveragePlugin implements Plugin { } project.afterEvaluate { - def scalaFullVersion = resolveScalaVersion(project) - def scalaBinaryVersion = scalaFullVersion.substring(0, scalaFullVersion.lastIndexOf('.')) + def scalaVersion = resolveScalaVersions(project) + def scoverageVersion = project.extensions.scoverage.scoverageVersion.get() + project.logger.info("Using scoverage scalac plugin $scoverageVersion for scala $scalaVersion") - project.logger.info("Using scoverage scalac plugin $scoverageVersion for scala $scalaFullVersion") + def scalacScoverageVersion = scalaVersion.scalacScoverageVersion + def scalacScoveragePluginVersion = scalaVersion.scalacScoveragePluginVersion + def scalacScoverageRuntimeVersion = scalaVersion.scalacScoverageRuntimeVersion project.dependencies { - scoverage("org.scoverage:scalac-scoverage-plugin_$scalaFullVersion:$scoverageVersion") - scoverage("org.scoverage:scalac-scoverage-runtime_$scalaBinaryVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-domain_$scalacScoverageVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-reporter_$scalacScoverageVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-serializer_$scalacScoverageVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-runtime_$scalacScoverageRuntimeVersion:$scoverageVersion") + scoverage("org.scoverage:scalac-scoverage-plugin_$scalacScoveragePluginVersion:$scoverageVersion") } } } @@ -162,109 +168,92 @@ class ScoveragePlugin implements Plugin { if (existingParameters) { parameters.addAll(existingParameters) } - parameters.add("-P:scoverage:dataDir:${extension.dataDir.get().absolutePath}".toString()) - if (extension.excludedPackages.get()) { - def packages = extension.excludedPackages.get().join(';') - parameters.add("-P:scoverage:excludedPackages:$packages".toString()) - } - if (extension.excludedFiles.get()) { - def packages = extension.excludedFiles.get().join(';') - parameters.add("-P:scoverage:excludedFiles:$packages".toString()) - } - if (extension.highlighting.get()) { - parameters.add('-Yrangepos') - } - scalaCompileOptions.additionalParameters = parameters - // the compile task creates a store of measured statements - outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage')) - dependsOn project.configurations[CONFIGURATION_NAME] - doFirst { - /* - It is crucial that this would run in `doFirst`, as this resolves the (dependencies of the) - configuration, which we do not want to do at configuration time (but only at execution time). - */ - def pluginFile = project.configurations[CONFIGURATION_NAME].find { - it.name.startsWith("scalac-scoverage-plugin") + def scalaVersion = resolveScalaVersions(project) + if (scalaVersion.majorVersion < 3) { + parameters.add("-P:scoverage:dataDir:${extension.dataDir.get().absolutePath}".toString()) + parameters.add("-P:scoverage:sourceRoot:${extension.project.getRootDir().absolutePath}".toString()) + if (extension.excludedPackages.get()) { + def packages = extension.excludedPackages.get().join(';') + parameters.add("-P:scoverage:excludedPackages:$packages".toString()) } - scalaCompileOptions.additionalParameters.add('-Xplugin:' + pluginFile.absolutePath) - } - } - - if (project.hasProperty(SCOVERAGE_COMPILE_ONLY_PROPERTY)) { - project.logger.info("Making scoverage compilation the primary compilation task (instead of compileScala)") - - originalCompileTask.enabled = false; - compileTask.destinationDirectory = originalCompileTask.destinationDirectory - - project.getTasks().each { - if (recursiveDependenciesOf(it, true).contains(originalCompileTask)) { - it.dependsOn(compileTask) + if (extension.excludedFiles.get()) { + def packages = extension.excludedFiles.get().join(';') + parameters.add("-P:scoverage:excludedFiles:$packages".toString()) } - } + if (extension.highlighting.get()) { + parameters.add('-Yrangepos') + } + scalaCompileOptions.additionalParameters = parameters + // the compile task creates a store of measured statements + outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage')) - // make this project's scoverage compilation depend on scoverage compilation of any other project - // which this project depends on its normal compilation - def originalCompilationDependencies = recursiveDependenciesOf(compileTask, false).findAll { - it instanceof ScalaCompile - } - originalCompilationDependencies.each { - def dependencyProjectCompileTask = it.project.tasks.findByName(COMPILE_NAME) - def dependencyProjectReportTask = it.project.tasks.findByName(REPORT_NAME) - if (dependencyProjectCompileTask != null) { - compileTask.dependsOn(dependencyProjectCompileTask) - // we don't want this project's tests to affect the other project's report - testTasks.each { - it.mustRunAfter(dependencyProjectReportTask) + dependsOn project.configurations[CONFIGURATION_NAME] + doFirst { + /* + It is crucial that this would run in `doFirst`, as this resolves the (dependencies of the) + configuration, which we do not want to do at configuration time (but only at execution time). + */ + def pluginFiles = project.configurations[CONFIGURATION_NAME].findAll { + it.name.startsWith("scalac-scoverage-plugin") || + it.name.startsWith("scalac-scoverage-domain") || + it.name.startsWith("scalac-scoverage-serializer") + }.collect { + it.absolutePath } + scalaCompileOptions.additionalParameters.add('-Xplugin:' + pluginFiles.join(":")) } + } else { + parameters.add("-sourceroot:${project.rootDir.absolutePath}".toString()) + parameters.add("-coverage-out:${extension.dataDir.get().absolutePath}".toString()) + scalaCompileOptions.additionalParameters = parameters } - } else { - compileTask.configure { - doFirst { - destinationDir.deleteDir() - } - - // delete non-instrumented classes by comparing normally compiled classes to those compiled with scoverage - doLast { - project.logger.info("Deleting classes compiled by scoverage but non-instrumented (identical to normal compilation)") - def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - .getCompileTaskName("scala") - def originalDestinationDirectory = project.tasks[originalCompileTaskName].destinationDirectory - def originalDestinationDir = originalDestinationDirectory.get().asFile - def destinationDir = destinationDirectory.get().asFile - + } - def findFiles = { File dir, Closure condition = null -> - def files = [] + compileTask.configure { + doFirst { + destinationDirectory.get().getAsFile().deleteDir() + } - if (dir.exists()) { - dir.eachFileRecurse(FILES) { f -> - if (condition == null || condition(f)) { - def relativePath = dir.relativePath(f) - files << relativePath - } + // delete non-instrumented classes by comparing normally compiled classes to those compiled with scoverage + doLast { + project.logger.info("Deleting classes compiled by scoverage but non-instrumented (identical to normal compilation)") + def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getCompileTaskName("scala") + def originalDestinationDirectory = project.tasks[originalCompileTaskName].destinationDirectory + def originalDestinationDir = originalDestinationDirectory.get().asFile + def destinationDir = destinationDirectory.get().asFile + + + def findFiles = { File dir, Closure condition = null -> + def files = [] + + if (dir.exists()) { + dir.eachFileRecurse(FILES) { f -> + if (condition == null || condition(f)) { + def relativePath = dir.relativePath(f) + files << relativePath } } - - files } - def isSameFile = { String relativePath -> - def fileA = new File(originalDestinationDir, relativePath) - def fileB = new File(destinationDir, relativePath) - FileUtils.contentEquals(fileA, fileB) - } + files + } + + def isSameFile = { String relativePath -> + def fileA = new File(originalDestinationDir, relativePath) + def fileB = new File(destinationDir, relativePath) + FileUtils.contentEquals(fileA, fileB) + } - def originalClasses = findFiles(originalDestinationDir) - def identicalInstrumentedClasses = findFiles(destinationDir, { f -> - def relativePath = destinationDir.relativePath(f) - originalClasses.contains(relativePath) && isSameFile(relativePath) - }) + def originalClasses = findFiles(originalDestinationDir) + def identicalInstrumentedClasses = findFiles(destinationDir, { f -> + def relativePath = destinationDir.relativePath(f) + originalClasses.contains(relativePath) && isSameFile(relativePath) + }) - identicalInstrumentedClasses.each { f -> - Files.deleteIfExists(destinationDir.toPath().resolve(f)) - } + identicalInstrumentedClasses.each { f -> + Files.deleteIfExists(destinationDir.toPath().resolve(f)) } } } @@ -364,27 +353,41 @@ class ScoveragePlugin implements Plugin { } } - private String resolveScalaVersion(Project project) { - + private ScalaVersion resolveScalaVersions(Project project) { def scalaVersionProperty = project.extensions.scoverage.scoverageScalaVersion if (scalaVersionProperty.isPresent()) { def configuredScalaVersion = scalaVersionProperty.get() project.logger.info("Using configured Scala version: $configuredScalaVersion") - return configuredScalaVersion + return new ScalaVersion(configuredScalaVersion) } else { project.logger.info("No Scala version configured. Detecting scala library...") def components = project.configurations.compileClasspath.incoming.resolutionResult.getAllComponents() + + def scala3Library = components.find { + it.moduleVersion.group == "org.scala-lang" && it.moduleVersion.name == "scala3-library_3" + } def scalaLibrary = components.find { it.moduleVersion.group == "org.scala-lang" && it.moduleVersion.name == "scala-library" } + + // Scala 3 + if (scala3Library != null) { + def scala3Version = scala3Library.moduleVersion.version + def scala2Version = scalaLibrary.moduleVersion.version + project.logger.info("Detected scala 3 library in compilation classpath. Scala 3 version: $scala3Version; using Scala 2 library: $scala2Version") + return new ScalaVersion(scala3Version, Optional.of(scala2Version)) + } + + // Scala 2 if (scalaLibrary != null) { - def scalaVersion = scalaLibrary.moduleVersion.version - project.logger.info("Detected scala library in compilation classpath. Scala version: $scalaVersion") - return scalaVersion - } else { - project.logger.info("No scala library detected. Using default Scala version: $DEFAULT_SCALA_VERSION") - return DEFAULT_SCALA_VERSION + def scala2Version = scalaLibrary.moduleVersion.version + project.logger.info("Detected scala library in compilation classpath. Scala version: $scala2Version") + return new ScalaVersion(scala2Version) } + + // No Scala library was found, using default Scala version + project.logger.info("No scala library detected. Using default Scala version: $DEFAULT_SCALA_VERSION") + return new ScalaVersion(DEFAULT_SCALA_VERSION) } } diff --git a/src/main/groovy/org/scoverage/ScoverageReport.groovy b/src/main/groovy/org/scoverage/ScoverageReport.groovy index 8a15aab..ef09483 100644 --- a/src/main/groovy/org/scoverage/ScoverageReport.groovy +++ b/src/main/groovy/org/scoverage/ScoverageReport.groovy @@ -11,7 +11,7 @@ import org.gradle.api.tasks.Nested import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.TaskAction -import scoverage.report.CoverageAggregator +import scoverage.reporter.CoverageAggregator import static org.gradle.api.tasks.PathSensitivity.RELATIVE @@ -50,7 +50,8 @@ class ScoverageReport extends DefaultTask { reportDir.get().delete() reportDir.get().mkdirs() - def coverage = CoverageAggregator.aggregate([dataDir.get()] as File[]) + def sourceRoot = getProject().getRootDir() + def coverage = CoverageAggregator.aggregate([dataDir.get()] as File[], sourceRoot) if (coverage.isEmpty()) { project.logger.info("[scoverage] Could not find coverage file, skipping...") diff --git a/src/main/groovy/org/scoverage/ScoverageWriter.java b/src/main/groovy/org/scoverage/ScoverageWriter.java index 1e09105..5e0278e 100644 --- a/src/main/groovy/org/scoverage/ScoverageWriter.java +++ b/src/main/groovy/org/scoverage/ScoverageWriter.java @@ -5,11 +5,11 @@ import scala.Some; import scala.collection.immutable.Seq; import scala.collection.mutable.Buffer; -import scoverage.Constants; -import scoverage.Coverage; -import scoverage.report.CoberturaXmlWriter; -import scoverage.report.ScoverageHtmlWriter; -import scoverage.report.ScoverageXmlWriter; +import scoverage.domain.Constants; +import scoverage.domain.Coverage; +import scoverage.reporter.CoberturaXmlWriter; +import scoverage.reporter.ScoverageHtmlWriter; +import scoverage.reporter.ScoverageXmlWriter; import scala.collection.JavaConverters; import java.io.File; @@ -65,11 +65,17 @@ public void write(Set sourceDirs, if (coverageOutputCobertura) { Constructor cst; try { - cst = CoberturaXmlWriter.class.getConstructor(Class.forName("scala.collection.immutable.Seq"), File.class); + cst = CoberturaXmlWriter.class.getConstructor( + Class.forName("scala.collection.immutable.Seq"), + File.class, + Class.forName("scala.Option")); } catch (NoSuchMethodException | ClassNotFoundException e) { - cst = CoberturaXmlWriter.class.getConstructor(Class.forName("scala.collection.Seq"), File.class); + cst = CoberturaXmlWriter.class.getConstructor( + Class.forName("scala.collection.Seq"), + File.class, + Class.forName("scala.Option")); } - CoberturaXmlWriter writer = cst.newInstance(sourceDirsSeq, reportDir); + CoberturaXmlWriter writer = cst.newInstance(sourceDirsSeq, reportDir, new Some<>(sourceEncoding)); writer.write(coverage); logger.info("[scoverage] Written Cobertura XML report to " + reportDir.getAbsolutePath() + @@ -80,11 +86,19 @@ public void write(Set sourceDirs, if (coverageOutputXML) { Constructor cst; try { - cst = ScoverageXmlWriter.class.getConstructor(Class.forName("scala.collection.immutable.Seq"), File.class, boolean.class); + cst = ScoverageXmlWriter.class.getConstructor( + Class.forName("scala.collection.immutable.Seq"), + File.class, + boolean.class, + Class.forName("scala.Option")); } catch (NoSuchMethodException | ClassNotFoundException e) { - cst = ScoverageXmlWriter.class.getConstructor(Class.forName("scala.collection.Seq"), File.class, boolean.class); + cst = ScoverageXmlWriter.class.getConstructor( + Class.forName("scala.collection.Seq"), + File.class, + boolean.class, + Class.forName("scala.Option")); } - ScoverageXmlWriter writer = cst.newInstance(sourceDirsSeq, reportDir, false); + ScoverageXmlWriter writer = cst.newInstance(sourceDirsSeq, reportDir, false, new Some<>(sourceEncoding)); writer.write(coverage); logger.info("[scoverage] Written XML report to " + reportDir.getAbsolutePath() +