diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 549ed219..5a67c54b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '8', '11', '17' ] + java: [ '8', '11' ] steps: - name: checkout the repo @@ -43,7 +43,7 @@ jobs: uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: '17' + java-version: '11' - name: Check Formatting run: sbt scalafmtCheckAll diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3370fc55..ab6be9a4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,14 +6,14 @@ on: tags: ["*"] jobs: publish: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - uses: actions/setup-java@v2 with: - distribution: 'adopt' + distribution: 'temurin' java-version: '11' - run: sbt ci-release env: diff --git a/.scalafmt.conf b/.scalafmt.conf index 7016fbeb..230159eb 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1 +1,3 @@ version = "3.0.8" +project.git = true +runner.dialect = "scala213" diff --git a/build.sbt b/build.sbt index ea6a5e80..46d413f8 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := "sbt-scoverage" import sbt.ScriptedPlugin.autoImport.scriptedLaunchOpts -def scoverageVersion = "1.4.9" +lazy val scoverageVersion = "2.0.0-M2" inThisBuild( List( @@ -33,7 +33,10 @@ lazy val root = Project("sbt-scoverage", file(".")) .enablePlugins(SbtPlugin, BuildInfoPlugin) .settings( libraryDependencies ++= Seq( - "org.scoverage" %% "scalac-scoverage-plugin" % scoverageVersion cross (CrossVersion.full) + "org.scoverage" %% "scalac-scoverage-plugin" % scoverageVersion cross (CrossVersion.full), + "org.scoverage" %% "scalac-scoverage-reporter" % scoverageVersion, + "org.scoverage" %% "scalac-scoverage-domain" % scoverageVersion, + "org.scoverage" %% "scalac-scoverage-serializer" % scoverageVersion ), buildInfoKeys := Seq[BuildInfoKey]("scoverageVersion" -> scoverageVersion), buildInfoPackage := "scoverage", diff --git a/src/main/scala/scoverage/CoverageMinimum.scala b/src/main/scala/scoverage/CoverageMinimum.scala index 8e0c3030..a3ae9af7 100644 --- a/src/main/scala/scoverage/CoverageMinimum.scala +++ b/src/main/scala/scoverage/CoverageMinimum.scala @@ -1,7 +1,9 @@ package scoverage import sbt._ -import scoverage.DoubleFormat.twoFractionDigits +import scoverage.domain.Coverage +import scoverage.domain.CoverageMetrics +import scoverage.domain.DoubleFormat.twoFractionDigits case class CoverageMinimum( statement: Double, diff --git a/src/main/scala/scoverage/ScoverageKeys.scala b/src/main/scala/scoverage/ScoverageKeys.scala index add12d0c..3ee9bcd3 100644 --- a/src/main/scala/scoverage/ScoverageKeys.scala +++ b/src/main/scala/scoverage/ScoverageKeys.scala @@ -19,6 +19,7 @@ object ScoverageKeys { lazy val coverageOutputTeamCity = settingKey[Boolean]("turn on teamcity reporting") lazy val coverageScalacPluginVersion = settingKey[String]("version of scalac-scoverage-plugin to use") lazy val coverageDataDir = settingKey[File]("directory where the measurements and report files will be stored") + lazy val coverageSourceRoot = settingKey[File]("the source root of the project") // format: on @deprecated("Use coverageMinimumStmtTotal instead", "v1.8.0") diff --git a/src/main/scala/scoverage/ScoverageSbtPlugin.scala b/src/main/scala/scoverage/ScoverageSbtPlugin.scala index ba664651..98271a05 100644 --- a/src/main/scala/scoverage/ScoverageSbtPlugin.scala +++ b/src/main/scala/scoverage/ScoverageSbtPlugin.scala @@ -3,10 +3,14 @@ package scoverage import sbt.Keys._ import sbt._ import sbt.plugins.JvmPlugin -import scoverage.report.CoberturaXmlWriter -import scoverage.report.CoverageAggregator -import scoverage.report.ScoverageHtmlWriter -import scoverage.report.ScoverageXmlWriter +import scoverage.reporter.CoberturaXmlWriter +import scoverage.domain.Constants +import scoverage.domain.Coverage +import scoverage.reporter.CoverageAggregator +import scoverage.reporter.IOUtils +import scoverage.reporter.ScoverageHtmlWriter +import scoverage.reporter.ScoverageXmlWriter +import scoverage.serialize.Serializer import java.time.Instant @@ -15,6 +19,9 @@ object ScoverageSbtPlugin extends AutoPlugin { val orgScoverage = "org.scoverage" val scalacRuntimeArtifact = "scalac-scoverage-runtime" val scalacPluginArtifact = "scalac-scoverage-plugin" + val scalacDomainArtifact = "scalac-scoverage-domain" + val scalacReporterArtifact = "scalac-scoverage-reporter" + val scalacSerializerArtifact = "scalac-scoverage-serializer" val defaultScoverageVersion = BuildInfo.scoverageVersion val autoImport = ScoverageKeys lazy val ScoveragePluginConfig = config("scoveragePlugin").hide @@ -48,7 +55,8 @@ object ScoverageSbtPlugin extends AutoPlugin { coverageOutputCobertura := true, coverageOutputDebug := false, coverageOutputTeamCity := false, - coverageScalacPluginVersion := defaultScoverageVersion + coverageScalacPluginVersion := defaultScoverageVersion, + coverageSourceRoot := (ThisBuild / baseDirectory).value ) override def buildSettings: Seq[Setting[_]] = super.buildSettings ++ @@ -64,10 +72,21 @@ object ScoverageSbtPlugin extends AutoPlugin { coverageDataDir := crossTarget.value ) ++ coverageSettings ++ scalacSettings + private def isScala2(scalaVersion: String) = + CrossVersion + .partialVersion(scalaVersion) + .collect { case (2, _) => + true + } + .getOrElse(false) + private lazy val coverageSettings = Seq( libraryDependencies ++= { - if (coverageEnabled.value) { + if (coverageEnabled.value && isScala2(scalaVersion.value)) { Seq( + orgScoverage %% scalacDomainArtifact % coverageScalacPluginVersion.value, + orgScoverage %% scalacReporterArtifact % coverageScalacPluginVersion.value, + orgScoverage %% scalacSerializerArtifact % coverageScalacPluginVersion.value, // 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( @@ -85,23 +104,47 @@ object ScoverageSbtPlugin extends AutoPlugin { private lazy val scalacSettings = Seq( Compile / compile / scalacOptions ++= { val updateReport = update.value - if (coverageEnabled.value) { + if (coverageEnabled.value && isScala2(scalaVersion.value)) { val scoverageDeps: Seq[File] = - updateReport matching configurationFilter(ScoveragePluginConfig.name) - val pluginPath: File = scoverageDeps.find( - _.getAbsolutePath.contains(scalacPluginArtifact) - ) match { - case None => - throw new Exception( - s"Fatal: $scalacPluginArtifact not in libraryDependencies" - ) - case Some(pluginPath) => pluginPath + updateReport.matching(configurationFilter(ScoveragePluginConfig.name)) + + // Since everything isn't contained in a single plugin jar since we + // want to share reporter/domain code between the plugin and the + // reporter which can be used for Scala3 we need to essentially put + // together the little classpath to pass in to the compiler which + // includes everything it needs for the compiler plugin phase: + // 1. the plugin jar + // 2. the domain jar + // 3. the serializer jar + // NOTE: Even though you'd think that since plugin relies on domain + // it'd just auto include it... it doesn't. + // https://github.com/sbt/sbt/issues/2255 + val pluginPaths = scoverageDeps.collect { + case path + if path.getAbsolutePath().contains(scalacPluginArtifact) || path + .getAbsolutePath() + .contains(scalacDomainArtifact) || + path.getAbsolutePath().contains(scalacSerializerArtifact) => + path.getAbsolutePath() } + + // NOTE: A little hacky, but make sure we are matching on the exact + // number of deps that we expect to collect up above. + if (pluginPaths.size != 3) + throw new Exception( + s"Fatal: Not finding either $scalacDomainArtifact or $scalacPluginArtifact or $scalacSerializerArtifact in libraryDependencies." + ) + Seq( - Some(s"-Xplugin:${pluginPath.getAbsolutePath}"), + Some( + s"-Xplugin:${pluginPaths.mkString(":")}" + ), Some( s"-P:scoverage:dataDir:${coverageDataDir.value.getAbsolutePath}/scoverage-data" ), + Some( + s"-P:scoverage:sourceRoot:${coverageSourceRoot.value.getAbsolutePath}" + ), Option(coverageExcludedPackages.value.trim) .filter(_.nonEmpty) .map(v => s"-P:scoverage:excludedPackages:$v"), @@ -112,6 +155,16 @@ object ScoverageSbtPlugin extends AutoPlugin { // rangepos is broken in some releases of scala so option to turn it off if (coverageHighlighting.value) Some("-Yrangepos") else None ).flatten + } else if ( + // TODO this is very temporary until support for this gets merged in. + // For now we restrict this to this exact SNAPSHOT version which needs + // to be published localled in order to test + coverageEnabled.value && scalaVersion.value == "3.1.2-RC1-bin-SNAPSHOT" + ) { + Seq( + "-coverage", + s"${coverageDataDir.value.getAbsolutePath()}/scoverage-data" + ) } else { Nil } @@ -145,7 +198,11 @@ object ScoverageSbtPlugin extends AutoPlugin { 1000 ) // have noticed some delay in writing on windows, hacky but works - loadCoverage(target, log) match { + loadCoverage( + target, + log, + coverageSourceRoot.value.getAbsoluteFile() + ) match { case Some(cov) => writeReports( target, @@ -176,7 +233,7 @@ object ScoverageSbtPlugin extends AutoPlugin { file / Constants.DataDir } - CoverageAggregator.aggregate(dataDirs) match { + CoverageAggregator.aggregate(dataDirs, coverageSourceRoot.value) match { case Some(cov) => writeReports( coverageDataDir.value, @@ -220,7 +277,11 @@ object ScoverageSbtPlugin extends AutoPlugin { reportDir.mkdirs() if (coverageOutputCobertura) { - new CoberturaXmlWriter(compileSourceDirectories, coberturaDir).write( + new CoberturaXmlWriter( + compileSourceDirectories, + coberturaDir, + coverageSourceEncoding + ).write( coverage ) log.info( @@ -229,11 +290,21 @@ object ScoverageSbtPlugin extends AutoPlugin { } if (coverageOutputXML) { - new ScoverageXmlWriter(compileSourceDirectories, reportDir, false).write( + new ScoverageXmlWriter( + compileSourceDirectories, + reportDir, + false, + coverageSourceEncoding + ).write( coverage ) if (coverageDebug) { - new ScoverageXmlWriter(compileSourceDirectories, reportDir, true).write( + new ScoverageXmlWriter( + compileSourceDirectories, + reportDir, + true, + coverageSourceEncoding + ).write( coverage ) } @@ -306,7 +377,11 @@ object ScoverageSbtPlugin extends AutoPlugin { ) } - private def loadCoverage(crossTarget: File, log: Logger): Option[Coverage] = { + private def loadCoverage( + crossTarget: File, + log: Logger, + sourceRoot: File + ): Option[Coverage] = { val dataDir = crossTarget / "/scoverage-data" val coverageFile = Serializer.coverageFile(dataDir) @@ -315,7 +390,10 @@ object ScoverageSbtPlugin extends AutoPlugin { if (coverageFile.exists) { - val coverage = Serializer.deserialize(coverageFile) + val coverage = Serializer.deserialize( + coverageFile, + sourceRoot + ) log.info(s"Reading scoverage measurements...") val measurementFiles = IOUtils.findMeasurementFiles(dataDir) diff --git a/src/sbt-test/scoverage/scalac-plugin-version/build.sbt b/src/sbt-test/scoverage/scalac-plugin-version/build.sbt deleted file mode 100644 index df7f1c7c..00000000 --- a/src/sbt-test/scoverage/scalac-plugin-version/build.sbt +++ /dev/null @@ -1,17 +0,0 @@ -lazy val root = (project in file(".")).settings( - coverageScalacPluginVersion := "1.3.0" -) - -TaskKey[Unit]("check") := { - assert( - libraryDependencies.value.count(module => - module.organization == "org.scoverage" && module.revision == "1.3.0" - ) == 2 - ) -} - -resolvers ++= { - if (sys.props.get("plugin.version").exists(_.endsWith("-SNAPSHOT"))) - Seq(Resolver.sonatypeRepo("snapshots")) - else Seq.empty -} diff --git a/src/sbt-test/scoverage/scalac-plugin-version/project/plugins.sbt b/src/sbt-test/scoverage/scalac-plugin-version/project/plugins.sbt deleted file mode 100644 index 8d349239..00000000 --- a/src/sbt-test/scoverage/scalac-plugin-version/project/plugins.sbt +++ /dev/null @@ -1,16 +0,0 @@ -val pluginVersion = sys.props.getOrElse( - "plugin.version", - throw new RuntimeException( - """|The system property 'plugin.version' is not defined. - |Specify this property using the scriptedLaunchOpts -D.""".stripMargin - ) -) - -addSbtPlugin("org.scoverage" % "sbt-scoverage" % pluginVersion) - -resolvers ++= { - if (pluginVersion.endsWith("-SNAPSHOT")) - Seq(Resolver.sonatypeRepo("snapshots")) - else - Seq.empty -} diff --git a/src/sbt-test/scoverage/scalac-plugin-version/test b/src/sbt-test/scoverage/scalac-plugin-version/test deleted file mode 100644 index 4309c4d3..00000000 --- a/src/sbt-test/scoverage/scalac-plugin-version/test +++ /dev/null @@ -1,3 +0,0 @@ -# assert coverageScalacPluginVersion is taken into account when generating libraryDependencies -> coverage -> check \ No newline at end of file