diff --git a/build.gradle b/build.gradle index efecced..601e79b 100644 --- a/build.gradle +++ b/build.gradle @@ -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' @@ -46,7 +46,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' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180..41d9927 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..41dfb87 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ 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-7.4-bin.zip 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..cccbbc5 --- /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' +} 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/ScoverageFunctionalTest.java b/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java index 051b6bb..789ccfa 100644 --- a/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java +++ b/src/functionalTest/java/org/scoverage/ScoverageFunctionalTest.java @@ -123,7 +123,7 @@ private void configureArguments(String... arguments) { fullArguments.add("-PscalaVersionMajor=2"); fullArguments.add("-PscalaVersionMinor=12"); - fullArguments.add("-PscalaVersionBuild=8"); + fullArguments.add("-PscalaVersionBuild=17"); fullArguments.add("-PjunitVersion=5.3.2"); fullArguments.add("-PjunitPlatformVersion=1.3.2"); fullArguments.add("-PscalatestVersion=3.0.8"); 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..510f42d 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.7') 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..d9ba9c3 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,32 +168,45 @@ 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 (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 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("-coverage-out:${extension.dataDir.get().absolutePath}".toString()) + parameters.add("-sourceroot:${extension.project.getRootDir().absolutePath}".toString()) + scalaCompileOptions.additionalParameters = parameters } } @@ -364,27 +383,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() +