From 2a6385fd941c0b95ace351648522ac641aa64874 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 6 Oct 2017 15:47:46 -0400 Subject: [PATCH 01/12] Remove thunk for slash syntax Ref #3606, #3611, and #3613 This removes unnecessary thunk for slash syntax. The semantics using this approach is strictly better than the previous `in (ref, config, task)`. By removing the thunk, we retain `(a / b) / c == a / b / c`. See the following example: ```scala scala> import sbt._, Keys._ scala> val t: TaskKey[Unit] = (test in Test) t: sbt.TaskKey[Unit] = TaskKey(This / Select(ConfigKey(test)) / This / test) scala> ThisBuild / t ThisBuild / t res1: sbt.TaskKey[Unit] = TaskKey(Select(ThisBuild) / Select(ConfigKey(test)) / This / test) scala> ThisBuild / t / name ThisBuild / t / name res2: sbt.SettingKey[String] = SettingKey(Select(ThisBuild) / Select(ConfigKey(test)) / Select(test) / name) ``` so far so good? Now look at this: ``` scala> scala> name in (ThisBuild, t) name in (ThisBuild, t) res3: sbt.SettingKey[String] = SettingKey(Select(ThisBuild) / This / Select(test) / name) ``` `Test` configuration knowledge is lost! For `in (..)` maybe it was ok because mostly we don't use unscoped keys, but that's the difference between `in (..)` and `/`. Fixes #3605 --- .../src/main/scala/sbt/SlashSyntax.scala | 70 +++---- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 193 +++++++++--------- sbt/src/sbt-test/project/unified/build.sbt | 21 ++ 3 files changed, 147 insertions(+), 137 deletions(-) diff --git a/main-settings/src/main/scala/sbt/SlashSyntax.scala b/main-settings/src/main/scala/sbt/SlashSyntax.scala index fc98cfc39..9b1898a05 100644 --- a/main-settings/src/main/scala/sbt/SlashSyntax.scala +++ b/main-settings/src/main/scala/sbt/SlashSyntax.scala @@ -39,23 +39,31 @@ trait SlashSyntax { implicit def sbtSlashSyntaxRichConfigKey(c: ConfigKey): RichConfiguration = new RichConfiguration(Scope(This, Select(c), This, This)) - - implicit def sbtSlashSyntaxRichConfiguration(c: Configuration): RichConfiguration = (c: ConfigKey) - - implicit def sbtSlashSyntaxRichScopeFromScoped(t: Scoped): RichScope = - new RichScope(Scope(This, This, Select(t.key), This)) + implicit def sbtSlashSyntaxRichConfiguration(c: Configuration): RichConfiguration = + sbtSlashSyntaxRichConfigKey(c: ConfigKey) implicit def sbtSlashSyntaxRichScope(s: Scope): RichScope = new RichScope(s) - implicit def sbtSlashSyntaxScopeAndKeyRescope(scopeAndKey: ScopeAndKey[_]): TerminalScope = - scopeAndKey.rescope + /** + * This handles task scoping an existing scoped key (such as `Compile / test`) + * into a task scoping in `(Compile / test) / name`. + */ + implicit def sbtSlashSyntaxRichScopeFromScoped(t: Scoped): RichScope = + new RichScope(t.scope.copy(task = Select(t.key))) - implicit def sbtSlashSyntaxScopeAndKeyMaterialize[K <: Key[K]](scopeAndKey: ScopeAndKey[K]): K = - scopeAndKey.materialize + implicit val sbtSlashSyntaxSettingKeyCanScope: CanScope[SettingKey] = new SettingKeyCanScope() + implicit val sbtSlashSyntaxTaskKeyCanScope: CanScope[TaskKey] = new TaskKeyCanScope() + implicit val sbtSlashSyntaxInputKeyCanScope: CanScope[InputKey] = new InputKeyCanScope() } object SlashSyntax { + /** RichScopeLike wraps a general scope to provide the `/` operator for key scoping. */ + sealed trait RichScopeLike { + protected def scope: Scope + def /[A, F[_]: CanScope](key: F[A]): F[A] = implicitly[CanScope[F]].inScope(key, scope) + } + /** RichReference wraps a reference to provide the `/` operator for scoping. */ final class RichReference(protected val scope: Scope) extends RichScopeLike { def /(c: ConfigKey): RichConfiguration = new RichConfiguration(scope in c) @@ -68,46 +76,28 @@ object SlashSyntax { /** RichConfiguration wraps a configuration to provide the `/` operator for scoping. */ final class RichConfiguration(protected val scope: Scope) extends RichScopeLike { - // This is for handling `Zero / Zero / Zero / name`. def /(taskAxis: ScopeAxis[AttributeKey[_]]): RichScope = new RichScope(scope.copy(task = taskAxis)) } - /** Both `Scoped.ScopingSetting` and `Scoped` are parents of `SettingKey`, `TaskKey` and - * `InputKey`. We'll need both, so this is a convenient type alias. */ - type Key[K] = Scoped.ScopingSetting[K] with Scoped - - sealed trait RichScopeLike { - protected def scope: Scope - - // We don't know what the key is for yet, so just capture for now. - def /[K <: Key[K]](key: K): ScopeAndKey[K] = new ScopeAndKey(scope, key) - } - /** RichScope wraps a general scope to provide the `/` operator for scoping. */ - final class RichScope(protected val scope: Scope) extends RichScopeLike - - /** TerminalScope provides the last `/` for scoping. */ - final class TerminalScope(scope: Scope) { - def /[K <: Key[K]](key: K): K = key in scope - } + final class RichScope(protected val scope: Scope) extends RichScopeLike {} /** - * ScopeAndKey is a synthetic DSL construct necessary to capture both the built-up scope with a - * given key, while we're not sure if the given key is terminal or task-scoping. The "materialize" - * method will be used if it's terminal, returning the scoped key, while "rescope" will be used - * if we're task-scoping. - * - * @param scope the built-up scope - * @param key a given key - * @tparam K the type of the given key, necessary to type "materialize" + * A typeclass that represents scoping. */ - final class ScopeAndKey[K <: Key[K]](scope: Scope, key: K) { - private[sbt] def materialize: K = key in scope - private[sbt] def rescope: TerminalScope = new TerminalScope(scope in key.key) - - override def toString: String = s"$scope / ${key.key}" + sealed trait CanScope[F[A]] { + def inScope[A](key: F[A], scope: Scope): F[A] } + final class SettingKeyCanScope extends CanScope[SettingKey] { + def inScope[A](key: SettingKey[A], scope: Scope): SettingKey[A] = key in scope + } + final class TaskKeyCanScope extends CanScope[TaskKey] { + def inScope[A](key: TaskKey[A], scope: Scope): TaskKey[A] = key in scope + } + final class InputKeyCanScope extends CanScope[InputKey] { + def inScope[A](key: InputKey[A], scope: Scope): InputKey[A] = key in scope + } } diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 8432a3f54..34f6b55d2 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -11,7 +11,7 @@ import org.scalacheck.{ Test => _, _ }, Arbitrary.arbitrary, Gen._, Prop._ import java.io.File import sbt.io.IO -import sbt.SlashSyntax, SlashSyntax.Key +import sbt.SlashSyntax import sbt.{ Scope, ScopeAxis, Scoped, Select, This, Zero }, Scope.{ Global, ThisScope } import sbt.{ BuildRef, LocalProject, LocalRootProject, ProjectRef, Reference, RootProject, ThisBuild, ThisProject } import sbt.ConfigKey @@ -63,21 +63,26 @@ object BuildDSLInstances { 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) )) - implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = - withScope(Gen.identifier map (InputKey[A](_))) + object WithScope { + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = + withScope(Gen.identifier map (InputKey[A](_))) - implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = - withScope(Gen.identifier map (SettingKey[A](_))) + implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = + withScope(Gen.identifier map (SettingKey[A](_))) - implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = - withScope(Gen.identifier map (TaskKey[A](_))) + implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = + withScope(Gen.identifier map (TaskKey[A](_))) + } - implicit def arbKey[A: Manifest]: Arbitrary[Key[_]] = Arbitrary { - Gen.frequency[Key[_]]( - 15 -> arbitrary[InputKey[A]], // 15,431 - 20 -> arbitrary[SettingKey[A]], // 19,645 - 23 -> arbitrary[TaskKey[A]], // 22,867 - ) + object WithoutScope { + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = + Arbitrary(Gen.identifier map (InputKey[A](_))) + + implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = + Arbitrary(Gen.identifier map (SettingKey[A](_))) + + implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = + Arbitrary(Gen.identifier map (TaskKey[A](_))) } implicit def arbScopeAxis[A: Arbitrary]: Arbitrary[ScopeAxis[A]] = @@ -134,111 +139,105 @@ import CustomEquality._ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("Global / key == key in Global") = { - def check[K <: Key[K]: Arbitrary] = forAll((k: K) => expectValue(k in Global)(Global / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + import WithScope._ + (forAll { (k: SettingKey[String]) => expectValue(k in Global)(Global / k) } + && forAll { (k: TaskKey[String]) => expectValue(k in Global)(Global / k) } + && forAll { (k: InputKey[String]) => expectValue(k in Global)(Global / k) }) } + property("Reference / key == key in Reference") = { - def check[K <: Key[K]: Arbitrary] = forAll((r: Reference, k: K) => expectValue(k in r)(r / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + import WithScope._ + (forAll { (r: Reference, k: SettingKey[String]) => expectValue(k in r)(r / k) } + && forAll { (r: Reference, k: TaskKey[String]) => expectValue(k in r)(r / k) } + && forAll { (r: Reference, k: InputKey[String]) => expectValue(k in r)(r / k) }) } + property("Reference / Config / key == key in Reference in Config") = { - def check[K <: Key[K]: Arbitrary] = - forAll((r: Reference, c: ConfigKey, k: K) => expectValue(k in r in c)(r / c / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + import WithScope._ + (forAll { (r: Reference, c: ConfigKey, k: SettingKey[String]) => expectValue(k in r in c)(r / c / k) } + && forAll { (r: Reference, c: ConfigKey, k: TaskKey[String]) => expectValue(k in r in c)(r / c / k) } + && forAll { (r: Reference, c: ConfigKey, k: InputKey[String]) => expectValue(k in r in c)(r / c / k) }) } + property("Reference / task / key == key in Reference in task") = { - def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((r: Reference, t: K, k: K) => expectValue(k in (r, t))(r / t / k)) - (true - && check[InputKey[String], InputKey[String]] - && check[InputKey[String], SettingKey[String]] - && check[InputKey[String], TaskKey[String]] - && check[SettingKey[String], InputKey[String]] - && check[SettingKey[String], SettingKey[String]] - && check[SettingKey[String], TaskKey[String]] - && check[TaskKey[String], InputKey[String]] - && check[TaskKey[String], SettingKey[String]] - && check[TaskKey[String], TaskKey[String]] - ) + import WithoutScope._ + (forAll { (r: Reference, t: TaskKey[String], k: SettingKey[String]) => expectValue(k in (r, t))(r / t / k) } + && forAll { (r: Reference, t: TaskKey[String], k: TaskKey[String]) => expectValue(k in (r, t))(r / t / k) } + && forAll { (r: Reference, t: TaskKey[String], k: InputKey[String]) => expectValue(k in (r, t))(r / t / k) }) } + property("Reference / Config / task / key == key in Reference in Config in task") = { - def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((r: Reference, c: ConfigKey, t: K, k: K) => expectValue(k in (r, c, t))(r / c / t / k)) - (true - && check[InputKey[String], InputKey[String]] - && check[InputKey[String], SettingKey[String]] - && check[InputKey[String], TaskKey[String]] - && check[SettingKey[String], InputKey[String]] - && check[SettingKey[String], SettingKey[String]] - && check[SettingKey[String], TaskKey[String]] - && check[TaskKey[String], InputKey[String]] - && check[TaskKey[String], SettingKey[String]] - && check[TaskKey[String], TaskKey[String]] - ) + import WithoutScope._ + (forAll { (r: Reference, c: ConfigKey, t: TaskKey[String], k: SettingKey[String]) => expectValue(k in (r, c, t))(r / c / t / k) } + && forAll { (r: Reference, c: ConfigKey, t: TaskKey[String], k: TaskKey[String]) => expectValue(k in (r, c, t))(r / c / t / k) } + && forAll { (r: Reference, c: ConfigKey, t: TaskKey[String], k: InputKey[String]) => expectValue(k in (r, c, t))(r / c / t / k) }) } + property("Config / key == key in Config") = { - def check[K <: Key[K]: Arbitrary] = - forAll((c: ConfigKey, k: K) => expectValue(k in c)(c / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + import WithScope._ + (forAll { (c: ConfigKey, k: SettingKey[String]) => expectValue(k in c)(c / k) } + && forAll { (c: ConfigKey, k: TaskKey[String]) => expectValue(k in c)(c / k) } + && forAll { (c: ConfigKey, k: InputKey[String]) => expectValue(k in c)(c / k) }) } + property("Config / task / key == key in Config in task") = { - def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((c: ConfigKey, t: K, k: K) => expectValue(k in c in t)(c / t / k)) - (true - && check[InputKey[String], InputKey[String]] - && check[InputKey[String], SettingKey[String]] - && check[InputKey[String], TaskKey[String]] - && check[SettingKey[String], InputKey[String]] - && check[SettingKey[String], SettingKey[String]] - && check[SettingKey[String], TaskKey[String]] - && check[TaskKey[String], InputKey[String]] - && check[TaskKey[String], SettingKey[String]] - && check[TaskKey[String], TaskKey[String]] - ) + import WithoutScope._ + (forAll { (c: ConfigKey, t: TaskKey[String], k: SettingKey[String]) => expectValue(k in c in t)(c / t / k) } + && forAll { (c: ConfigKey, t: TaskKey[String], k: TaskKey[String]) => expectValue(k in c in t)(c / t / k) } + && forAll { (c: ConfigKey, t: TaskKey[String], k: InputKey[String]) => expectValue(k in c in t)(c / t / k) }) } + property("task / key == key in task") = { - def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((t: K, k: K) => expectValue(k in t)(t / k)) - (true - && check[InputKey[String], InputKey[String]] - && check[InputKey[String], SettingKey[String]] - && check[InputKey[String], TaskKey[String]] - && check[SettingKey[String], InputKey[String]] - && check[SettingKey[String], SettingKey[String]] - && check[SettingKey[String], TaskKey[String]] - && check[TaskKey[String], InputKey[String]] - && check[TaskKey[String], SettingKey[String]] - && check[TaskKey[String], TaskKey[String]] - ) + import WithoutScope._ + (forAll { (t: TaskKey[String], k: SettingKey[String]) => expectValue(k in t)(t / k) } + && forAll { (t: TaskKey[String], k: TaskKey[String]) => expectValue(k in t)(t / k) } + && forAll { (t: TaskKey[String], k: InputKey[String]) => expectValue(k in t)(t / k) }) } + property("Scope / key == key in Scope") = { - def check[K <: Key[K]: Arbitrary] = forAll((s: Scope, k: K) => expectValue(k in s)(s / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + import WithScope._ + (forAll { (s: Scope, k: SettingKey[String]) => expectValue(k in s)(s / k) } + && forAll { (s: Scope, k: TaskKey[String]) => expectValue(k in s)(s / k) } + && forAll { (s: Scope, k: InputKey[String]) => expectValue(k in s)(s / k) }) } + property("Reference? / key == key in ThisScope.copy(..)") = { - def check[K <: Key[K]: Arbitrary] = - forAll((r: ScopeAxis[Reference], k: K) => - expectValue(k in ThisScope.copy(project = r))(r / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + import WithScope._ + (forAll { (r: ScopeAxis[Reference], k: SettingKey[String]) => + expectValue(k in ThisScope.copy(project = r))(r / k) } && + forAll { (r: ScopeAxis[Reference], k: TaskKey[String]) => + expectValue(k in ThisScope.copy(project = r))(r / k) } && + forAll { (r: ScopeAxis[Reference], k: InputKey[String]) => + expectValue(k in ThisScope.copy(project = r))(r / k) }) } + property("Reference? / ConfigKey? / key == key in ThisScope.copy(..)") = { - def check[K <: Key[K]: Arbitrary] = - forAll((r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: K) => - expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + import WithScope._ + (forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: SettingKey[String]) => + expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k) } && + forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: TaskKey[String]) => + expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k) } && + forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: InputKey[String]) => + expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k) }) } -// property("Reference? / AttributeKey? / key == key in ThisScope.copy(..)") = { -// def check[K <: Key[K]: Arbitrary] = -// forAll( -// (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: K) => -// expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k)) -// check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] -// } + + // property("Reference? / AttributeKey? / key == key in ThisScope.copy(..)") = { + // import WithScope._ + // (forAll { (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: SettingKey[String]) => + // expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k) } && + // forAll { (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: TaskKey[String]) => + // expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k) } && + // forAll { (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: InputKey[String]) => + // expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k) } + // } + property("Reference? / ConfigKey? / AttributeKey? / key == key in ThisScope.copy(..)") = { - def check[K <: Key[K]: Arbitrary] = - forAll( - (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: K) => - expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + import WithScope._ + (forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: SettingKey[String]) => + expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k) } && + forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: TaskKey[String]) => + expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k) } && + forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: InputKey[String]) => + expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k) }) } } diff --git a/sbt/src/sbt-test/project/unified/build.sbt b/sbt/src/sbt-test/project/unified/build.sbt index 91f772197..7f456b66c 100644 --- a/sbt/src/sbt-test/project/unified/build.sbt +++ b/sbt/src/sbt-test/project/unified/build.sbt @@ -1,8 +1,14 @@ import sbt.internal.CommandStrings.{ inspectBrief, inspectDetailed } import sbt.internal.Inspect +import sjsonnew._, BasicJsonProtocol._ val uTest = "com.lihaoyi" %% "utest" % "0.5.3" +val foo = taskKey[Int]("") +val bar = taskKey[Int]("") +val baz = inputKey[Unit]("") +val buildInfo = taskKey[Seq[File]]("The task that generates the build info.") + lazy val root = (project in file(".")) .settings( Global / cancelable := true, @@ -10,9 +16,24 @@ lazy val root = (project in file(".")) console / scalacOptions += "-deprecation", Compile / console / scalacOptions += "-Ywarn-numeric-widen", projA / Compile / console / scalacOptions += "-feature", + Zero / name := "foo", Zero / Zero / name := "foo", Zero / Zero / Zero / name := "foo", + Test / bar := 1, + Test / foo := (Test / bar).value + 1, + Compile / foo := { + (Compile / bar).previous.getOrElse(1) + }, + Compile / bar := { + (Compile / foo).previous.getOrElse(2) + }, + Test / buildInfo := Nil, + baz := { + val x = (Test / buildInfo).taskValue + (Compile / run).evaluated + }, + libraryDependencies += uTest % Test, testFrameworks += new TestFramework("utest.runner.Framework"), From e5898111fedca21be2088804ad891d0ac37c2207 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:07:19 +0100 Subject: [PATCH 02/12] Replace CanScope with Scoped.ScopingSetting --- .../src/main/scala/sbt/SlashSyntax.scala | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/main-settings/src/main/scala/sbt/SlashSyntax.scala b/main-settings/src/main/scala/sbt/SlashSyntax.scala index 9b1898a05..a88dbc828 100644 --- a/main-settings/src/main/scala/sbt/SlashSyntax.scala +++ b/main-settings/src/main/scala/sbt/SlashSyntax.scala @@ -50,10 +50,6 @@ trait SlashSyntax { */ implicit def sbtSlashSyntaxRichScopeFromScoped(t: Scoped): RichScope = new RichScope(t.scope.copy(task = Select(t.key))) - - implicit val sbtSlashSyntaxSettingKeyCanScope: CanScope[SettingKey] = new SettingKeyCanScope() - implicit val sbtSlashSyntaxTaskKeyCanScope: CanScope[TaskKey] = new TaskKeyCanScope() - implicit val sbtSlashSyntaxInputKeyCanScope: CanScope[InputKey] = new InputKeyCanScope() } object SlashSyntax { @@ -61,7 +57,7 @@ object SlashSyntax { /** RichScopeLike wraps a general scope to provide the `/` operator for key scoping. */ sealed trait RichScopeLike { protected def scope: Scope - def /[A, F[_]: CanScope](key: F[A]): F[A] = implicitly[CanScope[F]].inScope(key, scope) + final def /[K](key: Scoped.ScopingSetting[K]): K = key in scope } /** RichReference wraps a reference to provide the `/` operator for scoping. */ @@ -83,21 +79,4 @@ object SlashSyntax { /** RichScope wraps a general scope to provide the `/` operator for scoping. */ final class RichScope(protected val scope: Scope) extends RichScopeLike {} - - /** - * A typeclass that represents scoping. - */ - sealed trait CanScope[F[A]] { - def inScope[A](key: F[A], scope: Scope): F[A] - } - - final class SettingKeyCanScope extends CanScope[SettingKey] { - def inScope[A](key: SettingKey[A], scope: Scope): SettingKey[A] = key in scope - } - final class TaskKeyCanScope extends CanScope[TaskKey] { - def inScope[A](key: TaskKey[A], scope: Scope): TaskKey[A] = key in scope - } - final class InputKeyCanScope extends CanScope[InputKey] { - def inScope[A](key: InputKey[A], scope: Scope): InputKey[A] = key in scope - } } From 0bcd7c3b6d23aed2e4532bf155fb568e3d027f19 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:08:32 +0100 Subject: [PATCH 03/12] Remove boilerplate around sbtSlashSyntaxRichConfiguration --- main-settings/src/main/scala/sbt/SlashSyntax.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main-settings/src/main/scala/sbt/SlashSyntax.scala b/main-settings/src/main/scala/sbt/SlashSyntax.scala index a88dbc828..0e512c487 100644 --- a/main-settings/src/main/scala/sbt/SlashSyntax.scala +++ b/main-settings/src/main/scala/sbt/SlashSyntax.scala @@ -39,8 +39,8 @@ trait SlashSyntax { implicit def sbtSlashSyntaxRichConfigKey(c: ConfigKey): RichConfiguration = new RichConfiguration(Scope(This, Select(c), This, This)) - implicit def sbtSlashSyntaxRichConfiguration(c: Configuration): RichConfiguration = - sbtSlashSyntaxRichConfigKey(c: ConfigKey) + + implicit def sbtSlashSyntaxRichConfiguration(c: Configuration): RichConfiguration = (c: ConfigKey) implicit def sbtSlashSyntaxRichScope(s: Scope): RichScope = new RichScope(s) From 18d615701f6a90dc6f1314dbb4d12efedbb1b758 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:11:11 +0100 Subject: [PATCH 04/12] Remove WithScope wrapping --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 34f6b55d2..fd60ea06c 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -63,16 +63,14 @@ object BuildDSLInstances { 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) )) - object WithScope { - implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = - withScope(Gen.identifier map (InputKey[A](_))) + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = + withScope(Gen.identifier map (InputKey[A](_))) - implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = - withScope(Gen.identifier map (SettingKey[A](_))) + implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = + withScope(Gen.identifier map (SettingKey[A](_))) - implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = - withScope(Gen.identifier map (TaskKey[A](_))) - } + implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = + withScope(Gen.identifier map (TaskKey[A](_))) object WithoutScope { implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = @@ -139,21 +137,18 @@ import CustomEquality._ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("Global / key == key in Global") = { - import WithScope._ (forAll { (k: SettingKey[String]) => expectValue(k in Global)(Global / k) } && forAll { (k: TaskKey[String]) => expectValue(k in Global)(Global / k) } && forAll { (k: InputKey[String]) => expectValue(k in Global)(Global / k) }) } property("Reference / key == key in Reference") = { - import WithScope._ (forAll { (r: Reference, k: SettingKey[String]) => expectValue(k in r)(r / k) } && forAll { (r: Reference, k: TaskKey[String]) => expectValue(k in r)(r / k) } && forAll { (r: Reference, k: InputKey[String]) => expectValue(k in r)(r / k) }) } property("Reference / Config / key == key in Reference in Config") = { - import WithScope._ (forAll { (r: Reference, c: ConfigKey, k: SettingKey[String]) => expectValue(k in r in c)(r / c / k) } && forAll { (r: Reference, c: ConfigKey, k: TaskKey[String]) => expectValue(k in r in c)(r / c / k) } && forAll { (r: Reference, c: ConfigKey, k: InputKey[String]) => expectValue(k in r in c)(r / c / k) }) @@ -174,7 +169,6 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { } property("Config / key == key in Config") = { - import WithScope._ (forAll { (c: ConfigKey, k: SettingKey[String]) => expectValue(k in c)(c / k) } && forAll { (c: ConfigKey, k: TaskKey[String]) => expectValue(k in c)(c / k) } && forAll { (c: ConfigKey, k: InputKey[String]) => expectValue(k in c)(c / k) }) @@ -195,14 +189,12 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { } property("Scope / key == key in Scope") = { - import WithScope._ (forAll { (s: Scope, k: SettingKey[String]) => expectValue(k in s)(s / k) } && forAll { (s: Scope, k: TaskKey[String]) => expectValue(k in s)(s / k) } && forAll { (s: Scope, k: InputKey[String]) => expectValue(k in s)(s / k) }) } property("Reference? / key == key in ThisScope.copy(..)") = { - import WithScope._ (forAll { (r: ScopeAxis[Reference], k: SettingKey[String]) => expectValue(k in ThisScope.copy(project = r))(r / k) } && forAll { (r: ScopeAxis[Reference], k: TaskKey[String]) => @@ -212,7 +204,6 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { } property("Reference? / ConfigKey? / key == key in ThisScope.copy(..)") = { - import WithScope._ (forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: SettingKey[String]) => expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k) } && forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: TaskKey[String]) => @@ -222,7 +213,6 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { } // property("Reference? / AttributeKey? / key == key in ThisScope.copy(..)") = { - // import WithScope._ // (forAll { (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: SettingKey[String]) => // expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k) } && // forAll { (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: TaskKey[String]) => @@ -232,7 +222,6 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { // } property("Reference? / ConfigKey? / AttributeKey? / key == key in ThisScope.copy(..)") = { - import WithScope._ (forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: SettingKey[String]) => expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k) } && forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: TaskKey[String]) => From 165dc794ca8f830ef47a18e8fd70d0f3cf11cbe0 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:14:36 +0100 Subject: [PATCH 05/12] Extract gen{Input,Setting,Task}Key --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index fd60ea06c..5abbea8f4 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -63,24 +63,18 @@ object BuildDSLInstances { 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) )) - implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = - withScope(Gen.identifier map (InputKey[A](_))) + def genInputKey[A: Manifest]: Gen[InputKey[A]] = Gen.identifier map (InputKey[A](_)) + def genSettingKey[A: Manifest]: Gen[SettingKey[A]] = Gen.identifier map (SettingKey[A](_)) + def genTaskKey[A: Manifest]: Gen[TaskKey[A]] = Gen.identifier map (TaskKey[A](_)) - implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = - withScope(Gen.identifier map (SettingKey[A](_))) - - implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = - withScope(Gen.identifier map (TaskKey[A](_))) + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = withScope(genInputKey[A]) + implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = withScope(genSettingKey[A]) + implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = withScope(genTaskKey[A]) object WithoutScope { - implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = - Arbitrary(Gen.identifier map (InputKey[A](_))) - - implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = - Arbitrary(Gen.identifier map (SettingKey[A](_))) - - implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = - Arbitrary(Gen.identifier map (TaskKey[A](_))) + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = Arbitrary(genInputKey[A]) + implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = Arbitrary(genSettingKey[A]) + implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = Arbitrary(genTaskKey[A]) } implicit def arbScopeAxis[A: Arbitrary]: Arbitrary[ScopeAxis[A]] = From 025075efd0d9d417357f470dc766aa4641837eb0 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:25:04 +0100 Subject: [PATCH 06/12] Dedup, cleanup & cover all key types --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 139 +++++++++++------- 1 file changed, 86 insertions(+), 53 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 5abbea8f4..473d288a6 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -130,97 +130,130 @@ object CustomEquality { import CustomEquality._ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { + type Key[K] = Scoped.ScopingSetting[K] with Scoped + property("Global / key == key in Global") = { - (forAll { (k: SettingKey[String]) => expectValue(k in Global)(Global / k) } - && forAll { (k: TaskKey[String]) => expectValue(k in Global)(Global / k) } - && forAll { (k: InputKey[String]) => expectValue(k in Global)(Global / k) }) + def check[K <: Key[K]: Arbitrary] = forAll((k: K) => expectValue(k in Global)(Global / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } property("Reference / key == key in Reference") = { - (forAll { (r: Reference, k: SettingKey[String]) => expectValue(k in r)(r / k) } - && forAll { (r: Reference, k: TaskKey[String]) => expectValue(k in r)(r / k) } - && forAll { (r: Reference, k: InputKey[String]) => expectValue(k in r)(r / k) }) + def check[K <: Key[K]: Arbitrary] = forAll((r: Reference, k: K) => expectValue(k in r)(r / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } property("Reference / Config / key == key in Reference in Config") = { - (forAll { (r: Reference, c: ConfigKey, k: SettingKey[String]) => expectValue(k in r in c)(r / c / k) } - && forAll { (r: Reference, c: ConfigKey, k: TaskKey[String]) => expectValue(k in r in c)(r / c / k) } - && forAll { (r: Reference, c: ConfigKey, k: InputKey[String]) => expectValue(k in r in c)(r / c / k) }) + def check[K <: Key[K]: Arbitrary] = + forAll((r: Reference, c: ConfigKey, k: K) => expectValue(k in r in c)(r / c / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } property("Reference / task / key == key in Reference in task") = { import WithoutScope._ - (forAll { (r: Reference, t: TaskKey[String], k: SettingKey[String]) => expectValue(k in (r, t))(r / t / k) } - && forAll { (r: Reference, t: TaskKey[String], k: TaskKey[String]) => expectValue(k in (r, t))(r / t / k) } - && forAll { (r: Reference, t: TaskKey[String], k: InputKey[String]) => expectValue(k in (r, t))(r / t / k) }) + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((r: Reference, t: K, k: K) => expectValue(k in (r, t))(r / t / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) } property("Reference / Config / task / key == key in Reference in Config in task") = { import WithoutScope._ - (forAll { (r: Reference, c: ConfigKey, t: TaskKey[String], k: SettingKey[String]) => expectValue(k in (r, c, t))(r / c / t / k) } - && forAll { (r: Reference, c: ConfigKey, t: TaskKey[String], k: TaskKey[String]) => expectValue(k in (r, c, t))(r / c / t / k) } - && forAll { (r: Reference, c: ConfigKey, t: TaskKey[String], k: InputKey[String]) => expectValue(k in (r, c, t))(r / c / t / k) }) + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((r: Reference, c: ConfigKey, t: K, k: K) => expectValue(k in (r, c, t))(r / c / t / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) } property("Config / key == key in Config") = { - (forAll { (c: ConfigKey, k: SettingKey[String]) => expectValue(k in c)(c / k) } - && forAll { (c: ConfigKey, k: TaskKey[String]) => expectValue(k in c)(c / k) } - && forAll { (c: ConfigKey, k: InputKey[String]) => expectValue(k in c)(c / k) }) + def check[K <: Key[K]: Arbitrary] = + forAll((c: ConfigKey, k: K) => expectValue(k in c)(c / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } property("Config / task / key == key in Config in task") = { import WithoutScope._ - (forAll { (c: ConfigKey, t: TaskKey[String], k: SettingKey[String]) => expectValue(k in c in t)(c / t / k) } - && forAll { (c: ConfigKey, t: TaskKey[String], k: TaskKey[String]) => expectValue(k in c in t)(c / t / k) } - && forAll { (c: ConfigKey, t: TaskKey[String], k: InputKey[String]) => expectValue(k in c in t)(c / t / k) }) + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((c: ConfigKey, t: K, k: K) => expectValue(k in c in t)(c / t / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) } property("task / key == key in task") = { import WithoutScope._ - (forAll { (t: TaskKey[String], k: SettingKey[String]) => expectValue(k in t)(t / k) } - && forAll { (t: TaskKey[String], k: TaskKey[String]) => expectValue(k in t)(t / k) } - && forAll { (t: TaskKey[String], k: InputKey[String]) => expectValue(k in t)(t / k) }) + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((t: K, k: K) => expectValue(k in t)(t / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) } property("Scope / key == key in Scope") = { - (forAll { (s: Scope, k: SettingKey[String]) => expectValue(k in s)(s / k) } - && forAll { (s: Scope, k: TaskKey[String]) => expectValue(k in s)(s / k) } - && forAll { (s: Scope, k: InputKey[String]) => expectValue(k in s)(s / k) }) + def check[K <: Key[K]: Arbitrary] = forAll((s: Scope, k: K) => expectValue(k in s)(s / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } property("Reference? / key == key in ThisScope.copy(..)") = { - (forAll { (r: ScopeAxis[Reference], k: SettingKey[String]) => - expectValue(k in ThisScope.copy(project = r))(r / k) } && - forAll { (r: ScopeAxis[Reference], k: TaskKey[String]) => - expectValue(k in ThisScope.copy(project = r))(r / k) } && - forAll { (r: ScopeAxis[Reference], k: InputKey[String]) => - expectValue(k in ThisScope.copy(project = r))(r / k) }) + def check[K <: Key[K]: Arbitrary] = + forAll((r: ScopeAxis[Reference], k: K) => + expectValue(k in ThisScope.copy(project = r))(r / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } property("Reference? / ConfigKey? / key == key in ThisScope.copy(..)") = { - (forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: SettingKey[String]) => - expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k) } && - forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: TaskKey[String]) => - expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k) } && - forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: InputKey[String]) => - expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k) }) + def check[K <: Key[K]: Arbitrary] = + forAll((r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: K) => + expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } - // property("Reference? / AttributeKey? / key == key in ThisScope.copy(..)") = { - // (forAll { (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: SettingKey[String]) => - // expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k) } && - // forAll { (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: TaskKey[String]) => - // expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k) } && - // forAll { (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: InputKey[String]) => - // expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k) } - // } +// property("Reference? / AttributeKey? / key == key in ThisScope.copy(..)") = { +// def check[K <: Key[K]: Arbitrary] = +// forAll( +// (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: K) => +// expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k)) +// check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] +// } property("Reference? / ConfigKey? / AttributeKey? / key == key in ThisScope.copy(..)") = { - (forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: SettingKey[String]) => - expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k) } && - forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: TaskKey[String]) => - expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k) } && - forAll { (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: InputKey[String]) => - expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k) }) + def check[K <: Key[K]: Arbitrary] = + forAll( + (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: K) => + expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } } From 74ac7d9e07214457a70fdc01f0c7f2b26a750c28 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:30:54 +0100 Subject: [PATCH 07/12] Drop redudant label in expectValue --- main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 473d288a6..c4b13b27b 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -125,7 +125,7 @@ object CustomEquality { } } - def expectValue[A: Eq](expected: A)(x: A) = x.toString |: (expected =? x) + def expectValue[A: Eq](expected: A)(x: A) = expected =? x } import CustomEquality._ From fb28c201c8315aefa60285f1ed6346ad8aa46e90 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:32:25 +0100 Subject: [PATCH 08/12] Make scoped keys more frequent --- main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index c4b13b27b..b0505ac86 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -59,7 +59,7 @@ object BuildDSLInstances { def withScope[K <: Scoped.ScopingSetting[K]](keyGen: Gen[K]): Arbitrary[K] = Arbitrary(Gen.frequency( - 50 -> keyGen, + 5 -> keyGen, 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) )) From 8af3110c0047d41164ad31bcb98932529cf06977 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:35:06 +0100 Subject: [PATCH 09/12] Add .previous tests to SlashSyntaxTest --- .../src/test/scala/sbt/SlashSyntaxTest.scala | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala b/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala index f7a1ae065..31235f79d 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala @@ -7,7 +7,9 @@ package sbt.test -import sbt.Def.{ Setting, settingKey, taskKey } +import java.io.File +import sjsonnew._, BasicJsonProtocol._ +import sbt.Def.{ Setting, inputKey, settingKey, taskKey } import sbt.Scope.Global import sbt.librarymanagement.ModuleID import sbt.librarymanagement.syntax._ @@ -23,9 +25,15 @@ object SlashSyntaxTest extends sbt.SlashSyntax { val console = taskKey[Unit]("") val libraryDependencies = settingKey[Seq[ModuleID]]("") val name = settingKey[String]("") + val run = inputKey[Unit]("") val scalaVersion = settingKey[String]("") val scalacOptions = taskKey[Seq[String]]("") + val foo = taskKey[Int]("") + val bar = taskKey[Int]("") + val baz = inputKey[Unit]("") + val buildInfo = taskKey[Seq[File]]("") + val uTest = "com.lihaoyi" %% "utest" % "0.5.3" Seq[Setting[_]]( @@ -34,8 +42,23 @@ object SlashSyntaxTest extends sbt.SlashSyntax { console / scalacOptions += "-deprecation", Compile / console / scalacOptions += "-Ywarn-numeric-widen", projA / Compile / console / scalacOptions += "-feature", + Zero / name := "foo", Zero / Zero / name := "foo", Zero / Zero / Zero / name := "foo", + Test / bar := 1, + Test / foo := (Test / bar).value + 1, + Compile / foo := { + (Compile / bar).previous.getOrElse(1) + }, + Compile / bar := { + (Compile / foo).previous.getOrElse(2) + }, + Test / buildInfo := Nil, + baz := { + val _ = (Test / buildInfo).taskValue + (Compile / run).evaluated + }, + foo := (Test / bar).value + 1, libraryDependencies += uTest % Test, ) } From d19c350687094c28ccd1647d6aecf88f9bfc501b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:36:28 +0100 Subject: [PATCH 10/12] Correct name of WithoutScope properties --- main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index b0505ac86..8a267f81a 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -148,7 +148,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } - property("Reference / task / key == key in Reference in task") = { + property("Reference / task / key ~= key in Reference in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = forAll((r: Reference, t: K, k: K) => expectValue(k in (r, t))(r / t / k)) @@ -165,7 +165,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { ) } - property("Reference / Config / task / key == key in Reference in Config in task") = { + property("Reference / Config / task / key ~= key in Reference in Config in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = forAll((r: Reference, c: ConfigKey, t: K, k: K) => expectValue(k in (r, c, t))(r / c / t / k)) @@ -188,7 +188,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } - property("Config / task / key == key in Config in task") = { + property("Config / task / key ~= key in Config in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = forAll((c: ConfigKey, t: K, k: K) => expectValue(k in c in t)(c / t / k)) @@ -205,7 +205,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { ) } - property("task / key == key in task") = { + property("task / key ~= key in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = forAll((t: K, k: K) => expectValue(k in t)(t / k)) From ef4828cfc2169126b90730b050a0f51cd1f8abcf Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 12:09:31 +0100 Subject: [PATCH 11/12] Add slash syntax for AttrKey that parity with "in" syntax --- .../src/main/scala/sbt/SlashSyntax.scala | 18 ++++-- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 64 +++++++++++++++++++ 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/main-settings/src/main/scala/sbt/SlashSyntax.scala b/main-settings/src/main/scala/sbt/SlashSyntax.scala index 0e512c487..93d31b1aa 100644 --- a/main-settings/src/main/scala/sbt/SlashSyntax.scala +++ b/main-settings/src/main/scala/sbt/SlashSyntax.scala @@ -50,18 +50,25 @@ trait SlashSyntax { */ implicit def sbtSlashSyntaxRichScopeFromScoped(t: Scoped): RichScope = new RichScope(t.scope.copy(task = Select(t.key))) + + implicit def sbtSlashSyntaxRichScopeFromAttributeKey(a: AttributeKey[_]): RichScope = + Scope(This, This, Select(a), This) + } object SlashSyntax { - /** RichScopeLike wraps a general scope to provide the `/` operator for key scoping. */ - sealed trait RichScopeLike { + sealed trait HasSlashKey { protected def scope: Scope final def /[K](key: Scoped.ScopingSetting[K]): K = key in scope } + sealed trait HasSlashKeyOrAttrKey extends HasSlashKey { + final def /(key: AttributeKey[_]): RichScope = new RichScope(scope in key) + } + /** RichReference wraps a reference to provide the `/` operator for scoping. */ - final class RichReference(protected val scope: Scope) extends RichScopeLike { + final class RichReference(protected val scope: Scope) extends HasSlashKeyOrAttrKey { def /(c: ConfigKey): RichConfiguration = new RichConfiguration(scope in c) def /(c: Configuration): RichConfiguration = new RichConfiguration(scope in c) @@ -71,12 +78,13 @@ object SlashSyntax { } /** RichConfiguration wraps a configuration to provide the `/` operator for scoping. */ - final class RichConfiguration(protected val scope: Scope) extends RichScopeLike { + final class RichConfiguration(protected val scope: Scope) extends HasSlashKeyOrAttrKey { // This is for handling `Zero / Zero / Zero / name`. def /(taskAxis: ScopeAxis[AttributeKey[_]]): RichScope = new RichScope(scope.copy(task = taskAxis)) } /** RichScope wraps a general scope to provide the `/` operator for scoping. */ - final class RichScope(protected val scope: Scope) extends RichScopeLike {} + final class RichScope(protected val scope: Scope) extends HasSlashKey + } diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 8a267f81a..93a2e77bb 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -148,6 +148,22 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } + property("Reference / task.key / key == key in Reference in task") = { + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((r: Reference, t: K, k: K) => expectValue(k in (r, t))(r / t.key / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) + } + property("Reference / task / key ~= key in Reference in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = @@ -165,6 +181,22 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { ) } + property("Reference / Config / task.key / key == key in Reference in Config in task") = { + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((r: Reference, c: ConfigKey, t: K, k: K) => expectValue(k in (r, c, t))(r / c / t.key / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) + } + property("Reference / Config / task / key ~= key in Reference in Config in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = @@ -188,6 +220,22 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } + property("Config / task.key / key == key in Config in task") = { + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((c: ConfigKey, t: K, k: K) => expectValue(k in c in t)(c / t.key / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) + } + property("Config / task / key ~= key in Config in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = @@ -205,6 +253,22 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { ) } + property("task / key == key in task") = { + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((t: K, k: K) => expectValue(k in t)(t.key / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) + } + property("task / key ~= key in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = From 5e0b080f516510161be78eebaf9d83e8c998d103 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 7 Oct 2017 18:42:26 +0100 Subject: [PATCH 12/12] Fix some minor errors --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 93a2e77bb..d439809f3 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -150,7 +150,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("Reference / task.key / key == key in Reference in task") = { def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((r: Reference, t: K, k: K) => expectValue(k in (r, t))(r / t.key / k)) + forAll((r: Reference, t: T, k: K) => expectValue(k in (r, t))(r / t.key / k)) (true && check[InputKey[String], InputKey[String]] && check[InputKey[String], SettingKey[String]] @@ -167,7 +167,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("Reference / task / key ~= key in Reference in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((r: Reference, t: K, k: K) => expectValue(k in (r, t))(r / t / k)) + forAll((r: Reference, t: T, k: K) => expectValue(k in (r, t))(r / t / k)) (true && check[InputKey[String], InputKey[String]] && check[InputKey[String], SettingKey[String]] @@ -183,7 +183,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("Reference / Config / task.key / key == key in Reference in Config in task") = { def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((r: Reference, c: ConfigKey, t: K, k: K) => expectValue(k in (r, c, t))(r / c / t.key / k)) + forAll((r: Reference, c: ConfigKey, t: T, k: K) => expectValue(k in (r, c, t))(r / c / t.key / k)) (true && check[InputKey[String], InputKey[String]] && check[InputKey[String], SettingKey[String]] @@ -200,7 +200,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("Reference / Config / task / key ~= key in Reference in Config in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((r: Reference, c: ConfigKey, t: K, k: K) => expectValue(k in (r, c, t))(r / c / t / k)) + forAll((r: Reference, c: ConfigKey, t: T, k: K) => expectValue(k in (r, c, t))(r / c / t / k)) (true && check[InputKey[String], InputKey[String]] && check[InputKey[String], SettingKey[String]] @@ -222,7 +222,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("Config / task.key / key == key in Config in task") = { def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((c: ConfigKey, t: K, k: K) => expectValue(k in c in t)(c / t.key / k)) + forAll((c: ConfigKey, t: T, k: K) => expectValue(k in c in t)(c / t.key / k)) (true && check[InputKey[String], InputKey[String]] && check[InputKey[String], SettingKey[String]] @@ -239,7 +239,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("Config / task / key ~= key in Config in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((c: ConfigKey, t: K, k: K) => expectValue(k in c in t)(c / t / k)) + forAll((c: ConfigKey, t: T, k: K) => expectValue(k in c in t)(c / t / k)) (true && check[InputKey[String], InputKey[String]] && check[InputKey[String], SettingKey[String]] @@ -253,9 +253,9 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { ) } - property("task / key == key in task") = { + property("task.key / key == key in task") = { def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((t: K, k: K) => expectValue(k in t)(t.key / k)) + forAll((t: T, k: K) => expectValue(k in t)(t.key / k)) (true && check[InputKey[String], InputKey[String]] && check[InputKey[String], SettingKey[String]] @@ -272,7 +272,7 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("task / key ~= key in task") = { import WithoutScope._ def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((t: K, k: K) => expectValue(k in t)(t / k)) + forAll((t: T, k: K) => expectValue(k in t)(t / k)) (true && check[InputKey[String], InputKey[String]] && check[InputKey[String], SettingKey[String]]