Merge pull request #3032 from dwijnand/setting-query-json

Start handling default types when serialising query setting values
This commit is contained in:
Dale Wijnand 2017-03-28 10:45:46 +01:00 committed by GitHub
commit ece85b44bd
21 changed files with 560 additions and 175 deletions

View File

@ -181,7 +181,8 @@ lazy val protocolProj = (project in file("protocol")).
testedBaseSettings,
name := "Protocol",
libraryDependencies ++= Seq(sjsonNewScalaJson),
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala"
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats
).
configure(addSbtUtilLogging)
@ -211,6 +212,7 @@ lazy val mainSettingsProj = (project in file("main-settings")).
// The main integration project for sbt. It brings all of the projects together, configures them, and provides for overriding conventions.
lazy val mainProj = (project in file("main")).
dependsOn(actionsProj, mainSettingsProj, runProj, commandProj).
disablePlugins(SbtScalariform).
settings(
testedBaseSettings,
name := "Main",

View File

@ -3,19 +3,20 @@
*/
package sbt
/** An abstraction on top of Settings for build configuration and task definition. */
import scala.language.experimental.macros
import java.io.File
import ConcurrentRestrictions.Tag
import Def.{ Initialize, KeyedInitialize, ScopedKey, Setting, setting }
import sbt.io.{ FileFilter, PathFinder }
import sbt.io.syntax._
import std.TaskExtra.{ task => mktask, _ }
import sbt.internal.util.Types._
import sbt.internal.util.{ ~>, AList, AttributeKey, Settings, SourcePosition }
import sbt.util.OptJsonWriter
import sbt.ConcurrentRestrictions.Tag
import sbt.Def.{ Initialize, KeyedInitialize, ScopedKey, Setting, setting }
import std.TaskExtra.{ task => mktask, _ }
import language.experimental.macros
/** An abstraction on top of Settings for build configuration and task definition. */
sealed trait Scoped { def scope: Scope; val key: AttributeKey[_] }
@ -471,17 +472,17 @@ object TaskKey {
/** Constructs SettingKeys, which are associated with a value to define a basic setting.*/
object SettingKey {
def apply[T: Manifest](label: String, description: String = "", rank: Int = KeyRanks.DefaultSettingRank): SettingKey[T] =
def apply[T: Manifest: OptJsonWriter](label: String, description: String = "", rank: Int = KeyRanks.DefaultSettingRank): SettingKey[T] =
apply(AttributeKey[T](label, description, rank))
def apply[T: Manifest](label: String, description: String, extend1: Scoped, extendN: Scoped*): SettingKey[T] =
def apply[T: Manifest: OptJsonWriter](label: String, description: String, extend1: Scoped, extendN: Scoped*): SettingKey[T] =
apply(AttributeKey[T](label, description, extendScoped(extend1, extendN)))
def apply[T: Manifest](label: String, description: String, rank: Int, extend1: Scoped, extendN: Scoped*): SettingKey[T] =
def apply[T: Manifest: OptJsonWriter](label: String, description: String, rank: Int, extend1: Scoped, extendN: Scoped*): SettingKey[T] =
apply(AttributeKey[T](label, description, extendScoped(extend1, extendN), rank))
def apply[T](akey: AttributeKey[T]): SettingKey[T] =
new SettingKey[T] { val key = akey; def scope = Scope.ThisScope }
def local[T: Manifest]: SettingKey[T] = apply[T](AttributeKey.local[T])
def local[T: Manifest: OptJsonWriter]: SettingKey[T] = apply[T](AttributeKey.local[T])
}

View File

@ -1,12 +1,15 @@
package sbt
package std
import reflect.macros._
import scala.annotation.tailrec
import scala.reflect.macros._
import sbt.util.OptJsonWriter
private[sbt] object KeyMacro {
def settingKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(description: c.Expr[String]): c.Expr[SettingKey[T]] =
keyImpl[T, SettingKey[T]](c) { (name, mf) =>
c.universe.reify { SettingKey[T](name.splice, description.splice)(mf.splice) }
keyImpl2[T, SettingKey[T]](c) { (name, mf, ojw) =>
c.universe.reify { SettingKey[T](name.splice, description.splice)(mf.splice, ojw.splice) }
}
def taskKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(description: c.Expr[String]): c.Expr[TaskKey[T]] =
keyImpl[T, TaskKey[T]](c) { (name, mf) =>
@ -17,20 +20,33 @@ private[sbt] object KeyMacro {
c.universe.reify { InputKey[T](name.splice, description.splice)(mf.splice) }
}
def keyImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: blackbox.Context)(f: (c.Expr[String], c.Expr[Manifest[T]]) => c.Expr[S]): c.Expr[S] =
{
import c.universe._
val enclosingValName = definingValName(c, methodName => s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""")
val name = c.Expr[String](Literal(Constant(enclosingValName)))
val mf = c.Expr[Manifest[T]](c.inferImplicitValue(weakTypeOf[Manifest[T]]))
f(name, mf)
}
def keyImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: blackbox.Context)(
f: (c.Expr[String], c.Expr[Manifest[T]]) => c.Expr[S]
): c.Expr[S] =
f(getName(c), getImplicit[Manifest[T]](c))
private def keyImpl2[T: c.WeakTypeTag, S: c.WeakTypeTag](c: blackbox.Context)(
f: (c.Expr[String], c.Expr[Manifest[T]], c.Expr[OptJsonWriter[T]]) => c.Expr[S]
): c.Expr[S] =
f(getName(c), getImplicit[Manifest[T]](c), getImplicit[OptJsonWriter[T]](c))
private def getName[S: c.WeakTypeTag, T: c.WeakTypeTag](c: blackbox.Context): c.Expr[String] = {
import c.universe._
val enclosingValName = definingValName(c, methodName => s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""")
c.Expr[String](Literal(Constant(enclosingValName)))
}
private def getImplicit[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[T] = {
import c.universe._
c.Expr[T](c.inferImplicitValue(weakTypeOf[T]))
}
def definingValName(c: blackbox.Context, invalidEnclosingTree: String => String): String =
{
import c.universe.{ Apply => ApplyTree, _ }
val methodName = c.macroApplication.symbol.name
def processName(n: Name): String = n.decodedName.toString.trim // trim is not strictly correct, but macros don't expose the API necessary
def enclosingVal(trees: List[c.Tree]): String =
@tailrec def enclosingVal(trees: List[c.Tree]): String =
{
trees match {
case vd @ ValDef(_, name, _, _) :: ts => processName(name)
@ -44,6 +60,7 @@ private[sbt] object KeyMacro {
}
enclosingVal(enclosingTrees(c).toList)
}
def enclosingTrees(c: blackbox.Context): Seq[c.Tree] =
c.asInstanceOf[reflect.macros.runtime.Context].callsiteTyper.context.enclosingContextChain.map(_.tree.asInstanceOf[c.Tree])
}

View File

@ -34,6 +34,7 @@ import sbt.internal.io.WatchState
import sbt.internal.util.{ AttributeKey, CacheStore, SourcePosition }
import sbt.librarymanagement.Configurations.CompilerPlugin
import sbt.librarymanagement.LibraryManagementCodec._
import sbt.librarymanagement.{
Artifact,
Configuration,

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, Project structure s, Project.session(s).currentBuild)
val channel = new NetworkChannel(newChannelName, socket, Project structure s)
subscribe(channel)
channel.publishEventMessage(ChannelAcceptedEvent(channel.name))
}

View File

@ -699,7 +699,7 @@ private[sbt] object Load {
* sbt file to resolve a project.
* @param log A logger to report auto-plugin issues to.
*/
private[this] def resolveProject(
private[sbt] def resolveProject(
p: Project,
projectPlugins: Seq[AutoPlugin],
loadedPlugins: LoadedPlugins,

View File

@ -5,13 +5,12 @@ package sbt
package internal
package server
import java.net.{ Socket, SocketTimeoutException, URI }
import java.net.{ Socket, SocketTimeoutException }
import java.util.concurrent.atomic.AtomicBoolean
import scala.util.{ Left, Right }
import sbt.protocol._
import sjsonnew._
final class NetworkChannel(val name: String, connection: Socket, structure: BuildStructure, currentBuild: URI) extends CommandChannel {
final class NetworkChannel(val name: String, connection: Socket, structure: BuildStructure) extends CommandChannel {
private val running = new AtomicBoolean(true)
private val delimiter: Byte = '\n'.toByte
private val out = connection.getOutputStream
@ -79,32 +78,8 @@ final class NetworkChannel(val name: String, connection: Socket, structure: Buil
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)
}
private def onSettingQuery(req: SettingQuery) =
StandardMain.exchange publishEventMessage SettingQuery.handleSettingQuery(req, structure)
def shutdown(): Unit = {
println("Shutting down client connection")
@ -112,70 +87,3 @@ final class NetworkChannel(val name: String, connection: Socket, structure: Buil
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,118 @@
/*
* Copyright (C) 2016-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package sbt
package internal
package server
import java.net.URI
import scala.json.ast.unsafe.JValue
import scala.util.{ Left, Right }
import sbt.util.{ SomeJsonWriter, NoJsonWriter }
import sbt.librarymanagement.LibraryManagementCodec._
import sbt.protocol._
import sjsonnew._
import sjsonnew.support.scalajson.unsafe._
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 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)
def scopedKeyParser(structure: BuildStructure): Parser[ScopedKey[_]] =
scopedKey(
structure.index.keyIndex,
structure.root,
structure.extra.configurationsForAxis,
structure.index.keyMap,
structure.data
)
def getSettingValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, 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)
}
def getJsonWriter[A](key: AttributeKey[A]): Either[String, JsonWriter[A]] = key.optJsonWriter match {
case SomeJsonWriter(jw) => Right(jw)
case NoJsonWriter() => Left(s"JsonWriter for ${key.manifest} not found")
}
def toJson[A: JsonWriter](x: A): JValue = Converter toJsonUnsafe x
def getSettingJsonValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, JValue] =
getSettingValue(structure, key) flatMap (value =>
getJsonWriter(key.key) map { implicit jw: JsonWriter[A] => toJson(value) }
)
def handleSettingQuery(req: SettingQuery, structure: BuildStructure): SettingQueryResponse = {
val key = Parser.parse(req.setting, scopedKeyParser(structure))
val result =
for {
key <- key
json <- getSettingJsonValue(structure, key)
} yield SettingQuerySuccess(json, key.key.manifest.toString)
result match {
case Right(x) => x
case Left(s) => SettingQueryFailure(s)
}
}
}

View File

@ -0,0 +1,184 @@
package sbt
package internal
package server
import java.io._
import java.net._
import java.nio.file._
import java.util.concurrent._
import scala.collection.mutable
import xsbti._
import sbt.io.IO
import sbt.internal.util._
import sbt.internal.BuildStreams.{ Streams => _, _ }
import sbt.internal.Load._
import sbt.util._
import sbt.BuildPaths._
import sbt.Def.{ ScopeLocal, ScopedKey, Setting }
import sbt.Keys._
object SettingQueryTest extends org.specs2.mutable.Specification {
implicit class PathOps(val path: Path) extends AnyVal {
def /(other: String): Path = if (other == ".") path else path resolve other
}
val baseDir: Path = Files createTempDirectory "sbt-setting-query-test"
val globalDir: Path = Files createTempDirectory "sbt-setting-query-test-global-dir"
val bootDir: Path = Files createTempDirectory "sbt-setting-query-test-boot-dir"
val ivyHome: Path = Files createTempDirectory "sbt-setting-query-test-ivy-home"
val logFile: File = File.createTempFile("sbt", ".log")
val baseFile: File = baseDir.toFile
val baseUri: URI = IO directoryURI baseFile
IO assertAbsolute baseUri
val globalDirFile: File = globalDir.toFile
def ??? : Nothing = { Thread.dumpStack(); throw new NotImplementedError }
val noopLoader: ClassLoader = new URLClassLoader(Array(), null)
object NoGlobalLock extends GlobalLock { def apply[T](lockFile: File, run: Callable[T]) = run.call() }
lazy val structure: BuildStructure = {
val projectSettings: Seq[Setting[_]] = Seq(scalaVersion := "2.12.1")
val appConfig: AppConfiguration = new AppConfiguration {
def baseDirectory(): File = baseFile
def arguments(): Array[String] = Array()
def provider(): AppProvider = new AppProvider {
def scalaProvider(): ScalaProvider = new ScalaProvider { scalaProvider =>
def launcher(): Launcher = new Launcher {
def getScala(version: String): ScalaProvider = getScala(version, "")
def getScala(version: String, reason: String): ScalaProvider = getScala(version, reason, "org.scala-lang")
def getScala(version: String, reason: String, scalaOrg: String): ScalaProvider = scalaProvider
def app(id: ApplicationID, version: String): AppProvider = ???
def topLoader(): ClassLoader = noopLoader
def globalLock(): GlobalLock = NoGlobalLock
def bootDirectory(): File = bootDir.toFile
def ivyRepositories(): Array[Repository] = Array()
def appRepositories(): Array[Repository] = Array()
def isOverrideRepositories: Boolean = false
def ivyHome(): File = SettingQueryTest.this.ivyHome.toFile
def checksums(): Array[String] = Array()
}
def version(): String = "2.12.1"
def loader(): ClassLoader = noopLoader
def jars(): Array[File] = Array(libraryJar, compilerJar)
def libraryJar(): File = new File("scala-library.jar")
def compilerJar(): File = new File("scala-compiler.jar")
def app(id: ApplicationID): AppProvider = ???
}
def id(): ApplicationID = sbt.ApplicationID(
"org.scala-sbt", "sbt", "0.13.13", "sbt.xMain",
components = Seq(), crossVersionedValue = CrossValue.Disabled, extra = Seq()
)
def loader(): ClassLoader = noopLoader
def entryPoint(): Class[_] = ???
def mainClass(): Class[_ <: AppMain] = ???
def newMain(): AppMain = ???
def mainClasspath(): Array[File] = Array()
def components(): ComponentProvider = new ComponentProvider {
def componentLocation(id: String): File = ???
def component(componentID: String): Array[File] = ???
def defineComponent(componentID: String, components: Array[File]): Unit = ???
def addToComponent(componentID: String, components: Array[File]): Boolean = ???
def lockFile(): File = ???
}
}
}
val state: State =
StandardMain.initialState(appConfig, initialDefinitions = Seq(), preCommands = Seq())
.put(globalBaseDirectory, globalDirFile)
val config0 = defaultPreGlobal(state, baseFile, globalDirFile, state.log)
val config = defaultWithGlobal(state, baseFile, config0, globalDirFile, state.log)
val buildUnit: BuildUnit = {
val loadedPlugins: LoadedPlugins =
noPlugins(projectStandard(baseFile), config.copy(pluginManagement = config.pluginManagement.forPlugin))
val project: Project = {
val project0 = Project("t", baseFile) settings projectSettings
val fileToLoadedSbtFileMap = new mutable.HashMap[File, LoadedSbtFile]
val autoPlugins = loadedPlugins.detected.deducePluginsFromProject(project0, state.log)
val injectSettings = config.injectSettings
resolveProject(project0, autoPlugins, loadedPlugins, injectSettings, fileToLoadedSbtFileMap, state.log)
}
val projects: Seq[Project] = Seq(project)
val builds: Seq[BuildDef] = BuildDef.defaultAggregated(project.id, Nil) :: Nil
val defs: LoadedDefinitions = new LoadedDefinitions(baseFile, Nil, noopLoader, builds, projects, Nil)
new BuildUnit(baseUri, baseFile, defs, loadedPlugins)
}
val (partBuildUnit: PartBuildUnit, projectRefs: List[ProjectReference]) = loaded(buildUnit)
val partBuildUnits: Map[URI, PartBuildUnit] = Map(buildUnit.uri -> partBuildUnit)
val allProjectRefs: Map[URI, List[ProjectReference]] = Map(buildUnit.uri -> projectRefs)
checkAll(allProjectRefs, partBuildUnits)
val partBuild: PartBuild = new PartBuild(baseUri, partBuildUnits)
val loadedBuild: LoadedBuild = resolveProjects(partBuild)
val units: Map[URI, LoadedBuildUnit] = loadedBuild.units
val settings: Seq[Setting[_]] = finalTransforms(buildConfigurations(loadedBuild, getRootProject(units), config.injectSettings))
val delegates: Scope => Seq[Scope] = defaultDelegates(loadedBuild)
val scopeLocal: ScopeLocal = EvaluateTask.injectStreams
val display: Show[ScopedKey[_]] = Project showLoadingKey loadedBuild
val data: Settings[Scope] = Def.make(settings)(delegates, scopeLocal, display)
val extra: KeyIndex => BuildUtil[_] = index => BuildUtil(baseUri, units, index, data)
val index: StructureIndex = structureIndex(data, settings, extra, units)
val streams: State => Streams = mkStreams(units, baseUri, data)
val structure: BuildStructure =
new BuildStructure(units, baseUri, settings, data, index, streams, delegates, scopeLocal)
structure
}
def query(setting: String): String = {
import sbt.protocol._
val req: SettingQuery = protocol.SettingQuery(setting)
val rsp: SettingQueryResponse = server.SettingQuery.handleSettingQuery(req, structure)
val bytes: Array[Byte] = Serialization serializeEventMessage rsp
val payload: String = new String(bytes, java.nio.charset.StandardCharsets.UTF_8)
payload
}
// -.- avoid specs2's ko/ok
import org.specs2.matcher.MatchResult
def qok(x: String, t: String): String => MatchResult[Any] = query(_) must_== """{"type":"SettingQuerySuccess","value":""" + x + ""","contentType":"""" + t + """"}"""
def qko(msg: String): String => MatchResult[Any] = query(_) must_== """{"type":"SettingQueryFailure","message":"""" + msg + """"}"""
"setting query" should {
"t/scalaVersion" in qok("\"2.12.1\"", "java.lang.String")
"t/pollInterval" in qok("500", "Int")
"t/sourcesInBase" in qok("true", "Boolean")
"t/startYear" in qok("null", "scala.Option[Int]")
"t/scalaArtifacts" in qok("""["scala-library","scala-compiler","scala-reflect","scala-actors","scalap"]""", "scala.collection.Seq[java.lang.String]")
"t/libraryDependencies" in qok(
"""[{"organization":"org.scala-lang","name":"scala-library","revision":"2.12.1","isChanging":false,"isTransitive":true,"isForce":false,"crossVersion":{"type":"Disabled"}}]""",
"scala.collection.Seq[sbt.librarymanagement.ModuleID]")
"scalaVersion" in qko("Not a valid project ID: scalaVersion\\nscalaVersion\\n ^")
"t/scalacOptions" in qko(s"Key {$baseUri}t/compile:scalacOptions is a task, can only query settings")
"t/fooo" in qko("Expected ':' (if selecting a configuration)\\nNot a valid key: fooo (similar: fork)\\nt/fooo\\n ^")
}
}

View File

@ -22,6 +22,7 @@ object ContrabandConfig {
case "Option" | "Set" | "scala.Vector" => { tpe => getFormats(oneArg(tpe)) }
case "Map" | "Tuple2" | "scala.Tuple2" => { tpe => twoArgs(tpe).flatMap(getFormats) }
case "Int" | "Long" => { _ => Nil }
case "scala.json.ast.unsafe.JValue" => { _ => "sbt.internal.JValueFormat" :: Nil }
}
/** Returns the list of formats required to encode the given `TpeRef`. */

View File

@ -1,15 +1,13 @@
import sbt._
import sbt.Keys._
import com.typesafe.sbt.SbtScalariform._
import ScalariformKeys.{ format => scalariformFormat, preferences => scalariformPreferences }
import sbt._, Keys._
import com.typesafe.sbt.SbtScalariform._, autoImport._
object Formatting {
lazy val BuildConfig = config("build") extend Compile
lazy val BuildSbtConfig = config("buildsbt") extend Compile
val BuildConfig = config("build") extend Compile
val BuildSbtConfig = config("buildsbt") extend Compile
val scalariformCheck = taskKey[Unit]("Checks that the existing code is formatted, via git diff")
lazy val prefs: Seq[Setting[_]] = {
private val prefs: Seq[Setting[_]] = {
import scalariform.formatter.preferences._
Seq(
scalariformPreferences ~= (_
@ -20,8 +18,8 @@ object Formatting {
)
}
lazy val settings: Seq[Setting[_]] = Seq() ++ scalariformSettings ++ prefs
lazy val sbtFilesSettings: Seq[Setting[_]] = Seq() ++ scalariformSettings ++ prefs ++
val settings: Seq[Setting[_]] = Seq() ++ prefs
val sbtFilesSettings: Seq[Setting[_]] = Seq() ++ prefs ++
inConfig(BuildConfig)(configScalariformSettings) ++
inConfig(BuildSbtConfig)(configScalariformSettings) ++
Seq(

View File

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

View File

@ -4,29 +4,22 @@
// 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)
}
abstract class SettingQueryResponse() extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: SettingQueryResponse => true
case _ => false
}
override def hashCode: Int = {
17
}
override def toString: String = {
"SettingQueryResponse()"
}
}
object SettingQueryResponse {
def apply(value: String): SettingQueryResponse = new SettingQueryResponse(value)
}

View File

@ -0,0 +1,36 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
final class SettingQuerySuccess private (
val value: scala.json.ast.unsafe.JValue,
val contentType: String) extends sbt.protocol.SettingQueryResponse() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: SettingQuerySuccess => (this.value == x.value) && (this.contentType == x.contentType)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + value.##) + contentType.##)
}
override def toString: String = {
"SettingQuerySuccess(" + value + ", " + contentType + ")"
}
protected[this] def copy(value: scala.json.ast.unsafe.JValue = value, contentType: String = contentType): SettingQuerySuccess = {
new SettingQuerySuccess(value, contentType)
}
def withValue(value: scala.json.ast.unsafe.JValue): SettingQuerySuccess = {
copy(value = value)
}
def withContentType(contentType: String): SettingQuerySuccess = {
copy(contentType = contentType)
}
}
object SettingQuerySuccess {
def apply(value: scala.json.ast.unsafe.JValue, contentType: String): SettingQuerySuccess = new SettingQuerySuccess(value, contentType)
}

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 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")
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.JValueFormat with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat5[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure]("type")
}

View File

@ -11,7 +11,10 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.protocol.codec.ChannelAcceptedEventFormats
with sbt.protocol.codec.LogEventFormats
with sbt.protocol.codec.ExecStatusEventFormats
with sbt.protocol.codec.SettingQueryResponseFormats
with sbt.internal.JValueFormat
with sbt.protocol.codec.SettingQuerySuccessFormats
with sbt.protocol.codec.SettingQueryFailureFormats
with sbt.protocol.codec.EventMessageFormats
with sbt.protocol.codec.SettingQueryResponseFormats
with sbt.protocol.codec.ExecutionEventFormats
object JsonProtocol extends JsonProtocol

View File

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

View File

@ -5,23 +5,6 @@
// 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()
}
}
trait SettingQueryResponseFormats { self: sbt.internal.JValueFormat with sjsonnew.BasicJsonProtocol with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats =>
implicit lazy val SettingQueryResponseFormat: JsonFormat[sbt.protocol.SettingQueryResponse] = flatUnionFormat2[sbt.protocol.SettingQueryResponse, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure]("type")
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait SettingQuerySuccessFormats { self: sbt.internal.JValueFormat with sjsonnew.BasicJsonProtocol =>
implicit lazy val SettingQuerySuccessFormat: JsonFormat[sbt.protocol.SettingQuerySuccess] = new JsonFormat[sbt.protocol.SettingQuerySuccess] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQuerySuccess = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val value = unbuilder.readField[scala.json.ast.unsafe.JValue]("value")
val contentType = unbuilder.readField[String]("contentType")
unbuilder.endObject()
sbt.protocol.SettingQuerySuccess(value, contentType)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.SettingQuerySuccess, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("value", obj.value)
builder.addField("contentType", obj.contentType)
builder.endObject()
}
}
}

View File

@ -40,8 +40,15 @@ type ExecStatusEvent implements EventMessage {
commandQueue: [String]
}
type SettingQueryResponse implements EventMessage {
value: String!
interface SettingQueryResponse implements EventMessage {}
type SettingQuerySuccess implements SettingQueryResponse {
value: scala.json.ast.unsafe.JValue!
contentType: String!
}
type SettingQueryFailure implements SettingQueryResponse {
message: String!
}
# enum Status {

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2017 Lightbend Inc. <http://www.lightbend.com>
*/
package sbt
package internal
import sjsonnew.{ JsonWriter => JW, JsonReader => JR, JsonFormat => JF, _ }
import scala.json.ast.unsafe._
trait JValueFormat { self: sjsonnew.BasicJsonProtocol =>
implicit val JNullJF: JF[JNull.type] = new JF[JNull.type] {
def write[J](x: JNull.type, b: Builder[J]) = b.writeNull()
def read[J](j: Option[J], u: Unbuilder[J]) = JNull
}
implicit val JBooleanJF: JF[JBoolean] = project(_.get, JBoolean(_))
implicit val JStringJF: JF[JString] = project(_.value, JString(_))
implicit val JNumberJF: JF[JNumber] = project(x => BigDecimal(x.value), (x: BigDecimal) => JNumber(x.toString))
implicit val JArrayJF: JF[JArray] = project[JArray, Array[JValue]](_.value, JArray(_))
implicit lazy val JObjectJW: JW[JObject] = new JW[JObject] {
def write[J](x: JObject, b: Builder[J]) = {
b.beginObject()
x.value foreach (jsonField => JValueJW.addField(jsonField.field, jsonField.value, b))
b.endObject()
}
}
implicit lazy val JValueJW: JW[JValue] = new JW[JValue] {
def write[J](x: JValue, b: Builder[J]) = x match {
case x: JNull.type => JNullJF.write(x, b)
case x: JBoolean => JBooleanJF.write(x, b)
case x: JString => JStringJF.write(x, b)
case x: JNumber => JNumberJF.write(x, b)
case x: JArray => JArrayJF.write(x, b)
case x: JObject => JObjectJW.write(x, b)
}
}
implicit lazy val JValueJR: JR[JValue] = new JR[JValue] {
def read[J](j: Option[J], u: Unbuilder[J]) = ??? // Is this even possible? with no Manifest[J]?
}
implicit lazy val JValueJF: JF[JValue] = jsonFormat[JValue](JValueJR, JValueJW)
}