diff --git a/src/main/scala/sbtbuildinfo/BuildInfoOption.scala b/src/main/scala/sbtbuildinfo/BuildInfoOption.scala index 6d0750f..ed5e110 100644 --- a/src/main/scala/sbtbuildinfo/BuildInfoOption.scala +++ b/src/main/scala/sbtbuildinfo/BuildInfoOption.scala @@ -8,4 +8,5 @@ object BuildInfoOption { case class Traits(names: String*) extends BuildInfoOption case object BuildTime extends BuildInfoOption case object PackagePrivate extends BuildInfoOption + case object ConstantValue extends BuildInfoOption } diff --git a/src/main/scala/sbtbuildinfo/ScalaCaseObjectRenderer.scala b/src/main/scala/sbtbuildinfo/ScalaCaseObjectRenderer.scala index 8ab0eee..b777a52 100644 --- a/src/main/scala/sbtbuildinfo/ScalaCaseObjectRenderer.scala +++ b/src/main/scala/sbtbuildinfo/ScalaCaseObjectRenderer.scala @@ -6,6 +6,7 @@ private[sbtbuildinfo] case class ScalaCaseObjectRenderer(options: Seq[BuildInfoO override def extension = "scala" val traitNames = options.collect{case BuildInfoOption.Traits(ts @ _*) => ts}.flatten val objTraits = if (traitNames.isEmpty) "" else " extends " ++ traitNames.mkString(" with ") + val constantValue = options.contains(BuildInfoOption.ConstantValue) // It is safe to add `import scala.Predef` even though we need to keep `-Ywarn-unused-import` in mind // because we always generate code that has a reference to `String`. If the "base" generated code were to be @@ -31,13 +32,23 @@ private[sbtbuildinfo] case class ScalaCaseObjectRenderer(options: Seq[BuildInfoO toMapLine(buildInfoResults) ++ toJsonLine ++ footer + + private val constantTypes = Set("scala.Int", "scala.Long", "scala.Double", "scala.Boolean", "scala.Symbol", "String") + private def line(result: BuildInfoResult): Seq[String] = { import result._ - val typeDecl = getType(result.typeExpr) map { ": " + _ } getOrElse "" - + val (typeDecl, modifier) = + getType(result.typeExpr) match { + case Some(tp) if !constantValue || !constantTypes(tp) => + (s": $tp", "") + case _ if constantValue => + ("", "final ") + case _ => + ("", "") + } List( s" /** The value is ${quote(value)}. */", - s" val $identifier$typeDecl = ${quote(value)}" + s" ${modifier}val $identifier$typeDecl = ${quote(value)}" ) } diff --git a/src/main/scala/sbtbuildinfo/ScalaFinalCaseObjectRenderer.scala b/src/main/scala/sbtbuildinfo/ScalaFinalCaseObjectRenderer.scala deleted file mode 100644 index 4c47c2d..0000000 --- a/src/main/scala/sbtbuildinfo/ScalaFinalCaseObjectRenderer.scala +++ /dev/null @@ -1,89 +0,0 @@ -package sbtbuildinfo - -case class ScalaFinalCaseObjectRenderer(options: Seq[BuildInfoOption], pkg: String, obj: String) extends ScalaRenderer { - - override def fileType = BuildInfoType.Source - override def extension = "scala" - val traitNames = options.collect{case BuildInfoOption.Traits(ts @ _*) => ts}.flatten - val objTraits = if (traitNames.isEmpty) "" else " extends " ++ traitNames.mkString(" with ") - - // It is safe to add `import scala.Predef` even though we need to keep `-Ywarn-unused-import` in mind - // because we always generate code that has a reference to `String`. If the "base" generated code were to be - // changed and no longer contain a reference to `String`, we would need to remove `import scala.Predef` and - // fully qualify every reference. Note it is NOT safe to use `import scala._` because of the possibility of - // the project using `-Ywarn-unused-import` because we do not always generated references that are part of - // `scala` such as `scala.Option`. - def header = List( - "// $COVERAGE-OFF$", - s"package $pkg", - "", - "import scala.Predef._", - "", - s"/** This object was generated by sbt-buildinfo. */", - withPkgPriv(s"case object $obj$objTraits {") - ) - - def footer = List("}", "// $COVERAGE-ON$") - - override def renderKeys(buildInfoResults: Seq[BuildInfoResult]) = - header ++ - buildInfoResults.flatMap(line) ++ Seq(toStringLines(buildInfoResults)) ++ - toMapLine(buildInfoResults) ++ toJsonLine ++ - footer - - private def line(result: BuildInfoResult): Seq[String] = { - import result._ - val typeDecl = getType(result.typeExpr) map { ": " + _ } getOrElse "" - - List( - s" /** The value is ${quote(value)}. */", - s" final val $identifier$typeDecl = ${quote(value)}" - ) - } - - def toStringLines(results: Seq[BuildInfoResult]): String = { - val idents = results.map(_.identifier) - val fmt = idents.map("%s: %%s" format _).mkString(", ") - val vars = idents.mkString(", ") - s""" override val toString: String = { - | "$fmt".format( - | $vars - | ) - | }""".stripMargin - } - - def toMapLine(results: Seq[BuildInfoResult]): Seq[String] = - if (options.contains(BuildInfoOption.ToMap) || options.contains(BuildInfoOption.ToJson)) - results - .map(result => " \"%s\" -> %s".format(result.identifier, result.identifier)) - .mkString(" val toMap: Map[String, Any] = Map[String, Any](\n", ",\n", ")") - .split("\n") - .toList ::: List("") - else Nil - - def toJsonLine: Seq[String] = - if (options contains BuildInfoOption.ToJson) - List( - """| private def quote(x: Any): String = "\"" + x + "\"" - | private def toJsonValue(value: Any): String = { - | value match { - | case elem: Seq[_] => elem.map(toJsonValue).mkString("[", ",", "]") - | case elem: Option[_] => elem.map(toJsonValue).getOrElse("null") - | case elem: Map[_, Any] => elem.map { - | case (k, v) => toJsonValue(k.toString) + ":" + toJsonValue(v) - | }.mkString("{", ", ", "}") - | case d: Double => d.toString - | case f: Float => f.toString - | case l: Long => l.toString - | case i: Int => i.toString - | case s: Short => s.toString - | case bool: Boolean => bool.toString - | case str: String => quote(str) - | case other => quote(other.toString) - | } - | } - | - | val toJson: String = toJsonValue(toMap)""".stripMargin) - else Nil - -} diff --git a/src/main/scala/sbtbuildinfo/ScalaRenderer.scala b/src/main/scala/sbtbuildinfo/ScalaRenderer.scala index 9961606..c291982 100644 --- a/src/main/scala/sbtbuildinfo/ScalaRenderer.scala +++ b/src/main/scala/sbtbuildinfo/ScalaRenderer.scala @@ -52,6 +52,7 @@ abstract class ScalaRenderer extends BuildInfoRenderer { case x: Symbol => s"""scala.Symbol("${x.name}")""" case x: Long => x.toString + "L" case node: scala.xml.NodeSeq if node.toString().trim.nonEmpty => node.toString() + case node: scala.xml.NodeSeq => "scala.xml.NodeSeq.Empty" case (k, _v) => "(%s -> %s)" format(quote(k), quote(_v)) case mp: Map[_, _] => mp.toList.map(quote(_)).mkString("Map(", ", ", ")") case seq: collection.Seq[_] => seq.map(quote).mkString("scala.collection.immutable.Seq(", ", ", ")") diff --git a/src/sbt-test/sbt-buildinfo/caseclassrenderer/build.sbt b/src/sbt-test/sbt-buildinfo/caseclassrenderer/build.sbt index d72afbc..5593a23 100644 --- a/src/sbt-test/sbt-buildinfo/caseclassrenderer/build.sbt +++ b/src/sbt-test/sbt-buildinfo/caseclassrenderer/build.sbt @@ -62,7 +62,7 @@ lazy val root = (project in file(".")) """ name = "helloworld",""" :: """ projectVersion = 0.1,""" :: """ scalaVersion = "2.12.12",""" :: - """ ivyXML = scala.collection.immutable.Seq(),""" :: + """ ivyXML = scala.xml.NodeSeq.Empty,""" :: """ homepage = scala.Some(new java.net.URL("http://example.com")),""" :: """ licenses = scala.collection.immutable.Seq(("MIT License" -> new java.net.URL("https://github.com/sbt/sbt-buildinfo/blob/master/LICENSE"))),""" :: """ apiMappings = Map(),""" :: diff --git a/src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/build.sbt b/src/sbt-test/sbt-buildinfo/constantvalue/build.sbt similarity index 55% rename from src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/build.sbt rename to src/sbt-test/sbt-buildinfo/constantvalue/build.sbt index 197cc04..3aade05 100644 --- a/src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/build.sbt +++ b/src/sbt-test/sbt-buildinfo/constantvalue/build.sbt @@ -1,9 +1,7 @@ -import sbtbuildinfo.ScalaFinalCaseObjectRenderer - lazy val check = taskKey[Unit]("checks this plugin") ThisBuild / version := "0.1" -ThisBuild / scalaVersion := "2.12.12" +ThisBuild / scalaVersion := "2.13.3" ThisBuild / homepage := Some(url("http://example.com")) ThisBuild / licenses := Seq("MIT License" -> url("https://github.com/sbt/sbt-buildinfo/blob/master/LICENSE")) @@ -14,6 +12,7 @@ lazy val root = (project in file(".")). TaskKey[Classpath]("someCp") := Seq(Attributed.blank(file("/tmp/f.txt"))), buildInfoKeys := Seq[BuildInfoKey]( name, + version, BuildInfoKey.map(version) { case (n, v) => "projectVersion" -> v.toDouble }, scalaVersion, ivyXML, @@ -26,12 +25,12 @@ lazy val root = (project in file(".")). BuildInfoKey.action("buildTime") { 1234L }, TaskKey[Classpath]("someCp"), target), - buildInfoOptions += BuildInfoOption.Traits("traits.MyCustomTrait"), - buildInfoRenderFactory := ScalaFinalCaseObjectRenderer.apply, + buildInfoOptions ++= Seq(BuildInfoOption.Traits("traits.MyCustomTrait"), BuildInfoOption.ConstantValue), buildInfoPackage := "hello", - scalacOptions ++= Seq("-Ywarn-unused-import", "-Xfatal-warnings", "-Yno-imports"), - libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "1.0.5", + scalacOptions ++= Seq("-Xlint", "-Xfatal-warnings", "-Yno-imports"), + libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "1.3.0", check := { + val sv = scalaVersion.value val f = (sourceManaged in Compile).value / "sbt-buildinfo" / ("%s.scala" format "BuildInfo") val lines = scala.io.Source.fromFile(f).getLines.toList lines match { @@ -43,38 +42,41 @@ lazy val root = (project in file(".")). """/** This object was generated by sbt-buildinfo. */""" :: """case object BuildInfo extends traits.MyCustomTrait {""" :: """ /** The value is "helloworld". */""":: - """ final val name: String = "helloworld"""" :: + """ final val name = "helloworld"""" :: + """ /** The value is "0.1". */""":: + """ final val version = "0.1"""" :: """ /** The value is 0.1. */""":: """ final val projectVersion = 0.1""" :: - """ /** The value is "2.12.12". */""" :: - """ final val scalaVersion: String = "2.12.12"""" :: - """ /** The value is scala.collection.immutable.Seq(). */""" :: - """ final val ivyXML: scala.xml.NodeSeq = scala.collection.immutable.Seq()""" :: + scalaVersionInfoComment :: + scalaVersionInfo :: + """ /** The value is scala.xml.NodeSeq.Empty. */""" :: + """ val ivyXML: scala.xml.NodeSeq = scala.xml.NodeSeq.Empty""" :: """ /** The value is scala.Some(new java.net.URL("http://example.com")). */""" :: - """ final val homepage: scala.Option[java.net.URL] = scala.Some(new java.net.URL("http://example.com"))""" :: + """ val homepage: scala.Option[java.net.URL] = scala.Some(new java.net.URL("http://example.com"))""" :: """ /** The value is scala.collection.immutable.Seq(("MIT License" -> new java.net.URL("https://github.com/sbt/sbt-buildinfo/blob/master/LICENSE"))). */""" :: - """ final val licenses: scala.collection.immutable.Seq[(String, java.net.URL)] = scala.collection.immutable.Seq(("MIT License" -> new java.net.URL("https://github.com/sbt/sbt-buildinfo/blob/master/LICENSE")))""" :: + """ val licenses: scala.collection.immutable.Seq[(String, java.net.URL)] = scala.collection.immutable.Seq(("MIT License" -> new java.net.URL("https://github.com/sbt/sbt-buildinfo/blob/master/LICENSE")))""" :: """ /** The value is Map(). */""" :: - """ final val apiMappings: Map[java.io.File, java.net.URL] = Map()""" :: + """ val apiMappings: Map[java.io.File, java.net.URL] = Map()""" :: """ /** The value is false. */""" :: - """ final val isSnapshot: scala.Boolean = false""" :: + """ final val isSnapshot = false""" :: """ /** The value is 2012. */""" :: - """ final val year: scala.Int = 2012""" :: + """ final val year = 2012""" :: """ /** The value is scala.Symbol("Foo"). */""" :: - """ final val sym: scala.Symbol = scala.Symbol("Foo")""" :: + """ final val sym = scala.Symbol("Foo")""" :: """ /** The value is 1234L. */""" :: - """ final val buildTime: scala.Long = 1234L""" :: + """ final val buildTime = 1234L""" :: """ /** The value is scala.collection.immutable.Seq(new java.io.File("/tmp/f.txt")). */""" :: - """ final val someCp: scala.collection.immutable.Seq[java.io.File] = scala.collection.immutable.Seq(new java.io.File("/tmp/f.txt"))""" :: + """ val someCp: scala.collection.immutable.Seq[java.io.File] = scala.collection.immutable.Seq(new java.io.File("/tmp/f.txt"))""" :: targetInfoComment :: targetInfo :: // """ """ override val toString: String = {""" :: - """ "name: %s, projectVersion: %s, scalaVersion: %s, ivyXML: %s, homepage: %s, licenses: %s, apiMappings: %s, isSnapshot: %s, year: %s, sym: %s, buildTime: %s, someCp: %s, target: %s".format(""" :: - """ name, projectVersion, scalaVersion, ivyXML, homepage, licenses, apiMappings, isSnapshot, year, sym, buildTime, someCp, target""" :: + """ "name: %s, version: %s, projectVersion: %s, scalaVersion: %s, ivyXML: %s, homepage: %s, licenses: %s, apiMappings: %s, isSnapshot: %s, year: %s, sym: %s, buildTime: %s, someCp: %s, target: %s".format(""" :: + """ name, version, projectVersion, scalaVersion, ivyXML, homepage, licenses, apiMappings, isSnapshot, year, sym, buildTime, someCp, target""" :: """ )""" :: """ }""" :: """}""" :: - """// $COVERAGE-ON$""" :: Nil if targetInfo contains "target: java.io.File = new java.io.File(" => + """// $COVERAGE-ON$""" :: Nil if (targetInfo contains "target: java.io.File = new java.io.File(") && + (scalaVersionInfo.trim == s"""final val scalaVersion = "$sv"""") => () case _ => sys.error("unexpected output: \n" + lines.mkString("\n")) } () diff --git a/src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/project/plugins.sbt b/src/sbt-test/sbt-buildinfo/constantvalue/project/plugins.sbt similarity index 100% rename from src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/project/plugins.sbt rename to src/sbt-test/sbt-buildinfo/constantvalue/project/plugins.sbt diff --git a/src/sbt-test/sbt-buildinfo/constantvalue/src/main/scala/Test.scala b/src/sbt-test/sbt-buildinfo/constantvalue/src/main/scala/Test.scala new file mode 100644 index 0000000..8157fd9 --- /dev/null +++ b/src/sbt-test/sbt-buildinfo/constantvalue/src/main/scala/Test.scala @@ -0,0 +1,6 @@ +import scala.deprecated + +@deprecated("No longer used", hello.BuildInfo.version) +object Test { + +} diff --git a/src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/src/main/scala/traits/MyCustomTrait.scala b/src/sbt-test/sbt-buildinfo/constantvalue/src/main/scala/traits/MyCustomTrait.scala similarity index 100% rename from src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/src/main/scala/traits/MyCustomTrait.scala rename to src/sbt-test/sbt-buildinfo/constantvalue/src/main/scala/traits/MyCustomTrait.scala diff --git a/src/sbt-test/sbt-buildinfo/constantvalue/test b/src/sbt-test/sbt-buildinfo/constantvalue/test new file mode 100644 index 0000000..4ed98c4 --- /dev/null +++ b/src/sbt-test/sbt-buildinfo/constantvalue/test @@ -0,0 +1,4 @@ +> compile +$ exists target/scala-2.13/src_managed/main/sbt-buildinfo/BuildInfo.scala + +> check diff --git a/src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/test b/src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/test deleted file mode 100644 index dd586f3..0000000 --- a/src/sbt-test/sbt-buildinfo/finalcaseobjectrenderer/test +++ /dev/null @@ -1,4 +0,0 @@ -> compile -$ exists target/scala-2.12/src_managed/main/sbt-buildinfo/BuildInfo.scala - -> check diff --git a/src/sbt-test/sbt-buildinfo/simple/build.sbt b/src/sbt-test/sbt-buildinfo/simple/build.sbt index 3610b98..d979db6 100644 --- a/src/sbt-test/sbt-buildinfo/simple/build.sbt +++ b/src/sbt-test/sbt-buildinfo/simple/build.sbt @@ -46,8 +46,8 @@ lazy val root = (project in file(".")) """ val projectVersion = 0.1""" :: scalaVersionInfoComment :: scalaVersionInfo :: - """ /** The value is scala.collection.immutable.Seq(). */""" :: - """ val ivyXML: scala.xml.NodeSeq = scala.collection.immutable.Seq()""" :: + """ /** The value is scala.xml.NodeSeq.Empty. */""" :: + """ val ivyXML: scala.xml.NodeSeq = scala.xml.NodeSeq.Empty""" :: """ /** The value is scala.Some(new java.net.URL("http://example.com")). */""" :: """ val homepage: scala.Option[java.net.URL] = scala.Some(new java.net.URL("http://example.com"))""" :: """ /** The value is scala.collection.immutable.Seq(("MIT License" -> new java.net.URL("https://github.com/sbt/sbt-buildinfo/blob/master/LICENSE"))). */""" :: diff --git a/src/sbt-test/sbt-buildinfo/usepackageaspath/build.sbt b/src/sbt-test/sbt-buildinfo/usepackageaspath/build.sbt index d7e1e4e..529ea78 100644 --- a/src/sbt-test/sbt-buildinfo/usepackageaspath/build.sbt +++ b/src/sbt-test/sbt-buildinfo/usepackageaspath/build.sbt @@ -44,8 +44,8 @@ lazy val root = (project in file(".")) """ val projectVersion = 0.1""" :: """ /** The value is "2.12.12". */""" :: """ val scalaVersion: String = "2.12.12"""" :: - """ /** The value is scala.collection.immutable.Seq(). */""" :: - """ val ivyXML: scala.xml.NodeSeq = scala.collection.immutable.Seq()""" :: + """ /** The value is scala.xml.NodeSeq.Empty. */""" :: + """ val ivyXML: scala.xml.NodeSeq = scala.xml.NodeSeq.Empty""" :: """ /** The value is scala.Some(new java.net.URL("http://example.com")). */""" :: """ val homepage: scala.Option[java.net.URL] = scala.Some(new java.net.URL("http://example.com"))""" :: """ /** The value is scala.collection.immutable.Seq(("MIT License" -> new java.net.URL("https://github.com/sbt/sbt-buildinfo/blob/master/LICENSE"))). */""" ::