Skip to content

Commit

Permalink
Support Scoverage for Scala 3 (#2016)
Browse files Browse the repository at this point in the history
This is a follow-up to the work done in 
#2010 
to also add support for Scala 3.

Pull request: #2016
  • Loading branch information
lefou authored Sep 28, 2022
2 parents e237120 + ad13acb commit c9c409e
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 100 deletions.
6 changes: 6 additions & 0 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ object Deps {

val testScala213Version = "2.13.8"
val testScala212Version = "2.12.6"
val testScala211Version = "2.11.12"
val testScala30Version = "3.0.2"
val testScala31Version = "3.1.3"
val testScala32Version = "3.2.0"

val testScalaJs06Version = "0.6.33"

Expand Down Expand Up @@ -298,7 +301,10 @@ trait MillScalaModule extends ScalaModule with MillCoursierModule { outer =>
s"-DMILL_SCALA_2_12_VERSION=${Deps.workerScalaVersion212}",
s"-DTEST_SCALA_2_13_VERSION=${Deps.testScala213Version}",
s"-DTEST_SCALA_2_12_VERSION=${Deps.testScala212Version}",
s"-DTEST_SCALA_2_11_VERSION=${Deps.testScala211Version}",
s"-DTEST_SCALA_3_0_VERSION=${Deps.testScala30Version}",
s"-DTEST_SCALA_3_1_VERSION=${Deps.testScala31Version}",
s"-DTEST_SCALA_3_2_VERSION=${Deps.testScala32Version}",
s"-DTEST_UTEST_VERSION=${Deps.utest.dep.version}",
s"-DTEST_SCALAJS_0_6_VERSION=${Deps.testScalaJs06Version}"
) ++ outer.testArgs()
Expand Down
111 changes: 85 additions & 26 deletions contrib/scoverage/src/ScoverageModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import mill.contrib.scoverage.api.ScoverageReportWorkerApi.ReportType
import mill.define.{Command, Persistent, Sources, Target, Task}
import mill.scalalib.api.ZincWorkerUtil
import mill.scalalib.{Dep, DepSyntax, JavaModule, ScalaModule}
import mill.api.Result

/**
* Adds targets to a [[mill.scalalib.ScalaModule]] to create test coverage reports.
Expand Down Expand Up @@ -59,35 +60,61 @@ trait ScoverageModule extends ScalaModule { outer: ScalaModule =>

private def isScoverage2: Task[Boolean] = T.task { scoverageVersion().startsWith("2.") }

private def isScala3: Task[Boolean] = T.task { ZincWorkerUtil.isScala3(outer.scalaVersion()) }

private def isScala2: Task[Boolean] = T.task { !isScala3() }

/** Binary compatibility shim. */
@deprecated("Use scoverageRuntimeDeps instead.", "Mill after 0.10.7")
def scoverageRuntimeDep: T[Dep] = T {
T.log.error("scoverageRuntimeDep is no longer used. To customize your module, use scoverageRuntimeDeps.")
scoverageRuntimeDeps().toIndexedSeq.head
T.log.error(
"scoverageRuntimeDep is no longer used. To customize your module, use scoverageRuntimeDeps."
)
val result: Result[Dep] = if (isScala3()) {
Result.Failure("When using Scala 3 there is no external runtime dependency")
} else {
scoverageRuntimeDeps().toIndexedSeq.head
}
result
}

def scoverageRuntimeDeps: T[Agg[Dep]] = T {
Agg(ivy"org.scoverage::scalac-scoverage-runtime:${outer.scoverageVersion()}")
if (isScala3()) {
Agg.empty
} else {
Agg(ivy"org.scoverage::scalac-scoverage-runtime:${outer.scoverageVersion()}")
}
}

/** Binary compatibility shim. */
@deprecated("Use scoveragePluginDeps instead.", "Mill after 0.10.7")
def scoveragePluginDep: T[Dep] = T {
T.log.error("scoveragePluginDep is no longer used. To customize your module, use scoverageRuntimeDeps.")
scoveragePluginDeps().toIndexedSeq.head
T.log.error(
"scoveragePluginDep is no longer used. To customize your module, use scoverageRuntimeDeps."
)
val result: Result[Dep] = if (isScala3()) {
Result.Failure("When using Scala 3 there is no external plugin dependency")
} else {
scoveragePluginDeps().toIndexedSeq.head
}
result
}

def scoveragePluginDeps: T[Agg[Dep]] = T {
val sv = scoverageVersion()
if (isScoverage2()) {
Agg(
ivy"org.scoverage:::scalac-scoverage-plugin:${sv}",
ivy"org.scoverage::scalac-scoverage-domain:${sv}",
ivy"org.scoverage::scalac-scoverage-serializer:${sv}",
ivy"org.scoverage::scalac-scoverage-reporter:${sv}"
)
if (isScala3()) {
Agg.empty
} else {
Agg(ivy"org.scoverage:::scalac-scoverage-plugin:${sv}")
if (isScoverage2()) {
Agg(
ivy"org.scoverage:::scalac-scoverage-plugin:${sv}",
ivy"org.scoverage::scalac-scoverage-domain:${sv}",
ivy"org.scoverage::scalac-scoverage-serializer:${sv}",
ivy"org.scoverage::scalac-scoverage-reporter:${sv}"
)
} else {
Agg(ivy"org.scoverage:::scalac-scoverage-plugin:${sv}")
}
}
}

Expand All @@ -96,23 +123,47 @@ trait ScoverageModule extends ScalaModule { outer: ScalaModule =>
scoverageToolsClasspath()
}

private def checkVersions = T.task {
val sv = scalaVersion()
val isSov2 = scoverageVersion().startsWith("2.")
(sv.split('.'), isSov2) match {
case (Array("3", "0" | "1", _*), _) => Result.Failure(
"Scala 3.0 and 3.1 is not supported by Scoverage. You have to update to at least Scala 3.2 and Scoverage 2.0"
)
case (Array("3", _*), false) => Result.Failure(
"Scoverage 1.x does not support Scala 3. You have to update to at least Scala 3.2 and Scoverage 2.0"
)
case (Array("2", "11", _*), true) => Result.Failure(
"Scoverage 2.x is not compatible with Scala 2.11. Consider using Scoverage 1.x or switch to a newer Scala version."
)
case _ =>
}
}

def scoverageToolsClasspath: T[Agg[PathRef]] = T {
checkVersions()

scoverageReportWorkerClasspath() ++
resolveDeps(T.task {
// we need to resolve with same Scala version used for Mill, not the project Scala version
val scalaBinVersion = ZincWorkerUtil.scalaBinaryVersion(BuildInfo.scalaVersion)
val sv = scoverageVersion()
if (isScoverage2()) {
Agg(
ivy"org.scoverage:scalac-scoverage-plugin_${mill.BuildInfo.scalaVersion}:${sv}",
ivy"org.scoverage:scalac-scoverage-domain_${scalaBinVersion}:${sv}",
ivy"org.scoverage:scalac-scoverage-serializer_${scalaBinVersion}:${sv}",
ivy"org.scoverage:scalac-scoverage-reporter_${scalaBinVersion}:${sv}"
)

val baseDeps = Agg(
ivy"org.scoverage:scalac-scoverage-domain_${scalaBinVersion}:${sv}",
ivy"org.scoverage:scalac-scoverage-serializer_${scalaBinVersion}:${sv}",
ivy"org.scoverage:scalac-scoverage-reporter_${scalaBinVersion}:${sv}"
)

val pluginDep =
Agg(ivy"org.scoverage:scalac-scoverage-plugin_${mill.BuildInfo.scalaVersion}:${sv}")

if (isScala3() && isScoverage2()) {
baseDeps
} else if (isScoverage2()) {
baseDeps ++ pluginDep
} else {
Agg(
ivy"org.scoverage:scalac-scoverage-plugin_${mill.BuildInfo.scalaVersion}:${sv}"
)
pluginDep
}
})()
}
Expand Down Expand Up @@ -141,6 +192,7 @@ trait ScoverageModule extends ScalaModule { outer: ScalaModule =>
}

val scoverage: ScoverageData = new ScoverageData(implicitly)

class ScoverageData(ctx0: mill.define.Ctx) extends Module()(ctx0) with ScalaModule {

def doReport(reportType: ReportType): Task[Unit] = T.task {
Expand Down Expand Up @@ -180,9 +232,16 @@ trait ScoverageModule extends ScalaModule { outer: ScalaModule =>
/** Add the scoverage specific plugin settings (`dataDir`). */
override def scalacOptions: Target[Seq[String]] =
T {
outer.scalacOptions() ++
Seq(s"-P:scoverage:dataDir:${data().path.toIO.getPath()}") ++
(if (isScoverage2()) Seq(s"-P:scoverage:sourceRoot:${T.workspace}") else Seq())
val extras =
if (isScala3()) {
Seq(s"-coverage-out:${data().path.toIO.getPath()}")
} else {
val base = s"-P:scoverage:dataDir:${data().path.toIO.getPath()}"
if (isScoverage2()) Seq(base, s"-P:scoverage:sourceRoot:${T.workspace}")
else Seq(base)
}

outer.scalacOptions() ++ extras
}

def htmlReport(): Command[Unit] = T.command { doReport(ReportType.Html) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import org.scalatest._
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class GreetSpec extends WordSpec with Matchers {
class GreetSpec extends AnyWordSpec with Matchers {
"Greet" should {
"work" in {
Greet.greet("Nik", None) shouldBe ("Hello, Nik!")
Expand Down
Loading

0 comments on commit c9c409e

Please sign in to comment.