Merge pull request #2935 from dwijnand/query-settings

Query settings
This commit is contained in:
eugene yokota 2017-03-05 11:58:07 -05:00 committed by GitHub
commit 84502e7712
14 changed files with 252 additions and 92 deletions

View File

@ -4,6 +4,7 @@ import sbt.internal.util.Types.const
import sbt.internal.util.{ Attributed, AttributeKey, Init, Show }
import sbt.internal.util.complete.Parser
import java.io.File
import java.net.URI
import Scope.{ ThisScope, GlobalScope }
import KeyRanks.{ DTask, Invisible }
@ -26,12 +27,21 @@ object Def extends Init[Scope] with TaskMacroExtra {
def apply(key: ScopedKey[_]) =
Scope.display(key.scope, colored(key.key.label, keyNameColor), ref => displayRelative(current, multi, ref))
}
def showBuildRelativeKey(currentBuild: URI, multi: Boolean, keyNameColor: Option[String] = None): Show[ScopedKey[_]] = new Show[ScopedKey[_]] {
def apply(key: ScopedKey[_]) =
Scope.display(key.scope, colored(key.key.label, keyNameColor), ref => displayBuildRelative(currentBuild, multi, ref))
}
def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String = project match {
case BuildRef(current.build) => "{.}/"
case `current` => if (multi) current.project + "/" else ""
case ProjectRef(current.build, x) => x + "/"
case _ => Reference.display(project) + "/"
}
def displayBuildRelative(currentBuild: URI, multi: Boolean, project: Reference): String = project match {
case BuildRef(`currentBuild`) => "{.}/"
case ProjectRef(`currentBuild`, x) => x + "/"
case _ => Reference.display(project) + "/"
}
def displayFull(scoped: ScopedKey[_]): String = displayFull(scoped, None)
def displayFull(scoped: ScopedKey[_], keyNameColor: Option[String]): String = Scope.display(scoped.scope, colored(scoped.key.label, keyNameColor))
def displayMasked(scoped: ScopedKey[_], mask: ScopeMask): String = Scope.displayMasked(scoped.scope, scoped.key.label, mask)

View File

@ -36,26 +36,34 @@ object Act {
def scopedKeyFull(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], keyMap: Map[String, AttributeKey[_]]): Parser[Seq[Parser[ParsedKey]]] =
{
def taskKeyExtra(proj: Option[ResolvedReference], confAmb: ParsedAxis[String], baseMask: ScopeMask): Seq[Parser[ParsedKey]] =
for {
conf <- configs(confAmb, defaultConfigs, proj, index)
} yield for {
taskAmb <- taskAxis(conf, index.tasks(proj, conf), keyMap)
task = resolveTask(taskAmb)
key <- key(index, proj, conf, task, keyMap)
extra <- extraAxis(keyMap, IMap.empty)
} yield {
val mask = baseMask.copy(task = taskAmb.isExplicit, extra = true)
new ParsedKey(makeScopedKey(proj, conf, task, extra, key), mask)
}
for {
rawProject <- optProjectRef(index, current)
proj = resolveProject(rawProject, current)
confAmb <- config(index configs proj)
partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false)
} yield taskKeyExtra(proj, confAmb, partialMask)
} yield taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask)
}
def taskKeyExtra(
index: KeyIndex,
defaultConfigs: Option[ResolvedReference] => Seq[String],
keyMap: Map[String, AttributeKey[_]],
proj: Option[ResolvedReference],
confAmb: ParsedAxis[String],
baseMask: ScopeMask
): Seq[Parser[ParsedKey]] =
for {
conf <- configs(confAmb, defaultConfigs, proj, index)
} yield for {
taskAmb <- taskAxis(conf, index.tasks(proj, conf), keyMap)
task = resolveTask(taskAmb)
key <- key(index, proj, conf, task, keyMap)
extra <- extraAxis(keyMap, IMap.empty)
} yield {
val mask = baseMask.copy(task = taskAmb.isExplicit, extra = true)
new ParsedKey(makeScopedKey(proj, conf, task, extra, key), mask)
}
def makeScopedKey(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]], extra: ScopeAxis[AttributeMap], key: AttributeKey[_]): ScopedKey[_] =
ScopedKey(Scope(toAxis(proj, Global), toAxis(conf map ConfigKey.apply, Global), toAxis(task, Global), extra), key)
@ -309,4 +317,4 @@ object Act {
final object Omitted extends ParsedAxis[Nothing]
final class ParsedValue[T](val value: T) extends ParsedAxis[T]
def value[T](t: Parser[T]): Parser[ParsedAxis[T]] = t map { v => new ParsedValue(v) }
}
}

View File

@ -74,7 +74,7 @@ private[sbt] final class CommandExchange {
def onIncomingSocket(socket: Socket): Unit =
{
s.log.info(s"new client connected from: ${socket.getPort}")
val channel = new NetworkChannel(newChannelName, socket)
val channel = new NetworkChannel(newChannelName, socket, Project structure s, Project.session(s).currentBuild)
subscribe(channel)
channel.publishEventMessage(ChannelAcceptedEvent(channel.name))
}

View File

@ -5,12 +5,13 @@ package sbt
package internal
package server
import java.net.{ Socket, SocketTimeoutException }
import java.net.{ Socket, SocketTimeoutException, URI }
import java.util.concurrent.atomic.AtomicBoolean
import sbt.protocol.{ Serialization, CommandMessage, ExecCommand, EventMessage }
import sjsonnew.JsonFormat
import scala.util.{ Left, Right }
import sbt.protocol._
import sjsonnew._
final class NetworkChannel(val name: String, connection: Socket) extends CommandChannel {
final class NetworkChannel(val name: String, connection: Socket, structure: BuildStructure, currentBuild: URI) extends CommandChannel {
private val running = new AtomicBoolean(true)
private val delimiter: Byte = '\n'.toByte
private val out = connection.getOutputStream
@ -70,14 +71,111 @@ final class NetworkChannel(val name: String, connection: Socket) extends Command
out.flush()
}
def onCommand(command: CommandMessage): Unit =
command match {
case x: ExecCommand => append(Exec(x.commandLine, x.execId orElse Some(Exec.newExecId), Some(CommandSource(name))))
def onCommand(command: CommandMessage): Unit = command match {
case x: ExecCommand => onExecCommand(x)
case x: SettingQuery => onSettingQuery(x)
}
private def onExecCommand(cmd: ExecCommand) =
append(Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name))))
private def onSettingQuery(req: SettingQuery) = {
import sbt.internal.util.complete.Parser
val key = Parser.parse(req.setting, SettingQuery.scopedKeyParser(structure, currentBuild))
def getSettingValue[A](key: Def.ScopedKey[A]) =
structure.data.get(key.scope, key.key)
.toRight(s"Key ${Def displayFull key} not found")
.flatMap {
case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings")
case _: InputTask[_] => Left(s"Key ${Def displayFull key} is an input task, can only query settings")
case x => Right(x)
}
val values = key match {
case Left(msg) => Left(s"Invalid programmatic input: $msg")
case Right(key) => Right(getSettingValue(key))
}
val jsonValues = values match {
case Left(errors) => errors
case Right(value) => value.toString
}
StandardMain.exchange publishEventMessage SettingQueryResponse(jsonValues)
}
def shutdown(): Unit = {
println("Shutting down client connection")
running.set(false)
out.close()
}
}
object SettingQuery {
import sbt.internal.util.{ AttributeKey, Settings }
import sbt.internal.util.complete.{ DefaultParsers, Parser }, DefaultParsers._
import sbt.Def.{ showBuildRelativeKey, ScopedKey }
// Similar to Act.ParsedAxis / Act.projectRef / Act.resolveProject except you can't omit the project reference
sealed trait ParsedExplicitAxis[+T]
final object ParsedExplicitGlobal extends ParsedExplicitAxis[Nothing]
final class ParsedExplicitValue[T](val value: T) extends ParsedExplicitAxis[T]
def explicitValue[T](t: Parser[T]): Parser[ParsedExplicitAxis[T]] = t map { v => new ParsedExplicitValue(v) }
def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedExplicitAxis[ResolvedReference]] = {
val global = token(Act.GlobalString ~ '/') ^^^ ParsedExplicitGlobal
val trailing = '/' !!! "Expected '/' (if selecting a project)"
global | explicitValue(Act.resolvedReference(index, currentBuild, trailing))
}
def resolveProject(parsed: ParsedExplicitAxis[ResolvedReference]): Option[ResolvedReference] = parsed match {
case ParsedExplicitGlobal => None
case pv: ParsedExplicitValue[_] => Some(pv.value)
}
def scopedKeyFull(
index: KeyIndex,
currentBuild: URI,
defaultConfigs: Option[ResolvedReference] => Seq[String],
keyMap: Map[String, AttributeKey[_]]
): Parser[Seq[Parser[ParsedKey]]] = {
for {
rawProject <- projectRef(index, currentBuild)
proj = resolveProject(rawProject)
confAmb <- Act.config(index configs proj)
partialMask = ScopeMask(true, confAmb.isExplicit, false, false)
} yield Act.taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask)
}
def scopedKeyParser(structure: BuildStructure, currentBuild: URI): Parser[ScopedKey[_]] =
scopedKey(
structure.index.keyIndex,
currentBuild,
structure.extra.configurationsForAxis,
structure.index.keyMap,
structure.data
)
def scopedKeySelected(
index: KeyIndex,
currentBuild: URI,
defaultConfigs: Option[ResolvedReference] => Seq[String],
keyMap: Map[String, AttributeKey[_]],
data: Settings[Scope]
): Parser[ParsedKey] =
scopedKeyFull(index, currentBuild, defaultConfigs, keyMap) flatMap { choices =>
Act.select(choices, data)(showBuildRelativeKey(currentBuild, index.buildURIs.size > 1))
}
def scopedKey(
index: KeyIndex,
currentBuild: URI,
defaultConfigs: Option[ResolvedReference] => Seq[String],
keyMap: Map[String, AttributeKey[_]],
data: Settings[Scope]
): Parser[ScopedKey[_]] =
scopedKeySelected(index, currentBuild, defaultConfigs, keyMap, data).map(_.key)
}

View File

@ -0,0 +1,32 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
final class SettingQuery private (
val setting: String) extends sbt.protocol.CommandMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: SettingQuery => (this.setting == x.setting)
case _ => false
}
override def hashCode: Int = {
37 * (17 + setting.##)
}
override def toString: String = {
"SettingQuery(" + setting + ")"
}
protected[this] def copy(setting: String = setting): SettingQuery = {
new SettingQuery(setting)
}
def withSetting(setting: String): SettingQuery = {
copy(setting = setting)
}
}
object SettingQuery {
def apply(setting: String): SettingQuery = new SettingQuery(setting)
}

View File

@ -0,0 +1,32 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
final class SettingQueryResponse private (
val value: String) extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: SettingQueryResponse => (this.value == x.value)
case _ => false
}
override def hashCode: Int = {
37 * (17 + value.##)
}
override def toString: String = {
"SettingQueryResponse(" + value + ")"
}
protected[this] def copy(value: String = value): SettingQueryResponse = {
new SettingQueryResponse(value)
}
def withValue(value: String): SettingQueryResponse = {
copy(value = value)
}
}
object SettingQueryResponse {
def apply(value: String): SettingQueryResponse = new SettingQueryResponse(value)
}

View File

@ -5,6 +5,6 @@
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats =>
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat1[sbt.protocol.CommandMessage, sbt.protocol.ExecCommand]("type")
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats =>
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat2[sbt.protocol.CommandMessage, sbt.protocol.ExecCommand, sbt.protocol.SettingQuery]("type")
}

View File

@ -5,6 +5,6 @@
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat3[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent]("type")
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.protocol.codec.SettingQueryResponseFormats =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat4[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQueryResponse]("type")
}

View File

@ -1,29 +0,0 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait ExectionEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ExectionEventFormat: JsonFormat[sbt.protocol.ExectionEvent] = new JsonFormat[sbt.protocol.ExectionEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ExectionEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val success = unbuilder.readField[String]("success")
val commandLine = unbuilder.readField[String]("commandLine")
unbuilder.endObject()
sbt.protocol.ExectionEvent(success, commandLine)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.ExectionEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("success", obj.success)
builder.addField("commandLine", obj.commandLine)
builder.endObject()
}
}
}

View File

@ -6,10 +6,12 @@
package sbt.protocol.codec
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.protocol.codec.ExecCommandFormats
with sbt.protocol.codec.SettingQueryFormats
with sbt.protocol.codec.CommandMessageFormats
with sbt.protocol.codec.ChannelAcceptedEventFormats
with sbt.protocol.codec.LogEventFormats
with sbt.protocol.codec.ExecStatusEventFormats
with sbt.protocol.codec.SettingQueryResponseFormats
with sbt.protocol.codec.EventMessageFormats
with sbt.protocol.codec.ExecutionEventFormats
object JsonProtocol extends JsonProtocol

View File

@ -5,22 +5,22 @@
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait ConsolePromptEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ConsolePromptEventFormat: JsonFormat[sbt.protocol.ConsolePromptEvent] = new JsonFormat[sbt.protocol.ConsolePromptEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ConsolePromptEvent = {
trait SettingQueryFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val SettingQueryFormat: JsonFormat[sbt.protocol.SettingQuery] = new JsonFormat[sbt.protocol.SettingQuery] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQuery = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val setting = unbuilder.readField[String]("setting")
unbuilder.endObject()
sbt.protocol.ConsolePromptEvent()
sbt.protocol.SettingQuery(setting)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.ConsolePromptEvent, builder: Builder[J]): Unit = {
override def write[J](obj: sbt.protocol.SettingQuery, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("setting", obj.setting)
builder.endObject()
}
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait SettingQueryResponseFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val SettingQueryResponseFormat: JsonFormat[sbt.protocol.SettingQueryResponse] = new JsonFormat[sbt.protocol.SettingQueryResponse] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQueryResponse = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val value = unbuilder.readField[String]("value")
unbuilder.endObject()
sbt.protocol.SettingQueryResponse(value)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.SettingQueryResponse, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("value", obj.value)
builder.endObject()
}
}
}

View File

@ -1,29 +0,0 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait StatusEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val StatusEventFormat: JsonFormat[sbt.protocol.StatusEvent] = new JsonFormat[sbt.protocol.StatusEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.StatusEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val status = unbuilder.readField[String]("status")
val commandQueue = unbuilder.readField[Vector[String]]("commandQueue")
unbuilder.endObject()
sbt.protocol.StatusEvent(status, commandQueue)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.StatusEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("status", obj.status)
builder.addField("commandQueue", obj.commandQueue)
builder.endObject()
}
}
}

View File

@ -13,6 +13,11 @@ type ExecCommand implements CommandMessage {
execId: String @since("0.0.1")
}
type SettingQuery implements CommandMessage {
setting: String!
}
## Message for events.
interface EventMessage {
}
@ -35,6 +40,10 @@ type ExecStatusEvent implements EventMessage {
commandQueue: [String]
}
type SettingQueryResponse implements EventMessage {
value: String!
}
# enum Status {
# Ready
# Processing