Skip to content

Commit c9c409e

Browse files
authored
Support Scoverage for Scala 3 (#2016)
This is a follow-up to the work done in #2010 to also add support for Scala 3. Pull request: #2016
2 parents e237120 + ad13acb commit c9c409e

File tree

5 files changed

+271
-100
lines changed

5 files changed

+271
-100
lines changed

build.sc

+6
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ object Deps {
6565

6666
val testScala213Version = "2.13.8"
6767
val testScala212Version = "2.12.6"
68+
val testScala211Version = "2.11.12"
6869
val testScala30Version = "3.0.2"
70+
val testScala31Version = "3.1.3"
71+
val testScala32Version = "3.2.0"
6972

7073
val testScalaJs06Version = "0.6.33"
7174

@@ -298,7 +301,10 @@ trait MillScalaModule extends ScalaModule with MillCoursierModule { outer =>
298301
s"-DMILL_SCALA_2_12_VERSION=${Deps.workerScalaVersion212}",
299302
s"-DTEST_SCALA_2_13_VERSION=${Deps.testScala213Version}",
300303
s"-DTEST_SCALA_2_12_VERSION=${Deps.testScala212Version}",
304+
s"-DTEST_SCALA_2_11_VERSION=${Deps.testScala211Version}",
301305
s"-DTEST_SCALA_3_0_VERSION=${Deps.testScala30Version}",
306+
s"-DTEST_SCALA_3_1_VERSION=${Deps.testScala31Version}",
307+
s"-DTEST_SCALA_3_2_VERSION=${Deps.testScala32Version}",
302308
s"-DTEST_UTEST_VERSION=${Deps.utest.dep.version}",
303309
s"-DTEST_SCALAJS_0_6_VERSION=${Deps.testScalaJs06Version}"
304310
) ++ outer.testArgs()

contrib/scoverage/src/ScoverageModule.scala

+85-26
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import mill.contrib.scoverage.api.ScoverageReportWorkerApi.ReportType
77
import mill.define.{Command, Persistent, Sources, Target, Task}
88
import mill.scalalib.api.ZincWorkerUtil
99
import mill.scalalib.{Dep, DepSyntax, JavaModule, ScalaModule}
10+
import mill.api.Result
1011

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

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

63+
private def isScala3: Task[Boolean] = T.task { ZincWorkerUtil.isScala3(outer.scalaVersion()) }
64+
65+
private def isScala2: Task[Boolean] = T.task { !isScala3() }
66+
6267
/** Binary compatibility shim. */
6368
@deprecated("Use scoverageRuntimeDeps instead.", "Mill after 0.10.7")
6469
def scoverageRuntimeDep: T[Dep] = T {
65-
T.log.error("scoverageRuntimeDep is no longer used. To customize your module, use scoverageRuntimeDeps.")
66-
scoverageRuntimeDeps().toIndexedSeq.head
70+
T.log.error(
71+
"scoverageRuntimeDep is no longer used. To customize your module, use scoverageRuntimeDeps."
72+
)
73+
val result: Result[Dep] = if (isScala3()) {
74+
Result.Failure("When using Scala 3 there is no external runtime dependency")
75+
} else {
76+
scoverageRuntimeDeps().toIndexedSeq.head
77+
}
78+
result
6779
}
6880

6981
def scoverageRuntimeDeps: T[Agg[Dep]] = T {
70-
Agg(ivy"org.scoverage::scalac-scoverage-runtime:${outer.scoverageVersion()}")
82+
if (isScala3()) {
83+
Agg.empty
84+
} else {
85+
Agg(ivy"org.scoverage::scalac-scoverage-runtime:${outer.scoverageVersion()}")
86+
}
7187
}
7288

7389
/** Binary compatibility shim. */
7490
@deprecated("Use scoveragePluginDeps instead.", "Mill after 0.10.7")
7591
def scoveragePluginDep: T[Dep] = T {
76-
T.log.error("scoveragePluginDep is no longer used. To customize your module, use scoverageRuntimeDeps.")
77-
scoveragePluginDeps().toIndexedSeq.head
92+
T.log.error(
93+
"scoveragePluginDep is no longer used. To customize your module, use scoverageRuntimeDeps."
94+
)
95+
val result: Result[Dep] = if (isScala3()) {
96+
Result.Failure("When using Scala 3 there is no external plugin dependency")
97+
} else {
98+
scoveragePluginDeps().toIndexedSeq.head
99+
}
100+
result
78101
}
79102

80103
def scoveragePluginDeps: T[Agg[Dep]] = T {
81104
val sv = scoverageVersion()
82-
if (isScoverage2()) {
83-
Agg(
84-
ivy"org.scoverage:::scalac-scoverage-plugin:${sv}",
85-
ivy"org.scoverage::scalac-scoverage-domain:${sv}",
86-
ivy"org.scoverage::scalac-scoverage-serializer:${sv}",
87-
ivy"org.scoverage::scalac-scoverage-reporter:${sv}"
88-
)
105+
if (isScala3()) {
106+
Agg.empty
89107
} else {
90-
Agg(ivy"org.scoverage:::scalac-scoverage-plugin:${sv}")
108+
if (isScoverage2()) {
109+
Agg(
110+
ivy"org.scoverage:::scalac-scoverage-plugin:${sv}",
111+
ivy"org.scoverage::scalac-scoverage-domain:${sv}",
112+
ivy"org.scoverage::scalac-scoverage-serializer:${sv}",
113+
ivy"org.scoverage::scalac-scoverage-reporter:${sv}"
114+
)
115+
} else {
116+
Agg(ivy"org.scoverage:::scalac-scoverage-plugin:${sv}")
117+
}
91118
}
92119
}
93120

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

126+
private def checkVersions = T.task {
127+
val sv = scalaVersion()
128+
val isSov2 = scoverageVersion().startsWith("2.")
129+
(sv.split('.'), isSov2) match {
130+
case (Array("3", "0" | "1", _*), _) => Result.Failure(
131+
"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"
132+
)
133+
case (Array("3", _*), false) => Result.Failure(
134+
"Scoverage 1.x does not support Scala 3. You have to update to at least Scala 3.2 and Scoverage 2.0"
135+
)
136+
case (Array("2", "11", _*), true) => Result.Failure(
137+
"Scoverage 2.x is not compatible with Scala 2.11. Consider using Scoverage 1.x or switch to a newer Scala version."
138+
)
139+
case _ =>
140+
}
141+
}
142+
99143
def scoverageToolsClasspath: T[Agg[PathRef]] = T {
144+
checkVersions()
145+
100146
scoverageReportWorkerClasspath() ++
101147
resolveDeps(T.task {
102148
// we need to resolve with same Scala version used for Mill, not the project Scala version
103149
val scalaBinVersion = ZincWorkerUtil.scalaBinaryVersion(BuildInfo.scalaVersion)
104150
val sv = scoverageVersion()
105-
if (isScoverage2()) {
106-
Agg(
107-
ivy"org.scoverage:scalac-scoverage-plugin_${mill.BuildInfo.scalaVersion}:${sv}",
108-
ivy"org.scoverage:scalac-scoverage-domain_${scalaBinVersion}:${sv}",
109-
ivy"org.scoverage:scalac-scoverage-serializer_${scalaBinVersion}:${sv}",
110-
ivy"org.scoverage:scalac-scoverage-reporter_${scalaBinVersion}:${sv}"
111-
)
151+
152+
val baseDeps = Agg(
153+
ivy"org.scoverage:scalac-scoverage-domain_${scalaBinVersion}:${sv}",
154+
ivy"org.scoverage:scalac-scoverage-serializer_${scalaBinVersion}:${sv}",
155+
ivy"org.scoverage:scalac-scoverage-reporter_${scalaBinVersion}:${sv}"
156+
)
157+
158+
val pluginDep =
159+
Agg(ivy"org.scoverage:scalac-scoverage-plugin_${mill.BuildInfo.scalaVersion}:${sv}")
160+
161+
if (isScala3() && isScoverage2()) {
162+
baseDeps
163+
} else if (isScoverage2()) {
164+
baseDeps ++ pluginDep
112165
} else {
113-
Agg(
114-
ivy"org.scoverage:scalac-scoverage-plugin_${mill.BuildInfo.scalaVersion}:${sv}"
115-
)
166+
pluginDep
116167
}
117168
})()
118169
}
@@ -141,6 +192,7 @@ trait ScoverageModule extends ScalaModule { outer: ScalaModule =>
141192
}
142193

143194
val scoverage: ScoverageData = new ScoverageData(implicitly)
195+
144196
class ScoverageData(ctx0: mill.define.Ctx) extends Module()(ctx0) with ScalaModule {
145197

146198
def doReport(reportType: ReportType): Task[Unit] = T.task {
@@ -180,9 +232,16 @@ trait ScoverageModule extends ScalaModule { outer: ScalaModule =>
180232
/** Add the scoverage specific plugin settings (`dataDir`). */
181233
override def scalacOptions: Target[Seq[String]] =
182234
T {
183-
outer.scalacOptions() ++
184-
Seq(s"-P:scoverage:dataDir:${data().path.toIO.getPath()}") ++
185-
(if (isScoverage2()) Seq(s"-P:scoverage:sourceRoot:${T.workspace}") else Seq())
235+
val extras =
236+
if (isScala3()) {
237+
Seq(s"-coverage-out:${data().path.toIO.getPath()}")
238+
} else {
239+
val base = s"-P:scoverage:dataDir:${data().path.toIO.getPath()}"
240+
if (isScoverage2()) Seq(base, s"-P:scoverage:sourceRoot:${T.workspace}")
241+
else Seq(base)
242+
}
243+
244+
outer.scalacOptions() ++ extras
186245
}
187246

188247
def htmlReport(): Command[Unit] = T.command { doReport(ReportType.Html) }

contrib/scoverage/test/resources/hello-world/core/test/src/GreetSpec.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import org.scalatest._
1+
import org.scalatest.matchers.should.Matchers
2+
import org.scalatest.wordspec.AnyWordSpec
23

3-
class GreetSpec extends WordSpec with Matchers {
4+
class GreetSpec extends AnyWordSpec with Matchers {
45
"Greet" should {
56
"work" in {
67
Greet.greet("Nik", None) shouldBe ("Hello, Nik!")

0 commit comments

Comments
 (0)