diff --git a/build.sbt b/build.sbt index 9d8b039c7..20f0b16fa 100644 --- a/build.sbt +++ b/build.sbt @@ -176,12 +176,11 @@ lazy val actionsProj = (project in file("main-actions")). // General command support and core commands not specific to a build system lazy val commandProj = (project in file("main-command")). - dependsOn(serverProj). settings( testedBaseSettings, name := "Command", libraryDependencies ++= Seq(launcherInterface, compilerInterface, - sbtIO, utilLogging, utilCompletion, compilerClasspath) + sbtIO, utilLogging, utilCompletion, compilerClasspath, json4s, json4sNative) // to transitively get json4s) ) // Fixes scope=Scope for Setting (core defined in collectionProj) to define the settings system used in build definitions @@ -194,15 +193,9 @@ lazy val mainSettingsProj = (project in file("main-settings")). utilLogging, sbtIO, utilCompletion, compilerClasspath, libraryManagement) ) -lazy val serverProj = (project in mainPath / "server"). - settings( - baseSettings, - libraryDependencies ++= Seq(json4s, json4sNative) // to transitively get json4s - ) - // The main integration project for sbt. It brings all of the Projsystems together, configures them, and provides for overriding conventions. lazy val mainProj = (project in file("main")). - dependsOn(actionsProj, mainSettingsProj, runProj, commandProj, serverProj). + dependsOn(actionsProj, mainSettingsProj, runProj, commandProj). settings( testedBaseSettings, name := "Main", @@ -251,7 +244,7 @@ lazy val myProvided = config("provided") intransitive def allProjects = Seq( testingProj, testAgentProj, taskProj, stdTaskProj, runProj, scriptedSbtProj, scriptedPluginProj, - actionsProj, commandProj, mainSettingsProj, serverProj, mainProj, sbtProj, bundledLauncherProj) + actionsProj, commandProj, mainSettingsProj, mainProj, sbtProj, bundledLauncherProj) def projectsWithMyProvided = allProjects.map(p => p.copy(configurations = (p.configurations.filter(_ != Provided)) :+ myProvided)) lazy val nonRoots = projectsWithMyProvided.map(p => LocalProject(p.id)) diff --git a/main-command/src/main/scala/sbt/BasicCommandStrings.scala b/main-command/src/main/scala/sbt/BasicCommandStrings.scala index 032b51eda..bfd424a65 100644 --- a/main-command/src/main/scala/sbt/BasicCommandStrings.scala +++ b/main-command/src/main/scala/sbt/BasicCommandStrings.scala @@ -149,6 +149,9 @@ object BasicCommandStrings { def Shell = "shell" def ShellDetailed = "Provides an interactive prompt from which commands can be run." + def Server = "server" + def ServerDetailed = "Provides a network server and an interactive prompt from which commands can be run." + def StashOnFailure = "sbtStashOnFailure" def PopOnFailure = "sbtPopOnFailure" diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index 937de57bb..54f75a75d 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -13,14 +13,16 @@ import BasicCommandStrings._ import CommandUtil._ import BasicKeys._ -import sbt.server.Server import java.io.File import sbt.io.IO +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.atomic.AtomicBoolean import scala.util.control.NonFatal +import scala.annotation.tailrec object BasicCommands { - lazy val allBasicCommands = Seq(nop, ignore, help, completionsCommand, multi, ifLast, append, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, shell, read, alias) ++ compatCommands + lazy val allBasicCommands = Seq(nop, ignore, help, completionsCommand, multi, ifLast, append, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, shell, server, read, alias) ++ compatCommands def nop = Command.custom(s => success(() => s)) def ignore = Command.command(FailureWall)(idFun) @@ -180,9 +182,6 @@ object BasicCommands { } def shell = Command.command(Shell, Help.more(Shell, ShellDetailed)) { s => - // TODO hook it in, start in the right place, shutdown on termination - val server = Server.start("127.0.0.1", 12700) - val history = (s get historyPath) getOrElse Some(new File(s.baseDir, ".history")) val prompt = (s get shellPrompt) match { case Some(pf) => pf(s); case None => "> " } val reader = new FullReader(history, s.combinedParser) @@ -195,6 +194,32 @@ object BasicCommands { } } + var askingAlready = false + val commandListers = Seq(new ConsoleListener(), new NetworkListener()) + val commandQueue: ConcurrentLinkedQueue[Option[String]] = new ConcurrentLinkedQueue() + + @tailrec def blockUntilNextCommand: Option[String] = + Option(commandQueue.poll) match { + case Some(x) => x + case None => + Thread.sleep(50) + blockUntilNextCommand + } + def server = Command.command(Server, Help.more(Server, ServerDetailed)) { s => + if (!askingAlready) { + commandListers foreach { x => + x.run(commandQueue, CommandStatus(s, true)) + } + } + blockUntilNextCommand match { + case Some(line) => + // tell listern to be inactive . + val newState = s.copy(onFailure = Some(Server), remainingCommands = line +: Server +: s.remainingCommands).setInteractive(true) + if (line.trim.isEmpty) newState else newState.clearGlobalLog + case None => s.setInteractive(false) + } + } + def read = Command.make(ReadCommand, Help.more(ReadCommand, ReadDetailed))(s => applyEffect(readParser(s))(doRead(s))) def readParser(s: State) = { diff --git a/main/command/src/main/scala/sbt/CommandListener.scala b/main/command/src/main/scala/sbt/CommandListener.scala new file mode 100644 index 000000000..fcc71cb85 --- /dev/null +++ b/main/command/src/main/scala/sbt/CommandListener.scala @@ -0,0 +1,12 @@ +package sbt + +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.atomic.AtomicBoolean + +trait CommandListener { + // represents a loop that keeps asking an IO device for String input + def run(queue: ConcurrentLinkedQueue[Option[String]], + status: CommandStatus): Unit + def shutdown(): Unit + def setStatus(status: CommandStatus): Unit +} diff --git a/main/command/src/main/scala/sbt/CommandStatus.scala b/main/command/src/main/scala/sbt/CommandStatus.scala new file mode 100644 index 000000000..60d0eb76c --- /dev/null +++ b/main/command/src/main/scala/sbt/CommandStatus.scala @@ -0,0 +1,3 @@ +package sbt + +case class CommandStatus(state: State, canEnter: Boolean) diff --git a/main/command/src/main/scala/sbt/ConsoleListener.scala b/main/command/src/main/scala/sbt/ConsoleListener.scala new file mode 100644 index 000000000..1a2f7ae49 --- /dev/null +++ b/main/command/src/main/scala/sbt/ConsoleListener.scala @@ -0,0 +1,31 @@ +package sbt + +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference } + +class ConsoleListener extends CommandListener { + + // val history = (s get historyPath) getOrElse Some(new File(s.baseDir, ".history")) + // val prompt = (s get shellPrompt) match { case Some(pf) => pf(s); case None => "> " } + // val reader = new FullReader(history, s.combinedParser) + // val line = reader.readLine(prompt) + // line match { + // case Some(line) => + // val newState = s.copy(onFailure = Some(Shell), remainingCommands = line +: Shell +: s.remainingCommands).setInteractive(true) + // if (line.trim.isEmpty) newState else newState.clearGlobalLog + // case None => s.setInteractive(false) + // } + + def run(queue: ConcurrentLinkedQueue[Option[String]], + status: CommandStatus): Unit = + { + // spawn thread and loop + } + + def shutdown(): Unit = + { + // interrupt and kill the thread + } + + def setStatus(status: CommandStatus): Unit = ??? +} diff --git a/main/command/src/main/scala/sbt/NetworkListener.scala b/main/command/src/main/scala/sbt/NetworkListener.scala new file mode 100644 index 000000000..6853b256c --- /dev/null +++ b/main/command/src/main/scala/sbt/NetworkListener.scala @@ -0,0 +1,22 @@ +package sbt + +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.atomic.AtomicBoolean + +import server.Server + +class NetworkListener extends CommandListener { + def run(queue: ConcurrentLinkedQueue[Option[String]], + status: CommandStatus): Unit = + { + val server = Server.start("127.0.0.1", 12700) + // spawn thread and loop + } + + def shutdown(): Unit = + { + // interrupt and kill the thread + } + + def setStatus(status: CommandStatus): Unit = ??? +} diff --git a/main/server/src/main/scala/sbt/server/ClientConnection.scala b/main/command/src/main/scala/sbt/server/ClientConnection.scala similarity index 98% rename from main/server/src/main/scala/sbt/server/ClientConnection.scala rename to main/command/src/main/scala/sbt/server/ClientConnection.scala index 2e6426b51..34bbb9865 100644 --- a/main/server/src/main/scala/sbt/server/ClientConnection.scala +++ b/main/command/src/main/scala/sbt/server/ClientConnection.scala @@ -1,7 +1,8 @@ /* * Copyright (C) 2016 Lightbend Inc. */ -package sbt.server +package sbt +package server import java.net.{ SocketTimeoutException, Socket } import java.util.concurrent.atomic.AtomicBoolean diff --git a/main/server/src/main/scala/sbt/server/Serialization.scala b/main/command/src/main/scala/sbt/server/Serialization.scala similarity index 98% rename from main/server/src/main/scala/sbt/server/Serialization.scala rename to main/command/src/main/scala/sbt/server/Serialization.scala index 50730798b..37a4742c9 100644 --- a/main/server/src/main/scala/sbt/server/Serialization.scala +++ b/main/command/src/main/scala/sbt/server/Serialization.scala @@ -1,7 +1,8 @@ /* * Copyright (C) 2016 Lightbend Inc. */ -package sbt.server +package sbt +package server import org.json4s.JsonAST.{ JArray, JString } import org.json4s._ diff --git a/main/server/src/main/scala/sbt/server/Server.scala b/main/command/src/main/scala/sbt/server/Server.scala similarity index 98% rename from main/server/src/main/scala/sbt/server/Server.scala rename to main/command/src/main/scala/sbt/server/Server.scala index 0d8b46941..9a4302ff1 100644 --- a/main/server/src/main/scala/sbt/server/Server.scala +++ b/main/command/src/main/scala/sbt/server/Server.scala @@ -1,7 +1,8 @@ /* * Copyright (C) 2016 Lightbend Inc. */ -package sbt.server +package sbt +package server import java.net.{ SocketTimeoutException, InetAddress, ServerSocket } import java.util.concurrent.ConcurrentLinkedQueue diff --git a/main/server/src/main/scala/sbt/server/protocol.scala b/main/command/src/main/scala/sbt/server/protocol.scala similarity index 74% rename from main/server/src/main/scala/sbt/server/protocol.scala rename to main/command/src/main/scala/sbt/server/protocol.scala index 714a640ec..e96392edb 100644 --- a/main/server/src/main/scala/sbt/server/protocol.scala +++ b/main/command/src/main/scala/sbt/server/protocol.scala @@ -1,7 +1,8 @@ /* * Copyright (C) 2016 Lightbend Inc. */ -package sbt.server +package sbt +package server trait Event