From 7d17af47a677d764e054ce3aa673b0a438eae112 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 23 Feb 2018 13:56:39 +0000 Subject: [PATCH 1/4] Implicit convert TaskKey with the macro! When discussing the design of new in task graph execution, in #114, we completely didn't think of backing the existing TaskKey[A] to BuildInfoKey.Entry[A] implicit conversion with the macro! Fixes #117 --- src/main/scala/sbtbuildinfo/package.scala | 17 +++++++++------ src/sbt-test/sbt-buildinfo/simple/build.sbt | 2 +- src/sbt-test/sbt-buildinfo/task/build.sbt | 2 +- .../sbt-buildinfo/usepackageaspath/build.sbt | 2 +- .../scala/sbtbuildinfo/BuildInfoKeySpec.scala | 21 +++++++++++-------- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/main/scala/sbtbuildinfo/package.scala b/src/main/scala/sbtbuildinfo/package.scala index 4bd9f7f..0470557 100644 --- a/src/main/scala/sbtbuildinfo/package.scala +++ b/src/main/scala/sbtbuildinfo/package.scala @@ -4,21 +4,19 @@ package object sbtbuildinfo { type BuildInfoKey = BuildInfoKey.Entry[_] object BuildInfoKey { implicit def setting[A](key: SettingKey[A]): Entry[A] = Setting(key) - @deprecated("Explicitly wrap in BuildInfoKey.of/ofN. Or if out-of-graph execution is required use BuildInfoKey.outOfGraphUnsafe", "0.7.1") - implicit def task[A](key: TaskKey[A]): Entry[A] = Task(key) + implicit def task[A](key: TaskKey[A]): Entry[A] = macro BuildInfoKeyMacros.taskImpl implicit def taskValue[A: Manifest](task: sbt.Task[A]): Entry[A] = TaskValue(task) implicit def constant[A: Manifest](tuple: (String, A)): Entry[A] = Constant(tuple) def apply[A](key: SettingKey[A]): Entry[A] = Setting(key) - @deprecated("Explicitly wrap in BuildInfoKey.of/ofN. Or if out-of-graph execution is required use BuildInfoKey.outOfGraphUnsafe", "0.7.1") - def apply[A](key: TaskKey[A]): Entry[A] = Task(key) + def apply[A](key: TaskKey[A]): Entry[A] = macro BuildInfoKeyMacros.taskImpl def apply[A: Manifest](tuple: (String, A)): Entry[A] = Constant(tuple) def map[A, B: Manifest](from: Entry[A])(fun: ((String, A)) => (String, B)): Entry[B] = BuildInfoKey.Mapped(from, fun) def action[A: Manifest](name: String)(fun: => A): Entry[A] = Action(name, () => fun) - def of(x: Any): BuildInfoKey = macro BuildInfoKeyMacros.ofImpl - def ofN(xs: Any*): Seq[BuildInfoKey] = macro BuildInfoKeyMacros.ofNImpl + def of[A](x: BuildInfoKey.Entry[A]): BuildInfoKey.Entry[A] = x + def ofN(xs: BuildInfoKey*): Seq[BuildInfoKey] = xs def outOfGraphUnsafe[A](key: TaskKey[A]): Entry[A] = Task(key) @@ -54,6 +52,12 @@ package object sbtbuildinfo { val BuildInfoKey = q"_root_.sbtbuildinfo.BuildInfoKey" + def taskImpl(key: Tree): Tree = { + val A = key.tpe.typeArgs.head + q"$BuildInfoKey.taskValue[$A]($key.taskValue)($key.key.manifest.typeArguments.head.asInstanceOf[Manifest[$A]])" + } + + @deprecated("No longer used", "0.8.1") def ofImpl(x: Tree): Tree = { x.tpe match { case tpe if tpe <:< typeOf[SettingKey[_]] => @@ -72,6 +76,7 @@ package object sbtbuildinfo { } } + @deprecated("No longer used", "0.8.1") def ofNImpl(xs: Tree*): Tree = q"_root_.scala.Seq(..${xs map ofImpl})" } diff --git a/src/sbt-test/sbt-buildinfo/simple/build.sbt b/src/sbt-test/sbt-buildinfo/simple/build.sbt index def2e76..1c66cb7 100644 --- a/src/sbt-test/sbt-buildinfo/simple/build.sbt +++ b/src/sbt-test/sbt-buildinfo/simple/build.sbt @@ -38,7 +38,7 @@ lazy val root = (project in file(".")). """ /** The value is "helloworld". */""":: """ val name: String = "helloworld"""" :: """ /** The value is 0.1. */""":: - """ val projectVersion: scala.Double = 0.1""" :: + """ val projectVersion = 0.1""" :: """ /** The value is "2.11.8". */""" :: """ val scalaVersion: String = "2.11.8"""" :: """ /** The value is scala.collection.Seq(). */""" :: diff --git a/src/sbt-test/sbt-buildinfo/task/build.sbt b/src/sbt-test/sbt-buildinfo/task/build.sbt index 40271f9..d601354 100644 --- a/src/sbt-test/sbt-buildinfo/task/build.sbt +++ b/src/sbt-test/sbt-buildinfo/task/build.sbt @@ -9,7 +9,7 @@ val projOutOfTaskGraph1 = project settings ( val projOutOfTaskGraph2 = project dependsOn projOutOfTaskGraph1 settings ( BuildInfoPlugin.buildInfoDefaultSettings, addBuildInfoToConfig(Test), - buildInfoKeys in Test += fullClasspath in Compile // intentionally uses the deprecated implicit conversion + buildInfoKeys in Test += BuildInfoKey.outOfGraphUnsafe(fullClasspath in Compile), ) val projInTaskGraph1 = project settings ( diff --git a/src/sbt-test/sbt-buildinfo/usepackageaspath/build.sbt b/src/sbt-test/sbt-buildinfo/usepackageaspath/build.sbt index 15672b8..f05a0bd 100644 --- a/src/sbt-test/sbt-buildinfo/usepackageaspath/build.sbt +++ b/src/sbt-test/sbt-buildinfo/usepackageaspath/build.sbt @@ -37,7 +37,7 @@ lazy val root = (project in file(".")). """ /** The value is "helloworld". */""":: """ val name: String = "helloworld"""" :: """ /** The value is 0.1. */""":: - """ val projectVersion: scala.Double = 0.1""" :: + """ val projectVersion = 0.1""" :: """ /** The value is "2.10.2". */""" :: """ val scalaVersion: String = "2.10.2"""" :: """ /** The value is scala.collection.Seq(). */""" :: diff --git a/src/test/scala/sbtbuildinfo/BuildInfoKeySpec.scala b/src/test/scala/sbtbuildinfo/BuildInfoKeySpec.scala index 92237e1..99f48b0 100644 --- a/src/test/scala/sbtbuildinfo/BuildInfoKeySpec.scala +++ b/src/test/scala/sbtbuildinfo/BuildInfoKeySpec.scala @@ -3,10 +3,8 @@ package sbtbuildinfo import sbt._, Keys._ import BuildInfoPlugin.autoImport._ +/** This is a compile-only test of the BuildInfoKey syntax/macros. */ object BuildInfoKeySpec { - // duplicate the out of box implicit so we don't get deprecation warnings in this testing code - implicit def task[A](key: TaskKey[A]): BuildInfoKey.Entry[A] = BuildInfoKey.Task(key) - buildInfoKeys := Seq(name, version) // test `:=` works with setting keys buildInfoKeys := Seq(products, fullClasspath) // test `:=` works with task keys buildInfoKeys := Seq(name, fullClasspath) // test `:=` works with setting and task keys @@ -15,17 +13,19 @@ object BuildInfoKeySpec { fullClasspath, "year" -> 2012, BuildInfoKey.action("buildTime") { 1234L }, - BuildInfoKey.map(version) { case (_, v) => "projectVersion" -> v.toDouble } + BuildInfoKey.map(version) { case (_, v) => "projectVersion" -> v.toDouble }, + BuildInfoKey.map(fullClasspath) { case (ident, cp) => ident -> cp.files }, ) buildInfoKeys += name // test `+=` works with a setting key - buildInfoKeys += fullClasspath // test `+=` works with a task key +//buildInfoKeys += fullClasspath // test `+=` works with a task key + buildInfoKeys += (fullClasspath: BuildInfoKey) // test `+=` works with a task key buildInfoKeys += "year" -> 2012 // test `+=` works with constants buildInfoKeys += BuildInfoKey.action("buildTime") { 1234L } // test `+=` works with BuildInfoKey's buildInfoKeys += BuildInfoKey.map(version) { case (_, v) => "projectVersion" -> v.toDouble } buildInfoKeys ++= Seq(name, version) // test `++=` works with setting keys - buildInfoKeys ++= Seq(fullClasspath) // test `++=` works with 1 task key + buildInfoKeys ++= Seq[BuildInfoKey](fullClasspath) // test `++=` works with 1 task key buildInfoKeys ++= Seq[BuildInfoKey](products, fullClasspath) // test `++=` works with n task keys buildInfoKeys ++= Seq[BuildInfoKey](name, fullClasspath) // test `++=` works with setting and task keys buildInfoKeys ++= Seq[BuildInfoKey]( // test `++=` works with misc things @@ -33,7 +33,8 @@ object BuildInfoKeySpec { fullClasspath, "year" -> 2012, BuildInfoKey.action("buildTime") { 1234L }, - BuildInfoKey.map(version) { case (_, v) => "projectVersion" -> v.toDouble } + BuildInfoKey.map(version) { case (_, v) => "projectVersion" -> v.toDouble }, + BuildInfoKey.map(fullClasspath) { case (ident, cp) => ident -> cp.files }, ) @@ -45,7 +46,8 @@ object BuildInfoKeySpec { fullClasspath, "year" -> 2012, BuildInfoKey.action("buildTime") { 1234L }, - BuildInfoKey.map(version) { case (_, v) => "projectVersion" -> v.toDouble } + BuildInfoKey.map(version) { case (_, v) => "projectVersion" -> v.toDouble }, + BuildInfoKey.map(fullClasspath) { case (ident, cp) => ident -> cp.files }, ) buildInfoKeys += BuildInfoKey.of(name) // test `+=` works with a setting key @@ -60,6 +62,7 @@ object BuildInfoKeySpec { fullClasspath, "year" -> 2012, BuildInfoKey.action("buildTime") { 1234L }, - BuildInfoKey.map(version) { case (_, v) => "projectVersion" -> v.toDouble } + BuildInfoKey.map(version) { case (_, v) => "projectVersion" -> v.toDouble }, + BuildInfoKey.map(fullClasspath) { case (ident, cp) => ident -> cp.files }, ) } From ba0546fd6249741fb19017f8ea6b4a5aae635af3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 28 Feb 2018 10:35:48 +0000 Subject: [PATCH 2/4] Bump to 0.9.0 --- build.sbt | 2 +- src/main/scala/sbtbuildinfo/package.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 8756dff..c922a6c 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,5 @@ lazy val commonSettings: Seq[Setting[_]] = Seq( - git.baseVersion in ThisBuild := "0.8.0", + git.baseVersion in ThisBuild := "0.9.0", organization in ThisBuild := "com.eed3si9n" ) diff --git a/src/main/scala/sbtbuildinfo/package.scala b/src/main/scala/sbtbuildinfo/package.scala index 0470557..d8e3d11 100644 --- a/src/main/scala/sbtbuildinfo/package.scala +++ b/src/main/scala/sbtbuildinfo/package.scala @@ -57,7 +57,7 @@ package object sbtbuildinfo { q"$BuildInfoKey.taskValue[$A]($key.taskValue)($key.key.manifest.typeArguments.head.asInstanceOf[Manifest[$A]])" } - @deprecated("No longer used", "0.8.1") + @deprecated("No longer used", "0.9.0") def ofImpl(x: Tree): Tree = { x.tpe match { case tpe if tpe <:< typeOf[SettingKey[_]] => @@ -76,7 +76,7 @@ package object sbtbuildinfo { } } - @deprecated("No longer used", "0.8.1") + @deprecated("No longer used", "0.9.0") def ofNImpl(xs: Tree*): Tree = q"_root_.scala.Seq(..${xs map ofImpl})" } From ef77b0754f5509e601b116a9f00cd13fec458f20 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 28 Feb 2018 17:47:15 +0000 Subject: [PATCH 3/4] Drop usage of BuildInfoKey.ofN in the README --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index ad76084..9f559de 100644 --- a/README.markdown +++ b/README.markdown @@ -28,7 +28,7 @@ Add the following in your `build.sbt`: lazy val root = (project in file(".")). enablePlugins(BuildInfoPlugin). settings( - buildInfoKeys := BuildInfoKey.ofN(name, version, scalaVersion, sbtVersion), + buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion), buildInfoPackage := "hello" ) ``` From c47a72b2728a73458cb24d1f79c54b32edd92c3c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Mar 2018 14:46:54 +0000 Subject: [PATCH 4/4] Add 0.9.0 notes --- notes/0.9.0.markdown | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 notes/0.9.0.markdown diff --git a/notes/0.9.0.markdown b/notes/0.9.0.markdown new file mode 100644 index 0000000..9afe3a6 --- /dev/null +++ b/notes/0.9.0.markdown @@ -0,0 +1,44 @@ +[@dwijnand]: https://github.com/dwijnand + +sbt-buildinfo 0.9.0 is published for sbt 1. + +### `TaskKey` to `BuildInfoKey` conversion potentially breaking semantic change + +*TL;DR* No need for `BuildInfoKey.of(...)` or `BuildInfoKey.ofN(...)` any more. Use +`BuildInfoKey.outOfGraphUnsafe` if your build definition is now circular. + +sbt-buildinfo 0.8.0 deprecated the original `TaskKey[A]` to `BuildInfoKey.Entry[A]` implicit and explicit +conversions (`BuildInfoKey.task` and `BuildInfoKey.apply` respectively), that executed the underlying sbt Task +out of sbt's task graph execution, in favour of a newly introduced `BuildInfoKey.of(...)` and +`BuildInfoKey.ofN(...)` API, which correctly wired up the task graph. See [#114][]. + +As it was implemented (and released) it interacted poorly with sbt-buildinfo's `BuildInfoKey.map` API +([#117][]), due to a mistake in the implementation and test coverage. + +In resolving the issue it became clear that instead of introducing a new API, that required sbt-buildinfo users +to change their source code to use, the already used conversions could have been modified to use the new +Task-based semantics. + +However, this change breaks any build definition that declares as a build info key any `TaskKey` that depends on +`sourceGenerators` or `resourceGenerators`, because the build definiton would now be circular and fail to load. +To fix this breaking semantic change the user has to either drop the key used, choose another key, or fallback +to the previous semantics by using the not-deprecated `BuildInfoKey.outOfGraphUnsafe` API, also introduced in +sbt-buildinfo 0.8.0. + +[#117][]/[#119][] by [@dwijnand][] + +[#114]: https://github.com/sbt/sbt-buildinfo/pull/114 +[#117]: https://github.com/sbt/sbt-buildinfo/issues/117 +[#119]: https://github.com/sbt/sbt-buildinfo/pull/119 + +### Add direct support for sbt's `Attributed` + +A number of keys defined by sbt use sbt's `Attributed` type, specifically the keys that define classpaths. +Prior to this change defining any of these keys as a build info key would generate `Seq[String]` as `Attributed` +would be simply converted to string with `toString`. sbt-buildinfo 0.9.0 introduces direct support for these +keys so they generate `Seq[File]` instead. + +[#112][]/[#120][] by [@dwijnand][] + +[#112]: https://github.com/sbt/sbt-buildinfo/issues/112 +[#120]: https://github.com/sbt/sbt-buildinfo/pull/120