diff --git a/main-settings/src/main/scala/sbt/SlashSyntax.scala b/main-settings/src/main/scala/sbt/SlashSyntax.scala index 9b1898a05..93d31b1aa 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) @@ -51,21 +51,24 @@ 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() + 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 - 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 + } + + 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) @@ -75,29 +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 - /** - * 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 - } } diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 34f6b55d2..93a2e77bb 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -59,30 +59,22 @@ 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) )) - object WithScope { - 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]] = @@ -133,111 +125,199 @@ 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._ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { + type Key[K] = Scoped.ScopingSetting[K] with Scoped + 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) }) + 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") = { - 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) }) + 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") = { - 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) }) + 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) }) + 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 / Config / task / key == key in Reference in Config in task") = { + property("Reference / task / key ~= key in Reference 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, 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 == 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] = + 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") = { - 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) }) + 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") = { + 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._ - (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") = { + 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._ - (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") = { - 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) }) + 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(..)") = { - 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) }) + 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(..)") = { - 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) }) + 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(..)") = { - // 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? / 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(..)") = { - 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) }) + 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]] } } 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, ) }