From 176d1b600e60af77f88537db688bac1553dce134 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Sun, 2 Oct 2016 20:25:10 +0200 Subject: [PATCH 1/3] Add more artifact overrides --- build.sbt | 2 +- src/main/scala/scoverage/ScoverageKeys.scala | 11 +++++ .../scala/scoverage/ScoverageSbtPlugin.scala | 42 ++++++++++--------- version.sbt | 2 +- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/build.sbt b/build.sbt index 513a2d46..59511875 100644 --- a/build.sbt +++ b/build.sbt @@ -10,7 +10,7 @@ resolvers ++= { if (isSnapshot.value) Seq(Resolver.sonatypeRepo("snapshots")) else Nil } -libraryDependencies += "org.scoverage" %% "scalac-scoverage-plugin" % "1.3.0-SNAPSHOT" +libraryDependencies += "org.scoverage" %% "scalac-scoverage-plugin" % "1.3.0-RC2" publishMavenStyle := true diff --git a/src/main/scala/scoverage/ScoverageKeys.scala b/src/main/scala/scoverage/ScoverageKeys.scala index 52b0b262..1127da63 100644 --- a/src/main/scala/scoverage/ScoverageKeys.scala +++ b/src/main/scala/scoverage/ScoverageKeys.scala @@ -17,5 +17,16 @@ object ScoverageKeys { lazy val coverageOutputDebug = settingKey[Boolean]("turn on the debug report") lazy val coverageCleanSubprojectFiles = settingKey[Boolean]("removes subproject data after an aggregation") lazy val coverageOutputTeamCity = settingKey[Boolean]("turn on teamcity reporting") + + // Artifact settings allow the override of default settings for custom applications. + // The use of these settings is not advised for regular applications, and most definitely "breaks all warranties" + lazy val coverageScalacPluginOrg = settingKey[String]("organisation name of scalac-scoverage-plugin to use") + lazy val coverageScalacPluginArtifact = settingKey[String]("artifact name of scalac-scoverage-plugin to use") lazy val coverageScalacPluginVersion = settingKey[String]("version of scalac-scoverage-plugin to use") + lazy val coverageScalacRuntimeOrg = settingKey[String]("organisation name of scalac-scoverage-runtime to use") + lazy val coverageScalacRuntimeArtifact = settingKey[String]("artifact name of scalac-scoverage-runtime to use") + lazy val coverageScalacRuntimeVersion = settingKey[String]("version of scalac-scoverage-runtime to use") + + //Use this to completely overide the library settings: This is the last resort option: use this, on you're on your own. + lazy val coverageLibraryDependencies = settingKey[Seq[ModuleID]]("Use these library dependencies if coverage is enabled") } diff --git a/src/main/scala/scoverage/ScoverageSbtPlugin.scala b/src/main/scala/scoverage/ScoverageSbtPlugin.scala index e86bebca..dceea62a 100644 --- a/src/main/scala/scoverage/ScoverageSbtPlugin.scala +++ b/src/main/scala/scoverage/ScoverageSbtPlugin.scala @@ -7,11 +7,8 @@ import scoverage.report.{CoverageAggregator, CoberturaXmlWriter, ScoverageHtmlWr object ScoverageSbtPlugin extends AutoPlugin { - val OrgScoverage = "org.scoverage" - val ScalacRuntimeArtifact = "scalac-scoverage-runtime" - val ScalacPluginArtifact = "scalac-scoverage-plugin" - // this should match the version defined in build.sbt - val DefaultScoverageVersion = "1.3.0-SNAPSHOT" + private final val DefaultScoverageVersion = "1.3.0-RC2" // this should match the version defined in build.sbt + val autoImport = ScoverageKeys lazy val ScoveragePluginConfig = config("scoveragePlugin").hide @@ -36,7 +33,13 @@ object ScoverageSbtPlugin extends AutoPlugin { coverageOutputDebug := false, coverageCleanSubprojectFiles := true, coverageOutputTeamCity := false, - coverageScalacPluginVersion := DefaultScoverageVersion + coverageScalacPluginOrg := "org.scoverage", + coverageScalacPluginArtifact := "scalac-scoverage-plugin", + coverageScalacPluginVersion := DefaultScoverageVersion, + coverageScalacRuntimeOrg := coverageScalacPluginOrg.value, + coverageScalacRuntimeArtifact := "scalac-scoverage-runtime", + coverageScalacRuntimeVersion := coverageScalacPluginVersion.value, + coverageLibraryDependencies := Seq() ) override def buildSettings: Seq[Setting[_]] = super.buildSettings ++ @@ -54,13 +57,16 @@ object ScoverageSbtPlugin extends AutoPlugin { private lazy val coverageSettings = Seq( libraryDependencies ++= { if (coverageEnabled.value) - Seq( - // We only add for "compile"" because of macros. This setting could be optimed to just "test" if the handling - // of macro coverage was improved. - OrgScoverage %% (scalacRuntime(libraryDependencies.value)) % coverageScalacPluginVersion.value, - // We don't want to instrument the test code itself, nor add to a pom when published with coverage enabled. - OrgScoverage %% ScalacPluginArtifact % coverageScalacPluginVersion.value % ScoveragePluginConfig.name - ) + if (coverageLibraryDependencies.value.isEmpty) + Seq( + // We only add for "compile"" because of macros. This setting could be optimed to just "test" if the handling + // of macro coverage was improved. + coverageScalacRuntimeOrg.value %% (coverageScalacRuntimeArtifact.value + optionalScalaJsSuffix(libraryDependencies.value)) % coverageScalacRuntimeVersion.value, + // We don't want to instrument the test code itself, nor add to a pom when published with coverage enabled. + coverageScalacPluginOrg.value %% coverageScalacPluginArtifact.value % coverageScalacPluginVersion.value % ScoveragePluginConfig.name + ) + else + coverageLibraryDependencies.value else Nil } @@ -70,8 +76,8 @@ object ScoverageSbtPlugin extends AutoPlugin { scalacOptions in(Compile, compile) ++= { if (coverageEnabled.value) { val scoverageDeps: Seq[File] = update.value matching configurationFilter(ScoveragePluginConfig.name) - val pluginPath: File = scoverageDeps.find(_.getAbsolutePath.contains(ScalacPluginArtifact)) match { - case None => throw new Exception(s"Fatal: $ScalacPluginArtifact not in libraryDependencies") + val pluginPath: File = scoverageDeps.find(_.getAbsolutePath.contains(coverageScalacPluginArtifact.value)) match { + case None => throw new Exception(s"Fatal: ${coverageScalacPluginArtifact.value} not in libraryDependencies") case Some(pluginPath) => pluginPath } Seq( @@ -88,12 +94,8 @@ object ScoverageSbtPlugin extends AutoPlugin { } ) - private def scalacRuntime(deps: Seq[ModuleID]): String = { - ScalacRuntimeArtifact + optionalScalaJsSuffix(deps) - } - // returns "_sjs$sjsVersion" for Scala.js projects or "" otherwise - private def optionalScalaJsSuffix(deps: Seq[ModuleID]): String = { + def optionalScalaJsSuffix(deps: Seq[ModuleID]): String = { val sjsClassifier = deps.collectFirst{ case ModuleID("org.scala-js", "scalajs-library", v, _, _, _, _, _, _, _, _) => v }.map(_.take(3)).map(sjsVersion => "_sjs" + sjsVersion) diff --git a/version.sbt b/version.sbt index 77ab1602..9b4b4add 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "1.5.0-SNAPSHOT" +version in ThisBuild := "1.5.0-RC2" From 936f7dc640f80a369d2823d2b9910ab1d2d6fbbf Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Sun, 9 Oct 2016 18:42:42 +0200 Subject: [PATCH 2/3] Use scoverage core --- build.sbt | 3 ++- src/main/scala/scoverage/ScoverageSbtPlugin.scala | 8 ++++---- version.sbt | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build.sbt b/build.sbt index 59511875..19743546 100644 --- a/build.sbt +++ b/build.sbt @@ -10,7 +10,8 @@ resolvers ++= { if (isSnapshot.value) Seq(Resolver.sonatypeRepo("snapshots")) else Nil } -libraryDependencies += "org.scoverage" %% "scalac-scoverage-plugin" % "1.3.0-RC2" +libraryDependencies += "org.scoverage" % "scalac-scoverage-reporting" % "2.0.0-M0" cross CrossVersion.binary +libraryDependencies += "org.scoverage" %% "scalac-scoverage-plugin" % "2.0.0-M0" cross CrossVersion.full publishMavenStyle := true diff --git a/src/main/scala/scoverage/ScoverageSbtPlugin.scala b/src/main/scala/scoverage/ScoverageSbtPlugin.scala index dceea62a..6847331a 100644 --- a/src/main/scala/scoverage/ScoverageSbtPlugin.scala +++ b/src/main/scala/scoverage/ScoverageSbtPlugin.scala @@ -3,11 +3,11 @@ package scoverage import sbt.Keys._ import sbt._ import sbt.plugins.JvmPlugin -import scoverage.report.{CoverageAggregator, CoberturaXmlWriter, ScoverageHtmlWriter, ScoverageXmlWriter} +import scoverage.report.{CoverageAggregator, CoberturaXmlWriter, Deserializer, ScoverageHtmlWriter, ScoverageXmlWriter} object ScoverageSbtPlugin extends AutoPlugin { - private final val DefaultScoverageVersion = "1.3.0-RC2" // this should match the version defined in build.sbt + private final val DefaultScoverageVersion = "2.0.0-M0" // this should match the version defined in build.sbt val autoImport = ScoverageKeys lazy val ScoveragePluginConfig = config("scoveragePlugin").hide @@ -63,7 +63,7 @@ object ScoverageSbtPlugin extends AutoPlugin { // of macro coverage was improved. coverageScalacRuntimeOrg.value %% (coverageScalacRuntimeArtifact.value + optionalScalaJsSuffix(libraryDependencies.value)) % coverageScalacRuntimeVersion.value, // We don't want to instrument the test code itself, nor add to a pom when published with coverage enabled. - coverageScalacPluginOrg.value %% coverageScalacPluginArtifact.value % coverageScalacPluginVersion.value % ScoveragePluginConfig.name + coverageScalacPluginOrg.value %% coverageScalacPluginArtifact.value % coverageScalacPluginVersion.value % ScoveragePluginConfig.name cross CrossVersion.full ) else coverageLibraryDependencies.value @@ -230,7 +230,7 @@ object ScoverageSbtPlugin extends AutoPlugin { if (coverageFile.exists) { - val coverage = Serializer.deserialize(coverageFile) + val coverage = Deserializer.deserialize(coverageFile) log.info(s"Reading scoverage measurements...") val measurementFiles = IOUtils.findMeasurementFiles(dataDir) diff --git a/version.sbt b/version.sbt index 9b4b4add..dbba9da7 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "1.5.0-RC2" +version in ThisBuild := "2.0.0-M0" From e173a710c9fb649ba8b03c43bb381df146b4f370 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Sun, 16 Oct 2016 17:50:54 +0200 Subject: [PATCH 3/3] v2.0.0-M0 changes --- build.sbt | 3 +- patch/kind-projector.diff | 28 +++++++ patch/local-implicits.diff | 69 ++++++++++++++++ patch/notes.txt | 4 + src/main/resources/Invoker.scala | 51 ++++++++++++ src/main/scala/scoverage/ScoverageKeys.scala | 2 + .../scala/scoverage/ScoverageSbtPlugin.scala | 79 +++++++++++++++++-- 7 files changed, 226 insertions(+), 10 deletions(-) create mode 100644 patch/kind-projector.diff create mode 100644 patch/local-implicits.diff create mode 100644 patch/notes.txt create mode 100644 src/main/resources/Invoker.scala diff --git a/build.sbt b/build.sbt index 19743546..0beb324e 100644 --- a/build.sbt +++ b/build.sbt @@ -10,8 +10,7 @@ resolvers ++= { if (isSnapshot.value) Seq(Resolver.sonatypeRepo("snapshots")) else Nil } -libraryDependencies += "org.scoverage" % "scalac-scoverage-reporting" % "2.0.0-M0" cross CrossVersion.binary -libraryDependencies += "org.scoverage" %% "scalac-scoverage-plugin" % "2.0.0-M0" cross CrossVersion.full +libraryDependencies += "org.scoverage" % "scalac-scoverage-reporting" % "2.0.0-M0" cross CrossVersion.binary publishMavenStyle := true diff --git a/patch/kind-projector.diff b/patch/kind-projector.diff new file mode 100644 index 00000000..284b9df2 --- /dev/null +++ b/patch/kind-projector.diff @@ -0,0 +1,28 @@ +diff --git a/build.sbt b/build.sbt +index 4c0d07b..49d99d0 100644 +--- a/build.sbt ++++ b/build.sbt +@@ -34,7 +34,12 @@ List(Compile, Test) flatMap { config => + ) + } + +-scalacOptions in Test += "-Xplugin:" + (packageBin in Compile).value ++scalacOptions in Test ++= { ++ val jar = (packageBin in Compile).value ++ Seq(s"-Xplugin:${jar.getAbsolutePath}", s"-Jdummy=${jar.lastModified}") // ensures recompile ++} ++ ++coverageIsCompilerPlugin := true + + scalacOptions in Test += "-Yrangepos" + +diff --git a/project/plugins.sbt b/project/plugins.sbt +index 1bdc94e..54c60eb 100644 +--- a/project/plugins.sbt ++++ b/project/plugins.sbt +@@ -1,3 +1,5 @@ + addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") + addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.0") + addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.5.0") ++ ++addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.0-M0") diff --git a/patch/local-implicits.diff b/patch/local-implicits.diff new file mode 100644 index 00000000..7edf5af5 --- /dev/null +++ b/patch/local-implicits.diff @@ -0,0 +1,69 @@ +diff --git a/build.sbt b/build.sbt +index c0fa477..e045c79 100644 +--- a/build.sbt ++++ b/build.sbt +@@ -6,8 +6,8 @@ import ReleaseKeys._ + organization := "com.github.mpilquist" + name := "local-implicits" + +-scalaVersion := "2.11.5" +-crossScalaVersions := Seq("2.10.5", "2.11.5") ++scalaVersion := "2.11.8" ++crossScalaVersions := Seq("2.10.6", "2.11.8") + crossBuild := true + + libraryDependencies ++= Seq( +@@ -23,6 +23,7 @@ scalacOptions in Test <+= (packageBin in Compile) map { + pluginJar => "-Xplugin:" + pluginJar + } + ++coverageIsCompilerPlugin := true + licenses += ("Three-clause BSD-style", url("https://github.com/mpilquist/local-implicits/blob/master/LICENSE")) + publishTo <<= version { v: String => + val nexus = "https://oss.sonatype.org/" +@@ -58,6 +59,7 @@ pomPostProcess := { (node) => + val stripTestScope = stripIf { n => n.label == "dependency" && (n \ "scope").text == "test" } + new RuleTransformer(stripTestScope).transform(node)(0) + } ++/* + releaseSettings + releaseProcess := Seq[ReleaseStep]( + checkSnapshotDependencies, +@@ -71,4 +73,4 @@ releaseProcess := Seq[ReleaseStep]( + commitNextVersion, + pushChanges + ) +- ++ */ +diff --git a/project/Build.scala b/project/Build.scala +index 1f08023..b4f5ee2 100644 +--- a/project/Build.scala ++++ b/project/Build.scala +@@ -5,14 +5,15 @@ import ReleaseStateTransformations._ + import ReleasePlugin._ + import ReleaseKeys._ + import Utilities._ +-import com.typesafe.sbt.SbtPgp.PgpKeys._ ++//import com.typesafe.sbt.SbtPgp.PgpKeys._ + + object ProjectBuild extends Build { +- ++/* + lazy val publishSignedAction = { st: State => + val extracted = st.extract + val ref = extracted.get(thisProjectRef) + extracted.runAggregated(publishSigned in Global in ref, st) + } ++ */ + } + +diff --git a/project/plugins.sbt b/project/plugins.sbt +index 8075941..91ec738 100644 +--- a/project/plugins.sbt ++++ b/project/plugins.sbt +@@ -1,3 +1,4 @@ + addSbtPlugin("com.github.gseitz" % "sbt-release" % "0.8.5") +-addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8") ++//addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8") + ++addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.0-M0") diff --git a/patch/notes.txt b/patch/notes.txt new file mode 100644 index 00000000..b14d80c2 --- /dev/null +++ b/patch/notes.txt @@ -0,0 +1,4 @@ + https://github.com/BennyHill/local-implicits.git + + https://github.com/non/kind-projector.git + diff --git a/src/main/resources/Invoker.scala b/src/main/resources/Invoker.scala new file mode 100644 index 00000000..1a3549e8 --- /dev/null +++ b/src/main/resources/Invoker.scala @@ -0,0 +1,51 @@ +package scoverage + +import java.io.{File, FileWriter} +import scala.collection.concurrent.TrieMap + +/** @author Stephen Samuel */ +object Invoker { + + private val MeasurementsPrefix = "scoverage.measurements." + private val threadFiles = new ThreadLocal[TrieMap[String, FileWriter]] + private val ids = TrieMap.empty[(String, Int), Any] + + /** + * We record that the given id has been invoked by appending its id to the coverage + * data file. + * + * This will happen concurrently on as many threads as the application is using, + * so we use one file per thread, named for the thread id. + * + * This method is not thread-safe if the threads are in different JVMs, because + * the thread IDs may collide. + * You may not use `scoverage` on multiple processes in parallel without risking + * corruption of the measurement file. + * + * @param id the id of the statement that was invoked + * @param dataDir the directory where the measurement data is held + */ + def invoked(id: Int, dataDir: String): Unit = { + // [sam] we can do this simple check to save writing out to a file. + // This won't work across JVMs but since there's no harm in writing out the same id multiple + // times since for coverage we only care about 1 or more, (it just slows things down to + // do it more than once), anything we can do to help is good. This helps especially with code + // that is executed many times quickly, eg tight loops. + if (!ids.contains((dataDir, id))) { + // Each thread writes to a separate measurement file, to reduce contention + // and because file appends via FileWriter are not atomic on Windows. + var files = threadFiles.get() + if (files == null) { + files = TrieMap.empty[String, FileWriter] + threadFiles.set(files) + } + val writer = files.getOrElseUpdate(dataDir, new FileWriter(measurementFile(dataDir), true)) + writer.append(id.toString + '\n').flush() + + ids.put((dataDir, id), ()) + } + } + + def measurementFile(dataDir: File): File = measurementFile(dataDir.getAbsolutePath) + def measurementFile(dataDir: String): File = new File(dataDir, MeasurementsPrefix + Thread.currentThread.getId) +} diff --git a/src/main/scala/scoverage/ScoverageKeys.scala b/src/main/scala/scoverage/ScoverageKeys.scala index 1127da63..b5ee31ef 100644 --- a/src/main/scala/scoverage/ScoverageKeys.scala +++ b/src/main/scala/scoverage/ScoverageKeys.scala @@ -8,6 +8,7 @@ object ScoverageKeys { lazy val coverageAggregate = taskKey[Unit]("aggregate reports from subprojects") lazy val coverageExcludedPackages = settingKey[String]("regex for excluded packages") lazy val coverageExcludedFiles = settingKey[String]("regex for excluded file paths") + lazy val coverageExcludedSymbols = settingKey[String]("regex for excluded symbols") lazy val coverageMinimum = settingKey[Double]("scoverage-minimum-coverage") lazy val coverageFailOnMinimum = settingKey[Boolean]("if coverage is less than this value then fail build") lazy val coverageHighlighting = settingKey[Boolean]("enables range positioning for highlighting") @@ -17,6 +18,7 @@ object ScoverageKeys { lazy val coverageOutputDebug = settingKey[Boolean]("turn on the debug report") lazy val coverageCleanSubprojectFiles = settingKey[Boolean]("removes subproject data after an aggregation") lazy val coverageOutputTeamCity = settingKey[Boolean]("turn on teamcity reporting") + lazy val coverageIsCompilerPlugin = settingKey[Boolean]("True if this project needs compiler plugin support from scoverage") // Artifact settings allow the override of default settings for custom applications. // The use of these settings is not advised for regular applications, and most definitely "breaks all warranties" diff --git a/src/main/scala/scoverage/ScoverageSbtPlugin.scala b/src/main/scala/scoverage/ScoverageSbtPlugin.scala index 6847331a..7efa78c7 100644 --- a/src/main/scala/scoverage/ScoverageSbtPlugin.scala +++ b/src/main/scala/scoverage/ScoverageSbtPlugin.scala @@ -7,7 +7,8 @@ import scoverage.report.{CoverageAggregator, CoberturaXmlWriter, Deserializer, S object ScoverageSbtPlugin extends AutoPlugin { - private final val DefaultScoverageVersion = "2.0.0-M0" // this should match the version defined in build.sbt + // this should match the version defined in build.sbt + private final val DefaultScoverageVersion = "2.0.0-M0" val autoImport = ScoverageKeys lazy val ScoveragePluginConfig = config("scoveragePlugin").hide @@ -18,12 +19,14 @@ object ScoverageSbtPlugin extends AutoPlugin { inConfigurations(Compile)) // must be outside of the 'coverageAggregate' task (see: https://github.com/sbt/sbt/issues/1095 or https://github.com/sbt/sbt/issues/780) override def requires: JvmPlugin.type = plugins.JvmPlugin + override def trigger: PluginTrigger = allRequirements override def globalSettings: Seq[Def.Setting[_]] = super.globalSettings ++ Seq( coverageEnabled := false, coverageExcludedPackages := "", coverageExcludedFiles := "", + coverageExcludedSymbols := "", coverageMinimum := 0, // default is no minimum coverageFailOnMinimum := false, coverageHighlighting := true, @@ -37,9 +40,10 @@ object ScoverageSbtPlugin extends AutoPlugin { coverageScalacPluginArtifact := "scalac-scoverage-plugin", coverageScalacPluginVersion := DefaultScoverageVersion, coverageScalacRuntimeOrg := coverageScalacPluginOrg.value, - coverageScalacRuntimeArtifact := "scalac-scoverage-runtime", + coverageScalacRuntimeArtifact := "scalac-scoverage-runtime-scala", coverageScalacRuntimeVersion := coverageScalacPluginVersion.value, - coverageLibraryDependencies := Seq() + coverageLibraryDependencies := Seq(), + coverageIsCompilerPlugin := false ) override def buildSettings: Seq[Setting[_]] = super.buildSettings ++ @@ -52,18 +56,24 @@ object ScoverageSbtPlugin extends AutoPlugin { coverageReport <<= coverageReport0, coverageAggregate <<= coverageAggregate0, aggregate in coverageAggregate := false +<<<<<<< HEAD ) ++ coverageSettings ++ scalacSettings +======= + ) ++ coverageSettings ++ scalacSettings ++ coverageCompilerPluginpSettings +>>>>>>> v2.0.0-M0 changes private lazy val coverageSettings = Seq( - libraryDependencies ++= { + libraryDependencies ++= { if (coverageEnabled.value) if (coverageLibraryDependencies.value.isEmpty) Seq( // We only add for "compile"" because of macros. This setting could be optimed to just "test" if the handling // of macro coverage was improved. coverageScalacRuntimeOrg.value %% (coverageScalacRuntimeArtifact.value + optionalScalaJsSuffix(libraryDependencies.value)) % coverageScalacRuntimeVersion.value, + //coverageScalacRuntimeOrg.value %% coverageScalacRuntimeArtifact.value % coverageScalacRuntimeVersion.value cross CrossVersion.full, + // We don't want to instrument the test code itself, nor add to a pom when published with coverage enabled. - coverageScalacPluginOrg.value %% coverageScalacPluginArtifact.value % coverageScalacPluginVersion.value % ScoveragePluginConfig.name cross CrossVersion.full + coverageScalacPluginOrg.value %% coverageScalacPluginArtifact.value % coverageScalacPluginVersion.value % ScoveragePluginConfig.name cross CrossVersion.full ) else coverageLibraryDependencies.value @@ -72,11 +82,61 @@ object ScoverageSbtPlugin extends AutoPlugin { } ) + // I've not endeavoured to factor out the SBT repeated code + // as we don't know yet that this approach will be adopted. Let's wait for that first + // Also, this has been tested on single compiler plugin projects and seems to work. + // I have no idea how well it will scala, but if nothing else highlights the problem with on solution. + // + // The problem is that the scoverage plugin modifies compiled code to call Invoker.invoked, + // and hence a library implementing that must be available at the runtime of instrumented code. + // In the normal case, this is just a case of adding a runtime library to the test code. + // + // But, a compiler plugin wil not pick up that library, unless it has a custom loader to do so. + // So what I do here is, if coverageIsCompilerPlugin is true, add the Invoker code itself to the + // user plugin - that way it most definitely has an invoker to call. + // + // I think this, if nothing else, makes the problem easier to see. + // FTR, similar issues exist with macros and especially macros in scala.js cross compiled + // code as actually different invokers are required - a jvm only for runtime and a js one for + // runtime. This is another reason why I have excluded scala.js for now, as in the short term + // it will just confues issues. Adding it back later is trivial. + private lazy val coverageCompilerPluginpSettings = Seq( + unmanagedSources in Compile ++= { + if (coverageEnabled.value && coverageIsCompilerPlugin.value) + mkCpUnmanagedSources(crossTarget.value.toString) + else + Nil + }, + coverageExcludedPackages := coverageExcludedPackages.value + { + if (coverageEnabled.value && coverageIsCompilerPlugin.value) ";scoverage\\..*" + else "" + }, + coverageScalacRuntimeArtifact := { + if (coverageEnabled.value && coverageIsCompilerPlugin.value) "scalac-scoverage-runtime-java" + else coverageScalacRuntimeArtifact.value + } + ) + + private def mkCpUnmanagedSources(target: String) = { + import java.nio.file.Files + + val scoverageDir = s"$target/scoverage-data" + val invokerFile = "Invoker.scala" + val embeddedInvoker = file(s"$scoverageDir/$invokerFile") + + if (!embeddedInvoker.exists()) { + val invoker = getClass.getClassLoader.getResourceAsStream(invokerFile) + Files.createDirectories(file(s"$scoverageDir").toPath) + Files.copy(invoker, embeddedInvoker.toPath) + } + Seq(embeddedInvoker) + } + private lazy val scalacSettings = Seq( scalacOptions in(Compile, compile) ++= { if (coverageEnabled.value) { val scoverageDeps: Seq[File] = update.value matching configurationFilter(ScoveragePluginConfig.name) - val pluginPath: File = scoverageDeps.find(_.getAbsolutePath.contains(coverageScalacPluginArtifact.value)) match { + val pluginPath: File = scoverageDeps.find(_.getAbsolutePath.contains(coverageScalacPluginArtifact.value)) match { case None => throw new Exception(s"Fatal: ${coverageScalacPluginArtifact.value} not in libraryDependencies") case Some(pluginPath) => pluginPath } @@ -85,6 +145,7 @@ object ScoverageSbtPlugin extends AutoPlugin { Some(s"-P:scoverage:dataDir:${crossTarget.value.getAbsolutePath}/scoverage-data"), Option(coverageExcludedPackages.value.trim).filter(_.nonEmpty).map(v => s"-P:scoverage:excludedPackages:$v"), Option(coverageExcludedFiles.value.trim).filter(_.nonEmpty).map(v => s"-P:scoverage:excludedFiles:$v"), + Option(coverageExcludedSymbols.value.trim).filter(_.nonEmpty).map(v => s"-P:scoverage:excludedSymbols:$v"), // rangepos is broken in some releases of scala so option to turn it off if (coverageHighlighting.value) Some("-Yrangepos") else None ).flatten @@ -94,9 +155,12 @@ object ScoverageSbtPlugin extends AutoPlugin { } ) + def isScalaJsProject(deps: Seq[ModuleID]): Boolean = + !optionalScalaJsSuffix(deps).isEmpty + // returns "_sjs$sjsVersion" for Scala.js projects or "" otherwise def optionalScalaJsSuffix(deps: Seq[ModuleID]): String = { - val sjsClassifier = deps.collectFirst{ + val sjsClassifier = deps.collectFirst { case ModuleID("org.scala-js", "scalajs-library", v, _, _, _, _, _, _, _, _) => v }.map(_.take(3)).map(sjsVersion => "_sjs" + sjsVersion) @@ -273,5 +337,4 @@ object ScoverageSbtPlugin extends AutoPlugin { val i = scalacOptions.indexOf("-encoding") + 1 if (i > 0 && i < scalacOptions.length) Some(scalacOptions(i)) else None } - }