mirror of https://github.com/sbt/sbt.git
Unified slash syntax
Fixes sbt/sbt#1812 This adds unified slash syntax for both sbt shell and the build.sbt DSL. Instead of the current `<project-id>/config:intask::key`, this adds `<project-id>/<config-ident>/intask/key` where <config-ident> is the Scala identifier notation for the configurations like `Compile` and `Test`. This also adds a series of implicits called `SlashSyntax` that adds `/` operators to project refererences, configuration, and keys such that the same syntax works in build.sbt. These examples work for both from the shell and in build.sbt. Global / cancelable ThisBuild / scalaVersion Test / test root / Compile / compile / scalacOptions ProjectRef(uri("file:/xxx/helloworld/"),"root")/Compile/scalacOptions Zero / Zero / name The inspect command now outputs something that can be copy-pasted: > inspect compile [info] Task: sbt.inc.Analysis [info] Description: [info] Compiles sources. [info] Provided by: [info] ProjectRef(uri("file:/xxx/helloworld/"),"root")/Compile/compile [info] Defined at: [info] (sbt.Defaults) Defaults.scala:326 [info] Dependencies: [info] Compile/manipulateBytecode [info] Compile/incCompileSetup [info] Reverse dependencies: [info] Compile/printWarnings [info] Compile/products [info] Compile/discoveredSbtPlugins [info] Compile/discoveredMainClasses [info] Delegates: [info] Compile/compile [info] compile [info] ThisBuild/Compile/compile [info] ThisBuild/compile [info] Zero/Compile/compile [info] Global/compile [info] Related: [info] Test/compile
This commit is contained in:
parent
67d1da48f1
commit
33a01f3ceb
|
|
@ -45,7 +45,8 @@ def commonSettings: Seq[Setting[_]] =
|
||||||
resolvers += Resolver.sonatypeRepo("snapshots"),
|
resolvers += Resolver.sonatypeRepo("snapshots"),
|
||||||
resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/",
|
resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/",
|
||||||
concurrentRestrictions in Global += Util.testExclusiveRestriction,
|
concurrentRestrictions in Global += Util.testExclusiveRestriction,
|
||||||
testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
|
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
|
||||||
|
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2"),
|
||||||
javacOptions in compile ++= Seq("-target", "6", "-source", "6", "-Xlint", "-Xlint:-serial"),
|
javacOptions in compile ++= Seq("-target", "6", "-source", "6", "-Xlint", "-Xlint:-serial"),
|
||||||
crossScalaVersions := Seq(baseScalaVersion),
|
crossScalaVersions := Seq(baseScalaVersion),
|
||||||
bintrayPackage := (bintrayPackage in ThisBuild).value,
|
bintrayPackage := (bintrayPackage in ThisBuild).value,
|
||||||
|
|
@ -374,6 +375,11 @@ lazy val mainProj = (project in file("main"))
|
||||||
mimaBinaryIssueFilters ++= Vector(
|
mimaBinaryIssueFilters ++= Vector(
|
||||||
// Changed the signature of NetworkChannel ctor. internal.
|
// Changed the signature of NetworkChannel ctor. internal.
|
||||||
exclude[DirectMissingMethodProblem]("sbt.internal.server.NetworkChannel.*"),
|
exclude[DirectMissingMethodProblem]("sbt.internal.server.NetworkChannel.*"),
|
||||||
|
// ctor for ConfigIndex. internal.
|
||||||
|
exclude[DirectMissingMethodProblem]("sbt.internal.ConfigIndex.*"),
|
||||||
|
// New and changed methods on KeyIndex. internal.
|
||||||
|
exclude[ReversedMissingMethodProblem]("sbt.internal.KeyIndex.*"),
|
||||||
|
exclude[DirectMissingMethodProblem]("sbt.internal.KeyIndex.*"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.configure(
|
.configure(
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,11 @@ object AttributeKey {
|
||||||
rank0: Int
|
rank0: Int
|
||||||
)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
||||||
new SharedAttributeKey[T] {
|
new SharedAttributeKey[T] {
|
||||||
|
require(name.headOption match {
|
||||||
|
case Some(c) => c.isLower
|
||||||
|
case None => false
|
||||||
|
}, s"A named attribute key must start with a lowercase letter: $name")
|
||||||
|
|
||||||
def manifest = mf
|
def manifest = mf
|
||||||
val label = Util.hyphenToCamel(name)
|
val label = Util.hyphenToCamel(name)
|
||||||
def description = description0
|
def description = description0
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,12 @@ object SettingsTest extends Properties("settings") {
|
||||||
private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] = {
|
private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] = {
|
||||||
import Gen._
|
import Gen._
|
||||||
val nonEmptyAlphaStr =
|
val nonEmptyAlphaStr =
|
||||||
nonEmptyListOf(alphaChar).map(_.mkString).suchThat(_.forall(_.isLetter))
|
nonEmptyListOf(alphaChar)
|
||||||
|
.map({ xs: List[Char] =>
|
||||||
|
val s = xs.mkString
|
||||||
|
s.take(1).toLowerCase + s.drop(1)
|
||||||
|
})
|
||||||
|
.suchThat(_.forall(_.isLetter))
|
||||||
|
|
||||||
(for {
|
(for {
|
||||||
list <- Gen.listOfN(nr, nonEmptyAlphaStr) suchThat (l => l.size == l.distinct.size)
|
list <- Gen.listOfN(nr, nonEmptyAlphaStr) suchThat (l => l.size == l.distinct.size)
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,12 @@ trait Parsers {
|
||||||
/** Parses a single letter, according to Char.isLetter, into a Char. */
|
/** Parses a single letter, according to Char.isLetter, into a Char. */
|
||||||
lazy val Letter = charClass(_.isLetter, "letter")
|
lazy val Letter = charClass(_.isLetter, "letter")
|
||||||
|
|
||||||
|
/** Parses a single letter, according to Char.isUpper, into a Char. */
|
||||||
|
lazy val Upper = charClass(_.isUpper, "upper")
|
||||||
|
|
||||||
|
/** Parses a single letter, according to Char.isLower, into a Char. */
|
||||||
|
lazy val Lower = charClass(_.isLower, "lower")
|
||||||
|
|
||||||
/** Parses the first Char in an sbt identifier, which must be a [[Letter]].*/
|
/** Parses the first Char in an sbt identifier, which must be a [[Letter]].*/
|
||||||
def IDStart = Letter
|
def IDStart = Letter
|
||||||
|
|
||||||
|
|
@ -67,6 +73,9 @@ trait Parsers {
|
||||||
/** Parses a non-symbolic Scala-like identifier. The identifier must start with [[IDStart]] and contain zero or more [[ScalaIDChar]]s after that.*/
|
/** Parses a non-symbolic Scala-like identifier. The identifier must start with [[IDStart]] and contain zero or more [[ScalaIDChar]]s after that.*/
|
||||||
lazy val ScalaID = identifier(IDStart, ScalaIDChar)
|
lazy val ScalaID = identifier(IDStart, ScalaIDChar)
|
||||||
|
|
||||||
|
/** Parses a non-symbolic Scala-like identifier. The identifier must start with [[Upper]] and contain zero or more [[ScalaIDChar]]s after that.*/
|
||||||
|
lazy val CapitalizedID = identifier(Upper, ScalaIDChar)
|
||||||
|
|
||||||
/** Parses a String that starts with `start` and is followed by zero or more characters parsed by `rep`.*/
|
/** Parses a String that starts with `start` and is followed by zero or more characters parsed by `rep`.*/
|
||||||
def identifier(start: Parser[Char], rep: Parser[Char]): Parser[String] =
|
def identifier(start: Parser[Char], rep: Parser[Char]): Parser[String] =
|
||||||
start ~ rep.* map { case x ~ xs => (x +: xs).mkString }
|
start ~ rep.* map { case x ~ xs => (x +: xs).mkString }
|
||||||
|
|
|
||||||
|
|
@ -57,17 +57,36 @@ object Def extends Init[Scope] with TaskMacroExtra {
|
||||||
ref => displayBuildRelative(currentBuild, multi, ref)
|
ref => displayBuildRelative(currentBuild, multi, ref)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a String expression for the given [[Reference]] (BuildRef, [[ProjectRef]], etc)
|
||||||
|
* relative to the current project.
|
||||||
|
*/
|
||||||
|
def displayRelativeReference(current: ProjectRef, project: Reference): String =
|
||||||
|
displayRelative(current, project, false)
|
||||||
|
|
||||||
|
@deprecated("Use displayRelativeReference", "1.1.0")
|
||||||
def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String =
|
def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String =
|
||||||
|
displayRelative(current, project, true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the String of a given [[Reference]] relative to current.
|
||||||
|
* Note that this no longer takes "multi" parameter, and omits the subproject id at all times.
|
||||||
|
*/
|
||||||
|
private[sbt] def displayRelative(current: ProjectRef,
|
||||||
|
project: Reference,
|
||||||
|
trailingSlash: Boolean): String = {
|
||||||
|
val trailing = if (trailingSlash) "/" else ""
|
||||||
project match {
|
project match {
|
||||||
case BuildRef(current.build) => "{.}/"
|
case BuildRef(current.build) => "ThisBuild" + trailing
|
||||||
case `current` => if (multi) current.project + "/" else ""
|
case `current` => ""
|
||||||
case ProjectRef(current.build, x) => x + "/"
|
case ProjectRef(current.build, x) => x + trailing
|
||||||
case _ => Reference.display(project) + "/"
|
case _ => Reference.display(project) + trailing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def displayBuildRelative(currentBuild: URI, multi: Boolean, project: Reference): String =
|
def displayBuildRelative(currentBuild: URI, multi: Boolean, project: Reference): String =
|
||||||
project match {
|
project match {
|
||||||
case BuildRef(`currentBuild`) => "{.}/"
|
case BuildRef(`currentBuild`) => "ThisBuild/"
|
||||||
case ProjectRef(`currentBuild`, x) => x + "/"
|
case ProjectRef(`currentBuild`, x) => x + "/"
|
||||||
case _ => Reference.display(project) + "/"
|
case _ => Reference.display(project) + "/"
|
||||||
}
|
}
|
||||||
|
|
@ -80,6 +99,9 @@ object Def extends Init[Scope] with TaskMacroExtra {
|
||||||
def displayMasked(scoped: ScopedKey[_], mask: ScopeMask): String =
|
def displayMasked(scoped: ScopedKey[_], mask: ScopeMask): String =
|
||||||
Scope.displayMasked(scoped.scope, scoped.key.label, mask)
|
Scope.displayMasked(scoped.scope, scoped.key.label, mask)
|
||||||
|
|
||||||
|
def displayMasked(scoped: ScopedKey[_], mask: ScopeMask, showZeroConfig: Boolean): String =
|
||||||
|
Scope.displayMasked(scoped.scope, scoped.key.label, mask, showZeroConfig)
|
||||||
|
|
||||||
def withColor(s: String, color: Option[String]): String = {
|
def withColor(s: String, color: Option[String]): String = {
|
||||||
val useColor = ConsoleAppender.formatEnabledInEnv
|
val useColor = ConsoleAppender.formatEnabledInEnv
|
||||||
color match {
|
color match {
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ object Reference {
|
||||||
case LocalRootProject => "{<this>}<root>"
|
case LocalRootProject => "{<this>}<root>"
|
||||||
case LocalProject(id) => "{<this>}" + id
|
case LocalProject(id) => "{<this>}" + id
|
||||||
case RootProject(uri) => "{" + uri + " }<root>"
|
case RootProject(uri) => "{" + uri + " }<root>"
|
||||||
case ProjectRef(uri, id) => "{" + uri + "}" + id
|
case ProjectRef(uri, id) => s"""ProjectRef(uri("$uri"),"$id")"""
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildURI(ref: ResolvedReference): URI = ref match {
|
def buildURI(ref: ResolvedReference): URI = ref match {
|
||||||
|
|
|
||||||
|
|
@ -123,29 +123,84 @@ object Scope {
|
||||||
case BuildRef(uri) => BuildRef(resolveBuild(current, uri))
|
case BuildRef(uri) => BuildRef(resolveBuild(current, uri))
|
||||||
}
|
}
|
||||||
|
|
||||||
def display(config: ConfigKey): String = config.name + ":"
|
def display(config: ConfigKey): String = guessConfigIdent(config.name) + "/"
|
||||||
|
|
||||||
|
private[sbt] val configIdents: Map[String, String] =
|
||||||
|
Map(
|
||||||
|
"it" -> "IntegrationTest",
|
||||||
|
"scala-tool" -> "ScalaTool",
|
||||||
|
"plugin" -> "CompilerPlugin"
|
||||||
|
)
|
||||||
|
private[sbt] val configIdentsInverse: Map[String, String] =
|
||||||
|
configIdents map { _.swap }
|
||||||
|
|
||||||
|
private[sbt] def guessConfigIdent(conf: String): String =
|
||||||
|
configIdents.applyOrElse(conf, (x: String) => x.capitalize)
|
||||||
|
|
||||||
|
private[sbt] def unguessConfigIdent(conf: String): String =
|
||||||
|
configIdentsInverse.applyOrElse(conf, (x: String) => x.take(1).toLowerCase + x.drop(1))
|
||||||
|
|
||||||
|
def displayConfigKey012Style(config: ConfigKey): String = config.name + ":"
|
||||||
|
|
||||||
def display(scope: Scope, sep: String): String =
|
def display(scope: Scope, sep: String): String =
|
||||||
displayMasked(scope, sep, showProject, ScopeMask())
|
displayMasked(scope, sep, showProject, ScopeMask())
|
||||||
|
|
||||||
def displayMasked(scope: Scope, sep: String, mask: ScopeMask): String =
|
|
||||||
displayMasked(scope, sep, showProject, mask)
|
|
||||||
|
|
||||||
def display(scope: Scope, sep: String, showProject: Reference => String): String =
|
def display(scope: Scope, sep: String, showProject: Reference => String): String =
|
||||||
displayMasked(scope, sep, showProject, ScopeMask())
|
displayMasked(scope, sep, showProject, ScopeMask())
|
||||||
|
|
||||||
def displayMasked(
|
private[sbt] def displayPedantic(scope: Scope, sep: String): String =
|
||||||
scope: Scope,
|
displayMasked(scope, sep, showProject, ScopeMask(), true)
|
||||||
|
|
||||||
|
def displayMasked(scope: Scope, sep: String, mask: ScopeMask): String =
|
||||||
|
displayMasked(scope, sep, showProject, mask)
|
||||||
|
|
||||||
|
def displayMasked(scope: Scope, sep: String, mask: ScopeMask, showZeroConfig: Boolean): String =
|
||||||
|
displayMasked(scope, sep, showProject, mask, showZeroConfig)
|
||||||
|
|
||||||
|
def displayMasked(scope: Scope,
|
||||||
sep: String,
|
sep: String,
|
||||||
showProject: Reference => String,
|
showProject: Reference => String,
|
||||||
mask: ScopeMask
|
mask: ScopeMask): String =
|
||||||
): String = {
|
displayMasked(scope, sep, showProject, mask, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unified slash style introduced in sbt 1.1.0.
|
||||||
|
* By default, sbt will no longer display the Zero-config,
|
||||||
|
* so `name` will render as `name` as opposed to `{uri}proj/Zero/name`.
|
||||||
|
* Technically speaking an unspecified configuration axis defaults to
|
||||||
|
* the scope delegation (first configuration defining the key, then Zero).
|
||||||
|
*/
|
||||||
|
def displayMasked(scope: Scope,
|
||||||
|
sep: String,
|
||||||
|
showProject: Reference => String,
|
||||||
|
mask: ScopeMask,
|
||||||
|
showZeroConfig: Boolean): String = {
|
||||||
import scope.{ project, config, task, extra }
|
import scope.{ project, config, task, extra }
|
||||||
val configPrefix = config.foldStrict(display, "*:", ".:")
|
val zeroConfig = if (showZeroConfig) "Zero/" else ""
|
||||||
|
val configPrefix = config.foldStrict(display, zeroConfig, "./")
|
||||||
|
val taskPrefix = task.foldStrict(_.label + "/", "", "./")
|
||||||
|
val extras = extra.foldStrict(_.entries.map(_.toString).toList, Nil, Nil)
|
||||||
|
val postfix = if (extras.isEmpty) "" else extras.mkString("(", ", ", ")")
|
||||||
|
if (scope == GlobalScope) "Global/" + sep + postfix
|
||||||
|
else
|
||||||
|
mask.concatShow(projectPrefix(project, showProject), configPrefix, taskPrefix, sep, postfix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sbt 0.12 style
|
||||||
|
def display012StyleMasked(scope: Scope,
|
||||||
|
sep: String,
|
||||||
|
showProject: Reference => String,
|
||||||
|
mask: ScopeMask): String = {
|
||||||
|
import scope.{ project, config, task, extra }
|
||||||
|
val configPrefix = config.foldStrict(displayConfigKey012Style, "*:", ".:")
|
||||||
val taskPrefix = task.foldStrict(_.label + "::", "", ".::")
|
val taskPrefix = task.foldStrict(_.label + "::", "", ".::")
|
||||||
val extras = extra.foldStrict(_.entries.map(_.toString).toList, Nil, Nil)
|
val extras = extra.foldStrict(_.entries.map(_.toString).toList, Nil, Nil)
|
||||||
val postfix = if (extras.isEmpty) "" else extras.mkString("(", ", ", ")")
|
val postfix = if (extras.isEmpty) "" else extras.mkString("(", ", ", ")")
|
||||||
mask.concatShow(projectPrefix(project, showProject), configPrefix, taskPrefix, sep, postfix)
|
mask.concatShow(projectPrefix012Style(project, showProject),
|
||||||
|
configPrefix,
|
||||||
|
taskPrefix,
|
||||||
|
sep,
|
||||||
|
postfix)
|
||||||
}
|
}
|
||||||
|
|
||||||
def equal(a: Scope, b: Scope, mask: ScopeMask): Boolean =
|
def equal(a: Scope, b: Scope, mask: ScopeMask): Boolean =
|
||||||
|
|
@ -154,10 +209,12 @@ object Scope {
|
||||||
(!mask.task || a.task == b.task) &&
|
(!mask.task || a.task == b.task) &&
|
||||||
(!mask.extra || a.extra == b.extra)
|
(!mask.extra || a.extra == b.extra)
|
||||||
|
|
||||||
def projectPrefix(
|
def projectPrefix(project: ScopeAxis[Reference],
|
||||||
project: ScopeAxis[Reference],
|
show: Reference => String = showProject): String =
|
||||||
show: Reference => String = showProject
|
project.foldStrict(show, "Zero/", "./")
|
||||||
): String =
|
|
||||||
|
def projectPrefix012Style(project: ScopeAxis[Reference],
|
||||||
|
show: Reference => String = showProject): String =
|
||||||
project.foldStrict(show, "*/", "./")
|
project.foldStrict(show, "*/", "./")
|
||||||
|
|
||||||
def showProject = (ref: Reference) => Reference.display(ref) + "/"
|
def showProject = (ref: Reference) => Reference.display(ref) + "/"
|
||||||
|
|
|
||||||
|
|
@ -2336,13 +2336,20 @@ object Classpaths {
|
||||||
else Def.task((evictionWarningOptions in update).value)
|
else Def.task((evictionWarningOptions in update).value)
|
||||||
}.value
|
}.value
|
||||||
|
|
||||||
|
val extracted = (Project extract state0)
|
||||||
|
val isPlugin = sbtPlugin.value
|
||||||
|
val thisRef = thisProjectRef.value
|
||||||
|
val label =
|
||||||
|
if (isPlugin) Reference.display(thisRef)
|
||||||
|
else Def.displayRelativeReference(extracted.currentRef, thisRef)
|
||||||
|
|
||||||
LibraryManagement.cachedUpdate(
|
LibraryManagement.cachedUpdate(
|
||||||
// LM API
|
// LM API
|
||||||
lm = dependencyResolution.value,
|
lm = dependencyResolution.value,
|
||||||
// Ivy-free ModuleDescriptor
|
// Ivy-free ModuleDescriptor
|
||||||
module = ivyModule.value,
|
module = ivyModule.value,
|
||||||
s.cacheStoreFactory.sub(updateCacheName.value),
|
s.cacheStoreFactory.sub(updateCacheName.value),
|
||||||
Reference.display(thisProjectRef.value),
|
label = label,
|
||||||
updateConf,
|
updateConf,
|
||||||
substituteScalaFiles(scalaOrganization.value, _)(providedScalaJars),
|
substituteScalaFiles(scalaOrganization.value, _)(providedScalaJars),
|
||||||
skip = (skip in update).value,
|
skip = (skip in update).value,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import sbt.librarymanagement.Configuration
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SlashSyntax implements the slash syntax to scope keys for build.sbt DSL.
|
||||||
|
* The implicits are set up such that the order that the scope components
|
||||||
|
* must appear in the order of the project axis, the configuration axis, and
|
||||||
|
* the task axis. This ordering is the same as the shell syntax.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* {{{
|
||||||
|
* Global / cancelable := true
|
||||||
|
* ThisBuild / scalaVersion := "2.12.2"
|
||||||
|
* Test / test := ()
|
||||||
|
* console / scalacOptions += "-deprecation"
|
||||||
|
* Compile / console / scalacOptions += "-Ywarn-numeric-widen"
|
||||||
|
* projA / Compile / console / scalacOptions += "-feature"
|
||||||
|
* Zero / Zero / name := "foo"
|
||||||
|
* }}}
|
||||||
|
*/
|
||||||
|
trait SlashSyntax {
|
||||||
|
import SlashSyntax._
|
||||||
|
|
||||||
|
implicit def sbtScopeSyntaxRichReference(r: Reference): RichReference =
|
||||||
|
new RichReference(Scope(Select(r), This, This, This))
|
||||||
|
|
||||||
|
implicit def sbtScopeSyntaxRichProject(p: Project): RichReference =
|
||||||
|
new RichReference(Scope(Select(p), This, This, This))
|
||||||
|
|
||||||
|
implicit def sbtScopeSyntaxRichConfiguration(c: Configuration): RichConfiguration =
|
||||||
|
new RichConfiguration(Scope(This, Select(c), This, This))
|
||||||
|
|
||||||
|
implicit def sbtScopeSyntaxRichScope(s: Scope): RichScope =
|
||||||
|
new RichScope(s)
|
||||||
|
|
||||||
|
implicit def sbtScopeSyntaxRichScopeFromScoped(t: Scoped): RichScope =
|
||||||
|
new RichScope(Scope(This, This, Select(t.key), This))
|
||||||
|
|
||||||
|
implicit def sbtScopeSyntaxRichScopeAxis(a: ScopeAxis[Reference]): RichScopeAxis =
|
||||||
|
new RichScopeAxis(a)
|
||||||
|
|
||||||
|
// Materialize the setting key thunk
|
||||||
|
implicit def sbtScopeSyntaxSettingKeyThunkMaterialize[A](
|
||||||
|
thunk: SettingKeyThunk[A]): SettingKey[A] =
|
||||||
|
thunk.materialize
|
||||||
|
|
||||||
|
implicit def sbtScopeSyntaxSettingKeyThunkKeyRescope[A](thunk: SettingKeyThunk[A]): RichScope =
|
||||||
|
thunk.rescope
|
||||||
|
|
||||||
|
// Materialize the task key thunk
|
||||||
|
implicit def sbtScopeSyntaxTaskKeyThunkMaterialize[A](thunk: TaskKeyThunk[A]): TaskKey[A] =
|
||||||
|
thunk.materialize
|
||||||
|
|
||||||
|
implicit def sbtScopeSyntaxTaskKeyThunkRescope[A](thunk: TaskKeyThunk[A]): RichScope =
|
||||||
|
thunk.rescope
|
||||||
|
|
||||||
|
// Materialize the input key thunk
|
||||||
|
implicit def sbtScopeSyntaxInputKeyThunkMaterialize[A](thunk: InputKeyThunk[A]): InputKey[A] =
|
||||||
|
thunk.materialize
|
||||||
|
|
||||||
|
implicit def sbtScopeSyntaxInputKeyThunkRescope[A](thunk: InputKeyThunk[A]): RichScope =
|
||||||
|
thunk.rescope
|
||||||
|
}
|
||||||
|
|
||||||
|
object SlashSyntax {
|
||||||
|
|
||||||
|
/** RichReference wraps a project to provide the `/` operator for scoping. */
|
||||||
|
final class RichReference(s: Scope) {
|
||||||
|
def /(c: Configuration): RichConfiguration = new RichConfiguration(s in c)
|
||||||
|
|
||||||
|
// We don't know what the key is for yet, so just capture in a thunk.
|
||||||
|
def /[A](key: SettingKey[A]): SettingKeyThunk[A] = new SettingKeyThunk(s, key)
|
||||||
|
|
||||||
|
// We don't know what the key is for yet, so just capture in a thunk.
|
||||||
|
def /[A](key: TaskKey[A]): TaskKeyThunk[A] = new TaskKeyThunk(s, key)
|
||||||
|
|
||||||
|
// We don't know what the key is for yet, so just capture in a thunk.
|
||||||
|
def /[A](key: InputKey[A]): InputKeyThunk[A] = new InputKeyThunk(s, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** RichConfiguration wraps a configuration to provide the `/` operator for scoping. */
|
||||||
|
final class RichConfiguration(s: Scope) {
|
||||||
|
// We don't know what the key is for yet, so just capture in a thunk.
|
||||||
|
def /[A](key: SettingKey[A]): SettingKeyThunk[A] = new SettingKeyThunk(s, key)
|
||||||
|
|
||||||
|
// We don't know what the key is for yet, so just capture in a thunk.
|
||||||
|
def /[A](key: TaskKey[A]): TaskKeyThunk[A] = new TaskKeyThunk(s, key)
|
||||||
|
|
||||||
|
// We don't know what the key is for yet, so just capture in a thunk.
|
||||||
|
def /[A](key: InputKey[A]): InputKeyThunk[A] = new InputKeyThunk(s, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** RichScope wraps a general scope to provide the `/` operator for scoping. */
|
||||||
|
final class RichScope(scope: Scope) {
|
||||||
|
def /[A](key: SettingKey[A]): SettingKey[A] = key in scope
|
||||||
|
def /[A](key: TaskKey[A]): TaskKey[A] = key in scope
|
||||||
|
def /[A](key: InputKey[A]): InputKey[A] = key in scope
|
||||||
|
}
|
||||||
|
|
||||||
|
/** RichScopeAxis wraps a project axis to provide the `/` operator to `Zero` for scoping. */
|
||||||
|
final class RichScopeAxis(a: ScopeAxis[Reference]) {
|
||||||
|
private[this] def toScope: Scope = Scope(a, This, This, This)
|
||||||
|
|
||||||
|
def /(c: Configuration): RichConfiguration = new RichConfiguration(toScope in c)
|
||||||
|
|
||||||
|
// This is for handling `Zero / Zero / name`.
|
||||||
|
def /(configAxis: ScopeAxis[ConfigKey]): RichConfiguration =
|
||||||
|
new RichConfiguration(toScope.copy(config = configAxis))
|
||||||
|
|
||||||
|
// We don't know what the key is for yet, so just capture in a thunk.
|
||||||
|
def /[A](key: SettingKey[A]): SettingKeyThunk[A] = new SettingKeyThunk(toScope, key)
|
||||||
|
|
||||||
|
// We don't know what the key is for yet, so just capture in a thunk.
|
||||||
|
def /[A](key: TaskKey[A]): TaskKeyThunk[A] = new TaskKeyThunk(toScope, key)
|
||||||
|
|
||||||
|
// We don't know what the key is for yet, so just capture in a thunk.
|
||||||
|
def /[A](key: InputKey[A]): InputKeyThunk[A] = new InputKeyThunk(toScope, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SettingKeyThunk is a thunk used to hold a scope and a key
|
||||||
|
* while we're not sure if the key is terminal or task-scoping.
|
||||||
|
*/
|
||||||
|
final class SettingKeyThunk[A](base: Scope, key: SettingKey[A]) {
|
||||||
|
private[sbt] def materialize: SettingKey[A] = key in base
|
||||||
|
private[sbt] def rescope: RichScope = new RichScope(base in key.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TaskKeyThunk is a thunk used to hold a scope and a key
|
||||||
|
* while we're not sure if the key is terminal or task-scoping.
|
||||||
|
*/
|
||||||
|
final class TaskKeyThunk[A](base: Scope, key: TaskKey[A]) {
|
||||||
|
private[sbt] def materialize: TaskKey[A] = key in base
|
||||||
|
private[sbt] def rescope: RichScope = new RichScope(base in key.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InputKeyThunk is a thunk used to hold a scope and a key
|
||||||
|
* while we're not sure if the key is terminal or task-scoping.
|
||||||
|
*/
|
||||||
|
final class InputKeyThunk[A](base: Scope, key: InputKey[A]) {
|
||||||
|
private[sbt] def materialize: InputKey[A] = key in base
|
||||||
|
private[sbt] def rescope: RichScope = new RichScope(base in key.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,13 @@ final class ParsedKey(val key: ScopedKey[_], val mask: ScopeMask)
|
||||||
|
|
||||||
object Act {
|
object Act {
|
||||||
val ZeroString = "*"
|
val ZeroString = "*"
|
||||||
|
private[sbt] val GlobalIdent = "Global"
|
||||||
|
private[sbt] val ZeroIdent = "Zero"
|
||||||
|
private[sbt] val ThisBuildIdent = "ThisBuild"
|
||||||
|
|
||||||
|
// new separator for unified shell syntax. this allows optional whitespace around /.
|
||||||
|
private[sbt] val spacedSlash: Parser[Unit] =
|
||||||
|
token(OptSpace ~> '/' <~ OptSpace).examples("/").map(_ => ())
|
||||||
|
|
||||||
// this does not take aggregation into account
|
// this does not take aggregation into account
|
||||||
def scopedKey(index: KeyIndex,
|
def scopedKey(index: KeyIndex,
|
||||||
|
|
@ -52,12 +59,29 @@ object Act {
|
||||||
current: ProjectRef,
|
current: ProjectRef,
|
||||||
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||||
keyMap: Map[String, AttributeKey[_]]): Parser[Seq[Parser[ParsedKey]]] = {
|
keyMap: Map[String, AttributeKey[_]]): Parser[Seq[Parser[ParsedKey]]] = {
|
||||||
|
def fullKey =
|
||||||
for {
|
for {
|
||||||
rawProject <- optProjectRef(index, current)
|
rawProject <- optProjectRef(index, current)
|
||||||
proj = resolveProject(rawProject, current)
|
proj = resolveProject(rawProject, current)
|
||||||
confAmb <- config(index configs proj)
|
confAmb <- configIdent(index configs proj,
|
||||||
|
index configIdents proj,
|
||||||
|
index.fromConfigIdent(proj))
|
||||||
partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false)
|
partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false)
|
||||||
} yield taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask)
|
} yield taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask)
|
||||||
|
|
||||||
|
val globalIdent = token(GlobalIdent ~ spacedSlash) ^^^ ParsedGlobal
|
||||||
|
def globalKey =
|
||||||
|
for {
|
||||||
|
g <- globalIdent
|
||||||
|
} yield
|
||||||
|
taskKeyExtra(index,
|
||||||
|
defaultConfigs,
|
||||||
|
keyMap,
|
||||||
|
None,
|
||||||
|
ParsedZero,
|
||||||
|
ScopeMask(true, true, false, false))
|
||||||
|
|
||||||
|
globalKey | fullKey
|
||||||
}
|
}
|
||||||
|
|
||||||
def taskKeyExtra(
|
def taskKeyExtra(
|
||||||
|
|
@ -148,6 +172,23 @@ object Act {
|
||||||
token((ZeroString ^^^ ParsedZero | value(examples(ID, confs, "configuration"))) <~ sep) ?? Omitted
|
token((ZeroString ^^^ ParsedZero | value(examples(ID, confs, "configuration"))) <~ sep) ?? Omitted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New configuration parser that's able to parse configuration ident trailed by slash.
|
||||||
|
private[sbt] def configIdent(confs: Set[String],
|
||||||
|
idents: Set[String],
|
||||||
|
fromIdent: String => String): Parser[ParsedAxis[String]] = {
|
||||||
|
val oldSep: Parser[Char] = ':'
|
||||||
|
val sep: Parser[Unit] = spacedSlash !!! "Expected '/'"
|
||||||
|
token(
|
||||||
|
((ZeroString ^^^ ParsedZero) <~ oldSep)
|
||||||
|
| ((ZeroString ^^^ ParsedZero) <~ sep)
|
||||||
|
| ((ZeroIdent ^^^ ParsedZero) <~ sep)
|
||||||
|
| (value(examples(ID, confs, "configuration")) <~ oldSep)
|
||||||
|
| (value(examples(CapitalizedID, idents, "configuration ident") map {
|
||||||
|
fromIdent(_)
|
||||||
|
}) <~ sep)
|
||||||
|
) ?? Omitted
|
||||||
|
}
|
||||||
|
|
||||||
def configs(explicit: ParsedAxis[String],
|
def configs(explicit: ParsedAxis[String],
|
||||||
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||||
proj: Option[ResolvedReference],
|
proj: Option[ResolvedReference],
|
||||||
|
|
@ -156,7 +197,7 @@ object Act {
|
||||||
case Omitted =>
|
case Omitted =>
|
||||||
None +: defaultConfigurations(proj, index, defaultConfigs).flatMap(
|
None +: defaultConfigurations(proj, index, defaultConfigs).flatMap(
|
||||||
nonEmptyConfig(index, proj))
|
nonEmptyConfig(index, proj))
|
||||||
case ParsedZero => None :: Nil
|
case ParsedZero | ParsedGlobal => None :: Nil
|
||||||
case pv: ParsedValue[x] => Some(pv.value) :: Nil
|
case pv: ParsedValue[x] => Some(pv.value) :: Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,11 +261,15 @@ object Act {
|
||||||
val valid = allKnown ++ normKeys
|
val valid = allKnown ++ normKeys
|
||||||
val suggested = normKeys.map(_._1).toSet
|
val suggested = normKeys.map(_._1).toSet
|
||||||
val keyP = filterStrings(examples(ID, suggested, "key"), valid.keySet, "key") map valid
|
val keyP = filterStrings(examples(ID, suggested, "key"), valid.keySet, "key") map valid
|
||||||
(token(value(keyP) | ZeroString ^^^ ParsedZero) <~ token("::".id)) ?? Omitted
|
(token(
|
||||||
|
value(keyP)
|
||||||
|
| ZeroString ^^^ ParsedZero
|
||||||
|
| ZeroIdent ^^^ ParsedZero) <~ (token("::".id) | spacedSlash)) ?? Omitted
|
||||||
}
|
}
|
||||||
|
|
||||||
def resolveTask(task: ParsedAxis[AttributeKey[_]]): Option[AttributeKey[_]] =
|
def resolveTask(task: ParsedAxis[AttributeKey[_]]): Option[AttributeKey[_]] =
|
||||||
task match {
|
task match {
|
||||||
case ParsedZero | Omitted => None
|
case ParsedZero | ParsedGlobal | Omitted => None
|
||||||
case t: ParsedValue[AttributeKey[_]] @unchecked => Some(t.value)
|
case t: ParsedValue[AttributeKey[_]] @unchecked => Some(t.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,10 +305,36 @@ object Act {
|
||||||
}
|
}
|
||||||
|
|
||||||
def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedAxis[ResolvedReference]] = {
|
def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedAxis[ResolvedReference]] = {
|
||||||
val zero = token(ZeroString ~ '/') ^^^ ParsedZero
|
val global = token(ZeroString ~ spacedSlash) ^^^ ParsedZero
|
||||||
val trailing = '/' !!! "Expected '/' (if selecting a project)"
|
val zeroIdent = token(ZeroIdent ~ spacedSlash) ^^^ ParsedZero
|
||||||
zero | value(resolvedReference(index, currentBuild, trailing))
|
val thisBuildIdent = value(token(ThisBuildIdent ~ spacedSlash) ^^^ BuildRef(currentBuild))
|
||||||
|
val trailing = spacedSlash !!! "Expected '/' (if selecting a project)"
|
||||||
|
global | zeroIdent | thisBuildIdent |
|
||||||
|
value(resolvedReferenceIdent(index, currentBuild, trailing)) |
|
||||||
|
value(resolvedReference(index, currentBuild, trailing))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private[sbt] def resolvedReferenceIdent(index: KeyIndex,
|
||||||
|
currentBuild: URI,
|
||||||
|
trailing: Parser[_]): Parser[ResolvedReference] = {
|
||||||
|
def projectID(uri: URI) =
|
||||||
|
token(
|
||||||
|
DQuoteChar ~> examplesStrict(ID, index projects uri, "project ID") <~ DQuoteChar <~ OptSpace <~ ")" <~ trailing)
|
||||||
|
def projectRef(uri: URI) = projectID(uri) map { id =>
|
||||||
|
ProjectRef(uri, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
val uris = index.buildURIs
|
||||||
|
val resolvedURI = Uri(uris).map(uri => Scope.resolveBuild(currentBuild, uri))
|
||||||
|
|
||||||
|
val buildRef = token(
|
||||||
|
"ProjectRef(" ~> OptSpace ~> "uri(" ~> OptSpace ~> DQuoteChar ~>
|
||||||
|
resolvedURI <~ DQuoteChar <~ OptSpace <~ ")" <~ spacedComma)
|
||||||
|
buildRef flatMap { uri =>
|
||||||
|
projectRef(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def resolvedReference(index: KeyIndex,
|
def resolvedReference(index: KeyIndex,
|
||||||
currentBuild: URI,
|
currentBuild: URI,
|
||||||
trailing: Parser[_]): Parser[ResolvedReference] = {
|
trailing: Parser[_]): Parser[ResolvedReference] = {
|
||||||
|
|
@ -284,11 +355,13 @@ object Act {
|
||||||
}
|
}
|
||||||
def optProjectRef(index: KeyIndex, current: ProjectRef): Parser[ParsedAxis[ResolvedReference]] =
|
def optProjectRef(index: KeyIndex, current: ProjectRef): Parser[ParsedAxis[ResolvedReference]] =
|
||||||
projectRef(index, current.build) ?? Omitted
|
projectRef(index, current.build) ?? Omitted
|
||||||
|
|
||||||
def resolveProject(parsed: ParsedAxis[ResolvedReference],
|
def resolveProject(parsed: ParsedAxis[ResolvedReference],
|
||||||
current: ProjectRef): Option[ResolvedReference] =
|
current: ProjectRef): Option[ResolvedReference] =
|
||||||
parsed match {
|
parsed match {
|
||||||
case Omitted => Some(current)
|
case Omitted => Some(current)
|
||||||
case ParsedZero => None
|
case ParsedZero => None
|
||||||
|
case ParsedGlobal => None
|
||||||
case pv: ParsedValue[rr] => Some(pv.value)
|
case pv: ParsedValue[rr] => Some(pv.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -375,6 +448,7 @@ object Act {
|
||||||
sealed trait ParsedAxis[+T] {
|
sealed trait ParsedAxis[+T] {
|
||||||
final def isExplicit = this != Omitted
|
final def isExplicit = this != Omitted
|
||||||
}
|
}
|
||||||
|
final object ParsedGlobal extends ParsedAxis[Nothing]
|
||||||
final object ParsedZero extends ParsedAxis[Nothing]
|
final object ParsedZero extends ParsedAxis[Nothing]
|
||||||
final object Omitted extends ParsedAxis[Nothing]
|
final object Omitted extends ParsedAxis[Nothing]
|
||||||
final class ParsedValue[T](val value: T) extends ParsedAxis[T]
|
final class ParsedValue[T](val value: T) extends ParsedAxis[T]
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,32 @@ import Def.ScopedKey
|
||||||
import sbt.internal.util.complete.DefaultParsers.validID
|
import sbt.internal.util.complete.DefaultParsers.validID
|
||||||
import sbt.internal.util.Types.some
|
import sbt.internal.util.Types.some
|
||||||
import sbt.internal.util.{ AttributeKey, Relation }
|
import sbt.internal.util.{ AttributeKey, Relation }
|
||||||
|
import sbt.librarymanagement.Configuration
|
||||||
|
|
||||||
object KeyIndex {
|
object KeyIndex {
|
||||||
def empty: ExtendableKeyIndex = new KeyIndex0(emptyBuildIndex)
|
def empty: ExtendableKeyIndex = new KeyIndex0(emptyBuildIndex)
|
||||||
def apply(known: Iterable[ScopedKey[_]], projects: Map[URI, Set[String]]): ExtendableKeyIndex =
|
def apply(known: Iterable[ScopedKey[_]],
|
||||||
(base(projects) /: known) { _ add _ }
|
projects: Map[URI, Set[String]],
|
||||||
|
configurations: Map[String, Seq[Configuration]]): ExtendableKeyIndex =
|
||||||
|
(base(projects, configurations) /: known) { _ add _ }
|
||||||
def aggregate(known: Iterable[ScopedKey[_]],
|
def aggregate(known: Iterable[ScopedKey[_]],
|
||||||
extra: BuildUtil[_],
|
extra: BuildUtil[_],
|
||||||
projects: Map[URI, Set[String]]): ExtendableKeyIndex =
|
projects: Map[URI, Set[String]],
|
||||||
(base(projects) /: known) { (index, key) =>
|
configurations: Map[String, Seq[Configuration]]): ExtendableKeyIndex =
|
||||||
|
(base(projects, configurations) /: known) { (index, key) =>
|
||||||
index.addAggregated(key, extra)
|
index.addAggregated(key, extra)
|
||||||
}
|
}
|
||||||
private[this] def base(projects: Map[URI, Set[String]]): ExtendableKeyIndex = {
|
private[this] def base(projects: Map[URI, Set[String]],
|
||||||
val data = for ((uri, ids) <- projects) yield {
|
configurations: Map[String, Seq[Configuration]]): ExtendableKeyIndex = {
|
||||||
val data = ids.map(id => Option(id) -> new ConfigIndex(Map.empty))
|
val data = for {
|
||||||
|
(uri, ids) <- projects
|
||||||
|
} yield {
|
||||||
|
val data = ids map { id =>
|
||||||
|
val configs = configurations.getOrElse(id, Seq())
|
||||||
|
Option(id) -> new ConfigIndex(Map.empty, Map(configs map { c =>
|
||||||
|
(c.name, c.id)
|
||||||
|
}: _*))
|
||||||
|
}
|
||||||
Option(uri) -> new ProjectIndex(data.toMap)
|
Option(uri) -> new ProjectIndex(data.toMap)
|
||||||
}
|
}
|
||||||
new KeyIndex0(new BuildIndex(data.toMap))
|
new KeyIndex0(new BuildIndex(data.toMap))
|
||||||
|
|
@ -33,6 +45,14 @@ object KeyIndex {
|
||||||
def projects(uri: URI) = concat(_.projects(uri))
|
def projects(uri: URI) = concat(_.projects(uri))
|
||||||
def exists(project: Option[ResolvedReference]): Boolean = indices.exists(_ exists project)
|
def exists(project: Option[ResolvedReference]): Boolean = indices.exists(_ exists project)
|
||||||
def configs(proj: Option[ResolvedReference]) = concat(_.configs(proj))
|
def configs(proj: Option[ResolvedReference]) = concat(_.configs(proj))
|
||||||
|
private[sbt] def configIdents(proj: Option[ResolvedReference]) = concat(_.configIdents(proj))
|
||||||
|
private[sbt] def fromConfigIdent(proj: Option[ResolvedReference])(configIdent: String): String =
|
||||||
|
(indices find { idx =>
|
||||||
|
idx.exists(proj)
|
||||||
|
}) match {
|
||||||
|
case Some(idx) => idx.fromConfigIdent(proj)(configIdent)
|
||||||
|
case _ => Scope.unguessConfigIdent(configIdent)
|
||||||
|
}
|
||||||
def tasks(proj: Option[ResolvedReference], conf: Option[String]) = concat(_.tasks(proj, conf))
|
def tasks(proj: Option[ResolvedReference], conf: Option[String]) = concat(_.tasks(proj, conf))
|
||||||
def tasks(proj: Option[ResolvedReference], conf: Option[String], key: String) =
|
def tasks(proj: Option[ResolvedReference], conf: Option[String], key: String) =
|
||||||
concat(_.tasks(proj, conf, key))
|
concat(_.tasks(proj, conf, key))
|
||||||
|
|
@ -46,7 +66,7 @@ object KeyIndex {
|
||||||
private[sbt] def getOr[A, B](m: Map[A, B], key: A, or: B): B = m.getOrElse(key, or)
|
private[sbt] def getOr[A, B](m: Map[A, B], key: A, or: B): B = m.getOrElse(key, or)
|
||||||
private[sbt] def keySet[A, B](m: Map[Option[A], B]): Set[A] = m.keys.flatten.toSet
|
private[sbt] def keySet[A, B](m: Map[Option[A], B]): Set[A] = m.keys.flatten.toSet
|
||||||
private[sbt] val emptyAKeyIndex = new AKeyIndex(Relation.empty)
|
private[sbt] val emptyAKeyIndex = new AKeyIndex(Relation.empty)
|
||||||
private[sbt] val emptyConfigIndex = new ConfigIndex(Map.empty)
|
private[sbt] val emptyConfigIndex = new ConfigIndex(Map.empty, Map.empty)
|
||||||
private[sbt] val emptyProjectIndex = new ProjectIndex(Map.empty)
|
private[sbt] val emptyProjectIndex = new ProjectIndex(Map.empty)
|
||||||
private[sbt] val emptyBuildIndex = new BuildIndex(Map.empty)
|
private[sbt] val emptyBuildIndex = new BuildIndex(Map.empty)
|
||||||
}
|
}
|
||||||
|
|
@ -73,6 +93,8 @@ trait KeyIndex {
|
||||||
def keys(proj: Option[ResolvedReference],
|
def keys(proj: Option[ResolvedReference],
|
||||||
conf: Option[String],
|
conf: Option[String],
|
||||||
task: Option[AttributeKey[_]]): Set[String]
|
task: Option[AttributeKey[_]]): Set[String]
|
||||||
|
private[sbt] def configIdents(project: Option[ResolvedReference]): Set[String]
|
||||||
|
private[sbt] def fromConfigIdent(proj: Option[ResolvedReference])(configIdent: String): String
|
||||||
}
|
}
|
||||||
trait ExtendableKeyIndex extends KeyIndex {
|
trait ExtendableKeyIndex extends KeyIndex {
|
||||||
def add(scoped: ScopedKey[_]): ExtendableKeyIndex
|
def add(scoped: ScopedKey[_]): ExtendableKeyIndex
|
||||||
|
|
@ -87,13 +109,34 @@ private[sbt] final class AKeyIndex(val data: Relation[Option[AttributeKey[_]], S
|
||||||
def tasks: Set[AttributeKey[_]] = data._1s.flatten.toSet
|
def tasks: Set[AttributeKey[_]] = data._1s.flatten.toSet
|
||||||
def tasks(key: String): Set[AttributeKey[_]] = data.reverse(key).flatten.toSet
|
def tasks(key: String): Set[AttributeKey[_]] = data.reverse(key).flatten.toSet
|
||||||
}
|
}
|
||||||
private[sbt] final class ConfigIndex(val data: Map[Option[String], AKeyIndex]) {
|
|
||||||
|
/*
|
||||||
|
* data contains the mapping between a configuration and keys.
|
||||||
|
* identData contains the mapping between a configuration and its identifier.
|
||||||
|
*/
|
||||||
|
private[sbt] final class ConfigIndex(val data: Map[Option[String], AKeyIndex],
|
||||||
|
val identData: Map[String, String]) {
|
||||||
def add(config: Option[String],
|
def add(config: Option[String],
|
||||||
task: Option[AttributeKey[_]],
|
task: Option[AttributeKey[_]],
|
||||||
key: AttributeKey[_]): ConfigIndex =
|
key: AttributeKey[_]): ConfigIndex = {
|
||||||
new ConfigIndex(data updated (config, keyIndex(config).add(task, key)))
|
new ConfigIndex(data updated (config, keyIndex(config).add(task, key)), this.identData)
|
||||||
|
}
|
||||||
|
|
||||||
def keyIndex(conf: Option[String]): AKeyIndex = getOr(data, conf, emptyAKeyIndex)
|
def keyIndex(conf: Option[String]): AKeyIndex = getOr(data, conf, emptyAKeyIndex)
|
||||||
def configs: Set[String] = keySet(data)
|
def configs: Set[String] = keySet(data)
|
||||||
|
|
||||||
|
private[sbt] val configIdentsInverse: Map[String, String] =
|
||||||
|
identData map { _.swap }
|
||||||
|
|
||||||
|
private[sbt] lazy val idents: Set[String] =
|
||||||
|
configs map { config =>
|
||||||
|
identData.getOrElse(config, Scope.guessConfigIdent(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
// guess Configuration name from an identifier.
|
||||||
|
// There's a guessing involved because we could have scoped key that Project is not aware of.
|
||||||
|
private[sbt] def fromConfigIdent(ident: String): String =
|
||||||
|
configIdentsInverse.getOrElse(ident, Scope.unguessConfigIdent(ident))
|
||||||
}
|
}
|
||||||
private[sbt] final class ProjectIndex(val data: Map[Option[String], ConfigIndex]) {
|
private[sbt] final class ProjectIndex(val data: Map[Option[String], ConfigIndex]) {
|
||||||
def add(id: Option[String],
|
def add(id: Option[String],
|
||||||
|
|
@ -122,6 +165,13 @@ private[sbt] final class KeyIndex0(val data: BuildIndex) extends ExtendableKeyIn
|
||||||
data.data.get(build).flatMap(_.data.get(project)).isDefined
|
data.data.get(build).flatMap(_.data.get(project)).isDefined
|
||||||
}
|
}
|
||||||
def configs(project: Option[ResolvedReference]): Set[String] = confIndex(project).configs
|
def configs(project: Option[ResolvedReference]): Set[String] = confIndex(project).configs
|
||||||
|
|
||||||
|
private[sbt] def configIdents(project: Option[ResolvedReference]): Set[String] =
|
||||||
|
confIndex(project).idents
|
||||||
|
|
||||||
|
private[sbt] def fromConfigIdent(proj: Option[ResolvedReference])(configIdent: String): String =
|
||||||
|
confIndex(proj).fromConfigIdent(configIdent)
|
||||||
|
|
||||||
def tasks(proj: Option[ResolvedReference], conf: Option[String]): Set[AttributeKey[_]] =
|
def tasks(proj: Option[ResolvedReference], conf: Option[String]): Set[AttributeKey[_]] =
|
||||||
keyIndex(proj, conf).tasks
|
keyIndex(proj, conf).tasks
|
||||||
def tasks(proj: Option[ResolvedReference],
|
def tasks(proj: Option[ResolvedReference],
|
||||||
|
|
|
||||||
|
|
@ -324,8 +324,13 @@ private[sbt] object Load {
|
||||||
val attributeKeys = Index.attributeKeys(data) ++ keys.map(_.key)
|
val attributeKeys = Index.attributeKeys(data) ++ keys.map(_.key)
|
||||||
val scopedKeys = keys ++ data.allKeys((s, k) => ScopedKey(s, k)).toVector
|
val scopedKeys = keys ++ data.allKeys((s, k) => ScopedKey(s, k)).toVector
|
||||||
val projectsMap = projects.mapValues(_.defined.keySet)
|
val projectsMap = projects.mapValues(_.defined.keySet)
|
||||||
val keyIndex = KeyIndex(scopedKeys.toVector, projectsMap)
|
val configsMap: Map[String, Seq[Configuration]] = Map(projects.values.toSeq flatMap { bu =>
|
||||||
val aggIndex = KeyIndex.aggregate(scopedKeys.toVector, extra(keyIndex), projectsMap)
|
bu.defined map {
|
||||||
|
case (k, v) => (k, v.configurations)
|
||||||
|
}
|
||||||
|
}: _*)
|
||||||
|
val keyIndex = KeyIndex(scopedKeys.toVector, projectsMap, configsMap)
|
||||||
|
val aggIndex = KeyIndex.aggregate(scopedKeys.toVector, extra(keyIndex), projectsMap, configsMap)
|
||||||
new StructureIndex(
|
new StructureIndex(
|
||||||
Index.stringToKeyMap(attributeKeys),
|
Index.stringToKeyMap(attributeKeys),
|
||||||
Index.taskToKeyMap(data),
|
Index.taskToKeyMap(data),
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,13 @@ package sbt
|
||||||
import Project._
|
import Project._
|
||||||
import sbt.internal.util.Types.idFun
|
import sbt.internal.util.Types.idFun
|
||||||
import sbt.internal.TestBuild._
|
import sbt.internal.TestBuild._
|
||||||
|
import sbt.librarymanagement.Configuration
|
||||||
import org.scalacheck._
|
import org.scalacheck._
|
||||||
import Prop._
|
import Prop._
|
||||||
import Gen._
|
import Gen._
|
||||||
|
|
||||||
object Delegates extends Properties("delegates") {
|
object Delegates extends Properties("delegates") {
|
||||||
property("generate non-empty configs") = forAll { (c: Seq[Config]) =>
|
property("generate non-empty configs") = forAll { (c: Seq[Configuration]) =>
|
||||||
c.nonEmpty
|
c.nonEmpty
|
||||||
}
|
}
|
||||||
property("generate non-empty tasks") = forAll { (t: Seq[Taskk]) =>
|
property("generate non-empty tasks") = forAll { (t: Seq[Taskk]) =>
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,17 @@ object ParseKey extends Properties("Key parser test") {
|
||||||
|
|
||||||
property("An explicitly specified axis is always parsed to that explicit value") =
|
property("An explicitly specified axis is always parsed to that explicit value") =
|
||||||
forAllNoShrink(structureDefinedKey) { (skm: StructureKeyMask) =>
|
forAllNoShrink(structureDefinedKey) { (skm: StructureKeyMask) =>
|
||||||
import skm.{ structure, key, mask }
|
import skm.{ structure, key, mask => mask0 }
|
||||||
|
|
||||||
|
val hasZeroConfig = key.scope.config == Zero
|
||||||
|
val mask = if (hasZeroConfig) mask0.copy(project = true) else mask0
|
||||||
val expected = resolve(structure, key, mask)
|
val expected = resolve(structure, key, mask)
|
||||||
val string = displayMasked(key, mask)
|
// Note that this explicitly displays the configuration axis set to Zero.
|
||||||
|
// This is to disambiguate `proj/Zero/name`, which could render potentially
|
||||||
("Key: " + displayFull(key)) |:
|
// as `Zero/name`, but could be interpretted as `Zero/Zero/name`.
|
||||||
parseExpected(structure, string, expected, mask)
|
val s = displayMasked(key, mask, hasZeroConfig)
|
||||||
|
("Key: " + displayPedantic(key)) |:
|
||||||
|
parseExpected(structure, s, expected, mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
property("An unspecified project axis resolves to the current project") =
|
property("An unspecified project axis resolves to the current project") =
|
||||||
|
|
@ -37,12 +41,15 @@ object ParseKey extends Properties("Key parser test") {
|
||||||
|
|
||||||
val mask = skm.mask.copy(project = false)
|
val mask = skm.mask.copy(project = false)
|
||||||
val string = displayMasked(key, mask)
|
val string = displayMasked(key, mask)
|
||||||
|
// skip when config axis is set to Zero
|
||||||
|
val hasZeroConfig = key.scope.config == Zero
|
||||||
|
|
||||||
("Key: " + displayFull(key)) |:
|
("Key: " + displayPedantic(key)) |:
|
||||||
("Mask: " + mask) |:
|
("Mask: " + mask) |:
|
||||||
("Current: " + structure.current) |:
|
("Current: " + structure.current) |:
|
||||||
parse(structure, string) {
|
parse(structure, string) {
|
||||||
case Left(err) => false
|
case Left(err) => false
|
||||||
|
case Right(sk) if hasZeroConfig => true
|
||||||
case Right(sk) => sk.scope.project == Select(structure.current)
|
case Right(sk) => sk.scope.project == Select(structure.current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +60,7 @@ object ParseKey extends Properties("Key parser test") {
|
||||||
val mask = skm.mask.copy(task = false)
|
val mask = skm.mask.copy(task = false)
|
||||||
val string = displayMasked(key, mask)
|
val string = displayMasked(key, mask)
|
||||||
|
|
||||||
("Key: " + displayFull(key)) |:
|
("Key: " + displayPedantic(key)) |:
|
||||||
("Mask: " + mask) |:
|
("Mask: " + mask) |:
|
||||||
parse(structure, string) {
|
parse(structure, string) {
|
||||||
case Left(err) => false
|
case Left(err) => false
|
||||||
|
|
@ -69,15 +76,18 @@ object ParseKey extends Properties("Key parser test") {
|
||||||
val string = displayMasked(key, mask)
|
val string = displayMasked(key, mask)
|
||||||
val resolvedConfig = Resolve.resolveConfig(structure.extra, key.key, mask)(key.scope).config
|
val resolvedConfig = Resolve.resolveConfig(structure.extra, key.key, mask)(key.scope).config
|
||||||
|
|
||||||
("Key: " + displayFull(key)) |:
|
("Key: " + displayPedantic(key)) |:
|
||||||
("Mask: " + mask) |:
|
("Mask: " + mask) |:
|
||||||
("Expected configuration: " + resolvedConfig.map(_.name)) |:
|
("Expected configuration: " + resolvedConfig.map(_.name)) |:
|
||||||
parse(structure, string) {
|
parse(structure, string) {
|
||||||
case Right(sk) => sk.scope.config == resolvedConfig
|
case Right(sk) => (sk.scope.config == resolvedConfig) || (sk.scope == Scope.GlobalScope)
|
||||||
case Left(err) => false
|
case Left(err) => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def displayPedantic(scoped: ScopedKey[_]): String =
|
||||||
|
Scope.displayPedantic(scoped.scope, scoped.key.label)
|
||||||
|
|
||||||
lazy val structureDefinedKey: Gen[StructureKeyMask] = structureKeyMask { s =>
|
lazy val structureDefinedKey: Gen[StructureKeyMask] = structureKeyMask { s =>
|
||||||
for (scope <- TestBuild.scope(s.env); key <- oneOf(s.allAttributeKeys.toSeq))
|
for (scope <- TestBuild.scope(s.env); key <- oneOf(s.allAttributeKeys.toSeq))
|
||||||
yield ScopedKey(scope, key)
|
yield ScopedKey(scope, key)
|
||||||
|
|
@ -101,7 +111,10 @@ object ParseKey extends Properties("Key parser test") {
|
||||||
("Mask: " + mask) |:
|
("Mask: " + mask) |:
|
||||||
parse(structure, s) {
|
parse(structure, s) {
|
||||||
case Left(err) => false
|
case Left(err) => false
|
||||||
case Right(sk) => Project.equal(sk, expected, mask)
|
case Right(sk) =>
|
||||||
|
(s"${sk}.key == ${expected}.key: ${sk.key == expected.key}") |:
|
||||||
|
(s"${sk.scope} == ${expected.scope}: ${Scope.equal(sk.scope, expected.scope, mask)}") |:
|
||||||
|
Project.equal(sk, expected, mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(structure: Structure, s: String)(f: Either[String, ScopedKey[_]] => Prop): Prop = {
|
def parse(structure: Structure, s: String)(f: Either[String, ScopedKey[_]] => Prop): Prop = {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import Def.{ ScopedKey, Setting }
|
||||||
import sbt.internal.util.{ AttributeKey, AttributeMap, Relation, Settings }
|
import sbt.internal.util.{ AttributeKey, AttributeMap, Relation, Settings }
|
||||||
import sbt.internal.util.Types.{ const, some }
|
import sbt.internal.util.Types.{ const, some }
|
||||||
import sbt.internal.util.complete.Parser
|
import sbt.internal.util.complete.Parser
|
||||||
|
import sbt.librarymanagement.Configuration
|
||||||
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import org.scalacheck._
|
import org.scalacheck._
|
||||||
|
|
@ -119,7 +120,7 @@ abstract class TestBuild {
|
||||||
lazy val allProjects = builds.flatMap(_.allProjects)
|
lazy val allProjects = builds.flatMap(_.allProjects)
|
||||||
def rootProject(uri: URI): String = buildMap(uri).root.id
|
def rootProject(uri: URI): String = buildMap(uri).root.id
|
||||||
def inheritConfig(ref: ResolvedReference, config: ConfigKey) =
|
def inheritConfig(ref: ResolvedReference, config: ConfigKey) =
|
||||||
projectFor(ref).confMap(config.name).extended map toConfigKey
|
projectFor(ref).confMap(config.name).extendsConfigs map toConfigKey
|
||||||
def inheritTask(task: AttributeKey[_]) = taskMap.get(task) match {
|
def inheritTask(task: AttributeKey[_]) = taskMap.get(task) match {
|
||||||
case None => Nil; case Some(t) => t.delegates map getKey
|
case None => Nil; case Some(t) => t.delegates map getKey
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +145,7 @@ abstract class TestBuild {
|
||||||
} yield Scope(project = ref, config = c, task = t, extra = Zero)
|
} yield Scope(project = ref, config = c, task = t, extra = Zero)
|
||||||
}
|
}
|
||||||
def getKey: Taskk => AttributeKey[_] = _.key
|
def getKey: Taskk => AttributeKey[_] = _.key
|
||||||
def toConfigKey: Config => ConfigKey = c => ConfigKey(c.name)
|
def toConfigKey: Configuration => ConfigKey = c => ConfigKey(c.name)
|
||||||
final class Build(val uri: URI, val projects: Seq[Proj]) {
|
final class Build(val uri: URI, val projects: Seq[Proj]) {
|
||||||
override def toString = "Build " + uri.toString + " :\n " + projects.mkString("\n ")
|
override def toString = "Build " + uri.toString + " :\n " + projects.mkString("\n ")
|
||||||
val allProjects = projects map { p =>
|
val allProjects = projects map { p =>
|
||||||
|
|
@ -156,7 +157,7 @@ abstract class TestBuild {
|
||||||
final class Proj(
|
final class Proj(
|
||||||
val id: String,
|
val id: String,
|
||||||
val delegates: Seq[ProjectRef],
|
val delegates: Seq[ProjectRef],
|
||||||
val configurations: Seq[Config]
|
val configurations: Seq[Configuration]
|
||||||
) {
|
) {
|
||||||
override def toString =
|
override def toString =
|
||||||
"Project " + id + "\n Delegates:\n " + delegates.mkString("\n ") +
|
"Project " + id + "\n Delegates:\n " + delegates.mkString("\n ") +
|
||||||
|
|
@ -164,9 +165,6 @@ abstract class TestBuild {
|
||||||
val confMap = mapBy(configurations)(_.name)
|
val confMap = mapBy(configurations)(_.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Config(val name: String, val extended: Seq[Config]) {
|
|
||||||
override def toString = name + " (extends: " + extended.map(_.name).mkString(", ") + ")"
|
|
||||||
}
|
|
||||||
final class Taskk(val key: AttributeKey[String], val delegates: Seq[Taskk]) {
|
final class Taskk(val key: AttributeKey[String], val delegates: Seq[Taskk]) {
|
||||||
override def toString =
|
override def toString =
|
||||||
key.label + " (delegates: " + delegates.map(_.key.label).mkString(", ") + ")"
|
key.label + " (delegates: " + delegates.map(_.key.label).mkString(", ") + ")"
|
||||||
|
|
@ -222,7 +220,15 @@ abstract class TestBuild {
|
||||||
val keys = data.allKeys((s, key) => ScopedKey(s, key))
|
val keys = data.allKeys((s, key) => ScopedKey(s, key))
|
||||||
val keyMap = keys.map(k => (k.key.label, k.key)).toMap[String, AttributeKey[_]]
|
val keyMap = keys.map(k => (k.key.label, k.key)).toMap[String, AttributeKey[_]]
|
||||||
val projectsMap = env.builds.map(b => (b.uri, b.projects.map(_.id).toSet)).toMap
|
val projectsMap = env.builds.map(b => (b.uri, b.projects.map(_.id).toSet)).toMap
|
||||||
new Structure(env, current, data, KeyIndex(keys, projectsMap), keyMap)
|
val confs = for {
|
||||||
|
b <- env.builds.toVector
|
||||||
|
p <- b.projects.toVector
|
||||||
|
c <- p.configurations.toVector
|
||||||
|
} yield c
|
||||||
|
val confMap = Map(confs map { c =>
|
||||||
|
(c.name, Seq(c))
|
||||||
|
}: _*)
|
||||||
|
new Structure(env, current, data, KeyIndex(keys, projectsMap, confMap), keyMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit lazy val mkEnv: Gen[Env] = {
|
implicit lazy val mkEnv: Gen[Env] = {
|
||||||
|
|
@ -239,7 +245,14 @@ abstract class TestBuild {
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit lazy val idGen: Gen[String] =
|
implicit lazy val idGen: Gen[String] =
|
||||||
for (size <- chooseShrinkable(1, MaxIDSize); cs <- listOfN(size, alphaChar)) yield cs.mkString
|
for {
|
||||||
|
size <- chooseShrinkable(1, MaxIDSize)
|
||||||
|
cs <- listOfN(size, alphaChar)
|
||||||
|
} yield {
|
||||||
|
val xs = cs.mkString
|
||||||
|
xs.take(1).toLowerCase + xs.drop(1)
|
||||||
|
}
|
||||||
|
|
||||||
implicit lazy val optIDGen: Gen[Option[String]] = frequency((1, idGen map some.fn), (1, None))
|
implicit lazy val optIDGen: Gen[Option[String]] = frequency((1, idGen map some.fn), (1, None))
|
||||||
implicit lazy val uriGen: Gen[URI] = for (sch <- idGen; ssp <- idGen; frag <- optIDGen)
|
implicit lazy val uriGen: Gen[URI] = for (sch <- idGen; ssp <- idGen; frag <- optIDGen)
|
||||||
yield new URI(sch, ssp, frag.orNull)
|
yield new URI(sch, ssp, frag.orNull)
|
||||||
|
|
@ -256,7 +269,7 @@ abstract class TestBuild {
|
||||||
implicit def genProjects(build: URI)(implicit genID: Gen[String],
|
implicit def genProjects(build: URI)(implicit genID: Gen[String],
|
||||||
maxDeps: Gen[Int],
|
maxDeps: Gen[Int],
|
||||||
count: Gen[Int],
|
count: Gen[Int],
|
||||||
confs: Gen[Seq[Config]]): Gen[Seq[Proj]] =
|
confs: Gen[Seq[Configuration]]): Gen[Seq[Proj]] =
|
||||||
genAcyclic(maxDeps, genID, count) { (id: String) =>
|
genAcyclic(maxDeps, genID, count) { (id: String) =>
|
||||||
for (cs <- confs) yield { (deps: Seq[Proj]) =>
|
for (cs <- confs) yield { (deps: Seq[Proj]) =>
|
||||||
new Proj(id, deps.map { dep =>
|
new Proj(id, deps.map { dep =>
|
||||||
|
|
@ -264,10 +277,16 @@ abstract class TestBuild {
|
||||||
}, cs)
|
}, cs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def genConfigs(implicit genName: Gen[String],
|
def genConfigs(implicit genName: Gen[String],
|
||||||
maxDeps: Gen[Int],
|
maxDeps: Gen[Int],
|
||||||
count: Gen[Int]): Gen[Seq[Config]] =
|
count: Gen[Int]): Gen[Seq[Configuration]] =
|
||||||
genAcyclicDirect[Config, String](maxDeps, genName, count)((key, deps) => new Config(key, deps))
|
genAcyclicDirect[Configuration, String](maxDeps, genName, count)(
|
||||||
|
(key, deps) =>
|
||||||
|
Configuration
|
||||||
|
.of(key.capitalize, key)
|
||||||
|
.withExtendsConfigs(deps.toVector))
|
||||||
|
|
||||||
def genTasks(implicit genName: Gen[String], maxDeps: Gen[Int], count: Gen[Int]): Gen[Seq[Taskk]] =
|
def genTasks(implicit genName: Gen[String], maxDeps: Gen[Int], count: Gen[Int]): Gen[Seq[Taskk]] =
|
||||||
genAcyclicDirect[Taskk, String](maxDeps, genName, count)((key, deps) =>
|
genAcyclicDirect[Taskk, String](maxDeps, genName, count)((key, deps) =>
|
||||||
new Taskk(AttributeKey[String](key), deps))
|
new Taskk(AttributeKey[String](key), deps))
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ object SettingQueryTest extends org.specs2.mutable.Specification {
|
||||||
|
|
||||||
"scalaVersion" in qko("Not a valid project ID: scalaVersion\\nscalaVersion\\n ^")
|
"scalaVersion" in qko("Not a valid project ID: scalaVersion\\nscalaVersion\\n ^")
|
||||||
"t/scalacOptions" in qko(
|
"t/scalacOptions" in qko(
|
||||||
s"Key {$baseUri}t/compile:scalacOptions is a task, can only query settings")
|
s"""Key ProjectRef(uri(\\"$baseUri\\"),\\"t\\")/Compile/scalacOptions is a task, can only query settings""")
|
||||||
"t/fooo" in qko(
|
"t/fooo" in qko(
|
||||||
"Expected ':' (if selecting a configuration)\\nNot a valid key: fooo (similar: fork)\\nt/fooo\\n ^")
|
"Expected ':' (if selecting a configuration)\\nNot a valid key: fooo (similar: fork)\\nt/fooo\\n ^")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
### Fixes with compatibility implications
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
- Unifies sbt shell and build.sbt syntax. See below.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
### Unified slash syntax for sbt shell and build.sbt
|
||||||
|
|
||||||
|
This adds unified slash syntax for both sbt shell and the build.sbt DSL.
|
||||||
|
Instead of the current `<project-id>/config:intask::key`, this adds
|
||||||
|
`<project-id>/<config-ident>/intask/key` where `<config-ident>` is the Scala identifier
|
||||||
|
notation for the configurations like `Compile` and `Test`. (The old shell syntax will continue to function)
|
||||||
|
|
||||||
|
These examples work both from the shell and in build.sbt.
|
||||||
|
|
||||||
|
Global / cancelable
|
||||||
|
ThisBuild / scalaVersion
|
||||||
|
Test / test
|
||||||
|
root / Compile / compile / scalacOptions
|
||||||
|
ProjectRef(uri("file:/xxx/helloworld/"),"root")/Compile/scalacOptions
|
||||||
|
Zero / Zero / name
|
||||||
|
|
||||||
|
The inspect command now outputs something that can be copy-pasted:
|
||||||
|
|
||||||
|
> inspect compile
|
||||||
|
[info] Task: sbt.inc.Analysis
|
||||||
|
[info] Description:
|
||||||
|
[info] Compiles sources.
|
||||||
|
[info] Provided by:
|
||||||
|
[info] ProjectRef(uri("file:/xxx/helloworld/"),"root")/Compile/compile
|
||||||
|
[info] Defined at:
|
||||||
|
[info] (sbt.Defaults) Defaults.scala:326
|
||||||
|
[info] Dependencies:
|
||||||
|
[info] Compile/manipulateBytecode
|
||||||
|
[info] Compile/incCompileSetup
|
||||||
|
....
|
||||||
|
|
||||||
|
[#3434][3434] by [@eed3si9n][@eed3si9n]
|
||||||
|
|
||||||
|
[3434]: https://github.com/sbt/sbt/pull/3434
|
||||||
|
[@eed3si9n]: https://github.com/eed3si9n
|
||||||
|
[@dwijnand]: http://github.com/dwijnand
|
||||||
|
|
@ -16,6 +16,7 @@ package object sbt
|
||||||
with sbt.ScopeFilter.Make
|
with sbt.ScopeFilter.Make
|
||||||
with sbt.BuildSyntax
|
with sbt.BuildSyntax
|
||||||
with sbt.OptionSyntax
|
with sbt.OptionSyntax
|
||||||
|
with sbt.SlashSyntax
|
||||||
with sbt.Import {
|
with sbt.Import {
|
||||||
// IO
|
// IO
|
||||||
def uri(s: String): URI = new URI(s)
|
def uri(s: String): URI = new URI(s)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
lazy val akey = AttributeKey[Int]("TestKey")
|
lazy val akey = AttributeKey[Int]("testKey")
|
||||||
lazy val testTask = taskKey[String]("")
|
lazy val testTask = taskKey[String]("")
|
||||||
lazy val check = inputKey[Unit]("")
|
lazy val check = inputKey[Unit]("")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
lazy val plugins = (project in file("."))
|
||||||
|
.dependsOn(proguard, git)
|
||||||
|
|
||||||
|
// e7b4732969c137db1b5
|
||||||
|
// d4974f7362bf55d3f52
|
||||||
|
lazy val proguard = uri("git://github.com/sbt/sbt-proguard.git#e7b4732969c137db1b5")
|
||||||
|
lazy val git = uri("git://github.com/sbt/sbt-git.git#2e7c2503850698d60bb")
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
import Dependencies._
|
||||||
|
import sbt.internal.CommandStrings.{ inspectBrief, inspectDetailed }
|
||||||
|
import sbt.internal.Inspect
|
||||||
|
|
||||||
|
lazy val root = (project in file("."))
|
||||||
|
.settings(
|
||||||
|
Global / cancelable := true,
|
||||||
|
ThisBuild / scalaVersion := "2.12.3",
|
||||||
|
console / scalacOptions += "-deprecation",
|
||||||
|
Compile / console / scalacOptions += "-Ywarn-numeric-widen",
|
||||||
|
projA / Compile / console / scalacOptions += "-feature",
|
||||||
|
Zero / Zero / name := "foo",
|
||||||
|
|
||||||
|
libraryDependencies += uTest % Test,
|
||||||
|
testFrameworks += new TestFramework("utest.runner.Framework"),
|
||||||
|
|
||||||
|
commands += Command("inspectCheck", inspectBrief, inspectDetailed)(Inspect.parser) {
|
||||||
|
case (s, (option, sk)) =>
|
||||||
|
val actual = Inspect.output(s, option, sk)
|
||||||
|
val expected = s"""Task: Unit
|
||||||
|
Description:
|
||||||
|
\tExecutes all tests.
|
||||||
|
Provided by:
|
||||||
|
\tProjectRef(uri("${baseDirectory.value.toURI}"),"root")/Test/test
|
||||||
|
Defined at:
|
||||||
|
\t(sbt.Defaults.testTasks) Defaults.scala:670
|
||||||
|
Dependencies:
|
||||||
|
\tTest/executeTests
|
||||||
|
\tTest/test/streams
|
||||||
|
\tTest/state
|
||||||
|
\tTest/test/testResultLogger
|
||||||
|
Delegates:
|
||||||
|
\tTest/test
|
||||||
|
\tRuntime/test
|
||||||
|
\tCompile/test
|
||||||
|
\ttest
|
||||||
|
\tThisBuild/Test/test
|
||||||
|
\tThisBuild/Runtime/test
|
||||||
|
\tThisBuild/Compile/test
|
||||||
|
\tThisBuild/test
|
||||||
|
\tZero/Test/test
|
||||||
|
\tZero/Runtime/test
|
||||||
|
\tZero/Compile/test
|
||||||
|
\tGlobal/test
|
||||||
|
Related:
|
||||||
|
\tprojA/Test/test"""
|
||||||
|
|
||||||
|
if (processText(actual) == processText(expected)) ()
|
||||||
|
else {
|
||||||
|
sys.error(s"""actual:
|
||||||
|
$actual
|
||||||
|
|
||||||
|
expected:
|
||||||
|
$expected
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
s.log.info(actual)
|
||||||
|
s
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
lazy val projA = (project in file("a"))
|
||||||
|
|
||||||
|
def processText(s: String): Vector[String] = {
|
||||||
|
val xs = s.split(IO.Newline).toVector
|
||||||
|
.map( _.trim )
|
||||||
|
// declared location of the task is unstable.
|
||||||
|
.filterNot( _.contains("Defaults.scala") )
|
||||||
|
xs
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import sbt._
|
||||||
|
|
||||||
|
object Dependencies {
|
||||||
|
val uTest = "com.lihaoyi" %% "utest" % "0.5.3"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package example
|
||||||
|
|
||||||
|
import utest._
|
||||||
|
|
||||||
|
import java.nio.file.{ Files, Path, Paths }
|
||||||
|
|
||||||
|
object HelloTests extends TestSuite {
|
||||||
|
val tests = Tests {
|
||||||
|
'test1 - {
|
||||||
|
val p = Paths.get("target", "foo")
|
||||||
|
Files.createFile(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
> root/Compile/compile
|
||||||
|
|
||||||
|
# "Global" now works in shell.
|
||||||
|
> show Global/cancelable
|
||||||
|
|
||||||
|
# "Global" now works in shell. optional whitespace around /
|
||||||
|
> show Global / cancelable
|
||||||
|
|
||||||
|
# "ThisBuild" now works in shell.
|
||||||
|
> show ThisBuild/scalaVersion
|
||||||
|
|
||||||
|
# "ThisBuild" now works in shell. optional whitespace around /
|
||||||
|
> show ThisBuild / scalaVersion
|
||||||
|
|
||||||
|
$ mkdir target
|
||||||
|
> Test/test
|
||||||
|
# Check the side-effect of executing uTest
|
||||||
|
$ exists target/foo
|
||||||
|
|
||||||
|
# use all axes
|
||||||
|
> show root/Compile/compile/scalacOptions
|
||||||
|
|
||||||
|
# use all axes. optional whitespace around /
|
||||||
|
> show root / Compile / compile / scalacOptions
|
||||||
|
|
||||||
|
> show Zero / Zero / name
|
||||||
|
|
||||||
|
> inspectCheck test
|
||||||
Loading…
Reference in New Issue