Since Scala 2.13, macro-paradise has been inlined in the compiler and is available via a compiler flag:
Compile / scalacOptions += "-Ymacro-annotations"
For reference, in previous versions of Scala, macro-paradise was available via a compiler plugin:
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
What is the canonical way of conditionally add the first setting or the second, according to the value of the Scala version, in a build targetting both Scala 2.12 and 2.13?
I would like to write the following but it doesn’t work:
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, n)) if n >= 13 => Compile / scalacOptions += "-Ymacro-annotations"
case _ => addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
}
It fails with the following error:
error: `value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.
CrossVersion.partialVersion(scalaVersion.value) match {
^
In the meantime, I can use the following workaround but I wish a simpler solution was supported:
Compile / scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, n)) if n >= 13 => "-Ymacro-annotations" :: Nil
case _ => Nil
}
}
libraryDependencies ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, n)) if n >= 13 => Nil
case _ => compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full) :: Nil
}
}
If you would like to write
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, n)) if n >= 13 => Compile / scalacOptions += "-Ymacro-annotations"
case _ => addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
}
one option is defining a SBT custom command like so
def compileWithMacroParadise: Command = Command.command("compileWithMacroParadise") { state =>
import Project._
val extractedState = extract(state)
val stateWithMacroParadise = CrossVersion.partialVersion(extractedState.get(scalaVersion)) match {
case Some((2, n)) if n >= 13 => extractedState.appendWithSession(Seq(Compile / scalacOptions += "-Ymacro-annotations"), state)
case _ => extractedState.appendWithSession(addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full), state)
}
val (stateAfterCompileWithMacroParadise, _) = extract(stateWithMacroParadise).runTask(Compile / compile, stateWithMacroParadise)
stateAfterCompileWithMacroParadise
}
commands ++= Seq(compileWithMacroParadise),
addCommandAlias("compile", "compileWithMacroParadise")
sbt compile
should now make appropriate modifications to build state (stateWithMacroParadise
) before running the compile
task.
Fully working example, add this code in a Compiler.scala
file in your project
directory:
import sbt._
import sbt.Keys._
object Compiler extends AutoPlugin {
override def trigger = allRequirements
override def projectSettings: Seq[Def.Setting[_]] =
Seq(
libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, x)) if x < 13 =>
Seq(
compilerPlugin(("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)),
"org.scala-lang.modules" %% "scala-collection-compat" % "2.1.6"
)
case _ => Nil
}),
Compile / scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, x)) if x >= 13 =>
Seq("-Ymacro-annotations")
case _ => Nil
})
)
}
Note that you have to use projectSettings
, buildSettings
will not work
The "scala-collection-compat"
dependency is another one you typically want when cross compiling 2.12 and 2.13. It let's you do
import scala.jdk.CollectionConverters._
Instead of using the deprecated scala.collection.JavaConverters
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With