Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.0.0.-M0 changes #196

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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-reporting" % "2.0.0-M0" cross CrossVersion.binary

publishMavenStyle := true

Expand Down
28 changes: 28 additions & 0 deletions patch/kind-projector.diff
Original file line number Diff line number Diff line change
@@ -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")
69 changes: 69 additions & 0 deletions patch/local-implicits.diff
Original file line number Diff line number Diff line change
@@ -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")
4 changes: 4 additions & 0 deletions patch/notes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
https://github.com/BennyHill/local-implicits.git

https://github.com/non/kind-projector.git

51 changes: 51 additions & 0 deletions src/main/resources/Invoker.scala
Original file line number Diff line number Diff line change
@@ -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)
}
13 changes: 13 additions & 0 deletions src/main/scala/scoverage/ScoverageKeys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -17,5 +18,17 @@ 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"
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")
}
111 changes: 88 additions & 23 deletions src/main/scala/scoverage/ScoverageSbtPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ 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 {

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 = "2.0.0-M0"

val autoImport = ScoverageKeys
lazy val ScoveragePluginConfig = config("scoveragePlugin").hide

Expand All @@ -21,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,
Expand All @@ -36,7 +36,14 @@ 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-scala",
coverageScalacRuntimeVersion := coverageScalacPluginVersion.value,
coverageLibraryDependencies := Seq(),
coverageIsCompilerPlugin := false
)

override def buildSettings: Seq[Setting[_]] = super.buildSettings ++
Expand All @@ -49,36 +56,96 @@ 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)
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,
//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
)
else
coverageLibraryDependencies.value
else
Nil
}
)

// 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(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(
Some(s"-Xplugin:${pluginPath.getAbsolutePath}"),
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
Expand All @@ -88,13 +155,12 @@ object ScoverageSbtPlugin extends AutoPlugin {
}
)

private def scalacRuntime(deps: Seq[ModuleID]): String = {
ScalacRuntimeArtifact + optionalScalaJsSuffix(deps)
}
def isScalaJsProject(deps: Seq[ModuleID]): Boolean =
!optionalScalaJsSuffix(deps).isEmpty

// returns "_sjs$sjsVersion" for Scala.js projects or "" otherwise
private def optionalScalaJsSuffix(deps: Seq[ModuleID]): String = {
val sjsClassifier = deps.collectFirst{
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)

Expand Down Expand Up @@ -228,7 +294,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)
Expand Down Expand Up @@ -271,5 +337,4 @@ object ScoverageSbtPlugin extends AutoPlugin {
val i = scalacOptions.indexOf("-encoding") + 1
if (i > 0 && i < scalacOptions.length) Some(scalacOptions(i)) else None
}

}
2 changes: 1 addition & 1 deletion version.sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version in ThisBuild := "1.5.0-SNAPSHOT"
version in ThisBuild := "2.0.0-M0"