Merge pull request #3620 from eed3si9n/wip/previous4

Remove thunk for slash syntax
This commit is contained in:
eugene yokota 2017-10-07 14:25:08 -04:00 committed by GitHub
commit 84dafd0bc0
4 changed files with 174 additions and 74 deletions

View File

@ -42,22 +42,33 @@ trait SlashSyntax {
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 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 sbtSlashSyntaxRichScopeFromAttributeKey(a: AttributeKey[_]): RichScope =
Scope(This, This, Select(a), This)
implicit def sbtSlashSyntaxScopeAndKeyMaterialize[K <: Key[K]](scopeAndKey: ScopeAndKey[K]): K =
scopeAndKey.materialize
}
object SlashSyntax {
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)
@ -67,47 +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))
}
/** 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
}
/**
* 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"
*/
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}"
}
final class RichScope(protected val scope: Scope) extends HasSlashKey
}

View File

@ -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
@ -59,25 +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)
))
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 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])
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(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]] =
@ -128,27 +125,32 @@ 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") = {
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") = {
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") = {
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") = {
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 / 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]]
@ -161,9 +163,11 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax {
&& 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._
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, t: T, k: K) => expectValue(k in (r, t))(r / t / k))
(true
&& check[InputKey[String], InputKey[String]]
&& check[InputKey[String], SettingKey[String]]
@ -176,14 +180,49 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax {
&& 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: 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]]
&& 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: T, 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") = {
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 / 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]]
@ -196,9 +235,11 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax {
&& check[TaskKey[String], TaskKey[String]]
)
}
property("task / key == key in task") = {
property("Config / task / key ~= key in Config 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((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]]
@ -209,36 +250,74 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax {
&& check[TaskKey[String], InputKey[String]]
&& check[TaskKey[String], SettingKey[String]]
&& check[TaskKey[String], TaskKey[String]]
)
)
}
property("task.key / key == key in task") = {
def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] =
forAll((t: T, 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] =
forAll((t: T, 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") = {
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(..)") = {
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]]
check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]]
}
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]]
check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]]
}
// 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]]
// check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]]
// }
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]]
check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]]
}
}

View File

@ -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,
)
}

View File

@ -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"),