├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── actor-client └── src │ ├── main │ └── scala │ │ └── sbt │ │ └── client │ │ └── actors │ │ ├── SbtClientProxy.scala │ │ └── SbtConnectionProxy.scala │ └── test │ └── scala │ └── sbt │ └── client │ └── actors │ ├── FakeSbtChannel.scala │ ├── FakeSbtClient.scala │ ├── FakeSbtConnector.scala │ ├── SbtClientBuilderSpec.scala │ ├── SbtClientProxySpec.scala │ ├── SbtConnectionProxySpec.scala │ └── TestHelpers.scala ├── client └── src │ ├── main │ └── scala │ │ └── sbt │ │ └── client │ │ ├── RemoteKeys.scala │ │ ├── SbtChannel.scala │ │ ├── SbtClient.scala │ │ ├── SbtConnector.scala │ │ ├── SettingKey.scala │ │ ├── Subscription.scala │ │ ├── TaskKey.scala │ │ ├── impl │ │ ├── DebugClient.scala │ │ ├── RequestLifeCycle.scala │ │ ├── SbtServerLocator.scala │ │ ├── SimpleChannel.scala │ │ ├── SimpleClient.scala │ │ ├── SimpleConnector.scala │ │ └── SimpleLocator.scala │ │ ├── interaction.scala │ │ └── package.scala │ └── test │ ├── resources │ └── UTF-8-demo.txt │ └── scala │ └── sbt │ └── client │ └── impl │ └── ExecutionContextsTest.scala ├── commons └── protocol │ └── src │ └── main │ └── scala │ └── sbt │ ├── impl │ └── ipc │ │ ├── IPC.scala │ │ └── package.scala │ └── protocol │ ├── CoreEvents.scala │ ├── EventTracker.scala │ ├── Keys.scala │ ├── Protocol.scala │ ├── Values.scala │ ├── WireProtocol.scala │ └── package.scala ├── integration-tests └── src │ └── main │ └── scala │ ├── com │ └── typesafe │ │ └── sbtrc │ │ └── it │ │ ├── IntegrationTest.scala │ │ ├── SbtClientTest.scala │ │ ├── TestUtil.scala │ │ ├── execution │ │ └── TestExecution.scala │ │ ├── loading │ │ ├── CanCancelTasks.scala │ │ ├── CanKillServer.scala │ │ ├── CanLoadJavaProject.scala │ │ ├── CanLoadSimpleProject.scala │ │ ├── CanUseUiInteractionPlugin.scala │ │ └── SerializedThing.scala │ │ └── package.scala │ └── sbt │ └── client │ └── impl │ └── TestingServerLocator.scala ├── project ├── Dependencies.scala ├── Properties.scala ├── SbtSupport.scala ├── build.properties ├── build.scala ├── dispatch.sbt ├── integration.scala ├── ivy.scala ├── jgit.sbt ├── name.sbt ├── plugins.sbt ├── sbtrc.scala └── site.sbt ├── protocol-test └── src │ └── test │ ├── resource │ └── saved-protocol │ │ ├── 1 │ │ ├── complex │ │ │ ├── build_structure.json │ │ │ ├── compile_failed.json │ │ │ ├── empty_exception.json │ │ │ ├── exception.json │ │ │ ├── key.json │ │ │ ├── moduleid.json │ │ │ ├── project_ref.json │ │ │ ├── scope.json │ │ │ └── scoped_key.json │ │ ├── event │ │ │ ├── bg.json │ │ │ ├── bg_finished.json │ │ │ ├── bg_started.json │ │ │ ├── build_structure_changed.json │ │ │ ├── detached.json │ │ │ ├── exec_failure.json │ │ │ ├── exec_starting.json │ │ │ ├── exec_success.json │ │ │ ├── exec_waiting.json │ │ │ ├── log │ │ │ │ ├── debug.json │ │ │ │ ├── detached_log_event.json │ │ │ │ ├── error.json │ │ │ │ ├── info.json │ │ │ │ ├── log_std_err.json │ │ │ │ ├── log_std_out.json │ │ │ │ ├── task_log_event.json │ │ │ │ └── warn.json │ │ │ ├── play_started.json │ │ │ ├── task_event.json │ │ │ ├── task_finished.json │ │ │ ├── task_finished_failed.json │ │ │ ├── task_finished_none.json │ │ │ ├── task_started.json │ │ │ ├── task_started_none.json │ │ │ └── value_changed │ │ │ │ ├── boolean.json │ │ │ │ ├── double.json │ │ │ │ ├── float.json │ │ │ │ ├── int.json │ │ │ │ ├── long.json │ │ │ │ └── string.json │ │ ├── list │ │ │ ├── boolean.json │ │ │ ├── double.json │ │ │ ├── int.json │ │ │ ├── nil_as_list.json │ │ │ └── string.json │ │ ├── message │ │ │ ├── cancel_exec_request.json │ │ │ ├── cancel_exec_response.json │ │ │ ├── completion_request.json │ │ │ ├── completion_response.json │ │ │ ├── confirm_request.json │ │ │ ├── confirm_response.json │ │ │ ├── daemon_req.json │ │ │ ├── error_response.json │ │ │ ├── exec_request.json │ │ │ ├── kill_server_req.json │ │ │ ├── listen_to_build_change.json │ │ │ ├── listen_to_events.json │ │ │ ├── listen_to_value.json │ │ │ ├── readline_request.json │ │ │ ├── readline_response.json │ │ │ ├── received_response.json │ │ │ ├── register_client_request.json │ │ │ └── register_client_response.json │ │ ├── seq │ │ │ ├── boolean.json │ │ │ ├── double.json │ │ │ ├── empty.json │ │ │ ├── int.json │ │ │ └── string.json │ │ ├── simple │ │ │ ├── build.json │ │ │ ├── double.json │ │ │ ├── false.json │ │ │ ├── file.json │ │ │ ├── float.json │ │ │ ├── int.json │ │ │ ├── long.json │ │ │ ├── none.json │ │ │ ├── short.json │ │ │ ├── some_boolean.json │ │ │ ├── some_int.json │ │ │ ├── some_string.json │ │ │ ├── string.json │ │ │ └── true.json │ │ └── vector │ │ │ ├── boolean.json │ │ │ ├── double.json │ │ │ ├── empty.json │ │ │ ├── int.json │ │ │ └── string.json │ │ ├── 2 │ │ ├── complex │ │ │ ├── build_structure.json │ │ │ ├── compile_failed.json │ │ │ ├── empty_exception.json │ │ │ ├── exception.json │ │ │ ├── key.json │ │ │ ├── moduleid.json │ │ │ ├── project_ref.json │ │ │ ├── scope.json │ │ │ └── scoped_key.json │ │ ├── event │ │ │ ├── bg.json │ │ │ ├── bg_finished.json │ │ │ ├── bg_started.json │ │ │ ├── build_structure_changed.json │ │ │ ├── build_structure_changed_with_deps.json │ │ │ ├── detached.json │ │ │ ├── exec_failure.json │ │ │ ├── exec_starting.json │ │ │ ├── exec_success.json │ │ │ ├── exec_waiting.json │ │ │ ├── log │ │ │ │ ├── debug.json │ │ │ │ ├── detached_log_event.json │ │ │ │ ├── error.json │ │ │ │ ├── info.json │ │ │ │ ├── log_std_err.json │ │ │ │ ├── log_std_out.json │ │ │ │ ├── task_log_event.json │ │ │ │ └── warn.json │ │ │ ├── play_started.json │ │ │ ├── task_event.json │ │ │ ├── task_finished.json │ │ │ ├── task_finished_failed.json │ │ │ ├── task_finished_none.json │ │ │ ├── task_started.json │ │ │ ├── task_started_none.json │ │ │ └── value_changed │ │ │ │ ├── boolean.json │ │ │ │ ├── double.json │ │ │ │ ├── float.json │ │ │ │ ├── int.json │ │ │ │ ├── long.json │ │ │ │ └── string.json │ │ ├── list │ │ │ ├── boolean.json │ │ │ ├── double.json │ │ │ ├── int.json │ │ │ ├── nil_as_list.json │ │ │ └── string.json │ │ ├── message │ │ │ ├── cancel_exec_request.json │ │ │ ├── cancel_exec_response.json │ │ │ ├── completion_request.json │ │ │ ├── completion_response.json │ │ │ ├── confirm_request.json │ │ │ ├── confirm_response.json │ │ │ ├── daemon_req.json │ │ │ ├── error_response.json │ │ │ ├── exec_request.json │ │ │ ├── inspect_request.json │ │ │ ├── inspect_response.json │ │ │ ├── kill_server_req.json │ │ │ ├── list_settings_request.json │ │ │ ├── list_settings_response.json │ │ │ ├── listen_to_build_change.json │ │ │ ├── listen_to_events.json │ │ │ ├── listen_to_value.json │ │ │ ├── readline_request.json │ │ │ ├── readline_response.json │ │ │ ├── received_response.json │ │ │ ├── register_client_request.json │ │ │ └── register_client_response.json │ │ ├── seq │ │ │ ├── boolean.json │ │ │ ├── double.json │ │ │ ├── empty.json │ │ │ ├── int.json │ │ │ └── string.json │ │ ├── simple │ │ │ ├── build.json │ │ │ ├── double.json │ │ │ ├── false.json │ │ │ ├── file.json │ │ │ ├── float.json │ │ │ ├── int.json │ │ │ ├── long.json │ │ │ ├── none.json │ │ │ ├── short.json │ │ │ ├── some_boolean.json │ │ │ ├── some_int.json │ │ │ ├── some_string.json │ │ │ ├── string.json │ │ │ └── true.json │ │ └── vector │ │ │ ├── boolean.json │ │ │ ├── double.json │ │ │ ├── empty.json │ │ │ ├── int.json │ │ │ └── string.json │ │ └── 3 │ │ ├── complex │ │ ├── build_structure.json │ │ ├── compile_failed.json │ │ ├── empty_exception.json │ │ ├── exception.json │ │ ├── key.json │ │ ├── moduleid.json │ │ ├── project_ref.json │ │ ├── scope.json │ │ └── scoped_key.json │ │ ├── event │ │ ├── bg.json │ │ ├── bg_finished.json │ │ ├── bg_started.json │ │ ├── build_structure_changed.json │ │ ├── build_structure_changed_with_builds_data.json │ │ ├── build_structure_changed_with_deps.json │ │ ├── detached.json │ │ ├── exec_failure.json │ │ ├── exec_starting.json │ │ ├── exec_success.json │ │ ├── exec_waiting.json │ │ ├── log │ │ │ ├── debug.json │ │ │ ├── detached_log_event.json │ │ │ ├── error.json │ │ │ ├── info.json │ │ │ ├── log_std_err.json │ │ │ ├── log_std_out.json │ │ │ ├── task_log_event.json │ │ │ └── warn.json │ │ ├── play_started.json │ │ ├── task_event.json │ │ ├── task_finished.json │ │ ├── task_finished_failed.json │ │ ├── task_finished_none.json │ │ ├── task_started.json │ │ ├── task_started_none.json │ │ └── value_changed │ │ │ ├── boolean.json │ │ │ ├── double.json │ │ │ ├── float.json │ │ │ ├── int.json │ │ │ ├── long.json │ │ │ └── string.json │ │ ├── list │ │ ├── boolean.json │ │ ├── double.json │ │ ├── int.json │ │ ├── nil_as_list.json │ │ └── string.json │ │ ├── message │ │ ├── cancel_exec_request.json │ │ ├── cancel_exec_response.json │ │ ├── completion_request.json │ │ ├── completion_response.json │ │ ├── confirm_request.json │ │ ├── confirm_response.json │ │ ├── daemon_req.json │ │ ├── error_response.json │ │ ├── exec_request.json │ │ ├── inspect_request.json │ │ ├── inspect_response.json │ │ ├── kill_server_req.json │ │ ├── list_settings_request.json │ │ ├── list_settings_response.json │ │ ├── listen_to_build_change.json │ │ ├── listen_to_events.json │ │ ├── listen_to_value.json │ │ ├── readline_request.json │ │ ├── readline_response.json │ │ ├── received_response.json │ │ ├── register_client_request.json │ │ └── register_client_response.json │ │ ├── seq │ │ ├── boolean.json │ │ ├── double.json │ │ ├── empty.json │ │ ├── int.json │ │ └── string.json │ │ ├── simple │ │ ├── build.json │ │ ├── double.json │ │ ├── false.json │ │ ├── file.json │ │ ├── float.json │ │ ├── int.json │ │ ├── long.json │ │ ├── none.json │ │ ├── short.json │ │ ├── some_boolean.json │ │ ├── some_int.json │ │ ├── some_string.json │ │ ├── string.json │ │ └── true.json │ │ └── vector │ │ ├── boolean.json │ │ ├── double.json │ │ ├── empty.json │ │ ├── int.json │ │ └── string.json │ └── scala │ └── sbt │ └── protocol │ ├── EventTrackerTest.scala │ ├── JUnitMessageUtil.scala │ ├── JUnitUtil.scala │ ├── PlayStartedEvent.scala │ ├── ProtocolSpec.scala │ ├── ProtocolTest.scala │ ├── SerializedValuePicklerSpec.scala │ └── TypeExpressionPicklerSpec.scala ├── server └── src │ ├── main │ └── scala │ │ └── sbt │ │ └── server │ │ ├── BuildStructureCache.scala │ │ ├── CompileReporter.scala │ │ ├── DynamicConversion.scala │ │ ├── DynamicSerialization.scala │ │ ├── FakeAppConfiguration.scala │ │ ├── FileLogger.scala │ │ ├── Inspect.scala │ │ ├── RequestListeners.scala │ │ ├── RequestProcessor.scala │ │ ├── SbtClientHandler.scala │ │ ├── SbtDiscovery.scala │ │ ├── SbtServer.scala │ │ ├── SbtServerMain.scala │ │ ├── SbtServerSocketHandler.scala │ │ ├── SbtToProtocolUtils.scala │ │ ├── Serializations.scala │ │ ├── ServerBoot.scala │ │ ├── ServerEngine.scala │ │ ├── ServerEngineQueue.scala │ │ ├── ServerEngineWork.scala │ │ ├── ServerListener.scala │ │ ├── ServerLogging.scala │ │ ├── ServerState.scala │ │ ├── SettingUtil.scala │ │ ├── TaskCancellationShim.scala │ │ ├── TaskIdRecorder.scala │ │ ├── TaskProgressShim.scala │ │ ├── TestShims.scala │ │ └── UIShim.scala │ └── test │ ├── resources │ └── UTF-8-demo.txt │ └── scala │ └── sbt │ └── server │ ├── ByteDecoderTest.scala │ ├── TestSbtDiscovery.scala │ └── TestSbtToProtocolUtils.scala └── terminal └── src └── main └── scala ├── com └── typesafe │ └── sbtrc │ └── client │ └── SimpleSbtTerminal.scala └── sbt └── terminal └── JLineReader.scala /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.lock 3 | *.komodoproject 4 | .DS_Store 5 | .history 6 | .idea 7 | .classpath 8 | .cache 9 | .project/ 10 | .project 11 | .idea/ 12 | .idea_modules/ 13 | .settings/ 14 | .target/ 15 | project/boot/ 16 | workspace/ 17 | repository/ 18 | target/ 19 | logs/ 20 | .settings 21 | .classpath 22 | .project 23 | .cache 24 | bin/ 25 | .sbtserver 26 | project/.sbtserver 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Use Docker-based container (instead of OpenVZ) 2 | sudo: false 3 | 4 | cache: 5 | directories: 6 | - $HOME/.ivy2/cache 7 | 8 | # At the moment, sbt 0.13.5 is preinstalled in Travis VM image, 9 | # which fortunately corresponds to current scalaz settings. 10 | # The line below can be used to cache a given sbt version. 11 | # - $HOME/.sbt/launchers/0.13.x 12 | 13 | # The line below is used to cache the scala version used by the build 14 | # job, as these versions might be replaced after a Travis CI build 15 | # environment upgrade (e.g. scala 2.11.2 could be replaced by scala 2.11.4). 16 | - $HOME/.sbt/boot/scala-$TRAVIS_SCALA_VERSION 17 | 18 | language: scala 19 | 20 | scala: 21 | - 2.10.3 22 | jdk: 23 | - openjdk6 24 | - openjdk7 25 | - oraclejdk7 26 | notifications: 27 | email: 28 | - qbranch@typesafe.com 29 | 30 | script: 31 | - sbt -Dsbt.integration.timeout=250 clean test integrationTests 32 | 33 | # Tricks to avoid unnecessary cache updates 34 | - find $HOME/.sbt -name "*.lock" | xargs rm 35 | - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Copyright 2012-2013 Typesafe Inc. [http://www.typesafe.com] 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | use this file except in compliance with the License. You may obtain a copy of 7 | the License at 8 | 9 | [http://www.apache.org/licenses/LICENSE-2.0] 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | License for the specific language governing permissions and limitations under 15 | the License. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sbt remote control 2 | 3 | [![Join the chat at https://gitter.im/sbt/sbt-remote-control](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sbt/sbt-remote-control?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | This is an API for controlling [sbt](http://scala-sbt.org/) from 6 | another Scala program. It would be used for example by command line tools, 7 | IDEs, [Activator](https://github.com/typesafehub/activator), 8 | etc. to inspect a project build and run build tasks such as compilation. 9 | 10 | In essence sbt-remote-control converts sbt from a command line tool into a 11 | general backend for any kind of user interface, command line or GUI. 12 | 13 | ## Using 14 | 15 | TODO - Let's write some documentation, for now check out the tests. 16 | 17 | 18 | ## Developing 19 | 20 | Use [sbt](http://scala-sbt.org/) to build the project. Make sure you have an SBT launcher, and run it in the checked out directory. 21 | 22 | 23 | Some possibly-still-relevant architectural information can be 24 | found [on this wiki page](https://github.com/sbt/sbt/wiki/Client-server-split). 25 | 26 | ### Testing 27 | 28 | There are two types of tests: unit and integration. To run all unit tests, simple run: 29 | 30 | sbt> test 31 | 32 | 33 | To run the integration tests, which operate inside the sbt-launcher environment, run: 34 | 35 | sbt> integration-tests 36 | 37 | 38 | ### Running the terminal 39 | 40 | You need to do the equivalent of this: 41 | 42 | ``` 43 | rm -rf ~/.sbt/boot/scala-2.10.[0-9]/com.typesafe.sbtrc/ 44 | java -jar /home/jsuereth/projects/sbt/sbt/target/sbt-launch-0.13.2-SNAPSHOT.jar @/home/jsuereth/projects/sbt/sbt-remote-control/terminal/target/resource_managed/main/sbt-client.properties 45 | ``` 46 | 47 | i.e. remove the sbtrc stuff in .sbt/boot and then launch with an 48 | sbt launcher. Doesn't matter which sbt launcher (in theory). 49 | 50 | ### Publishing 51 | 52 | This project currently publishes to typesafe's ivy-releases repository. To cut a distribution, first tag the project via: 53 | 54 | 55 | git tag -u v 56 | 57 | 58 | Then start or reload sbt: 59 | 60 | 61 | sbt> reload 62 | 63 | 64 | You should see the tagged version of the project if you run the `version` command in sbt. Now just run `publish-local` and the release is out. 65 | 66 | 67 | ### Trying out the Terminal 68 | 69 | First, make sure to publish a local instance of the server 70 | 71 | sbt> publishLocal 72 | 73 | Next, create a staged distribution of the terminal client 74 | 75 | sbt> stage 76 | 77 | Finally, look in the `terminal/target/universal/stage/bin` directory for the `sbt-terminal` script which will let you play with the new sbt-terminal implementation. 78 | 79 | ## License 80 | 81 | This software is licensed under the [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0). 82 | -------------------------------------------------------------------------------- /actor-client/src/test/scala/sbt/client/actors/FakeSbtChannel.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2014 Typesafe Inc. 3 | */ 4 | package sbt.client.actors 5 | 6 | import sbt.client.SbtChannel 7 | 8 | object FakeSbtChannel { 9 | val empty: SbtChannel = null 10 | } 11 | -------------------------------------------------------------------------------- /actor-client/src/test/scala/sbt/client/actors/FakeSbtConnector.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2014 Typesafe Inc. 3 | */ 4 | package sbt.client.actors 5 | 6 | import sbt.client.{ Subscription, SbtConnector, SbtClient, Interaction, SettingKey, TaskKey, SbtChannel } 7 | import sbt.client.{ BuildStructureListener, RawValueListener, ValueListener, EventListener } 8 | import scala.concurrent.ExecutionContext 9 | import akka.actor._ 10 | import akka.util.Timeout 11 | import akka.pattern.ask 12 | import SbtClientProxy.WatchEvent 13 | import java.util.concurrent.atomic.AtomicBoolean 14 | import scala.concurrent.duration._ 15 | 16 | object FakeSbtConnector { 17 | sealed trait Value 18 | case class Client(client: FakeSbtClient) extends Value 19 | case class Error(restart: Boolean, message: String) extends Value 20 | case object Channel extends Value 21 | } 22 | 23 | final class FakeSbtConnector(refFactory: ActorRefFactory) extends SbtConnector { 24 | import FakeSbtConnector._ 25 | private val connections = refFactory.actorOf(Props(new SubscriptionManager[Value]())) 26 | 27 | def sendValue(in: Value): Unit = connections ! in 28 | 29 | def open(onConnect: SbtClient => Unit, onError: (Boolean, String) => Unit)(implicit ex: ExecutionContext): Subscription = { 30 | def unwrapEvent(e: Value): Unit = e match { 31 | case Client(c) => onConnect(c) 32 | case Error(restart, message) => onError(restart, message) 33 | case Channel => // ignore 34 | } 35 | val r = new FakeSubscription[Value](refFactory, unwrapEvent, ex) 36 | connections ! SubscriptionManager.Subscribe(r.runner) 37 | r 38 | } 39 | def openChannel(onConnect: SbtChannel => Unit, onError: (Boolean, String) => Unit)(implicit ex: ExecutionContext): Subscription = { 40 | def unwrapEvent(e: Value): Unit = e match { 41 | case Client(_) => // ignore 42 | case Error(restart, message) => onError(restart, message) 43 | case Channel => onConnect(FakeSbtChannel.empty) 44 | } 45 | val r = new FakeSubscription[Value](refFactory, unwrapEvent, ex) 46 | connections ! SubscriptionManager.Subscribe(r.runner) 47 | r 48 | } 49 | 50 | def close(): Unit = { 51 | import refFactory.dispatcher 52 | implicit val timeout = Timeout(5.seconds) 53 | for { 54 | _ <- ask(connections, SubscriptionManager.StopAll) 55 | } {} 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /actor-client/src/test/scala/sbt/client/actors/SbtClientBuilderSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2014 Typesafe Inc. 3 | */ 4 | package sbt.client.actors 5 | 6 | import org.junit.{ AfterClass, Assert, BeforeClass, Test } 7 | import akka.actor._ 8 | import akka.testkit._ 9 | import concurrent.ExecutionContext.Implicits.global 10 | import sbt.client.{ Subscription, SbtConnector, SbtClient, Interaction, SettingKey, TaskKey } 11 | 12 | object SbtClientBuilderSpec { 13 | val fakeSubscription: Subscription = new Subscription { 14 | def cancel(): Unit = {} 15 | } 16 | 17 | val reconnectableErrorResult = SbtClientBuilder.Error(true, "reconnectable") 18 | val notReconnectableErrorResult = SbtClientBuilder.Error(false, "not reconnectable") 19 | 20 | class Forwarder(sink: ActorRef) extends Actor { 21 | val receive: Receive = { 22 | case m: SbtClientProxy.Close => 23 | sink ! m 24 | m.closed() 25 | context stop self 26 | case m => sink ! m 27 | } 28 | } 29 | 30 | def forwrderProps(sink: ActorRef): Props = Props(new Forwarder(sink)) 31 | } 32 | 33 | class SbtClientBuilderSpec extends DefaultSpecification { 34 | import SbtClientBuilderSpec._, SbtClientBuilder._ 35 | 36 | @Test 37 | def testNewClientConnection(): Unit = withHelper { helper => 38 | import helper._ 39 | 40 | def fakeClientBuilder(client: SbtClient): Props = forwrderProps(testActor) 41 | 42 | withFakeSbtClient() { client => 43 | val nc = SbtConnectionProxy.NewClient(testActor) 44 | val cb = system.actorOf(Props(new SbtClientBuilder(nc, fakeClientBuilder))) 45 | cb ! Subscription(fakeSubscription) 46 | cb ! Client(client) 47 | expectMsgType[SbtConnectionProxy.NewClientResponse.Connected] 48 | system stop cb 49 | } 50 | } 51 | 52 | @Test 53 | def testDeliverAnError(): Unit = withHelper { helper => 54 | import helper._ 55 | 56 | def fakeClientBuilder(client: SbtClient): Props = forwrderProps(testActor) 57 | 58 | val probe = TestProbe() 59 | val nc = SbtConnectionProxy.NewClient(testActor) 60 | val cb = system.actorOf(Props(new SbtClientBuilder(nc, fakeClientBuilder))) 61 | probe.watch(cb) 62 | cb ! Subscription(fakeSubscription) 63 | cb ! reconnectableErrorResult 64 | expectNoMsg() 65 | cb ! notReconnectableErrorResult 66 | expectMsgType[SbtConnectionProxy.NewClientResponse.Error] 67 | probe.expectTerminated(cb) 68 | } 69 | 70 | @Test 71 | def testHandleErrorsAfterConntect(): Unit = withHelper { helper => 72 | import helper._ 73 | 74 | def fakeClientBuilder(client: SbtClient): Props = forwrderProps(testActor) 75 | 76 | withFakeSbtClient() { client => 77 | val probe = TestProbe() 78 | val nc = SbtConnectionProxy.NewClient(testActor) 79 | val cb = system.actorOf(Props(new SbtClientBuilder(nc, fakeClientBuilder))) 80 | probe.watch(cb) 81 | cb ! Subscription(fakeSubscription) 82 | cb ! Client(client) 83 | expectMsgType[SbtConnectionProxy.NewClientResponse.Connected] 84 | cb ! reconnectableErrorResult 85 | expectNoMsg() 86 | cb ! notReconnectableErrorResult 87 | expectMsgType[SbtClientProxy.Close] 88 | probe.expectTerminated(cb) 89 | } 90 | } 91 | 92 | @Test 93 | def testHandleReconnect(): Unit = withHelper { helper => 94 | import helper._ 95 | 96 | def fakeClientBuilder(client: SbtClient): Props = forwrderProps(testActor) 97 | 98 | withFakeSbtClient() { client => 99 | val probe = TestProbe() 100 | val nc = SbtConnectionProxy.NewClient(testActor) 101 | val cb = system.actorOf(Props(new SbtClientBuilder(nc, fakeClientBuilder))) 102 | probe.watch(cb) 103 | cb ! Subscription(fakeSubscription) 104 | cb ! Client(client) 105 | expectMsgType[SbtConnectionProxy.NewClientResponse.Connected] 106 | cb ! Client(client) 107 | expectMsgType[SbtClientProxy.UpdateClient] 108 | cb ! reconnectableErrorResult 109 | expectNoMsg() 110 | cb ! Client(client) 111 | expectMsgType[SbtClientProxy.UpdateClient] 112 | cb ! notReconnectableErrorResult 113 | expectMsgType[SbtClientProxy.Close] 114 | probe.expectTerminated(cb) 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /actor-client/src/test/scala/sbt/client/actors/SbtConnectionProxySpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2014 Typesafe Inc. 3 | */ 4 | package sbt.client.actors 5 | 6 | import org.junit.{ AfterClass, Assert, BeforeClass, Test, Ignore } 7 | import akka.actor._ 8 | import akka.testkit._ 9 | import concurrent.ExecutionContext.Implicits.global 10 | import sbt.client.{ Subscription, SbtConnector, SbtClient, Interaction, SettingKey, TaskKey } 11 | import com.typesafe.config.{ ConfigFactory, Config } 12 | 13 | object SbtConnectionProxySpec { 14 | def builderProps(newClient: SbtConnectionProxy.NewClient, sbtClientProxyProps: SbtClient => Props) = Props(new SbtClientBuilder(newClient, sbtClientProxyProps)) 15 | def clientProps(client: SbtClient, notificationSink: SbtClientProxy.Notification => Unit = _ => ()) = Props(new SbtClientProxy(client, global, notificationSink)) 16 | 17 | class SbtConnectionProxySpecHelper(_system: ActorSystem) extends AkkaTestKitHelper(_system) { 18 | import SbtConnectionProxy._ 19 | import concurrent.ExecutionContext 20 | 21 | def this(config: Config) = this(ActorSystem(AkkaTestKitHelper.randomActorSystemName, config)) 22 | def this() = this(AkkaTestKitHelper.config) 23 | 24 | def withSbtConnectionProxy[T](conn: SbtConnector, 25 | client: SbtClient, 26 | notificationSink: SbtConnectionProxy.Notification => Unit = _ => (), 27 | testClosed: Boolean = true, 28 | builderProps: (SbtConnectionProxy.NewClient, SbtClient => Props) => Props = SbtConnectionProxySpec.builderProps _, 29 | clientProps: SbtClient => Props = SbtConnectionProxySpec.clientProps(_))(body: ActorRef => T)(implicit ex: ExecutionContext): T = { 30 | import SbtConnectionProxy._ 31 | val cp = system.actorOf(Props(new SbtConnectionProxy(conn, (_ => client), builderProps, clientProps, ex, notificationSink))) 32 | try { 33 | body(cp) 34 | } finally { 35 | cp ! Close(testActor) 36 | expectMsgType[Closed.type] 37 | if (testClosed) Assert.assertTrue("Ensure the client is closed", client.isClosed) 38 | } 39 | } 40 | } 41 | 42 | class SbtConnectionProxySpecification extends Specification[SbtConnectionProxySpecHelper] { 43 | def gen() = new SbtConnectionProxySpecHelper() 44 | } 45 | } 46 | 47 | class SbtConnectionProxySpec extends SbtConnectionProxySpec.SbtConnectionProxySpecification { 48 | import SbtConnectionProxySpec._, SbtConnectionProxy._ 49 | 50 | @Test 51 | def testShutdownGracefully(): Unit = withHelper { helper => 52 | import helper._ 53 | withFakeEverything() { (conn, client) => 54 | withSbtConnectionProxy(conn, client, testClosed = false) { cp => } 55 | } 56 | } 57 | 58 | @Test 59 | def defCreateANewClient(): Unit = withHelper { helper => 60 | import helper._ 61 | withFakeEverything() { (conn, client) => 62 | withSbtConnectionProxy(conn, client, x => testActor ! x) { cp => 63 | cp ! NewClient(testActor) 64 | expectMsg(Notifications.BuilderAwaitingChannel) 65 | conn.sendValue(FakeSbtConnector.Channel) 66 | val NewClientResponse.Connected(proxy) = expectMsgType[NewClientResponse.Connected] 67 | } 68 | } 69 | } 70 | 71 | @Test 72 | def testPropogateReconnect(): Unit = withHelper { helper => 73 | import helper._ 74 | withFakeEverything() { (conn, client) => 75 | withSbtConnectionProxy(conn, client, x => testActor ! x, clientProps = clientProps(_, x => testActor ! x)) { cp => 76 | cp ! NewClient(testActor) 77 | expectMsg(Notifications.BuilderAwaitingChannel) 78 | conn.sendValue(FakeSbtConnector.Channel) 79 | val NewClientResponse.Connected(proxy) = expectMsgType[NewClientResponse.Connected] 80 | conn.sendValue(FakeSbtConnector.Channel) 81 | expectMsg(SbtClientProxy.Notifications.Reconnected) 82 | } 83 | } 84 | } 85 | 86 | @Test 87 | def testAbsorbRecoverableErrors(): Unit = withHelper { helper => 88 | import helper._ 89 | withFakeEverything() { (conn, client) => 90 | withSbtConnectionProxy(conn, client, x => testActor ! x) { cp => 91 | cp ! NewClient(testActor) 92 | expectMsg(Notifications.BuilderAwaitingChannel) 93 | conn.sendValue(FakeSbtConnector.Channel) 94 | val NewClientResponse.Connected(proxy) = expectMsgType[NewClientResponse.Connected] 95 | conn.sendValue(FakeSbtConnector.Error(true, "recoverable")) 96 | expectNoMsg() 97 | } 98 | } 99 | } 100 | 101 | @Test 102 | def testCleanUpAfterUnrecoverableError(): Unit = withHelper { helper => 103 | import helper._ 104 | withFakeEverything() { (conn, client) => 105 | withSbtConnectionProxy(conn, client, x => testActor ! x) { cp => 106 | cp ! NewClient(testActor) 107 | expectMsg(Notifications.BuilderAwaitingChannel) 108 | conn.sendValue(FakeSbtConnector.Channel) 109 | val NewClientResponse.Connected(proxy) = expectMsgType[NewClientResponse.Connected] 110 | conn.sendValue(FakeSbtConnector.Error(false, "unrecoverable")) 111 | } 112 | } 113 | } 114 | 115 | @Test 116 | def defCleanUpAfterClientClosure(): Unit = withHelper { helper => 117 | import helper._ 118 | withFakeEverything() { (conn, client) => 119 | withSbtConnectionProxy(conn, client, x => testActor ! x) { cp => 120 | cp ! NewClient(testActor) 121 | expectMsg(Notifications.BuilderAwaitingChannel) 122 | conn.sendValue(FakeSbtConnector.Channel) 123 | val NewClientResponse.Connected(proxy) = expectMsgType[NewClientResponse.Connected] 124 | proxy ! SbtClientProxy.Close(testActor) 125 | expectMsgType[SbtClientProxy.Closed.type] 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /actor-client/src/test/scala/sbt/client/actors/TestHelpers.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2014 Typesafe Inc. 3 | */ 4 | package sbt.client.actors 5 | 6 | import org.junit.{ AfterClass, Assert, BeforeClass, Test } 7 | import akka.actor._ 8 | import akka.util.Timeout 9 | import scala.concurrent.duration._ 10 | import akka.testkit._ 11 | import com.typesafe.config.{ ConfigFactory, Config } 12 | import scala.util.Random 13 | import sbt.client.{ Subscription, SbtConnector, SbtClient, Interaction, SettingKey, TaskKey, SbtChannel } 14 | import scala.concurrent.ExecutionContext 15 | import sbt.protocol 16 | import scala.concurrent.Future 17 | 18 | object AkkaTestKitHelper { 19 | val configString = 20 | """ 21 | |akka { 22 | | loglevel = "OFF" 23 | | stdout-loglevel = "OFF" 24 | |} 25 | """.stripMargin 26 | 27 | val config = ConfigFactory.parseString(configString) 28 | def randomActorSystemName: String = s"test-actor-system-${new String(Random.alphanumeric.take(10).toArray)}" 29 | } 30 | 31 | class AkkaTestKitHelper(_system: ActorSystem) extends TestKit(_system) with ImplicitSender { 32 | def this(config: Config) = this(ActorSystem(AkkaTestKitHelper.randomActorSystemName, config)) 33 | def this() = this(AkkaTestKitHelper.config) 34 | 35 | def after() = system.shutdown() 36 | 37 | lazy val name: String = s"test-${new String(Random.alphanumeric.take(10).toArray)}" 38 | 39 | def withFakeSbtConnector[T](body: FakeSbtConnector => T): T = { 40 | val connector = new FakeSbtConnector(system) 41 | try { 42 | body(connector) 43 | } finally { 44 | connector.close() 45 | } 46 | } 47 | 48 | def withFakeSbtClient[T](configName: String = name, 49 | humanReadableName: String = name, 50 | autocompletions: (String, Int) => Future[Vector[protocol.Completion]] = FakeSbtClient.emptyPossibleAutocompletions, 51 | scopedKeyLookup: String => Future[Seq[protocol.ScopedKey]] = FakeSbtClient.emptyLookupScopedKey, 52 | analyzeExecution: String => Future[protocol.ExecutionAnalysis] = FakeSbtClient.emptyAnalyzeExecution, 53 | requestExecutionCommand: (String, Option[(Interaction, ExecutionContext)]) => Future[Long] = FakeSbtClient.emptyRequestExecutionCommand, 54 | requestExecutionKey: (protocol.ScopedKey, Option[(Interaction, ExecutionContext)]) => Future[Long] = FakeSbtClient.emptyRequestExecutionKey, 55 | cancelExecution: Long => Future[Boolean] = FakeSbtClient.emptyCancelExecution)(body: FakeSbtClient => T): T = { 56 | val client = new FakeSbtClient(refFactory = system, 57 | configName = configName, 58 | humanReadableName = humanReadableName, 59 | autocompletions = autocompletions, 60 | scopedKeyLookup = scopedKeyLookup, 61 | _analyzeExecution = analyzeExecution, 62 | requestExecutionCommand = requestExecutionCommand, 63 | requestExecutionKey = requestExecutionKey, 64 | _cancelExecution = cancelExecution) 65 | try { 66 | body(client) 67 | } finally { 68 | client.close() 69 | } 70 | } 71 | 72 | def withSbtClientProxy[T](client: SbtClient)(body: ActorRef => T)(implicit ex: ExecutionContext): T = { 73 | import SbtClientProxy._ 74 | val cp = system.actorOf(Props(new SbtClientProxy(client, ex, x => testActor ! x))) 75 | try { 76 | body(cp) 77 | } finally { 78 | cp ! Close(testActor) 79 | Assert.assertEquals(expectMsgType[Closed.type], Closed) 80 | } 81 | } 82 | 83 | def withFakeEverything[T](configName: String = name, 84 | humanReadableName: String = name, 85 | autocompletions: (String, Int) => Future[Vector[protocol.Completion]] = FakeSbtClient.emptyPossibleAutocompletions, 86 | scopedKeyLookup: String => Future[Seq[protocol.ScopedKey]] = FakeSbtClient.emptyLookupScopedKey, 87 | analyzeExecution: String => Future[protocol.ExecutionAnalysis] = FakeSbtClient.emptyAnalyzeExecution, 88 | requestExecutionCommand: (String, Option[(Interaction, ExecutionContext)]) => Future[Long] = FakeSbtClient.emptyRequestExecutionCommand, 89 | requestExecutionKey: (protocol.ScopedKey, Option[(Interaction, ExecutionContext)]) => Future[Long] = FakeSbtClient.emptyRequestExecutionKey, 90 | cancelExecution: Long => Future[Boolean] = FakeSbtClient.emptyCancelExecution)(body: (FakeSbtConnector, FakeSbtClient) => T): T = 91 | withFakeSbtConnector { connector => 92 | withFakeSbtClient(configName = configName, 93 | humanReadableName = humanReadableName, 94 | autocompletions = autocompletions, 95 | scopedKeyLookup = scopedKeyLookup, 96 | analyzeExecution = analyzeExecution, 97 | requestExecutionCommand = requestExecutionCommand, 98 | requestExecutionKey = requestExecutionKey, 99 | cancelExecution = cancelExecution) { client => 100 | body(connector, client) 101 | } 102 | } 103 | } 104 | 105 | abstract class Specification[T <: AkkaTestKitHelper] { 106 | def gen(): T 107 | def withHelper[U](body: T => U): U = { 108 | val h = gen() 109 | try body(h) 110 | finally (h.after()) 111 | } 112 | } 113 | 114 | class DefaultSpecification extends Specification[AkkaTestKitHelper] { 115 | def gen() = new AkkaTestKitHelper() 116 | } 117 | -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/RemoteKeys.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package client 3 | 4 | import java.net.URI 5 | import protocol.{ 6 | AttributeKey, 7 | ScopedKey, // Warning here is wrong. We're pulling in the object. 8 | SbtScope 9 | } 10 | import java.io.File 11 | import sbt.Attributed 12 | 13 | // TODO - This kind of needs to line up with sbt versions. 14 | // Maybe this should be in some kind of compatibility layer or soemthing? 15 | object RemoteKeys { 16 | private def attributeKey[T](name: String)(implicit mf: Manifest[T]): ScopedKey = 17 | ScopedKey( 18 | AttributeKey[T](name), 19 | SbtScope()) 20 | def settingKey[T](name: String)(implicit mf: Manifest[T]): sbt.client.SettingKey[T] = 21 | sbt.client.SettingKey[T](attributeKey(name)) 22 | def taskKey[T](name: String)(implicit mf: Manifest[T]): sbt.client.TaskKey[T] = 23 | sbt.client.TaskKey[T](attributeKey[T](name)) 24 | 25 | val name = settingKey[String]("name") 26 | val fullClasspath = taskKey[Seq[Attributed[File]]]("fullClasspath") 27 | 28 | } 29 | // A set of default configurations we can try. 30 | // TODO - This needs to line up with real configurations to be useful. 31 | object RemoteConfigurations { 32 | val Compile = "compile" 33 | val Test = "test" 34 | val Runtime = "runtime" 35 | } 36 | -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/SbtChannel.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package client 3 | 4 | import java.io.Closeable 5 | import concurrent.{ ExecutionContext, Future } 6 | import sbt.serialization._ 7 | import sbt.protocol.{ Message, Response, ProtocolVersion, FeatureTag } 8 | 9 | final class ChannelInUseException() extends Exception("This channel is already in use and can only be claimed once") 10 | 11 | /** 12 | * SbtChannel is a "raw" connection to the sbt server which gives you the plain 13 | * protocol without keeping track of or caching anything for you. Wrap it 14 | * in SbtClient for a much more convenient API. 15 | * 16 | * Note: this trait will add methods over time, which will be ABI-compatible but not source compatible 17 | * if you subtype it. Don't extend this trait if you can't live with that. 18 | */ 19 | trait SbtChannel extends Closeable { 20 | /** UUID of this sbt connection, different every time we connect. */ 21 | def uuid: java.util.UUID 22 | /** 23 | * Name used to store configuration associated with this connection; usually 24 | * the same machine-readable name every time the same app connects. 25 | */ 26 | def configName: String 27 | /** Human-readable name of this client, such as the name of the app. */ 28 | def humanReadableName: String 29 | 30 | /** version of protocol supported by server */ 31 | def serverProtocolVersion: ProtocolVersion 32 | 33 | /** protocol feature tags exported by server */ 34 | def serverTags: Seq[FeatureTag] 35 | 36 | /** 37 | * Send a message over the sbt socket. 38 | * If we fail to write to the socket, the future gets an exception. Note that just because 39 | * the future succeeds doesn't mean the server received and acted on the message. 40 | */ 41 | def sendMessage(message: Message): Future[Unit] 42 | 43 | /** 44 | * Send a message over the sbt socket, getting the serial in a callback which allows you to 45 | * provide a result based on the reply. The "registration" callback is run synchronously 46 | * (before this method returns) and will always run before the message is sent. 47 | */ 48 | def sendMessageWithRegistration[R](message: Message)(registration: Long => Future[R]): Future[R] 49 | 50 | // TODO remove type parameter if we don't add any implicits 51 | /** 52 | * Send a reply message (replyTo is the serial of the request we are replying to; 53 | * each request gets 0 or 1 replies, defined in the protocol for each kind of request. 54 | * If we fail to write to the socket, the future gets an exception. Note that just because 55 | * the future succeeds doesn't mean the server received and acted on the message. 56 | */ 57 | def replyMessage(replyTo: Long, message: Response): Future[Unit] 58 | 59 | /** 60 | * Invoke a function in the given ExecutionContext for every message received over this channel. 61 | * This may be called ONLY ONCE by whoever will primarily use the channel; calling it claims the channel 62 | * and starts handling events. This avoids races on startup (we don't want to lose events before 63 | * a handler has been attached). 64 | * If this is called twice you will get ChannelInUseException. All channels need a "primary 65 | * owner" which controls when the stream of events starts and handles requests and such. 66 | * 67 | * The listener is guaranteed to get a ClosedEvent as the last message; if the channel is 68 | * already closed, it will be sent immediately (through the provided ExecutionContext). 69 | * 70 | * NOTE your ExecutionContext needs to keep messages in order or you will be sad! 71 | */ 72 | def claimMessages(listener: protocol.Envelope => Unit)(implicit ex: ExecutionContext): Subscription 73 | 74 | /** 75 | * Like claimMessages but can be called more than once and does not start the message stream. No messages will be sent 76 | * until someone does claimMessages(). 77 | */ 78 | def handleMessages(listener: protocol.Envelope => Unit)(implicit ex: ExecutionContext): Subscription 79 | 80 | /** true if close() has been called or the socket was closed by the server. */ 81 | def isClosed: Boolean 82 | } 83 | -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/SbtConnector.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package client 3 | 4 | import java.io.Closeable 5 | import concurrent.{ ExecutionContext, Future } 6 | 7 | /** 8 | * This represents something that will connect to the sbt server *and* reconnect on failure. 9 | * 10 | * Start trying to connect by calling open(). 11 | * You can close the connection and stop reconnecting by calling `close()`. 12 | */ 13 | trait SbtConnector extends Closeable { 14 | 15 | /** 16 | * Begin trying to connect to the server. Handlers may be called multiple times 17 | * if we disconnect and then reconnect. If a connection is already active when you call 18 | * this, your onConnect handler will be called immediately. If the connector 19 | * has already been closed when you call this, your onError handler will be called immediately. 20 | * Otherwise the handlers are called when connection or error occurs. 21 | * 22 | * The onConnect handler is invoked for initial connection and each subsequent successful 23 | * reconnect. 24 | * 25 | * The onError handler is invoked anytime we fail to connect or anytime the connection 26 | * is closed. The boolean parameter is true if we will try to connect again and false 27 | * if we are permanently closed. The string parameter is the error message. 28 | * 29 | * Both handlers are run in the provided execution context. 30 | * 31 | * The returned subscription may be canceled to remove both handlers. The subscription 32 | * will also be canceled when the SbtConnector is closed. 33 | * 34 | */ 35 | def open(onConnect: SbtClient => Unit, onError: (Boolean, String) => Unit)(implicit ex: ExecutionContext): Subscription 36 | 37 | /** Lower-level version of open() which returns a raw channel rather than an SbtClient */ 38 | def openChannel(onConnect: SbtChannel => Unit, onError: (Boolean, String) => Unit)(implicit ex: ExecutionContext): Subscription 39 | } 40 | 41 | object SbtConnector { 42 | /** 43 | * Factory method which returns a default SbtConnector. Use open() on the connector 44 | * to connect, and close() on the connector to disconnect. 45 | * 46 | * @param configName an alphanumeric ASCII name used as a config key to track per-client-type state 47 | * @param humanReadableName human-readable name of your client used to show in UIs 48 | * @param directory the directory to open as an sbt build 49 | */ 50 | def apply(configName: String, humanReadableName: String, directory: java.io.File): SbtConnector = { 51 | new impl.SimpleConnector(configName, humanReadableName, directory, 52 | impl.SimpleLocator) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/SettingKey.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package client 3 | 4 | import java.net.URI 5 | 6 | /** This is a wrapper around scoped keys which you can use to promote typesafe interfaces. */ 7 | final case class SettingKey[T](key: protocol.ScopedKey) { 8 | // TODO - scope changing methods. 9 | 10 | def in(build: URI): SettingKey[T] = SettingKey[T](key.copy(scope = key.scope.copy(build = Some(build)))) 11 | def in(project: protocol.ProjectReference): SettingKey[T] = 12 | SettingKey[T](key.copy(scope = 13 | key.scope.copy( 14 | project = Some(project), 15 | build = Some(project.build)))) 16 | // TODO - make this typesafe!!!! 17 | def in(config: String): SettingKey[T] = SettingKey[T](key.copy(scope = key.scope.copy(config = Some(config)))) 18 | 19 | // TODO - in with a key as the option... 20 | } -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/Subscription.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package client 3 | 4 | trait Subscription { 5 | def cancel(): Unit 6 | } -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/TaskKey.scala: -------------------------------------------------------------------------------- 1 | package sbt.client 2 | 3 | import java.net.URI 4 | 5 | /** This is a wrapper around scoped keys which you can use to promote typesafe interfaces. */ 6 | final case class TaskKey[T](key: sbt.protocol.ScopedKey) { 7 | // TODO - scope changing methods. 8 | 9 | def in(build: URI): TaskKey[T] = TaskKey[T](key.copy(scope = key.scope.copy(build = Some(build)))) 10 | def in(project: sbt.protocol.ProjectReference): TaskKey[T] = 11 | 12 | TaskKey[T](key.copy(scope = 13 | key.scope.copy( 14 | project = Some(project), 15 | build = Some(project.build)))) 16 | // TODO - make this typesafe!!!! 17 | def in(config: String): TaskKey[T] = TaskKey[T](key.copy(scope = key.scope.copy(config = Some(config)))) 18 | 19 | // TODO - in with a key as the option... 20 | } -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/impl/DebugClient.scala: -------------------------------------------------------------------------------- 1 | 2 | package sbt.client.impl 3 | 4 | import sbt.impl.ipc 5 | import java.net._ 6 | import sbt.client._ 7 | import sbt.protocol._ 8 | 9 | private[sbt] object DebugClient { 10 | 11 | def apply(port: Int): SbtClient = { 12 | val client = new ipc.Client(new Socket("127.0.0.1", port)) 13 | val uuid = java.util.UUID.randomUUID() 14 | val configName = "debug-client" 15 | val humanReadableName = "Debug Client" 16 | client.sendJson[Message](RegisterClientRequest(ClientInfo(uuid.toString, configName, humanReadableName, ProtocolVersion1, Vector.empty)), 17 | client.serialGetAndIncrement()) 18 | val channel = new SimpleSbtChannel(uuid, configName, humanReadableName, 19 | ProtocolVersion.protocolVersionLatest, 20 | Nil, // tags 21 | client, closeHandler = () => ()) 22 | SbtClient(channel) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/impl/SimpleLocator.scala: -------------------------------------------------------------------------------- 1 | 2 | package sbt.client.impl 3 | 4 | import java.io.File 5 | import java.net.URL 6 | 7 | private[client] object SimpleLocator extends LaunchedSbtServerLocator { 8 | def sbtProperties(directory: File): URL = { 9 | // TODO - Check sbt binary/full version first! 10 | getClass.getClassLoader.getResource("sbt-server.properties") 11 | } 12 | } -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/interaction.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package client 3 | 4 | /** 5 | * An interface which clients that support interaction with tasks implement. 6 | */ 7 | trait Interaction { 8 | /** Prompts the user for input, optionally with a mask for characters. */ 9 | def readLine(prompt: String, mask: Boolean): Option[String] 10 | /** Ask the user to confirm something (yes or no) before continuing. */ 11 | def confirm(msg: String): Boolean 12 | } 13 | -------------------------------------------------------------------------------- /client/src/main/scala/sbt/client/package.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | 3 | import scala.util.Try 4 | 5 | package object client { 6 | // Wrapper names for functions 7 | type BuildStructureListener = protocol.MinimalBuildStructure => Unit 8 | type RawValueListener = (protocol.ScopedKey, protocol.TaskResult) => Unit 9 | type ValueListener[T] = (protocol.ScopedKey, Try[T]) => Unit 10 | type EventListener = protocol.Event => Unit 11 | } 12 | -------------------------------------------------------------------------------- /client/src/test/scala/sbt/client/impl/ExecutionContextsTest.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Typesafe Inc. 3 | */ 4 | package sbt.client.impl 5 | 6 | import org.junit.Assert._ 7 | import org.junit._ 8 | 9 | class ExecutionContextsTest { 10 | 11 | @Test 12 | def testSerializedCached(): Unit = { 13 | val ctx = ExecutionContexts.serializedCachedExecutionContext 14 | def execute(f: => Unit): Unit = { 15 | ctx.execute(new Runnable { override def run() = f }) 16 | } 17 | // supposed to run one thing at a time, in order. 18 | @volatile var items = Vector.empty[Int] 19 | for (v <- 1 to 100) 20 | execute(items :+= v) 21 | val latch = new java.util.concurrent.CountDownLatch(1) 22 | execute(latch.countDown()) 23 | latch.await() 24 | assertEquals(100, items.size) 25 | for (v <- 1 to 100) 26 | assertEquals(v, items(v - 1)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /commons/protocol/src/main/scala/sbt/impl/ipc/IPC.scala: -------------------------------------------------------------------------------- 1 | package sbt.impl 2 | package ipc 3 | 4 | import java.net.{ InetAddress, ServerSocket, Socket } 5 | import java.io.DataInputStream 6 | import java.io.BufferedInputStream 7 | import java.io.DataOutputStream 8 | import java.io.BufferedOutputStream 9 | import java.io.IOException 10 | import java.nio.charset.Charset 11 | import java.io.InputStream 12 | import java.net.SocketException 13 | import java.util.concurrent.atomic.AtomicInteger 14 | import sbt.serialization._ 15 | 16 | trait Envelope[T] { 17 | def serial: Long 18 | def replyTo: Long 19 | def content: T 20 | } 21 | 22 | final case class WireEnvelope(length: Int, override val serial: Long, override val replyTo: Long, override val content: Array[Byte]) extends Envelope[Array[Byte]] { 23 | def asString: String = { 24 | new String(content, utf8) 25 | } 26 | } 27 | /** Thrown if we have issues performing a handshake between client + server. */ 28 | class HandshakeException(msg: String, cause: Exception, val socket: Socket) extends Exception(msg, cause) 29 | 30 | // This is thread-safe in that it should send/receive each message atomically, 31 | // but multiple threads will have to be careful that they don't send messages 32 | // in a nonsensical sequence. 33 | abstract class Peer(protected val socket: Socket) { 34 | require(!socket.isClosed()) 35 | require(socket.getInputStream() ne null) 36 | require(socket.getOutputStream() ne null) 37 | 38 | // these two need to be protected by synchronized on the streams 39 | private val in = new DataInputStream(new BufferedInputStream(socket.getInputStream())) 40 | private val out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())) 41 | 42 | // this would only be useful if we buffered received messages and 43 | // allowed replies to be sent out of order 44 | private val nextSerial = new AtomicInteger(1) 45 | 46 | protected def handshake(toSend: String, toExpect: String): Unit = try { 47 | sendString(toSend, serialGetAndIncrement()) 48 | 49 | val m = receive() 50 | if (m.serial != 1L) { 51 | close() 52 | throw new HandshakeException("Expected handshake serial 1", null, socket) 53 | } 54 | 55 | val s = m.asString 56 | if (s != toExpect) { 57 | close() 58 | throw new HandshakeException("Expected greeting '" + toExpect + "' received '" + s + "'", null, socket) 59 | } 60 | } catch { 61 | case e: IOException => throw new HandshakeException("Unable to perform handshake", e, socket) 62 | } 63 | 64 | def isClosed = socket.isClosed() 65 | 66 | // this is not automatic because if you want to use the serial you need 67 | // to be sure to record it BEFORE you send the message with that serial. 68 | def serialGetAndIncrement(): Long = 69 | nextSerial.getAndIncrement() 70 | 71 | def send(message: WireEnvelope): Unit = out.synchronized { 72 | require(message.serial < nextSerial.get) 73 | if (isClosed) 74 | throw new SocketException("socket is closed") 75 | out.writeInt(message.length) 76 | out.writeLong(message.serial) 77 | out.writeLong(message.replyTo) 78 | out.write(message.content) 79 | out.flush() 80 | } 81 | 82 | def send(message: Array[Byte], serial: Long): Unit = 83 | send(WireEnvelope(length = message.length, serial = serial, 84 | replyTo = 0L, content = message)) 85 | 86 | def reply(replyTo: Long, message: Array[Byte]): Unit = { 87 | require(replyTo != 0L) 88 | send(WireEnvelope(length = message.length, serial = serialGetAndIncrement(), 89 | replyTo = replyTo, content = message)) 90 | } 91 | 92 | def receive(): WireEnvelope = in.synchronized { 93 | if (isClosed) 94 | throw new SocketException("socket is closed") 95 | val length = in.readInt() 96 | val serial = in.readLong() 97 | val replyTo = in.readLong() 98 | if (length > (1024 * 1024 * 100)) 99 | throw new RuntimeException("Ridiculously huge message (" + length + " bytes)") 100 | val bytes = new Array[Byte](length) 101 | in.readFully(bytes) 102 | WireEnvelope(length, serial, replyTo, bytes) 103 | } 104 | 105 | def sendString(message: String, serial: Long): Unit = { 106 | send(message.getBytes(utf8), serial) 107 | } 108 | 109 | def replyString(replyTo: Long, message: String): Unit = { 110 | require(replyTo != 0L) 111 | reply(replyTo, message.getBytes(utf8)) 112 | } 113 | 114 | private def jsonString[T: Pickler](message: T): String = { 115 | SerializedValue(message).toJsonString 116 | } 117 | 118 | def sendJson[T: Pickler](message: T, serial: Long): Unit = { 119 | sendString(jsonString(message), serial) 120 | } 121 | 122 | def replyJson[T: Pickler](replyTo: Long, message: T): Unit = { 123 | require(replyTo != 0L) 124 | replyString(replyTo, jsonString(message)) 125 | } 126 | 127 | def close(): Unit = { 128 | // don't synchronize the close() calls, we need to be able 129 | // to close from another thread (and we're assuming that 130 | // Java streams are OK with that) 131 | ignoringIOException { in.close() } 132 | ignoringIOException { out.close() } 133 | ignoringIOException { socket.close() } 134 | } 135 | } 136 | 137 | class Server(private val serverSocket: ServerSocket) extends MultiClientServer(serverSocket.accept()) { 138 | 139 | handshake(ServerGreeting, ClientGreeting) 140 | 141 | def port = serverSocket.getLocalPort() 142 | 143 | override def close() = { 144 | super.close() 145 | ignoringIOException { serverSocket.close() } 146 | } 147 | } 148 | 149 | class MultiClientServer(socket: Socket) extends Peer(socket) { 150 | handshake(ServerGreeting, ClientGreeting) 151 | } 152 | 153 | class Client(socket: Socket) extends Peer(socket) { 154 | handshake(ClientGreeting, ServerGreeting) 155 | } 156 | -------------------------------------------------------------------------------- /commons/protocol/src/main/scala/sbt/impl/ipc/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Typesafe, Inc. 3 | * Based on sbt IPC code copyright 2009 Mark Harrah 4 | */ 5 | 6 | package sbt.impl 7 | 8 | import java.net.{ InetAddress, ServerSocket, Socket } 9 | import java.io.IOException 10 | import java.nio.charset.Charset 11 | 12 | package object ipc { 13 | private[ipc] val loopback = InetAddress.getByName(null) 14 | 15 | private[ipc] def ignoringIOException[T](block: => T): Unit = { 16 | try { 17 | block 18 | } catch { 19 | case e: IOException => () 20 | } 21 | } 22 | 23 | private[ipc] val version = "1" 24 | private[ipc] val ServerGreeting = "I am Server: " + version 25 | private[ipc] val ClientGreeting = "I am Client: " + version 26 | 27 | private[ipc] val utf8 = Charset.forName("UTF-8") 28 | 29 | def openServerSocket(): ServerSocket = { 30 | new ServerSocket(0, 1, loopback) 31 | } 32 | 33 | def accept(serverSocket: ServerSocket): Server = { 34 | new Server(serverSocket) 35 | } 36 | 37 | def openClient(port: Int): Client = { 38 | new Client(new Socket(loopback, port)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commons/protocol/src/main/scala/sbt/protocol/Values.scala: -------------------------------------------------------------------------------- 1 | package sbt.protocol 2 | 3 | import sbt.serialization._ 4 | import scala.util.{ Try, Success, Failure } 5 | 6 | /** 7 | * Represents a serialized value with a stringValue fallback. 8 | */ 9 | final case class BuildValue(serialized: SerializedValue, stringValue: String) { 10 | def value[T](implicit unpickler: Unpickler[T]): Try[T] = 11 | serialized.parse[T] 12 | override def equals(o: Any): Boolean = 13 | o match { 14 | case x: BuildValue => x.serialized == serialized 15 | case _ => false 16 | } 17 | override def hashCode: Int = serialized.hashCode 18 | } 19 | 20 | object BuildValue { 21 | def apply[T](value: T)(implicit pickler: Pickler[T]): BuildValue = { 22 | BuildValue(serialized = SerializedValue(value)(pickler), stringValue = value.toString) 23 | } 24 | 25 | implicit val pickler: Pickler[BuildValue] = genPickler[BuildValue] 26 | implicit val unpickler: Unpickler[BuildValue] = genUnpickler[BuildValue] 27 | } 28 | 29 | object ThrowableDeserializers { 30 | val empty: ThrowableDeserializers = ThrowableDeserializers() 31 | private[protocol]type TypePair[T] = (Manifest[T], Unpickler[T]) 32 | private[protocol] def toPair[T](implicit mf: Manifest[T], reader: Unpickler[T]): TypePair[T] = (mf, reader) 33 | } 34 | 35 | final case class ThrowableDeserializers(readers: Map[Manifest[_], Unpickler[_]] = Map.empty[Manifest[_], Unpickler[_]]) { 36 | import ThrowableDeserializers._ 37 | def add[T](implicit mf: Manifest[T], reader: Unpickler[T]): ThrowableDeserializers = 38 | ThrowableDeserializers(this.readers + toPair[T](mf, reader)) 39 | 40 | def tryAnyReader(in: SerializedValue): Option[Throwable] = 41 | readers.foldLeft[Option[Throwable]](None) { 42 | case (None, (_, reader)) => in.parse(reader).toOption.map(_.asInstanceOf[Throwable]) 43 | case (x, _) => x 44 | } 45 | } 46 | 47 | /** 48 | * Represents the outcome of a task. The outcome can be a value or an exception. 49 | */ 50 | sealed trait TaskResult { 51 | 52 | /** Returns whether or not a task was executed successfully. */ 53 | def isSuccess: Boolean 54 | 55 | final def result[A](implicit unpickleResult: Unpickler[A]): Try[A] = 56 | resultWithCustomThrowable[A, Throwable](unpickleResult, implicitly[Unpickler[Throwable]]) 57 | def resultWithCustomThrowable[A, B <: Throwable](implicit unpickleResult: Unpickler[A], unpickleFailure: Unpickler[B]): Try[A] 58 | def resultWithCustomThrowables[A](throwableDeserializers: ThrowableDeserializers)(implicit unpickleResult: Unpickler[A]): Try[A] 59 | } 60 | 61 | /** This represents that the task was run successfully. */ 62 | final case class TaskSuccess(value: BuildValue) extends TaskResult { 63 | override def isSuccess = true 64 | override def resultWithCustomThrowable[A, B <: Throwable](implicit unpickleResult: Unpickler[A], unpickleFailure: Unpickler[B]): Try[A] = 65 | value.value[A] 66 | override def resultWithCustomThrowables[A](throwableDeserializers: ThrowableDeserializers)(implicit unpickleResult: Unpickler[A]): Try[A] = 67 | value.value[A] 68 | } 69 | 70 | final case class TaskFailure(cause: BuildValue) extends TaskResult { 71 | override def isSuccess = false 72 | override def resultWithCustomThrowable[A, B <: Throwable](implicit unpickleResult: Unpickler[A], unpickleFailure: Unpickler[B]): Try[A] = { 73 | val t = cause.serialized.parse[B].getOrElse(new Exception(cause.stringValue)) 74 | Failure(t) 75 | } 76 | override def resultWithCustomThrowables[A](throwableDeserializers: ThrowableDeserializers)(implicit unpickleResult: Unpickler[A]): Try[A] = { 77 | val t = throwableDeserializers.tryAnyReader(cause.serialized).getOrElse(new Exception(cause.stringValue)) 78 | Failure(t) 79 | } 80 | } 81 | 82 | object TaskSuccess { 83 | implicit val pickler: Pickler[TaskSuccess] = genPickler[TaskSuccess] 84 | implicit val unpickler: Unpickler[TaskSuccess] = genUnpickler[TaskSuccess] 85 | } 86 | 87 | object TaskFailure { 88 | implicit val pickler: Pickler[TaskFailure] = genPickler[TaskFailure] 89 | implicit val unpickler: Unpickler[TaskFailure] = genUnpickler[TaskFailure] 90 | } 91 | 92 | // TODO currently due to a pickling bug caused by a Scala bug, 93 | // the macros won't know all the subtypes of TaskResult if we 94 | // put this companion object earlier in the file. 95 | object TaskResult { 96 | implicit val pickler: Pickler[TaskResult] = genPickler[TaskResult] 97 | implicit val unpickler: Unpickler[TaskResult] = genUnpickler[TaskResult] 98 | } 99 | -------------------------------------------------------------------------------- /commons/protocol/src/main/scala/sbt/protocol/WireProtocol.scala: -------------------------------------------------------------------------------- 1 | package sbt.protocol 2 | 3 | import sbt.impl.ipc 4 | import java.io.File 5 | import language.existentials 6 | import scala.util.control.NonFatal 7 | import sbt.serialization._ 8 | 9 | private[sbt] final case class Envelope(override val serial: Long, override val replyTo: Long, override val content: Message) extends ipc.Envelope[Message] 10 | 11 | /** 12 | * This class is responsible for extracting from the wire protocol into 13 | * the "class" protocol. This may disappear at some point, as the duplication with ipc.Envelope may not be necessary. 14 | */ 15 | private[sbt] object Envelope { 16 | def apply(wire: ipc.WireEnvelope): Envelope = { 17 | //System.err.println(s"Attempting to parse ${wire.asString}") 18 | val serialized = SerializedValue.fromJsonString(wire.asString) 19 | val message: Message = 20 | serialized.parse[Message].recover({ 21 | case NonFatal(e) => 22 | try { 23 | if (System.getProperty("sbt.client.debug") == "true") 24 | System.err.println(s"Failed to parse message ${wire.asString}: ${e.getClass.getName}: ${e.getMessage}") 25 | } catch { 26 | case _: Throwable => 27 | } 28 | if (wire.replyTo != 0L) 29 | ErrorResponse(s"exception parsing response: ${e.getMessage}") 30 | else 31 | UnknownMessage(serialized) 32 | }).get 33 | //System.err.println(s"Parsed it as $message") 34 | 35 | new Envelope(wire.serial, wire.replyTo, message) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /commons/protocol/src/main/scala/sbt/protocol/package.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | 3 | import sbt.serialization._ 4 | 5 | package object protocol { 6 | import sbt.serialization.CanToString 7 | 8 | private implicit val severityCanToString: CanToString[xsbti.Severity] = CanToString( 9 | _.toString, { 10 | case "Info" => xsbti.Severity.Info 11 | case "Warn" => xsbti.Severity.Warn 12 | case "Error" => xsbti.Severity.Error 13 | }) 14 | 15 | implicit val severityPickler: Pickler[xsbti.Severity] with Unpickler[xsbti.Severity] = 16 | canToStringPickler[xsbti.Severity] 17 | 18 | private def convert[T](o: Option[T]): xsbti.Maybe[T] = 19 | o match { 20 | case Some(value) => xsbti.Maybe.just(value) 21 | case None => xsbti.Maybe.nothing() 22 | } 23 | 24 | private def convertToOption[T](o: xsbti.Maybe[T]): Option[T] = 25 | if (o.isDefined()) Some(o.get()) 26 | else None 27 | 28 | // TODO there's no real logic to why these are here and others are in 29 | // companion objects. 30 | implicit val positionPickler = genPickler[Position] 31 | implicit val positionUnpickler = genUnpickler[Position] 32 | implicit val compilationFailurePickler = genPickler[CompilationFailure] 33 | implicit val compilationFailureUnpickler = genUnpickler[CompilationFailure] 34 | implicit val moduleIdPickler = genPickler[ModuleId] 35 | implicit object moduleIdUnpickler extends Unpickler[ModuleId] { 36 | private val stringUnpickler = sbt.serialization.stringPickler 37 | private val attrsUnpickler = sbt.serialization.stringMapPickler[String] 38 | override def unpickle(tag: String, reader: PReader): Any = { 39 | reader.pushHints() 40 | reader.hintTag(this.tag) 41 | reader.hintStaticallyElidedType() 42 | // TODO - this was already called... 43 | reader.beginEntry() 44 | def readString(field: String): String = { 45 | stringUnpickler.unpickleEntry(reader.readField(field)).toString 46 | } 47 | val org = readString("organization") 48 | val n = readString("name") 49 | val attrs = { 50 | val sr = reader.readField("attributes") 51 | sr.hintTag(attrsUnpickler.tag) 52 | sr.hintStaticallyElidedType() 53 | attrsUnpickler.unpickle(attrsUnpickler.tag.key, sr).asInstanceOf[Map[String, String]] 54 | } 55 | reader.endEntry() 56 | reader.popHints() 57 | ModuleId(org, n, attrs) 58 | } 59 | override val tag: FastTypeTag[ModuleId] = implicitly[FastTypeTag[ModuleId]] 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /integration-tests/src/main/scala/com/typesafe/sbtrc/it/IntegrationTest.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.sbtrc 2 | package it 3 | 4 | import sbt.client._ 5 | import sbt.client.impl.{ 6 | SimpleConnector, 7 | SimpleLocator 8 | } 9 | //import akka.actor.ActorSystem 10 | /** Base class for integration tests. */ 11 | abstract class IntegrationTest extends DelayedInit with xsbti.AppMain { 12 | // Junk to make delayed init work. 13 | private var _config: xsbti.AppConfiguration = null 14 | private var _test: () => Unit = null 15 | // We create one per test... 16 | //private var _system: ActorSystem = null 17 | final def delayedInit(x: => Unit): Unit = _test = () => x 18 | 19 | /** Returns an actor system we can use. */ 20 | //final def system: ActorSystem = _system 21 | 22 | /** Returns the current sbt launcher configuration for the test. */ 23 | final def configuration: xsbti.AppConfiguration = _config 24 | 25 | def repositories = configuration.provider.scalaProvider.launcher.appRepositories 26 | // Runs our test, we hardcode this to return success in the absence of failure, so we can use 27 | // classic exceptions to fail an integration test. 28 | final def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = 29 | try withContextClassloader { 30 | _config = configuration 31 | //_system = ActorSystem("IntegrationTest") 32 | try _test() 33 | finally { 34 | //system.shutdown() 35 | //system.awaitTermination() 36 | } 37 | // IF we don't throw an exception, we've succeeded 38 | Success 39 | } catch { 40 | case t: Exception => 41 | t.printStackTrace() 42 | Failure 43 | } 44 | 45 | def assertEquals(o: Any, o2: Any): Unit = { 46 | if (o != o2) sys.error(s"Objects are not equal! $o != $o2") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /integration-tests/src/main/scala/com/typesafe/sbtrc/it/TestUtil.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.sbtrc 2 | 3 | import java.io.File 4 | 5 | final class TestUtil(val scratchDir: File) { 6 | import TestUtil.defaultSbtTestVersion 7 | 8 | def createFile(name: java.io.File, content: String): Unit = { 9 | val writer = new java.io.FileWriter(name) 10 | try writer.write(content) 11 | finally writer.close() 12 | } 13 | 14 | def makeDummyEmptyDirectory(relativeDir: String): File = { 15 | val dir = new File(scratchDir, relativeDir) 16 | if (!dir.isDirectory()) dir.mkdirs() 17 | dir 18 | } 19 | 20 | def makeEmptySbtProject(relativeDir: String, sbtVersion: String = defaultSbtTestVersion): File = { 21 | val dir = makeDummyEmptyDirectory(relativeDir) 22 | 23 | val project = new File(dir, "project") 24 | if (!project.isDirectory()) project.mkdirs() 25 | 26 | val props = new File(project, "build.properties") 27 | createFile(props, "sbt.version=" + sbtVersion) 28 | 29 | dir 30 | } 31 | 32 | /** Creates a dummy project we can run sbt against. */ 33 | def makeDummySbtProject(relativeDir: String, sbtVersion: String = defaultSbtTestVersion): File = { 34 | val dir = makeEmptySbtProject(relativeDir, sbtVersion) 35 | 36 | val build = new File(dir, "build.sbt") 37 | createFile(build, s""" 38 | name := "${relativeDir}" 39 | 40 | libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test" 41 | """) 42 | 43 | val scalaSource = new File(dir, "src/main/scala") 44 | if (!scalaSource.isDirectory()) scalaSource.mkdirs() 45 | val main = new File(scalaSource, "hello.scala") 46 | createFile(main, "object Main extends App { println(\"Hello World\") }\n") 47 | 48 | val testSource = new File(dir, "src/test/scala") 49 | if (!testSource.isDirectory()) testSource.mkdirs() 50 | val tests = new File(testSource, "tests.scala") 51 | createFile(tests, """ 52 | import org.junit.Assert._ 53 | import org.junit._ 54 | 55 | class OnePassOneFailTest { 56 | @Test 57 | def testThatShouldPass: Unit = { 58 | } 59 | 60 | @Test 61 | def testThatShouldFail: Unit = { 62 | assertTrue("this is not true", false) 63 | } 64 | } 65 | 66 | class OnePassTest { 67 | @Test 68 | def testThatShouldPass: Unit = { 69 | } 70 | } 71 | 72 | class OneFailTest { 73 | @Test 74 | def testThatShouldFail: Unit = { 75 | assertTrue("this is not true", false) 76 | } 77 | } 78 | """) 79 | 80 | dir 81 | } 82 | 83 | def makeDummySbtProjectWithBrokenBuild(relativeDir: String, sbtVersion: String = defaultSbtTestVersion): File = { 84 | val dir = makeDummySbtProject(relativeDir, sbtVersion) 85 | 86 | val build = new File(dir, "build.sbt") 87 | createFile(build, "BLARG := \"" + relativeDir + "\"\n") 88 | 89 | dir 90 | } 91 | 92 | def makeDummySbtProjectWithNoMain(relativeDir: String, sbtVersion: String = defaultSbtTestVersion): File = { 93 | val dir = makeDummySbtProject(relativeDir, sbtVersion) 94 | 95 | val main = new File(dir, "src/main/scala/hello.scala") 96 | // doesn't extend App 97 | createFile(main, "object Main { println(\"Hello World\") }\n") 98 | 99 | dir 100 | } 101 | 102 | def makeDummySbtProjectWithMultipleMain(relativeDir: String, sbtVersion: String = defaultSbtTestVersion): File = { 103 | val dir = makeDummySbtProject(relativeDir, sbtVersion) 104 | 105 | val main = new File(dir, "src/main/scala/hello.scala") 106 | createFile(main, """ 107 | object Main1 extends App { println("Hello World 1") } 108 | object Main2 extends App { println("Hello World 2") } 109 | object Main3 extends App { println("Hello World 3") } 110 | """) 111 | 112 | dir 113 | } 114 | } 115 | 116 | object TestUtil { 117 | // TODO - hook this up to build in some fashion 118 | val sbt13TestVersion = "0.13.9" 119 | def defaultSbtTestVersion = sbt13TestVersion 120 | } 121 | -------------------------------------------------------------------------------- /integration-tests/src/main/scala/com/typesafe/sbtrc/it/loading/CanKillServer.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.sbtrc 2 | package it 3 | package loading 4 | 5 | import sbt.client._ 6 | import sbt.protocol._ 7 | import sbt.serialization._ 8 | import java.util.concurrent.Executors 9 | import concurrent.duration.Duration.Inf 10 | import concurrent.{ Await, ExecutionContext } 11 | import java.io.File 12 | import java.util.concurrent.LinkedBlockingQueue 13 | import annotation.tailrec 14 | 15 | class CanKillServer extends SbtClientTest { 16 | def sleepUntilDead(client: SbtClient): Unit = { 17 | var count = 0 18 | while (!client.isClosed && count < 7) { 19 | val secondsToWait = (count * 2) + 1 20 | System.out.println(s"$count: Client has not closed yet, so sleeping until death (${secondsToWait} seconds).") 21 | Thread.sleep(secondsToWait * 1000L) 22 | count += 1 23 | } 24 | 25 | if (client.isClosed) 26 | System.out.println("Looks like client has closed!") 27 | else 28 | System.out.println(s"Giving up after $count waits - client never closed") 29 | } 30 | 31 | // Exit via "exit" command 32 | val dummy1 = utils.makeDummySbtProject("testExit") 33 | withSbt(dummy1) { client => 34 | import concurrent.ExecutionContext.Implicits.global 35 | client.watchBuild { build => 36 | System.out.println("Telling sbt to exit via exit command.") 37 | client.requestExecution("exit", interaction = None) 38 | } 39 | 40 | sleepUntilDead(client) 41 | 42 | assert(client.isClosed, s"Did not lose connection to sbt server which was supposed to be closed after exit") 43 | } 44 | 45 | // "reboot" command 46 | // from a client perspective, reboot looks just like quitting, 47 | // but on the server side it theoretically optimizes by 48 | // keeping the old JVM. 49 | val dummy2 = utils.makeDummySbtProject("testReboot") 50 | withSbt(dummy2) { client => 51 | import concurrent.ExecutionContext.Implicits.global 52 | client.watchBuild { build => 53 | System.out.println("Telling sbt to reboot.") 54 | client.requestExecution("reboot", interaction = None) 55 | } 56 | 57 | sleepUntilDead(client) 58 | 59 | assert(client.isClosed, s"Did not lose connection to sbt server which was supposed to be closed after reboot") 60 | } 61 | 62 | // exit via requestSelfDestruct() which we probably don't need anymore. 63 | val dummy3 = utils.makeDummySbtProject("testKill") 64 | withSbt(dummy3) { client => 65 | import concurrent.ExecutionContext.Implicits.global 66 | client.watchBuild { build => 67 | System.out.println("Telling sbt to die via special kill request.") 68 | client.requestSelfDestruct() 69 | } 70 | 71 | sleepUntilDead(client) 72 | 73 | assert(client.isClosed, s"Did not lose connection to sbt server which was supposed to be closed after requestSelfDestruct") 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /integration-tests/src/main/scala/com/typesafe/sbtrc/it/loading/SerializedThing.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.sbtrc 2 | package it 3 | package loading 4 | 5 | final case class SerializedThing(name: String, value: Int) 6 | object SerializedThing { 7 | import sbt.serialization._ 8 | // TODO - we don't want this to gunky everything up. 9 | 10 | implicit val pickler: Pickler[SerializedThing] = Pickler.generate[SerializedThing] 11 | implicit val unpickler: Unpickler[SerializedThing] = Unpickler.generate[SerializedThing] 12 | } 13 | -------------------------------------------------------------------------------- /integration-tests/src/main/scala/com/typesafe/sbtrc/it/package.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.sbtrc 2 | 3 | import sbt.IO 4 | // Helper methods for running tests. 5 | package object it { 6 | 7 | // This method has to be used around any code the makes use of Akka to ensure the classloader is right. 8 | def withContextClassloader[A](f: => A): A = { 9 | val current = Thread.currentThread 10 | val old = current.getContextClassLoader 11 | current setContextClassLoader getClass.getClassLoader 12 | try f 13 | finally current setContextClassLoader old 14 | } 15 | 16 | // Success and failure conditions for tests. 17 | case object Success extends xsbti.Exit { 18 | val code = 0 19 | } 20 | case object Failure extends xsbti.Exit { 21 | val code = 1 22 | } 23 | } -------------------------------------------------------------------------------- /integration-tests/src/main/scala/sbt/client/impl/TestingServerLocator.scala: -------------------------------------------------------------------------------- 1 | package sbt.client.impl 2 | 3 | import sbt.client._ 4 | import java.io.File 5 | import java.net.URL 6 | import xsbti.{ 7 | AppConfiguration, 8 | PredefinedRepository, 9 | MavenRepository, 10 | IvyRepository 11 | } 12 | import java.nio.charset.Charset.defaultCharset 13 | 14 | object Testing { 15 | def connector(configuration: xsbti.AppConfiguration, projectDirectory: java.io.File): SbtConnector = 16 | new SimpleConnector("sbt-client-test", "SbtClientTest unit test", 17 | projectDirectory, locator(configuration, new File(projectDirectory, "../sbt-global"))) 18 | 19 | // TODO - Create a prop-file locator that uses our own repositories to 20 | // find the classes, so we use cached values... 21 | def locator(configuration: xsbti.AppConfiguration, globalDir: File): LaunchedSbtServerLocator = new LaunchedSbtServerLocator { 22 | // TODO - Do we care if the version for this directory is different? 23 | def sbtProperties(directory: File): URL = 24 | rewrittenPropsUrl 25 | 26 | lazy val propsFile: File = { 27 | val tmp = java.io.File.createTempFile("sbt-server", "properties") 28 | tmp.deleteOnExit() 29 | sbt.IO.write(tmp, s"sbt.global.base=${globalDir.toString}") 30 | tmp 31 | } 32 | 33 | // Rewrites boot properties for debugging. 34 | lazy val rewrittenPropsUrl: URL = { 35 | val tmp = java.io.File.createTempFile("sbt-server", "properties") 36 | val existing = getClass.getClassLoader.getResource("sbt-server.properties") 37 | val oldLines = sbt.IO.readLinesURL(existing, defaultCharset) 38 | val newLines = makeNewLaunchProperties(oldLines) 39 | sbt.IO.writeLines(tmp, newLines, defaultCharset, false) 40 | tmp.deleteOnExit() 41 | tmp.toURI.toURL 42 | } 43 | 44 | private def repositories: List[String] = { 45 | configuration.provider.scalaProvider.launcher.ivyRepositories.toList map { 46 | case ivy: IvyRepository => 47 | // TODO - We only support converting things we care about here, not unviersally ok. 48 | val pattern = Option(ivy.ivyPattern).map(",".+).getOrElse("") 49 | val aPattern = Option(ivy.artifactPattern).map(",".+).getOrElse("") 50 | val mvnCompat = if (ivy.mavenCompatible) ", mavenCompatible" else "" 51 | " " + ivy.id + ": " + ivy.url + pattern + aPattern + mvnCompat 52 | case mvn: MavenRepository => " " + mvn.id + ": " + mvn.url 53 | case predef: PredefinedRepository => " " + predef.id.toString 54 | } 55 | } 56 | 57 | // TODO - We also need to rewrite the line about the boot directory to use our boot directory. 58 | private def makeNewLaunchProperties(old: List[String]): List[String] = { 59 | val header = old.takeWhile(line => !line.contains("[repositories]")) 60 | val tail = old.dropWhile(line => !line.contains("[boot]")).map { 61 | // TODO - did we get all the overrides we need? 62 | case x if x contains "directory:" => s" directory: ${configuration.provider.scalaProvider.launcher.bootDirectory.toString}" 63 | case x if x contains "ivy-home:" => s" ivy-home: ${configuration.provider.scalaProvider.launcher.ivyHome.toString}" 64 | case x if x contains "override-build-repos:" => "override-build-repos: true" 65 | case x if x contains "repository-config:" => "" 66 | case x if x contains "jvmprops:" => s" jvmprops: ${propsFile.toString}" 67 | case x => x 68 | } 69 | header ++ ("[repositories]" :: repositories) ++ List("") ++ tail 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /project/Properties.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | 4 | // Defines how to generate properties file based on build attributes. 5 | object Properties { 6 | 7 | val makePropertiesSource = TaskKey[Seq[File]]("make-properties-source") 8 | 9 | def writeIfChanged(file: java.io.File, content: String): Unit = { 10 | val oldContent = if (file.exists) IO.read(file) else "" 11 | if (oldContent != content) { 12 | IO.write(file, content) 13 | } 14 | } 15 | 16 | def makePropertyClassSetting(sbtVersion: String, scalaVersion: String): Seq[Setting[_]] = Seq( 17 | resourceGenerators in Compile <+= makePropertiesSource, 18 | makePropertiesSource <<= (version, resourceManaged in Compile, compile in Compile) map { (v, dir, analysis) => 19 | val parent= dir / "com/typesafe/sbtrc/properties" 20 | IO createDirectory parent 21 | val target = parent / "sbtrc.properties" 22 | 23 | writeIfChanged(target, makeJavaPropertiesString(v, sbtVersion, scalaVersion)) 24 | 25 | Seq(target) 26 | } 27 | ) 28 | 29 | 30 | def lastCompilationTime(analysis: sbt.inc.Analysis): Long = { 31 | val times = analysis.apis.internal.values map (_.compilation.startTime) 32 | if(times.isEmpty) 0L else times.max 33 | } 34 | 35 | def makeDefaultSbtVersionFile(sbtVersion: String, resourceManagedDir: File): Seq[File] = { 36 | val file = resourceManagedDir / "default.sbt.version" 37 | IO.write(file, s"sbt.version=${sbtVersion}") 38 | Seq(file) 39 | } 40 | 41 | 42 | def makeJavaPropertiesString(version: String, sbtVersion: String, scalaVersion: String): String = 43 | """|app.version=%s 44 | |sbt.version=%s 45 | |sbt.scala.version=%s 46 | |app.scala.version=%s 47 | |sbt.Xmx=512M 48 | |sbt.PermSize=128M 49 | |sbt.atmos.default.version=%s 50 | |""".stripMargin format (version, sbtVersion, sbtScalaVersion(sbtVersion), scalaVersion, Dependencies.sbtAtmosDefaultVersion) 51 | 52 | 53 | def sbtScalaVersion(sbtVersion: String): String = 54 | (sbtVersion split "[\\.\\-]" take 3) match { 55 | case Array("0", "12", _) => "2.9.2" 56 | case Array("0", "13", _) => "2.10.2" 57 | case _ => "2.9.1" 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /project/SbtSupport.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | 4 | object SbtSupport { 5 | val sbtLaunchJarUrl = SettingKey[String]("sbt-launch-jar-url") 6 | val sbtLaunchJarLocation = SettingKey[File]("sbt-launch-jar-location") 7 | val sbtLaunchJar = TaskKey[File]("sbt-launch-jar", "Resolves SBT launch jar") 8 | 9 | def snapshotDownloadUrl(v: String) = "http://private-repo.typesafe.com/typesafe/ivy-snapshots/org.scala-sbt/sbt-launch/"+v+"/sbt-launch.jar" 10 | def currentDownloadUrl(v: String) = "http://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/"+v+"/sbt-launch.jar" 11 | def oldDownloadUrl(v: String) = "http://repo.typesafe.com/typesafe/ivy-releases/org.scala-tools.sbt/sbt-launch/"+v+"/sbt-launch.jar" 12 | 13 | def localFile(v: String): File = new File(System.getProperty("user.home") + "/.ivy2/local/org.scala-sbt/sbt-launch/"+v+"/jars/sbt-launch.jar") 14 | 15 | def downloadUrlForVersion(v: String) = (v split "[^\\d]" filter (_ matches "[\\d]+") map (_.toInt)) match { 16 | case Array(0, 11, x, _*) if x >= 3 => currentDownloadUrl(v) 17 | case Array(0, y, _*) if y >= 12 => currentDownloadUrl(v) 18 | case _ => oldDownloadUrl(v) 19 | } 20 | 21 | def downloadFile(uri: String, file: File): File = { 22 | import dispatch.classic._ 23 | // oddly, some places require us to create the file before writing... 24 | IO.touch(file) 25 | val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(file)) 26 | try Http(url(uri) >>> writer) 27 | catch { 28 | case e: Exception => 29 | // if download fails, attempt to get rid of the empty file 30 | file.delete() 31 | throw e 32 | } 33 | finally writer.close() 34 | // TODO - GPG Trust validation. 35 | file 36 | } 37 | 38 | val buildSettings: Seq[Setting[_]] = Seq( 39 | // TODO - Configure different SBT version... 40 | //sbtLaunchJarUrl <<= sbtVersion apply downloadUrlForVersion, 41 | // TODO - We use a milestone launcher for now... 42 | sbtLaunchJarUrl := currentDownloadUrl(Dependencies.sbtMainVersion), 43 | sbtLaunchJarLocation <<= baseDirectory (_ / "target" / "sbt" / "sbt-launch.jar"), 44 | sbtLaunchJar := { 45 | val dest = sbtLaunchJarLocation.value 46 | // nonexistent file returns 0 length 47 | if (dest.length == 0) { 48 | val local = localFile(Dependencies.sbtMainVersion) 49 | if (local.length > 0) { 50 | IO.copyFile(local, dest) 51 | dest 52 | } else { 53 | downloadFile(sbtLaunchJarUrl.value, dest) 54 | } 55 | } else { 56 | dest 57 | } 58 | } 59 | ) 60 | 61 | val settings: Seq[Setting[_]] = Seq( 62 | resourceGenerators in Compile <+= (sbtLaunchJar.task map (Seq apply _)) 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.9 2 | -------------------------------------------------------------------------------- /project/dispatch.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies += "net.databinder" % "dispatch-http_2.10" % "0.8.10" 2 | -------------------------------------------------------------------------------- /project/jgit.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.6.1") 2 | -------------------------------------------------------------------------------- /project/name.sbt: -------------------------------------------------------------------------------- 1 | name := "sbt-rc-build" 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1") 2 | 3 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2") 4 | 5 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0-RC1") 6 | 7 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") 8 | -------------------------------------------------------------------------------- /project/site.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.7.1") 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/complex/build_structure.json: -------------------------------------------------------------------------------- 1 | {"builds":["file:\/\/\/test\/project"],"projects":[{"id":{"build":"file:\/\/\/test\/project","name":"test"},"plugins":["com.foo.Plugin"],"$type":"sbt.protocol.MinimalProjectStructure"}],"$type":"sbt.protocol.MinimalBuildStructure"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/complex/compile_failed.json: -------------------------------------------------------------------------------- 1 | {"message":"the compile failed","cause":null,"problems":[{"category":"something","severity":"Error","message":"stuff didn't go well","position":{"lineContent":"this is some stuff on the line","sourcePath":"\/temp\/bar","sourceFile":"file:\/temp\/bar","line":10,"offset":11,"pointer":12,"pointerSpace":"foo"},"$type":"sbt.protocol.Problem"}],"$type":"sbt.protocol.CompileFailedException"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/complex/key.json: -------------------------------------------------------------------------------- 1 | {"name":"name","manifest":"java.lang.String","$type":"sbt.protocol.AttributeKey"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/complex/moduleid.json: -------------------------------------------------------------------------------- 1 | {"organization":"com.foo","name":"bar","attributes":{"a":"b"},"$type":"sbt.protocol.ModuleId"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/complex/project_ref.json: -------------------------------------------------------------------------------- 1 | {"build":"file:\/\/\/test\/project","name":"test","$type":"sbt.protocol.ProjectReference"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/complex/scope.json: -------------------------------------------------------------------------------- 1 | {"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null,"$type":"sbt.protocol.SbtScope"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/complex/scoped_key.json: -------------------------------------------------------------------------------- 1 | {"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/bg.json: -------------------------------------------------------------------------------- 1 | {"jobId":67,"serialized":{"$type":"sbt.server.PlayStartedEvent","port":10.0},"$type":"sbt.protocol.BackgroundJobEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/bg_finished.json: -------------------------------------------------------------------------------- 1 | {"executionId":9,"jobId":67,"$type":"sbt.protocol.BackgroundJobFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/bg_started.json: -------------------------------------------------------------------------------- 1 | {"executionId":9,"job":{"id":67,"humanReadableName":"foojob","spawningTask":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}}},"$type":"sbt.protocol.BackgroundJobStarted"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/build_structure_changed.json: -------------------------------------------------------------------------------- 1 | {"structure":{"builds":["file:\/\/\/test\/project"],"projects":[{"id":{"build":"file:\/\/\/test\/project","name":"test"},"plugins":["com.foo.Plugin"],"$type":"sbt.protocol.MinimalProjectStructure"}]},"$type":"sbt.protocol.BuildStructureChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/detached.json: -------------------------------------------------------------------------------- 1 | {"serialized":{"$type":"sbt.server.PlayStartedEvent","port":11.0},"$type":"sbt.protocol.DetachedEvent"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/exec_failure.json: -------------------------------------------------------------------------------- 1 | {"id":42,"$type":"sbt.protocol.ExecutionFailure"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/exec_starting.json: -------------------------------------------------------------------------------- 1 | {"id":56,"$type":"sbt.protocol.ExecutionStarting"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/exec_success.json: -------------------------------------------------------------------------------- 1 | {"id":44,"$type":"sbt.protocol.ExecutionSuccess"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/exec_waiting.json: -------------------------------------------------------------------------------- 1 | {"id":41,"command":"foo","client":{"uuid":"350954c2-6bf0-4925-b066-3bf20f32906b","configName":"foo","humanReadableName":"FOO","featureTags":["unknown"],"protocolVersion":"1"},"$type":"sbt.protocol.ExecutionWaiting"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/log/debug.json: -------------------------------------------------------------------------------- 1 | {"taskId":5,"entry":{"level":"debug","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/log/detached_log_event.json: -------------------------------------------------------------------------------- 1 | {"entry":{"message":"Hello, world","$type":"sbt.protocol.LogStdOut"},"$type":"sbt.protocol.DetachedLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/log/error.json: -------------------------------------------------------------------------------- 1 | {"taskId":3,"entry":{"level":"error","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/log/info.json: -------------------------------------------------------------------------------- 1 | {"taskId":2,"entry":{"level":"info","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/log/log_std_err.json: -------------------------------------------------------------------------------- 1 | {"taskId":6,"entry":{"message":"TEST","$type":"sbt.protocol.LogStdErr"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/log/log_std_out.json: -------------------------------------------------------------------------------- 1 | {"taskId":7,"entry":{"message":"TEST2","$type":"sbt.protocol.LogStdOut"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/log/task_log_event.json: -------------------------------------------------------------------------------- 1 | {"taskId":1,"entry":{"message":"Hello, world","$type":"sbt.protocol.LogStdOut"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/log/warn.json: -------------------------------------------------------------------------------- 1 | {"taskId":4,"entry":{"level":"warn","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/play_started.json: -------------------------------------------------------------------------------- 1 | {"taskId":8,"serialized":{"$type":"sbt.server.PlayStartedEvent","port":10.0},"$type":"sbt.protocol.TaskEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/task_event.json: -------------------------------------------------------------------------------- 1 | {"taskId":4,"serialized":{"$type":"sbt.protocol.TestEvent","error":null,"outcome":"passed","description":null,"duration":0.0,"name":"name"},"$type":"sbt.protocol.TaskEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/task_finished.json: -------------------------------------------------------------------------------- 1 | {"executionId":48,"taskId":1,"success":true,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"message":null,"$type":"sbt.protocol.TaskFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/task_finished_failed.json: -------------------------------------------------------------------------------- 1 | {"executionId":48,"taskId":1,"success":false,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"message":"error message here","$type":"sbt.protocol.TaskFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/task_finished_none.json: -------------------------------------------------------------------------------- 1 | {"executionId":48,"taskId":1,"success":true,"key":null,"message":null,"$type":"sbt.protocol.TaskFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/task_started.json: -------------------------------------------------------------------------------- 1 | {"executionId":47,"taskId":1,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"$type":"sbt.protocol.TaskStarted"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/task_started_none.json: -------------------------------------------------------------------------------- 1 | {"executionId":47,"taskId":1,"key":null,"$type":"sbt.protocol.TaskStarted"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/value_changed/boolean.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"true","serialized":true},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/value_changed/double.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"0.0","serialized":0.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/value_changed/float.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"0.0","serialized":0.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/value_changed/int.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"42","serialized":42.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/value_changed/long.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"43","serialized":43.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/event/value_changed/string.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"HI","serialized":"HI"},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/list/boolean.json: -------------------------------------------------------------------------------- 1 | [true,false,true,true,false] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/list/double.json: -------------------------------------------------------------------------------- 1 | [1.0,2.0,3.0] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/list/int.json: -------------------------------------------------------------------------------- 1 | [1,2,3] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/list/nil_as_list.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/list/string.json: -------------------------------------------------------------------------------- 1 | ["Bar","Baz"] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/cancel_exec_request.json: -------------------------------------------------------------------------------- 1 | {"id":1,"$type":"sbt.protocol.CancelExecutionRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/cancel_exec_response.json: -------------------------------------------------------------------------------- 1 | {"attempted":false,"$type":"sbt.protocol.CancelExecutionResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/completion_request.json: -------------------------------------------------------------------------------- 1 | {"in":"He","level":2,"$type":"sbt.protocol.CommandCompletionsRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/completion_response.json: -------------------------------------------------------------------------------- 1 | {"results":[{"append":"llo","display":"Hello","isEmpty":true,"$type":"sbt.protocol.Completion"}],"$type":"sbt.protocol.CommandCompletionsResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/confirm_request.json: -------------------------------------------------------------------------------- 1 | {"executionId":43,"message":"msg","$type":"sbt.protocol.ConfirmRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/confirm_response.json: -------------------------------------------------------------------------------- 1 | {"line":"line","$type":"sbt.protocol.ReadLineResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/daemon_req.json: -------------------------------------------------------------------------------- 1 | {"daemon":true,"$type":"sbt.protocol.DaemonRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/error_response.json: -------------------------------------------------------------------------------- 1 | {"error":"ZOMG","$type":"sbt.protocol.ErrorResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/exec_request.json: -------------------------------------------------------------------------------- 1 | {"command":"test command string","$type":"sbt.protocol.ExecutionRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/kill_server_req.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.KillServerRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/listen_to_build_change.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ListenToBuildChange"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/listen_to_events.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ListenToEvents"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/listen_to_value.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"$type":"sbt.protocol.ListenToValue"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/readline_request.json: -------------------------------------------------------------------------------- 1 | {"executionId":42,"prompt":"HI","mask":true,"$type":"sbt.protocol.ReadLineRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/readline_response.json: -------------------------------------------------------------------------------- 1 | {"line":"line","$type":"sbt.protocol.ReadLineResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/received_response.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ReceivedResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/register_client_request.json: -------------------------------------------------------------------------------- 1 | {"info":{"uuid":"350954c2-6bf0-4925-b066-3bf20f32906b","configName":"foo","humanReadableName":"FOO","featureTags":["unknown"],"protocolVersion":"1"},"$type":"sbt.protocol.RegisterClientRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/message/register_client_response.json: -------------------------------------------------------------------------------- 1 | {"info":{"featureTags":["unknown"],"protocolVersion":"1"},"$type":"sbt.protocol.RegisterClientResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/seq/boolean.json: -------------------------------------------------------------------------------- 1 | [true,false,true,true,false] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/seq/double.json: -------------------------------------------------------------------------------- 1 | [1.0,2.0,3.0] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/seq/empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/seq/int.json: -------------------------------------------------------------------------------- 1 | [1,2,3] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/seq/string.json: -------------------------------------------------------------------------------- 1 | ["Bar","Baz"] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/build.json: -------------------------------------------------------------------------------- 1 | "file:\/\/\/test\/project" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/double.json: -------------------------------------------------------------------------------- 1 | 14.0 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/false.json: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/file.json: -------------------------------------------------------------------------------- 1 | "file:\/tmp\/" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/float.json: -------------------------------------------------------------------------------- 1 | 13.0 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/int.json: -------------------------------------------------------------------------------- 1 | 11 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/long.json: -------------------------------------------------------------------------------- 1 | 12 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/none.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/short.json: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/some_boolean.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/some_int.json: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/some_string.json: -------------------------------------------------------------------------------- 1 | "Foo" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/string.json: -------------------------------------------------------------------------------- 1 | "Foo" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/simple/true.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/vector/boolean.json: -------------------------------------------------------------------------------- 1 | [true,false,true,true,false] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/vector/double.json: -------------------------------------------------------------------------------- 1 | [1.0,2.0,3.0] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/vector/empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/vector/int.json: -------------------------------------------------------------------------------- 1 | [1,2,3] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/1/vector/string.json: -------------------------------------------------------------------------------- 1 | ["Bar","Baz"] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/complex/build_structure.json: -------------------------------------------------------------------------------- 1 | {"builds":["file:\/\/\/test\/project"],"projects":[{"id":{"build":"file:\/\/\/test\/project","name":"test"},"plugins":["com.foo.Plugin"],"dependencies":null,"$type":"sbt.protocol.MinimalProjectStructure"}],"$type":"sbt.protocol.MinimalBuildStructure"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/complex/compile_failed.json: -------------------------------------------------------------------------------- 1 | {"message":"the compile failed","cause":null,"problems":[{"category":"something","severity":"Error","message":"stuff didn't go well","position":{"lineContent":"this is some stuff on the line","sourcePath":"\/temp\/bar","sourceFile":"file:\/temp\/bar","line":10,"offset":11,"pointer":12,"pointerSpace":"foo"},"$type":"sbt.protocol.Problem"}],"$type":"sbt.protocol.CompileFailedException"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/complex/key.json: -------------------------------------------------------------------------------- 1 | {"name":"name","manifest":"java.lang.String","$type":"sbt.protocol.AttributeKey"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/complex/moduleid.json: -------------------------------------------------------------------------------- 1 | {"organization":"com.foo","name":"bar","attributes":{"a":"b"},"$type":"sbt.protocol.ModuleId"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/complex/project_ref.json: -------------------------------------------------------------------------------- 1 | {"build":"file:\/\/\/test\/project","name":"test","$type":"sbt.protocol.ProjectReference"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/complex/scope.json: -------------------------------------------------------------------------------- 1 | {"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null,"$type":"sbt.protocol.SbtScope"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/complex/scoped_key.json: -------------------------------------------------------------------------------- 1 | {"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/bg.json: -------------------------------------------------------------------------------- 1 | {"jobId":67,"serialized":{"$type":"sbt.server.PlayStartedEvent","port":10.0},"$type":"sbt.protocol.BackgroundJobEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/bg_finished.json: -------------------------------------------------------------------------------- 1 | {"executionId":9,"jobId":67,"$type":"sbt.protocol.BackgroundJobFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/bg_started.json: -------------------------------------------------------------------------------- 1 | {"executionId":9,"job":{"id":67,"humanReadableName":"foojob","spawningTask":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}}},"$type":"sbt.protocol.BackgroundJobStarted"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/build_structure_changed.json: -------------------------------------------------------------------------------- 1 | {"structure":{"builds":["file:\/\/\/test\/project"],"projects":[{"id":{"build":"file:\/\/\/test\/project","name":"test"},"plugins":["com.foo.Plugin"],"dependencies":null,"$type":"sbt.protocol.MinimalProjectStructure"}]},"$type":"sbt.protocol.BuildStructureChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/build_structure_changed_with_deps.json: -------------------------------------------------------------------------------- 1 | {"structure":{"builds":["file:\/\/\/test\/project"],"projects":[{"id":{"build":"file:\/\/\/test\/project","name":"test"},"plugins":["com.foo.Plugin"],"dependencies":{"classpath":[{"project":{"build":"file:\/\/\/test\/project","name":"test"},"configuration":"compile","$type":"sbt.protocol.ClasspathDep"}],"aggregate":[{"build":"file:\/\/\/test\/project","name":"test","$type":"sbt.protocol.ProjectReference"}]},"$type":"sbt.protocol.MinimalProjectStructure"}]},"$type":"sbt.protocol.BuildStructureChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/detached.json: -------------------------------------------------------------------------------- 1 | {"serialized":{"$type":"sbt.server.PlayStartedEvent","port":11.0},"$type":"sbt.protocol.DetachedEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/exec_failure.json: -------------------------------------------------------------------------------- 1 | {"id":42,"$type":"sbt.protocol.ExecutionFailure"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/exec_starting.json: -------------------------------------------------------------------------------- 1 | {"id":56,"$type":"sbt.protocol.ExecutionStarting"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/exec_success.json: -------------------------------------------------------------------------------- 1 | {"id":44,"$type":"sbt.protocol.ExecutionSuccess"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/exec_waiting.json: -------------------------------------------------------------------------------- 1 | {"id":41,"command":"foo","client":{"uuid":"350954c2-6bf0-4925-b066-3bf20f32906b","configName":"foo","humanReadableName":"FOO","featureTags":["unknown"],"protocolVersion":"1"},"$type":"sbt.protocol.ExecutionWaiting"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/log/debug.json: -------------------------------------------------------------------------------- 1 | {"taskId":5,"entry":{"level":"debug","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/log/detached_log_event.json: -------------------------------------------------------------------------------- 1 | {"entry":{"message":"Hello, world","$type":"sbt.protocol.LogStdOut"},"$type":"sbt.protocol.DetachedLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/log/error.json: -------------------------------------------------------------------------------- 1 | {"taskId":3,"entry":{"level":"error","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/log/info.json: -------------------------------------------------------------------------------- 1 | {"taskId":2,"entry":{"level":"info","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/log/log_std_err.json: -------------------------------------------------------------------------------- 1 | {"taskId":6,"entry":{"message":"TEST","$type":"sbt.protocol.LogStdErr"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/log/log_std_out.json: -------------------------------------------------------------------------------- 1 | {"taskId":7,"entry":{"message":"TEST2","$type":"sbt.protocol.LogStdOut"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/log/task_log_event.json: -------------------------------------------------------------------------------- 1 | {"taskId":1,"entry":{"message":"Hello, world","$type":"sbt.protocol.LogStdOut"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/log/warn.json: -------------------------------------------------------------------------------- 1 | {"taskId":4,"entry":{"level":"warn","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/play_started.json: -------------------------------------------------------------------------------- 1 | {"taskId":8,"serialized":{"$type":"sbt.server.PlayStartedEvent","port":10.0},"$type":"sbt.protocol.TaskEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/task_event.json: -------------------------------------------------------------------------------- 1 | {"taskId":4,"serialized":{"$type":"sbt.protocol.TestEvent","error":null,"outcome":"passed","description":null,"duration":0.0,"name":"name"},"$type":"sbt.protocol.TaskEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/task_finished.json: -------------------------------------------------------------------------------- 1 | {"executionId":48,"taskId":1,"success":true,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"message":null,"$type":"sbt.protocol.TaskFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/task_finished_failed.json: -------------------------------------------------------------------------------- 1 | {"executionId":48,"taskId":1,"success":false,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"message":"error message here","$type":"sbt.protocol.TaskFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/task_finished_none.json: -------------------------------------------------------------------------------- 1 | {"executionId":48,"taskId":1,"success":true,"key":null,"message":null,"$type":"sbt.protocol.TaskFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/task_started.json: -------------------------------------------------------------------------------- 1 | {"executionId":47,"taskId":1,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"$type":"sbt.protocol.TaskStarted"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/task_started_none.json: -------------------------------------------------------------------------------- 1 | {"executionId":47,"taskId":1,"key":null,"$type":"sbt.protocol.TaskStarted"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/value_changed/boolean.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"true","serialized":true},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/value_changed/double.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"0.0","serialized":0.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/value_changed/float.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"0.0","serialized":0.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/value_changed/int.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"42","serialized":42.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/value_changed/long.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"43","serialized":43.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/event/value_changed/string.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"HI","serialized":"HI"},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/list/boolean.json: -------------------------------------------------------------------------------- 1 | [true,false,true,true,false] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/list/double.json: -------------------------------------------------------------------------------- 1 | [1.0,2.0,3.0] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/list/int.json: -------------------------------------------------------------------------------- 1 | [1,2,3] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/list/nil_as_list.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/list/string.json: -------------------------------------------------------------------------------- 1 | ["Bar","Baz"] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/cancel_exec_request.json: -------------------------------------------------------------------------------- 1 | {"id":1,"$type":"sbt.protocol.CancelExecutionRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/cancel_exec_response.json: -------------------------------------------------------------------------------- 1 | {"attempted":false,"$type":"sbt.protocol.CancelExecutionResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/completion_request.json: -------------------------------------------------------------------------------- 1 | {"in":"He","level":2,"$type":"sbt.protocol.CommandCompletionsRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/completion_response.json: -------------------------------------------------------------------------------- 1 | {"results":[{"append":"llo","display":"Hello","isEmpty":true,"$type":"sbt.protocol.Completion"}],"$type":"sbt.protocol.CommandCompletionsResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/confirm_request.json: -------------------------------------------------------------------------------- 1 | {"executionId":43,"message":"msg","$type":"sbt.protocol.ConfirmRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/confirm_response.json: -------------------------------------------------------------------------------- 1 | {"line":"line","$type":"sbt.protocol.ReadLineResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/daemon_req.json: -------------------------------------------------------------------------------- 1 | {"daemon":true,"$type":"sbt.protocol.DaemonRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/error_response.json: -------------------------------------------------------------------------------- 1 | {"error":"ZOMG","$type":"sbt.protocol.ErrorResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/exec_request.json: -------------------------------------------------------------------------------- 1 | {"command":"test command string","$type":"sbt.protocol.ExecutionRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/inspect_request.json: -------------------------------------------------------------------------------- 1 | {"preanalyze":true,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"$type":"sbt.protocol.InspectRequest"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/inspect_response.json: -------------------------------------------------------------------------------- 1 | {"description":"description blah","providedBy":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"definedAt":[{"path":"foo\/bar","startLine":10,"$type":"sbt.protocol.LinePosition"}],"dependencies":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"derivedDependencies":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"preanalysis":{"reverseDependencies":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"reverseDerivedDependencies":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"delegates":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"related":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}]},"$type":"sbt.protocol.InspectResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/kill_server_req.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.KillServerRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/list_settings_request.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ListSettingsRequest"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/list_settings_response.json: -------------------------------------------------------------------------------- 1 | {"settings":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"$type":"sbt.protocol.ListSettingsResponse"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/listen_to_build_change.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ListenToBuildChange"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/listen_to_events.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ListenToEvents"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/listen_to_value.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"$type":"sbt.protocol.ListenToValue"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/readline_request.json: -------------------------------------------------------------------------------- 1 | {"executionId":42,"prompt":"HI","mask":true,"$type":"sbt.protocol.ReadLineRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/readline_response.json: -------------------------------------------------------------------------------- 1 | {"line":"line","$type":"sbt.protocol.ReadLineResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/received_response.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ReceivedResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/register_client_request.json: -------------------------------------------------------------------------------- 1 | {"info":{"uuid":"350954c2-6bf0-4925-b066-3bf20f32906b","configName":"foo","humanReadableName":"FOO","featureTags":["unknown"],"protocolVersion":"1"},"$type":"sbt.protocol.RegisterClientRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/message/register_client_response.json: -------------------------------------------------------------------------------- 1 | {"info":{"featureTags":["unknown"],"protocolVersion":"1"},"$type":"sbt.protocol.RegisterClientResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/seq/boolean.json: -------------------------------------------------------------------------------- 1 | [true,false,true,true,false] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/seq/double.json: -------------------------------------------------------------------------------- 1 | [1.0,2.0,3.0] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/seq/empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/seq/int.json: -------------------------------------------------------------------------------- 1 | [1,2,3] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/seq/string.json: -------------------------------------------------------------------------------- 1 | ["Bar","Baz"] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/build.json: -------------------------------------------------------------------------------- 1 | "file:\/\/\/test\/project" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/double.json: -------------------------------------------------------------------------------- 1 | 14.0 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/false.json: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/file.json: -------------------------------------------------------------------------------- 1 | "file:\/tmp\/" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/float.json: -------------------------------------------------------------------------------- 1 | 13.0 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/int.json: -------------------------------------------------------------------------------- 1 | 11 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/long.json: -------------------------------------------------------------------------------- 1 | 12 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/none.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/short.json: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/some_boolean.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/some_int.json: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/some_string.json: -------------------------------------------------------------------------------- 1 | "Foo" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/string.json: -------------------------------------------------------------------------------- 1 | "Foo" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/simple/true.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/vector/boolean.json: -------------------------------------------------------------------------------- 1 | [true,false,true,true,false] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/vector/double.json: -------------------------------------------------------------------------------- 1 | [1.0,2.0,3.0] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/vector/empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/vector/int.json: -------------------------------------------------------------------------------- 1 | [1,2,3] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/2/vector/string.json: -------------------------------------------------------------------------------- 1 | ["Bar","Baz"] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/complex/build_structure.json: -------------------------------------------------------------------------------- 1 | {"builds":["file:\/\/\/test\/project"],"buildsData":[],"projects":[{"id":{"build":"file:\/\/\/test\/project","name":"test"},"plugins":["com.foo.Plugin"],"dependencies":null,"$type":"sbt.protocol.MinimalProjectStructure"}],"$type":"sbt.protocol.MinimalBuildStructure"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/complex/compile_failed.json: -------------------------------------------------------------------------------- 1 | {"message":"the compile failed","cause":null,"problems":[{"category":"something","severity":"Error","message":"stuff didn't go well","position":{"lineContent":"this is some stuff on the line","sourcePath":"\/temp\/bar","sourceFile":"file:\/temp\/bar","line":10,"offset":11,"pointer":12,"pointerSpace":"foo"},"$type":"sbt.protocol.Problem"}],"$type":"sbt.protocol.CompileFailedException"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/complex/key.json: -------------------------------------------------------------------------------- 1 | {"name":"name","manifest":"java.lang.String","$type":"sbt.protocol.AttributeKey"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/complex/moduleid.json: -------------------------------------------------------------------------------- 1 | {"organization":"com.foo","name":"bar","attributes":{"a":"b"},"$type":"sbt.protocol.ModuleId"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/complex/project_ref.json: -------------------------------------------------------------------------------- 1 | {"build":"file:\/\/\/test\/project","name":"test","$type":"sbt.protocol.ProjectReference"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/complex/scope.json: -------------------------------------------------------------------------------- 1 | {"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null,"$type":"sbt.protocol.SbtScope"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/complex/scoped_key.json: -------------------------------------------------------------------------------- 1 | {"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/bg.json: -------------------------------------------------------------------------------- 1 | {"jobId":67,"serialized":{"$type":"sbt.server.PlayStartedEvent","port":10.0},"$type":"sbt.protocol.BackgroundJobEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/bg_finished.json: -------------------------------------------------------------------------------- 1 | {"executionId":9,"jobId":67,"$type":"sbt.protocol.BackgroundJobFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/bg_started.json: -------------------------------------------------------------------------------- 1 | {"executionId":9,"job":{"id":67,"humanReadableName":"foojob","spawningTask":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}}},"$type":"sbt.protocol.BackgroundJobStarted"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/build_structure_changed.json: -------------------------------------------------------------------------------- 1 | {"structure":{"builds":["file:\/\/\/test\/project"],"buildsData":[],"projects":[{"id":{"build":"file:\/\/\/test\/project","name":"test"},"plugins":["com.foo.Plugin"],"dependencies":null,"$type":"sbt.protocol.MinimalProjectStructure"}]},"$type":"sbt.protocol.BuildStructureChanged"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/build_structure_changed_with_builds_data.json: -------------------------------------------------------------------------------- 1 | {"structure":{"builds":["file:\/\/\/test\/project"],"buildsData":[{"uri":"file:\/\/\/test\/project","classpath":["file:\/\/\/test\/foo.jar"],"imports":["import com.foo._"],"$type":"sbt.protocol.BuildData"}],"projects":[{"id":{"build":"file:\/\/\/test\/project","name":"test"},"plugins":["com.foo.Plugin"],"dependencies":null,"$type":"sbt.protocol.MinimalProjectStructure"}]},"$type":"sbt.protocol.BuildStructureChanged"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/build_structure_changed_with_deps.json: -------------------------------------------------------------------------------- 1 | {"structure":{"builds":["file:\/\/\/test\/project"],"buildsData":[],"projects":[{"id":{"build":"file:\/\/\/test\/project","name":"test"},"plugins":["com.foo.Plugin"],"dependencies":{"classpath":[{"project":{"build":"file:\/\/\/test\/project","name":"test"},"configuration":"compile","$type":"sbt.protocol.ClasspathDep"}],"aggregate":[{"build":"file:\/\/\/test\/project","name":"test","$type":"sbt.protocol.ProjectReference"}]},"$type":"sbt.protocol.MinimalProjectStructure"}]},"$type":"sbt.protocol.BuildStructureChanged"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/detached.json: -------------------------------------------------------------------------------- 1 | {"serialized":{"$type":"sbt.server.PlayStartedEvent","port":11.0},"$type":"sbt.protocol.DetachedEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/exec_failure.json: -------------------------------------------------------------------------------- 1 | {"id":42,"$type":"sbt.protocol.ExecutionFailure"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/exec_starting.json: -------------------------------------------------------------------------------- 1 | {"id":56,"$type":"sbt.protocol.ExecutionStarting"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/exec_success.json: -------------------------------------------------------------------------------- 1 | {"id":44,"$type":"sbt.protocol.ExecutionSuccess"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/exec_waiting.json: -------------------------------------------------------------------------------- 1 | {"id":41,"command":"foo","client":{"uuid":"350954c2-6bf0-4925-b066-3bf20f32906b","configName":"foo","humanReadableName":"FOO","featureTags":["unknown"],"protocolVersion":"1"},"$type":"sbt.protocol.ExecutionWaiting"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/log/debug.json: -------------------------------------------------------------------------------- 1 | {"taskId":5,"entry":{"level":"debug","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/log/detached_log_event.json: -------------------------------------------------------------------------------- 1 | {"entry":{"message":"Hello, world","$type":"sbt.protocol.LogStdOut"},"$type":"sbt.protocol.DetachedLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/log/error.json: -------------------------------------------------------------------------------- 1 | {"taskId":3,"entry":{"level":"error","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/log/info.json: -------------------------------------------------------------------------------- 1 | {"taskId":2,"entry":{"level":"info","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/log/log_std_err.json: -------------------------------------------------------------------------------- 1 | {"taskId":6,"entry":{"message":"TEST","$type":"sbt.protocol.LogStdErr"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/log/log_std_out.json: -------------------------------------------------------------------------------- 1 | {"taskId":7,"entry":{"message":"TEST2","$type":"sbt.protocol.LogStdOut"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/log/task_log_event.json: -------------------------------------------------------------------------------- 1 | {"taskId":1,"entry":{"message":"Hello, world","$type":"sbt.protocol.LogStdOut"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/log/warn.json: -------------------------------------------------------------------------------- 1 | {"taskId":4,"entry":{"level":"warn","message":"TEST","$type":"sbt.protocol.LogMessage"},"$type":"sbt.protocol.TaskLogEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/play_started.json: -------------------------------------------------------------------------------- 1 | {"taskId":8,"serialized":{"$type":"sbt.server.PlayStartedEvent","port":10.0},"$type":"sbt.protocol.TaskEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/task_event.json: -------------------------------------------------------------------------------- 1 | {"taskId":4,"serialized":{"$type":"sbt.protocol.TestEvent","error":null,"outcome":"passed","description":null,"duration":0.0,"name":"name"},"$type":"sbt.protocol.TaskEvent"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/task_finished.json: -------------------------------------------------------------------------------- 1 | {"executionId":48,"taskId":1,"success":true,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"message":null,"$type":"sbt.protocol.TaskFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/task_finished_failed.json: -------------------------------------------------------------------------------- 1 | {"executionId":48,"taskId":1,"success":false,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"message":"error message here","$type":"sbt.protocol.TaskFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/task_finished_none.json: -------------------------------------------------------------------------------- 1 | {"executionId":48,"taskId":1,"success":true,"key":null,"message":null,"$type":"sbt.protocol.TaskFinished"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/task_started.json: -------------------------------------------------------------------------------- 1 | {"executionId":47,"taskId":1,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"$type":"sbt.protocol.TaskStarted"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/task_started_none.json: -------------------------------------------------------------------------------- 1 | {"executionId":47,"taskId":1,"key":null,"$type":"sbt.protocol.TaskStarted"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/value_changed/boolean.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"true","serialized":true},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/value_changed/double.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"0.0","serialized":0.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/value_changed/float.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"0.0","serialized":0.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/value_changed/int.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"42","serialized":42.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/value_changed/long.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"43","serialized":43.0},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/event/value_changed/string.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"value":{"value":{"stringValue":"HI","serialized":"HI"},"$type":"sbt.protocol.TaskSuccess"},"$type":"sbt.protocol.ValueChanged"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/list/boolean.json: -------------------------------------------------------------------------------- 1 | [true,false,true,true,false] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/list/double.json: -------------------------------------------------------------------------------- 1 | [1.0,2.0,3.0] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/list/int.json: -------------------------------------------------------------------------------- 1 | [1,2,3] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/list/nil_as_list.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/list/string.json: -------------------------------------------------------------------------------- 1 | ["Bar","Baz"] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/cancel_exec_request.json: -------------------------------------------------------------------------------- 1 | {"id":1,"$type":"sbt.protocol.CancelExecutionRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/cancel_exec_response.json: -------------------------------------------------------------------------------- 1 | {"attempted":false,"$type":"sbt.protocol.CancelExecutionResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/completion_request.json: -------------------------------------------------------------------------------- 1 | {"in":"He","level":2,"$type":"sbt.protocol.CommandCompletionsRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/completion_response.json: -------------------------------------------------------------------------------- 1 | {"results":[{"append":"llo","display":"Hello","isEmpty":true,"$type":"sbt.protocol.Completion"}],"$type":"sbt.protocol.CommandCompletionsResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/confirm_request.json: -------------------------------------------------------------------------------- 1 | {"executionId":43,"message":"msg","$type":"sbt.protocol.ConfirmRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/confirm_response.json: -------------------------------------------------------------------------------- 1 | {"line":"line","$type":"sbt.protocol.ReadLineResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/daemon_req.json: -------------------------------------------------------------------------------- 1 | {"daemon":true,"$type":"sbt.protocol.DaemonRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/error_response.json: -------------------------------------------------------------------------------- 1 | {"error":"ZOMG","$type":"sbt.protocol.ErrorResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/exec_request.json: -------------------------------------------------------------------------------- 1 | {"command":"test command string","$type":"sbt.protocol.ExecutionRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/inspect_request.json: -------------------------------------------------------------------------------- 1 | {"preanalyze":true,"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"$type":"sbt.protocol.InspectRequest"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/inspect_response.json: -------------------------------------------------------------------------------- 1 | {"description":"description blah","providedBy":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"definedAt":[{"path":"foo\/bar","startLine":10,"$type":"sbt.protocol.LinePosition"}],"dependencies":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"derivedDependencies":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"preanalysis":{"reverseDependencies":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"reverseDerivedDependencies":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"delegates":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"related":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}]},"$type":"sbt.protocol.InspectResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/kill_server_req.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.KillServerRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/list_settings_request.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ListSettingsRequest"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/list_settings_response.json: -------------------------------------------------------------------------------- 1 | {"settings":[{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null},"$type":"sbt.protocol.ScopedKey"}],"$type":"sbt.protocol.ListSettingsResponse"} 2 | -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/listen_to_build_change.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ListenToBuildChange"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/listen_to_events.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ListenToEvents"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/listen_to_value.json: -------------------------------------------------------------------------------- 1 | {"key":{"key":{"name":"name","manifest":"java.lang.String"},"scope":{"build":null,"project":{"build":"file:\/\/\/test\/project","name":"test"},"config":null,"task":null}},"$type":"sbt.protocol.ListenToValue"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/readline_request.json: -------------------------------------------------------------------------------- 1 | {"executionId":42,"prompt":"HI","mask":true,"$type":"sbt.protocol.ReadLineRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/readline_response.json: -------------------------------------------------------------------------------- 1 | {"line":"line","$type":"sbt.protocol.ReadLineResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/received_response.json: -------------------------------------------------------------------------------- 1 | {"$type":"sbt.protocol.ReceivedResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/register_client_request.json: -------------------------------------------------------------------------------- 1 | {"info":{"uuid":"350954c2-6bf0-4925-b066-3bf20f32906b","configName":"foo","humanReadableName":"FOO","featureTags":["unknown"],"protocolVersion":"1"},"$type":"sbt.protocol.RegisterClientRequest"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/message/register_client_response.json: -------------------------------------------------------------------------------- 1 | {"info":{"featureTags":["unknown"],"protocolVersion":"1"},"$type":"sbt.protocol.RegisterClientResponse"} -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/seq/boolean.json: -------------------------------------------------------------------------------- 1 | [true,false,true,true,false] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/seq/double.json: -------------------------------------------------------------------------------- 1 | [1.0,2.0,3.0] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/seq/empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/seq/int.json: -------------------------------------------------------------------------------- 1 | [1,2,3] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/seq/string.json: -------------------------------------------------------------------------------- 1 | ["Bar","Baz"] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/build.json: -------------------------------------------------------------------------------- 1 | "file:\/\/\/test\/project" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/double.json: -------------------------------------------------------------------------------- 1 | 14.0 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/false.json: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/file.json: -------------------------------------------------------------------------------- 1 | "file:\/tmp\/" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/float.json: -------------------------------------------------------------------------------- 1 | 13.0 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/int.json: -------------------------------------------------------------------------------- 1 | 11 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/long.json: -------------------------------------------------------------------------------- 1 | 12 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/none.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/short.json: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/some_boolean.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/some_int.json: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/some_string.json: -------------------------------------------------------------------------------- 1 | "Foo" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/string.json: -------------------------------------------------------------------------------- 1 | "Foo" -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/simple/true.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/vector/boolean.json: -------------------------------------------------------------------------------- 1 | [true,false,true,true,false] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/vector/double.json: -------------------------------------------------------------------------------- 1 | [1.0,2.0,3.0] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/vector/empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/vector/int.json: -------------------------------------------------------------------------------- 1 | [1,2,3] -------------------------------------------------------------------------------- /protocol-test/src/test/resource/saved-protocol/3/vector/string.json: -------------------------------------------------------------------------------- 1 | ["Bar","Baz"] -------------------------------------------------------------------------------- /protocol-test/src/test/scala/sbt/protocol/EventTrackerTest.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Typesafe Inc. 3 | */ 4 | package sbt.protocol 5 | 6 | import org.junit.Assert._ 7 | import org.junit._ 8 | import sbt.protocol._ 9 | import sbt.serialization.TypeExpression 10 | import java.util.concurrent.Executors 11 | import java.util.concurrent.CountDownLatch 12 | import java.util.concurrent.TimeUnit 13 | 14 | class EventTrackerTest { 15 | 16 | @Test 17 | def testEventTracker(): Unit = { 18 | val build = new java.net.URI("file:///test/project") 19 | val projectRef1 = ProjectReference(build, "test1") 20 | val projectRef2 = ProjectReference(build, "test2") 21 | val key1 = AttributeKey("name", TypeExpression("java.lang.String", Nil)) 22 | val key2 = AttributeKey("foo", TypeExpression("java.lang.Integer", Nil)) 23 | val scope1 = SbtScope(project = Some(projectRef1)) 24 | val scope2 = SbtScope(project = Some(projectRef2)) 25 | val scopedKey1 = ScopedKey(key1, scope1) 26 | val scopedKey2 = ScopedKey(key2, scope2) 27 | val clientInfo = ClientInfo(java.util.UUID.randomUUID.toString(), "clientyclient", "Client Test", 28 | ProtocolVersion1, Vector.empty) 29 | val creationEvents = List(ExecutionWaiting(1, "frobulate", clientInfo), 30 | ExecutionStarting(1), 31 | TaskStarted(1, 1, Some(scopedKey1)), 32 | TaskStarted(1, 2, Some(scopedKey2)), 33 | TaskFinished(1, 1, key = Some(scopedKey1), success = false, message = Some("Task error message")), 34 | BackgroundJobStarted(1, BackgroundJobInfo(id = 3, spawningTask = scopedKey2, humanReadableName = "Some job 1")), 35 | BackgroundJobStarted(1, BackgroundJobInfo(id = 4, spawningTask = scopedKey2, humanReadableName = "Some job 2")), 36 | ExecutionWaiting(2, "foobar", clientInfo), 37 | ExecutionWaiting(3, "barfoo", clientInfo), 38 | ExecutionWaiting(4, "goesaway", clientInfo), 39 | ExecutionStarting(4), 40 | ExecutionSuccess(4), 41 | BackgroundJobFinished(1, 4)) 42 | 43 | val engine = ImpliedState.processEvents(ImpliedState.ExecutionEngine.empty, creationEvents) 44 | assertEquals(2, engine.waiting.size) 45 | assertEquals(1, engine.started.size) 46 | assertEquals(1, engine.started.values.head.tasks.size) 47 | assertEquals(1, engine.jobs.size) 48 | assertEquals("frobulate", engine.started.values.head.command) 49 | 50 | val toReach = ImpliedState.eventsToReachEngineState(engine).map(_.event) 51 | val recreated = ImpliedState.processEvents(ImpliedState.ExecutionEngine.empty, toReach) 52 | assertEquals(engine, recreated) 53 | 54 | val toEmpty = ImpliedState.eventsToEmptyEngineState(engine, success = false).map(_.event) 55 | val emptied = ImpliedState.processEvents(engine, toEmpty) 56 | assertEquals(0, emptied.waiting.size) 57 | assertEquals(0, emptied.started.size) 58 | assertEquals(0, emptied.jobs.size) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /protocol-test/src/test/scala/sbt/protocol/JUnitMessageUtil.scala: -------------------------------------------------------------------------------- 1 | package sbt.protocol.spec 2 | 3 | import sbt.protocol.Message 4 | import org.junit.Assert._ 5 | import org.junit._ 6 | import sbt.serialization._ 7 | import scala.pickling.Defaults.pickleOps 8 | 9 | object JUnitMessageUtil { 10 | private def addWhatWeWerePickling[T, U](t: T)(body: => U): U = try body 11 | catch { 12 | case e: Throwable => 13 | e.printStackTrace() 14 | throw new AssertionError(s"Crash round-tripping ${t.getClass.getName}: value was: ${t}", e) 15 | } 16 | 17 | def roundTripMessage(message: Message): Unit = addWhatWeWerePickling(message) { 18 | import scala.pickling._, sbt.serialization.json._ 19 | val expectedPickler = implicitly[Pickler[Message]] 20 | val expectedUnpickler = implicitly[Unpickler[Message]] 21 | val json: String = try SerializedValue(message).toJsonString 22 | catch { 23 | case t: Throwable => 24 | System.err.println(s"pickle of ${message.getClass.getName} failed: ${t.getClass.getName} ${t.getMessage}") 25 | System.err.println(s"value we failed to pickle: $message") 26 | System.err.println(s"implicit pickler was: ${expectedPickler.getClass.getName}") 27 | throw t 28 | } 29 | val parsed = try { 30 | JUnitUtil.parseMessage(json) 31 | } catch { 32 | case t: Throwable => 33 | System.err.println(s"parse of ${message.getClass.getName} failed: ${t.getClass.getName} ${t.getMessage}") 34 | System.err.println(s"value we had pickled: $message") 35 | System.err.println(s"json was: $json") 36 | System.err.println(s"implicit unpickler was: ${expectedUnpickler.getClass.getName}") 37 | throw t 38 | } 39 | try { 40 | assertEquals(message, parsed) 41 | } catch { 42 | case t: Throwable => 43 | System.err.println(s"roundtrip of ${message.getClass.getName} failed: ${t.getClass.getName} ${t.getMessage}") 44 | System.err.println(s"value we failed to roundtrip: $message") 45 | System.err.println(s"what we parsed was: $parsed") 46 | System.err.println(s"json was: $json") 47 | System.err.println(s"implicit pickler was: ${expectedPickler.getClass.getName} unpickler: ${expectedUnpickler.getClass.getName}") 48 | throw t 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /protocol-test/src/test/scala/sbt/protocol/JUnitUtil.scala: -------------------------------------------------------------------------------- 1 | package sbt.protocol.spec 2 | 3 | import org.junit.Assert._ 4 | import org.junit._ 5 | import sbt.protocol.Message 6 | import scala.pickling.PickleOps, sbt.serialization._, sbt.serialization.json._ 7 | import scala.pickling.static._ 8 | import scala.pickling.Defaults.pickleOps 9 | 10 | object JUnitUtil { 11 | private def addWhatWeWerePickling[T, U](t: T)(body: => U): U = try body 12 | catch { 13 | case e: Throwable => 14 | e.printStackTrace() 15 | throw new AssertionError(s"Crash round-tripping ${t.getClass.getName}: value was: ${t}", e) 16 | } 17 | 18 | def roundTripArray[A](x: Array[A])(implicit ev0: Pickler[Array[A]], ev1: Unpickler[Array[A]]): Unit = 19 | roundTripBase[Array[A]](x)((a, b) => 20 | assertEquals(a.toList, b.toList)) { (a, b) => 21 | assertEquals(s"Failed to round trip $x via ${implicitly[Pickler[Array[A]]]} and ${implicitly[Unpickler[Array[A]]]}", a.getMessage, b.getMessage) 22 | } 23 | def roundTrip[A: Pickler: Unpickler](x: A): Unit = 24 | roundTripBase[A](x)((a, b) => 25 | assertEquals(a, b)) { (a, b) => 26 | assertEquals(s"Failed to round trip $x via ${implicitly[Pickler[A]]} and ${implicitly[Unpickler[A]]}", a.getMessage, b.getMessage) 27 | } 28 | def roundTripBase[A: Pickler: Unpickler](a: A)(f: (A, A) => Unit)(e: (Throwable, Throwable) => Unit): Unit = addWhatWeWerePickling(a) { 29 | import sbt.serialization._, sbt.serialization.json._ 30 | val json = SerializedValue(a).toJsonString 31 | //System.err.println(s"json: $json") 32 | val parsed = SerializedValue.fromJsonString(json).parse[A].get 33 | (a, parsed) match { 34 | case (a: Throwable, parsed: Throwable) => e(a, parsed) 35 | case _ => f(a, parsed) 36 | } 37 | } 38 | implicit class AnyOp[A](a: A) { 39 | def must_==(b: A): Unit = assertEquals(b, a) 40 | } 41 | 42 | // TODO get rid of pickleMessage and parseMessage once pickle/unpickle are not macros 43 | def pickleMessage(m: Message): String = 44 | SerializedValue(m).toJsonString 45 | 46 | def parseMessage(s: String): Message = 47 | SerializedValue.fromJsonString(s).parse[Message].get 48 | } 49 | -------------------------------------------------------------------------------- /protocol-test/src/test/scala/sbt/protocol/PlayStartedEvent.scala: -------------------------------------------------------------------------------- 1 | package sbt.protocol.spec 2 | 3 | import sbt.protocol.{ TaskEventUnapply, BackgroundJobEventUnapply, DetachedEventUnapply } 4 | import sbt.serialization._ 5 | 6 | final case class PlayStartedEvent(port: Int) 7 | object PlayStartedEvent extends TaskEventUnapply[PlayStartedEvent] { 8 | implicit val pickler = genPickler[PlayStartedEvent] 9 | implicit val unpickler = genUnpickler[PlayStartedEvent] 10 | } 11 | object PlayStartedEventBg extends BackgroundJobEventUnapply[PlayStartedEvent] 12 | object PlayStartedEventDetached extends DetachedEventUnapply[PlayStartedEvent] 13 | -------------------------------------------------------------------------------- /protocol-test/src/test/scala/sbt/protocol/SerializedValuePicklerSpec.scala: -------------------------------------------------------------------------------- 1 | package sbt.protocol.spec 2 | 3 | import sbt.protocol 4 | import org.junit.Assert._ 5 | import org.junit._ 6 | import java.io.File 7 | import java.net.URI 8 | import JUnitUtil._ 9 | import sbt.serialization._ 10 | import protocol.TaskEventUnapply 11 | 12 | import scala.pickling.Defaults.pickleOps 13 | 14 | class SerializedValuePicklerTest { 15 | 16 | @Test 17 | def testRoundtripInt: Unit = { 18 | import scala.pickling._ 19 | val value: SerializedValue = SerializedValue(1) 20 | SerializedValue(value).toJsonString must_== "1.0" 21 | value.parse[Int].get must_== 1 22 | roundTrip(SerializedValue(1): SerializedValue) 23 | } 24 | 25 | @Test 26 | def testRoundtripPlayStarted: Unit = { 27 | import scala.pickling._ 28 | val value = SerializedValue(PlayStartedEvent(10)) 29 | val example = """{"$type":"sbt.protocol.spec.PlayStartedEvent","port":10.0}""" 30 | SerializedValue(value).toJsonString must_== example 31 | val recovered = SerializedValue.fromJsonString(example) 32 | recovered.parse[PlayStartedEvent].get must_== PlayStartedEvent(10) 33 | roundTrip(SerializedValue(PlayStartedEvent(10))) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /protocol-test/src/test/scala/sbt/protocol/TypeExpressionPicklerSpec.scala: -------------------------------------------------------------------------------- 1 | package sbt.protocol.spec 2 | 3 | import sbt.protocol 4 | import org.junit.Assert._ 5 | import org.junit._ 6 | import java.io.File 7 | import java.net.URI 8 | import sbt.serialization._ 9 | import JUnitUtil._ 10 | 11 | class TypeExpressionPicklerTest { 12 | 13 | @Test 14 | def testRoundtripStringType: Unit = { 15 | val value = TypeExpression.parse("java.lang.String")._1 16 | val example = "\"java.lang.String\"" 17 | SerializedValue(value).toJsonString must_== example 18 | roundTrip(value) 19 | } 20 | 21 | @Test 22 | def testRoundtripListOfStringType: Unit = { 23 | val value = TypeExpression.parse("scala.collection.immutable.List[java.lang.String]")._1 24 | val example = "\"scala.collection.immutable.List[java.lang.String]\"" 25 | SerializedValue(value).toJsonString must_== example 26 | roundTrip(value) 27 | } 28 | 29 | val key = protocol.AttributeKey("name", TypeExpression.parse("java.lang.String")._1) 30 | val build = new java.net.URI("file:///test/project") 31 | val projectRef = protocol.ProjectReference(build, "test") 32 | val scope = protocol.SbtScope(project = Some(projectRef)) 33 | val scopedKey = protocol.ScopedKey(key, scope) 34 | val seqAttributedFile = Seq( 35 | protocol.Attributed(new java.io.File("/some.file"))) 36 | 37 | @Test 38 | def testRoundtripAttributeKey: Unit = { 39 | roundTrip(key) 40 | } 41 | 42 | @Test 43 | def testRoundtripScope: Unit = { 44 | roundTrip(scope) 45 | } 46 | 47 | @Test 48 | def testRoundtripScopedKey: Unit = { 49 | roundTrip(scopedKey) 50 | } 51 | 52 | @Test 53 | def testRoundtripSeqAttributedFile: Unit = { 54 | roundTrip(seqAttributedFile) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/BuildStructureCache.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import sbt.serialization._ 5 | 6 | /** 7 | * Represents a cache of the previous build state(s), if they exist. 8 | * 9 | * We diff this after actual commands are run to ensure we notify clients of changes in the build structure. 10 | */ 11 | object BuildStructureCache { 12 | private val buildStructureCache = AttributeKey[protocol.MinimalBuildStructure]("Sbt's server cache") 13 | 14 | // TODO - ideally we use this mechanism to also track Setting listeners. 15 | 16 | def extract(state: State): Option[protocol.MinimalBuildStructure] = state get buildStructureCache 17 | private def updateImpl(state: State, cache: protocol.MinimalBuildStructure): State = state.put(buildStructureCache, cache) 18 | 19 | def update(state: State): State = { 20 | val structure = SbtDiscovery.buildStructure(state) 21 | extract(state) match { 22 | case Some(previous) if previous == structure => state 23 | case _ => 24 | val serverState = ServerState.extract(state) 25 | sendBuildStructure(serverState.buildListeners, structure) 26 | updateImpl(state, structure) 27 | } 28 | } 29 | 30 | def sendBuildStructure(listener: SbtClient, structure: protocol.MinimalBuildStructure): Unit = 31 | listener.send(protocol.BuildStructureChanged(structure)) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/CompileReporter.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import xsbti.Position 5 | import xsbti.Severity 6 | import sbt.serialization._ 7 | 8 | class CompileReporter( 9 | sendEventService: SendEventService, 10 | project: protocol.ProjectReference, 11 | maximumErrors: Int, 12 | log: Logger, 13 | sourcePositionMapper: Position => Position = { p => p }) 14 | extends LoggerReporter(maximumErrors, log, sourcePositionMapper) { 15 | 16 | override def reset(): Unit = { 17 | // TODO - Fire event to listeners that errors are reset. 18 | super.reset() 19 | } 20 | 21 | override def display(pos: Position, msg: String, severity: Severity) { 22 | val newPos = sourcePositionMapper(pos) 23 | val errorMessage = 24 | protocol.CompilationFailure( 25 | project, 26 | SbtToProtocolUtils.positionToProtocol(pos), 27 | severity, 28 | msg) 29 | sendEventService.sendEvent(errorMessage) 30 | super.display(pos, msg, severity) 31 | } 32 | 33 | override def printSummary(): Unit = { 34 | // TODO - Fire event that summary is complete. 35 | super.printSummary(); 36 | } 37 | } 38 | object CompileReporter { 39 | 40 | // Don't pass anything in here that's "internal" because we 41 | // should be moving this code into the default sbt compile task, 42 | // and it won't be able to use internals. You probably have to 43 | // add anything you need to UIContext. 44 | def makeShims(state: State): Seq[Setting[_]] = { 45 | // TODO - Override the derived compiler settings such that 46 | // our listener is installed on all compilers. 47 | val extracted = Project.extract(state) 48 | makeAllReporterSettings(extracted) 49 | } 50 | 51 | private def makeAllReporterSettings(extracted: Extracted): Seq[Setting[_]] = { 52 | for { 53 | setting <- extracted.structure.settings 54 | scope = setting.key.scope 55 | if setting.key.key == Keys.compilerReporter.key 56 | project <- scope.project.toOption.toList 57 | // TODO - Can we handle other reference types? 58 | // By this point they should all be unified to ProjectRef/BuildRef. 59 | if project.isInstanceOf[ProjectRef] 60 | } yield Keys.compilerReporter in scope := { 61 | val sendEventService = SendEventServiceKeys.sendEventService.value 62 | val inputs = (Keys.compileInputs in scope).value 63 | val log = Keys.streams.value.log 64 | val tmp = Keys.projectInfo 65 | Some(new CompileReporter( 66 | sendEventService, 67 | SbtToProtocolUtils.projectRefToProtocol(project.asInstanceOf[ProjectRef]), 68 | inputs.config.maxErrors, 69 | log, 70 | inputs.config.sourcePositionMapper)) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/DynamicConversion.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import sbt.protocol._ 5 | 6 | /** 7 | * An interface representing a mechanism to convert types 8 | * using their runtime manifest. 9 | * 10 | * This suffers from all the same limitations as scala.Manifest for 11 | * handling types. 12 | * 13 | * A DynamicConversion is immutable. 14 | */ 15 | sealed trait DynamicConversion { 16 | def convertWithRuntimeClass[F](value: F): Option[(_, Manifest[_])] 17 | def convert[F](value: F)(implicit mf: Manifest[F]): Option[(_, Manifest[_])] 18 | /** Add a conversion, returning the new modified DynamicConversion. */ 19 | def register[F, T](convert: F => T)(implicit fromMf: Manifest[F], toMf: Manifest[T]): DynamicConversion 20 | def ++(other: DynamicConversion): DynamicConversion 21 | } 22 | 23 | object DynamicConversion { 24 | val empty: DynamicConversion = ConcreteDynamicConversion(Map.empty, Map.empty) 25 | } 26 | 27 | private final case class RegisteredConversion[F, T](fromManifest: Manifest[F], toManifest: Manifest[T], convert: F => T) 28 | 29 | private final case class ConcreteDynamicConversion(registered: Map[Manifest[_], RegisteredConversion[_, _]], 30 | byClass: Map[Class[_], RegisteredConversion[_, _]]) extends DynamicConversion { 31 | override def convertWithRuntimeClass[F](value: F): Option[(_, Manifest[_])] = { 32 | byClass.get(value.getClass).map { conversion => 33 | val result = conversion.asInstanceOf[RegisteredConversion[F, _]].convert(value) 34 | (result, conversion.toManifest) 35 | } 36 | } 37 | 38 | override def convert[F](value: F)(implicit mf: Manifest[F]): Option[(_, Manifest[_])] = { 39 | registered.get(mf).map { conversion => 40 | val result = conversion.asInstanceOf[RegisteredConversion[F, _]].convert(value) 41 | (result, conversion.toManifest) 42 | } 43 | } 44 | 45 | override def register[F, T](convert: F => T)(implicit fromMf: Manifest[F], toMf: Manifest[T]): DynamicConversion = { 46 | val conversion = RegisteredConversion(fromMf, toMf, convert) 47 | copy(registered = registered + (fromMf -> conversion), byClass = byClass + (fromMf.runtimeClass -> conversion)) 48 | } 49 | 50 | override def ++(other: DynamicConversion): DynamicConversion = { 51 | other match { 52 | case c: ConcreteDynamicConversion => copy(registered = (registered ++ c.registered)) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/FakeAppConfiguration.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import xsbti._ 5 | 6 | object FakeAppConfiguration { 7 | // Reads the build-configured lowest-level sbt version 8 | def defaultSbtVersion = { 9 | val props = new java.util.Properties 10 | val input = getClass.getClassLoader.getResourceAsStream("default.sbt.version") 11 | try props load input 12 | finally { 13 | input.close() 14 | } 15 | Option(props.getProperty("sbt.version")) getOrElse 16 | sys.error("Unable to read default sbt properties file. This could be a bad bundle of sbt-remote-control.") 17 | } 18 | } 19 | // TODO - make this less hacky 20 | final case class FakeAppConfiguration(original: AppConfiguration, sbtVersion: String = FakeAppConfiguration.defaultSbtVersion) extends AppConfiguration { 21 | final val arguments: Array[String] = Array.empty 22 | final def baseDirectory: File = original.baseDirectory 23 | private def origAp = original.provider 24 | object provider extends xsbti.AppProvider { 25 | override def scalaProvider = origAp.scalaProvider 26 | object id extends ApplicationID { 27 | override val groupID = "org.scala-sbt" 28 | override val name = "sbt" 29 | override val version = sbtVersion 30 | override val mainClass = "sbt.xMain" 31 | override val mainComponents = origAp.id.mainComponents 32 | override val crossVersioned = origAp.id.crossVersioned 33 | override val crossVersionedValue = origAp.id.crossVersionedValue 34 | override val classpathExtra = origAp.id.classpathExtra 35 | } 36 | override def loader: ClassLoader = origAp.loader 37 | override def mainClass: Class[T] forSome { type T <: AppMain } = origAp.mainClass 38 | override def entryPoint: Class[_] = origAp.entryPoint 39 | override def newMain: AppMain = origAp.newMain 40 | override def mainClasspath: Array[File] = origAp.mainClasspath 41 | override def components: ComponentProvider = origAp.components 42 | } 43 | } -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/Inspect.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import sbt.protocol._ 5 | import sbt.State 6 | import sbt.Def 7 | 8 | object Inspect { 9 | def listSettingsResponse(state: State): ListSettingsResponse = { 10 | val extracted = sbt.Project.extract(state) 11 | val settings = 12 | for (setting <- extracted.session.mergeSettings) 13 | yield SbtToProtocolUtils.scopedKeyToProtocol(setting.key) 14 | 15 | ListSettingsResponse(settings.toVector) 16 | } 17 | 18 | def inspectResponse(state: State, request: InspectRequest): Response = { 19 | val extracted = sbt.Project.extract(state) 20 | val structure = extracted.structure 21 | 22 | val scopedOption = SbtToProtocolUtils.protocolToScopedKey(request.key, state) 23 | scopedOption map { scoped => 24 | inspectResponse(state, structure, scoped, preanalyze = request.preanalyze) 25 | } getOrElse { 26 | ErrorResponse(s"no such key ${request.key}") 27 | } 28 | } 29 | 30 | private def derivedDependencies(comp: Map[sbt.ScopedKey[_], sbt.Def.Compiled[_]], scoped: sbt.ScopedKey[_]): Set[sbt.ScopedKey[_]] = 31 | comp.get(scoped).map(_.settings.flatMap(s => if (s.isDerived) s.dependencies else Nil)).toList.flatten.toSet 32 | 33 | private def reverseDependencies(cMap: Map[sbt.ScopedKey[_], sbt.Def.Flattened], scoped: sbt.ScopedKey[_]): Iterable[sbt.ScopedKey[_]] = 34 | for ((key, compiled) <- cMap; dep <- compiled.dependencies if dep == scoped) yield key 35 | 36 | private def inspectResponse(state: State, structure: sbt.BuildStructure, scoped: sbt.ScopedKey[_], preanalyze: Boolean): InspectResponse = { 37 | def keySeqToProto(keys: Iterable[sbt.ScopedKey[_]]): Vector[sbt.protocol.ScopedKey] = 38 | keys.map(SbtToProtocolUtils.scopedKeyToProtocol(_)).toVector 39 | 40 | val display = sbt.Project.showContextKey(state) 41 | val key = scoped.key 42 | 43 | val definingScope = structure.data.definingScope(scoped.scope, key) 44 | val providedBy = definingScope match { 45 | case Some(sc) => 46 | scoped.copy(scope = sc) 47 | case None => 48 | scoped 49 | } 50 | val comp = Def.compiled(structure.settings, actual = true)(structure.delegates, structure.scopeLocal, display) 51 | val definedAt = ((comp get providedBy).toSeq flatMap { c => c.settings.map(_.pos) }).distinct.toVector 52 | 53 | val cMap = Def.flattenLocals(comp) 54 | 55 | val depends = cMap.get(scoped) match { case Some(c) => c.dependencies.toSet; case None => Set.empty } 56 | val derivedDepends: Set[sbt.ScopedKey[_]] = derivedDependencies(comp, providedBy) 57 | 58 | val preanalysisOption = if (preanalyze) { 59 | val reverse = reverseDependencies(cMap, scoped) 60 | val derivedReverse = reverse.filter(r => derivedDependencies(comp, r).contains(providedBy)).toSet 61 | val delegates = sbt.Project.delegates(structure, scoped.scope, key) 62 | val related = cMap.keys.filter(k => k.key == key && k.scope != scoped.scope) 63 | 64 | val preanalysis = InspectPreanalysis(reverseDependencies = keySeqToProto(reverse), 65 | reverseDerivedDependencies = keySeqToProto(derivedReverse), 66 | delegates = keySeqToProto(delegates), 67 | related = keySeqToProto(related)) 68 | Some(preanalysis) 69 | } else { 70 | None 71 | } 72 | 73 | InspectResponse(description = key.description, 74 | providedBy = SbtToProtocolUtils.scopedKeyToProtocol(providedBy), 75 | definedAt = definedAt.map(SbtToProtocolUtils.sourcePositionToProtocol(_)), 76 | dependencies = keySeqToProto(depends), 77 | derivedDependencies = keySeqToProto(derivedDepends), 78 | preanalysis = preanalysisOption) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/RequestListeners.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | /** 5 | * Represents the current listeners at the time a request is 6 | * handed off from RequestProcessor to ServerEngine. 7 | */ 8 | trait RequestListeners { 9 | def buildListeners: SbtClient 10 | def keyListeners: Seq[KeyValueClientListener[_]] 11 | } 12 | 13 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/SbtDiscovery.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import SbtToProtocolUtils._ 5 | 6 | // This is a helper class that lets us run discovery methods on sbt. 7 | private[server] object SbtDiscovery { 8 | 9 | private def getRootObjectName(o: AnyRef): String = { 10 | val rawClassname = o.getClass.getName 11 | // Technically, the $ shoudl ALWAYS be there for autoplugins. 12 | if (rawClassname endsWith "$") rawClassname.dropRight(1) 13 | else rawClassname 14 | } 15 | 16 | private def projectDependencies(r: ResolvedProject): protocol.ProjectDependencies = { 17 | val classpath = 18 | for { 19 | dep <- r.dependencies 20 | to = projectRefToProtocol(dep.project) 21 | } yield protocol.ClasspathDep(to, dep.configuration) 22 | val aggregate = 23 | for { 24 | dep <- r.aggregate 25 | } yield projectRefToProtocol(dep) 26 | protocol.ProjectDependencies(classpath, aggregate) 27 | } 28 | 29 | def buildStructure(state: State): protocol.MinimalBuildStructure = { 30 | val extracted = sbt.Project.extract(state) 31 | val projects = 32 | (for { 33 | (build, unit) <- extracted.structure.units 34 | resolved <- unit.defined.values 35 | ref = projectRefToProtocol(ProjectRef(build, resolved.id)) 36 | plugins = resolved.autoPlugins.map(getRootObjectName).toVector 37 | } yield protocol.MinimalProjectStructure(ref, plugins, Some(projectDependencies(resolved)))).toVector 38 | 39 | val buildsData = 40 | (for { 41 | (build, unit) <- extracted.structure.units 42 | classpath = unit.classpath.toVector 43 | imports = unit.imports.toVector 44 | } yield protocol.BuildData(build, classpath, imports)).toVector 45 | 46 | val builds = projects.map(_.id.build).distinct 47 | protocol.MinimalBuildStructure( 48 | builds = builds, 49 | buildsData = buildsData, 50 | projects = projects) 51 | } 52 | 53 | // have to leave the type inferencer here. 54 | def structure(state: State) = 55 | Project.extract(state).structure 56 | 57 | def keyIndex(state: State): sbt.KeyIndex = 58 | structure(state).index.keyIndex 59 | 60 | def builds(state: State): Set[String] = 61 | keyIndex(state).buildURIs map (_.toASCIIString) 62 | 63 | def projects(state: State, build: URI): Set[protocol.ProjectReference] = 64 | keyIndex(state).projects(build) map { name => 65 | protocol.ProjectReference(build, name) 66 | } 67 | 68 | val TaskClass = classOf[sbt.Task[_]] 69 | // NOTE - This in an approximation... 70 | def isTaskKey[T](key: sbt.ScopedKey[T]): Boolean = { 71 | val mf = key.key.manifest 72 | mf.runtimeClass == TaskClass 73 | } 74 | def isInputKey[T](key: sbt.ScopedKey[T]): Boolean = { 75 | val mf = key.key.manifest 76 | mf.runtimeClass == classOf[sbt.InputTask[_]] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/SbtServer.scala: -------------------------------------------------------------------------------- 1 | package sbt.server 2 | 3 | import java.net.ServerSocket 4 | import sbt.State 5 | import sbt.protocol 6 | import scala.util.control.NonFatal 7 | import java.util.concurrent.TimeUnit 8 | 9 | /** 10 | * This class implements the core sbt engine. We delegate all behavior down to a single 11 | * threaded sbt execution engine. 12 | */ 13 | class SbtServer(configuration: xsbti.AppConfiguration, socket: ServerSocket) extends xsbti.Server { 14 | 15 | override val uri: java.net.URI = { 16 | val port = socket.getLocalPort 17 | val addr = socket.getInetAddress.getHostAddress 18 | new java.net.URI(s"http://${addr}:${port}") 19 | } 20 | 21 | val masterLogFile = new java.io.File(configuration.baseDirectory, ".sbtserver/master.log") 22 | if (!masterLogFile.getParentFile.mkdirs()) 23 | System.err.println(s"Could not create directory ${masterLogFile.getParentFile}") 24 | val masterLog = FileLogger(masterLogFile) 25 | 26 | // The queue where requests go before we fullfill them. 27 | private val queue = new java.util.concurrent.LinkedBlockingDeque[SocketMessage] 28 | 29 | private val stateRef = new java.util.concurrent.atomic.AtomicReference[State](null) 30 | private val requestProcessor = new sbt.server.RequestProcessor(queue, stateRef) 31 | private val commandEngine = new sbt.server.ServerEngine(requestProcessor.engineWorkQueue, stateRef, masterLog, 32 | // this is a lot silly but keeping the ability to break them up later 33 | requestProcessor.eventSink, requestProcessor.eventSink, requestProcessor.eventSink, requestProcessor.eventSink, requestProcessor.eventSink, requestProcessor.eventSink, requestProcessor.eventSink) 34 | 35 | // External API to run queue. 36 | def queueSocketMessage(request: SocketMessage): Unit = queue.add(request) 37 | 38 | // Create the helper which will handle socket requests. 39 | private val socketHandler = new SbtServerSocketHandler(socket, queueSocketMessage, masterLogFile) 40 | 41 | // TODO - Maybe the command engine should extend thread too? 42 | val commandEngineThreadResult = concurrent.Promise[xsbti.MainResult]() 43 | private val commandEngineThread = new Thread("sbt-server-command-loop") { 44 | override def run(): Unit = { 45 | try { 46 | val originOut = System.out 47 | val originErr = System.err 48 | // TODO - Timeouts that lead to us shutting down the server. 49 | val result = try Right(commandEngine.execute(configuration)) 50 | catch { 51 | case e: xsbti.FullReload => 52 | // this means we want to reboot; we can kick it up 53 | // to another thread via commandEngineThreadResult 54 | Left(e) 55 | } 56 | masterLog.log(s"Done executing sbt server engine, result $result") 57 | result match { 58 | case Left(e) => commandEngineThreadResult.failure(e) 59 | case Right(r) => commandEngineThreadResult.success(r) 60 | } 61 | } catch { 62 | case t: Throwable => 63 | masterLog.error(s"command engine thread crash ${t.getClass.getName}: ${t.getMessage}", t) 64 | commandEngineThreadResult.tryFailure(new Exception("command engine thread crashed", t)) 65 | } 66 | } 67 | } 68 | override def awaitTermination(): xsbti.MainResult = try { 69 | // Log some handy debug info 70 | val mx = java.lang.management.ManagementFactory.getRuntimeMXBean(); 71 | masterLog.log(s"jvm ${mx.getName()}") 72 | masterLog.log(s"jvm vmName=${mx.getVmName} vmVendor=${mx.getVmVendor} vmVersion=${mx.getVmVersion}") 73 | masterLog.log(s"jvm specName=${mx.getSpecName} specVendor=${mx.getSpecVendor} specVersion=${mx.getSpecVersion}") 74 | masterLog.log(s"jvm arguments: ${mx.getInputArguments()}") 75 | 76 | // Here we actually start. 77 | masterLog.log("Starting event engine") 78 | requestProcessor.start() 79 | 80 | masterLog.log("Starting sbt command engine") 81 | commandEngineThread.start() 82 | 83 | // Wait for the server to stop (which means: no more sbt commands in State). 84 | // If requestProcessor stops first, it will send a command to the command 85 | // engine asking the command engine to stop. 86 | masterLog.log("Waiting for sbt command engine") 87 | commandEngineThread.join() 88 | 89 | // Close down the socket handler which should signal requestProcessor to stop 90 | // if it hasn't already. requestProcessor drains all requests then stops, if the 91 | // socket handler has stopped. 92 | masterLog.log("Closing listening server socket") 93 | socketHandler.stop() 94 | 95 | masterLog.log("Waiting for socket thread") 96 | socketHandler.join() 97 | 98 | masterLog.log("Waiting for request processor thread") 99 | requestProcessor.join() 100 | 101 | masterLog.log("Waiting for command engine result") 102 | 103 | // If this throws FullReload, then the entire server machinery should restart including 104 | // a new socket, in theory. 105 | val result = concurrent.Await.result(commandEngineThreadResult.future, concurrent.duration.Duration(2, TimeUnit.SECONDS)) 106 | 107 | masterLog.log(s"Returning control to sbt launcher with result $result") 108 | 109 | result 110 | } catch { 111 | case e: xsbti.FullReload => 112 | // this exception tells the launcher code to reload 113 | masterLog.log(s"Throwing FullReload up to sbt launcher") 114 | throw e 115 | case NonFatal(e) => 116 | masterLog.error(s"Unexpected error ${e.getClass.getName}: ${e.getMessage}", e) 117 | Exit(1) 118 | } finally { 119 | masterLog.close() 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/SbtServerMain.scala: -------------------------------------------------------------------------------- 1 | package sbt.server 2 | 3 | import xsbti.{ ServerMain, AppConfiguration, MainResult } 4 | import java.net.{ URI, ServerSocket } 5 | 6 | final case class Exit(code: Int) extends xsbti.Exit 7 | 8 | class SbtServerMain extends ServerMain { 9 | // Make an instance before we use this in a thread to avoid concurrency issue 10 | val serializations = DynamicSerialization.defaultSerializations 11 | 12 | def start(config: AppConfiguration): xsbti.Server = { 13 | System.err.println(s"Starting sbt server...") 14 | // Clean the log directory first.... 15 | val serverDir = new java.io.File(".sbtserver") 16 | // for debugging, it's useful to save the old log file 17 | val oldLogFile = new java.io.File(serverDir, "master.log") 18 | val previousLogFile = new java.io.File(serverDir, "previous.log") 19 | if (oldLogFile.exists) 20 | sbt.IO.move(oldLogFile, previousLogFile) 21 | for (f <- sbt.IO.listFiles(serverDir)) { 22 | if (f.getName != "previous.log") 23 | sbt.IO.delete(f) 24 | } 25 | new SbtServer(config, new ServerSocket(0)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/Serializations.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | object Serializations { 5 | val key = AttributeKey[DynamicSerialization]("Aggregate of registered serializations") 6 | 7 | def extractOpt(state: State): Option[DynamicSerialization] = state get key 8 | def update(state: State): State = { 9 | val extracted = Project.extract(state) 10 | val serializations = extracted.get(SerializersKeys.registeredSerializers).foldLeft(DynamicSerialization.defaultSerializations) { (sofar, next) => 11 | sofar.register(next.serializer)(next.manifest) 12 | } 13 | state.put(key, serializations) 14 | } 15 | } 16 | 17 | object Conversions { 18 | val key = AttributeKey[DynamicConversion]("Aggregate of registered conversions") 19 | 20 | def extractOpt(state: State): Option[DynamicConversion] = state get key 21 | def update(state: State): State = { 22 | val extracted = Project.extract(state) 23 | val conversions = extracted.get(SerializersKeys.registeredProtocolConversions) 24 | .foldLeft(addBuiltinConversions(DynamicConversion.empty)) { (sofar, next) => 25 | sofar.register(next.convert)(next.fromManifest, next.toManifest) 26 | } 27 | state.put(key, conversions) 28 | } 29 | 30 | private def register[F, T](convert: F => T)(implicit fromManifest: Manifest[F], toManifest: Manifest[T]): DynamicConversion => DynamicConversion = { base => 31 | base.register(convert)(fromManifest, toManifest) 32 | } 33 | 34 | private def addBuiltinConversions(base: DynamicConversion): DynamicConversion = { 35 | import SbtToProtocolUtils._ 36 | val conversions = Seq( 37 | register(compileFailedExceptionToProtocol), 38 | register(moduleIdToProtocol), 39 | register(seqAttributedFileToProtocol)) 40 | conversions.foldLeft(base) { (sofar, next) => 41 | next(sofar) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/ServerBoot.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import BasicCommands._ 5 | import BasicCommandStrings._ 6 | import CommandStrings._ 7 | import BuiltinCommands._ 8 | import CommandUtil._ 9 | import Project.LoadAction.{ Value => LoadActionValue } 10 | import Project.loadActionParser 11 | import complete.DefaultParsers._ 12 | import sbt.StateOps 13 | 14 | /** 15 | * Represents overrides of the project loading commands that are required for appropriate server 16 | * usage. 17 | * 18 | * For example: 19 | * - We do not want to hit System.in on load failure for confirmation of retry, but instead hit a client. 20 | * - We want to "hook" the notion of `reload plugins`/`reload return` for special handling. 21 | */ 22 | object ServerBootCommand { 23 | 24 | /** A new load failed command which handles the server requirements */ 25 | private def serverLoadFailed(eventSink: MessageSink[protocol.ExecutionEngineEvent], engine: ServerEngine) = 26 | Command(LoadFailed)(loadProjectParser) { (state, action) => 27 | state.log.error("Failed to load project.") 28 | eventSink.send(protocol.BuildFailedToLoad()) 29 | state.copy(remainingCommands = Seq(engine.HandleNextRebootRequest), next = State.Continue) 30 | } 31 | 32 | private def projectReload(engine: ServerEngine) = { 33 | Command(LoadProject)(_ => Project.loadActionParser) { (state, action) => 34 | action match { 35 | case Project.LoadAction.Current => 36 | engine.installBuildHooks(BuiltinCommands.doLoadProject(state, action)) 37 | // TODO : Detect setting changes and fire an event 38 | case _ => 39 | throw new IllegalArgumentException("'reload' command is not supported for plugins.") 40 | } 41 | } 42 | } 43 | 44 | /** List of commands which override sbt's default commands. */ 45 | def commandOverrides(engine: ServerEngine, eventSink: MessageSink[protocol.ExecutionEngineEvent]): Seq[Command] = 46 | Seq(serverLoadFailed(eventSink, engine), projectReload(engine)) 47 | 48 | val overriddenCommands = Seq(loadFailed, loadProject) 49 | 50 | def isOverridden(cmd: Command): Boolean = overriddenCommands.contains(cmd) 51 | 52 | // TODO - Copied out of BuiltInCommands 53 | private[this] def loadProjectParser = (s: State) => matched(loadActionParser) 54 | } 55 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/ServerEngineQueue.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | /** 5 | * This represents the synchronization interface between the event loop 6 | * and the command processing loop. 7 | */ 8 | trait ServerEngineQueue { 9 | /** 10 | * Blocks until we have a new request. None if "EOF" (server engine should stop). 11 | * 12 | * Note: This will ensure that the work has already been minimized 13 | * in the queue before responding. 14 | */ 15 | def blockAndTakeNext: (RequestListeners, ServerEngineWork) 16 | } -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/ServerEngineWork.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import sbt.protocol._ 5 | import concurrent.{ Future, Promise } 6 | 7 | final case class ExecutionId(id: Long) { 8 | require(id != 0L) 9 | } 10 | 11 | sealed trait ServerEngineWork 12 | 13 | /** 14 | * Our hook exposing cancellation status (and cleanup) 15 | * to the sbt build. 16 | */ 17 | trait WorkCancellationStatus { 18 | /** Run the given function if the work has been cancelled. */ 19 | def onCancel(f: () => Unit): Unit 20 | /** Returns true if the work has been cancelled. */ 21 | def isCancelled: Boolean 22 | /** Mark this work as completed (either success or failure). */ 23 | def complete(): Unit 24 | /** Attempts to cancel this work. */ 25 | def cancel(): Boolean 26 | } 27 | object WorkCancellationStatus { 28 | private object SameThreadExecutionContext extends concurrent.ExecutionContext { 29 | override def execute(r: Runnable): Unit = r.run() 30 | override def reportFailure(t: Throwable) = throw t 31 | } 32 | private object NotCancelled extends RuntimeException("Not Cancelled") 33 | /** constructs a new handler for cancel/complete notifications internally. */ 34 | def apply(): WorkCancellationStatus = 35 | new WorkCancellationStatus { 36 | private val completer = Promise[Unit] 37 | override def onCancel(f: () => Unit): Unit = 38 | completer.future.foreach(_ => f())(SameThreadExecutionContext) 39 | override def isCancelled: Boolean = completer.isCompleted 40 | // Should clear out the registered listeners. 41 | override def complete(): Unit = completer.tryFailure(NotCancelled) 42 | override def cancel(): Boolean = completer.trySuccess(()) 43 | } 44 | } 45 | 46 | // If you find yourself adding stuff from sbt.protocol such as the reply serial 47 | // to this, you are doing it wrong because from here on out MULTIPLE clients 48 | // not just the requester care about this work, so we don't want to special-case 49 | // the original request anymore. We also combine requests into one of these 50 | // chunks of work, thus allRequesters not a single requester. 51 | /** 52 | * A final case class representing a request for work to be performed. 53 | * 54 | * @param id - The id given to this request for execution. 55 | * @param command - The sbt command that is requested to be run 56 | * @param allRequesters - All clients associated with this request for work. 57 | * @param cancelRequest - A promise that will complete if cancel is requested. 58 | * If a cancel is never received, the future never completes. 59 | */ 60 | final case class CommandExecutionWork( 61 | id: ExecutionId, 62 | command: String, 63 | allRequesters: Set[LiveClient], 64 | // TODO - Cancel handler thing should allow 65 | // saying that work is done as well as cancelling. 66 | cancelStatus: WorkCancellationStatus) extends ServerEngineWork { 67 | require(allRequesters.nonEmpty) 68 | /** Whether or not the work was cancelled. */ 69 | def isCancelled: Boolean = cancelStatus.isCancelled 70 | /** Creates a new copy of this work, but with an additional client. */ 71 | def withNewRequester(requester: LiveClient): CommandExecutionWork = 72 | CommandExecutionWork(id, command, allRequesters + requester, cancelStatus) 73 | } 74 | 75 | case object EndOfWork extends ServerEngineWork 76 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/ServerListener.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import concurrent.Future 5 | import sbt.protocol.{ ScopedKey => _, _ } 6 | 7 | trait MessageSink[-J <: Message] { 8 | /** Sends a message out. This should be a safe call (doens't throw on bad client for example.) */ 9 | def send(msg: J): Unit 10 | } 11 | 12 | /** 13 | * An interface we can use to send messages to an sbt client. 14 | * 15 | * TODO - better name! 16 | */ 17 | sealed trait SbtClient extends MessageSink[Message] { 18 | /** Creates a new client that will send events to *both* of these clients. */ 19 | def zip(other: SbtClient): SbtClient = (this, other) match { 20 | case (NullSbtClient, NullSbtClient) => NullSbtClient 21 | case (NullSbtClient, client) => client 22 | case (client, NullSbtClient) => client 23 | case (JoinedSbtClient(clients), JoinedSbtClient(clients2)) => JoinedSbtClient(clients ++ clients2) 24 | case (JoinedSbtClient(clients), other) => JoinedSbtClient(clients + other) 25 | case (other, JoinedSbtClient(clients2)) => JoinedSbtClient(clients2 + other) 26 | case (other, other2) => JoinedSbtClient(Set(other, other2)) 27 | } 28 | // Removes a particular client from this potential aggregate client. 29 | def without(client: SbtClient): SbtClient = 30 | this match { 31 | case `client` | NullSbtClient => NullSbtClient 32 | case JoinedSbtClient(clients) if clients.contains(client) => 33 | JoinedSbtClient(clients filterNot (_ == client)) 34 | case other => other 35 | } 36 | } 37 | 38 | object NullSbtClient extends SbtClient { 39 | override final def send(msg: Message): Unit = () 40 | override def toString = "NullSbtClient" 41 | } 42 | final case class JoinedSbtClient(clients: Set[SbtClient]) extends SbtClient { 43 | // TODO - ignore individual failures? 44 | override final def send(msg: Message): Unit = 45 | clients foreach (_ send msg) 46 | override def toString = clients.mkString("Joined(", ",", ")") 47 | } 48 | 49 | // This is what concrete implementations implement. 50 | abstract class LiveClient extends SbtClient { 51 | def uuid: java.util.UUID 52 | def configName: String 53 | def humanReadableName: String 54 | def protocolVersion: ProtocolVersion 55 | def featureTags: Seq[FeatureTag] 56 | 57 | @volatile 58 | var daemon: Boolean = false 59 | 60 | def info: protocol.ClientInfo = 61 | protocol.ClientInfo(uuid = uuid.toString, configName = configName, humanReadableName = humanReadableName, protocolVersion = protocolVersion, featureTags = featureTags.toVector) 62 | 63 | /** requests a line of input from the client. This will return sometime in the future. */ 64 | def readLine(executionId: ExecutionId, prompt: String, mask: Boolean): Future[Option[String]] 65 | /** Confirms a message from a client. */ 66 | def confirm(executionId: ExecutionId, msg: String): Future[Boolean] 67 | def reply(replyTo: Long, msg: Response): Unit 68 | } 69 | 70 | final case class KeyValueClientListener[T]( 71 | key: ScopedKey[T], 72 | client: SbtClient) { 73 | /** Disconnect a client from this listener. */ 74 | def remove(c: SbtClient): KeyValueClientListener[T] = 75 | copy(client = client without c) 76 | 77 | def add(c: SbtClient): KeyValueClientListener[T] = 78 | copy(client = client zip c) 79 | } 80 | 81 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/ServerState.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | final case class LastCommand(command: CommandExecutionWork) 5 | 6 | /** 7 | * Represents the current state of ServerEngine, as passed between 8 | * commands (ServerState gets tacked on to sbt's State). 9 | */ 10 | final case class ServerState( 11 | requestListeners: RequestListeners, 12 | lastCommand: Option[LastCommand] = None) { 13 | 14 | def buildListeners: SbtClient = requestListeners.buildListeners 15 | 16 | def keyListeners: Seq[KeyValueClientListener[_]] = requestListeners.keyListeners 17 | 18 | def withLastCommand(cmd: LastCommand): ServerState = { 19 | copy(lastCommand = Some(cmd)) 20 | } 21 | def clearLastCommand: ServerState = copy(lastCommand = None) 22 | 23 | def optionalExecutionId: Option[ExecutionId] = lastCommand.map(_.command.id) 24 | def requiredExecutionId: ExecutionId = optionalExecutionId.getOrElse(throw new RuntimeException("Last command with execution ID should be set here but is not")) 25 | } 26 | 27 | object ServerState { 28 | val serverState = AttributeKey[ServerState]("Sbt's server state") 29 | 30 | def extract(state: State): ServerState = Project.getOrError(state, serverState, "Could not find sbt's server state.") 31 | def extractOpt(state: State): Option[ServerState] = state get serverState 32 | def update(state: State, serverState: ServerState): State = state.put(ServerState.serverState, serverState) 33 | } 34 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/SettingUtil.scala: -------------------------------------------------------------------------------- 1 | package sbt.server 2 | 3 | import _root_.sbt._ 4 | import sbt.Keys._ 5 | import sbt.Defaults._ 6 | 7 | // TODO - We should probably rename this to "SettingUtil" to represent what it's actualyl doing for us. 8 | private[server] object SettingUtil { 9 | 10 | /** A helper method to ensure that settings we're appending are scoped according to the current project ref. */ 11 | def makeAppendSettings(settings: Seq[Setting[_]], inProject: ProjectRef, extracted: Extracted) = { 12 | // transforms This scopes in 'settings' to be the desired project 13 | val appendSettings = Load.transformSettings(Load.projectScope(inProject), inProject.build, extracted.rootProject, settings) 14 | appendSettings 15 | } 16 | 17 | /** Reloads an sbt build with the given settings being appended to the current session. */ 18 | def reloadWithAppended(state: State, appendSettings: Seq[sbt.Setting[_]]): State = { 19 | // reloads with appended settings. 20 | val session = Project.session(state) 21 | //val structure = Project.structure(state) 22 | //implicit val display = Project.showContextKey(state) 23 | // When we reload, make sure we keep all reapplied settings... 24 | //val newStructure = Load.reapply(session.mergeSettings ++ appendSettings, structure) 25 | val newSession = session.appendRaw(appendSettings) 26 | // updates various aspects of State based on the new settings 27 | // and returns the updated State 28 | SessionSettings.reapply(newSession, state) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/TaskCancellationShim.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | /** 5 | * This is a strategy which can cancel tasks when their associated cancellation `Future[_]` 6 | * is completed. 7 | */ 8 | final class ServerTaskCancellation(serverState: ServerState, logSink: MessageSink[protocol.LogEvent]) extends TaskCancellationStrategy { 9 | def debug(s: String): Unit = 10 | logSink.send(protocol.DetachedLogEvent(protocol.LogMessage(protocol.LogMessage.DEBUG, s))) 11 | 12 | def warn(s: String): Unit = 13 | logSink.send(protocol.DetachedLogEvent(protocol.LogMessage(protocol.LogMessage.WARN, s))) 14 | 15 | /* override */ class State(canceller: RunningTaskEngine, val lastCommand: Option[LastCommand]) { 16 | @volatile 17 | var enabled = true 18 | def cancel(): Unit = { 19 | debug(s"cancel strategy cancel() with enabled=$enabled lastCommand=$lastCommand") 20 | if (enabled) canceller.cancelAndShutdown() 21 | } 22 | } 23 | override def onTaskEngineStart(canceller: RunningTaskEngine): State = { 24 | val state = new State(canceller, serverState.lastCommand) 25 | debug(s"cancel strategy start enabled=${state.enabled} lastCommand=${state.lastCommand}") 26 | state.lastCommand match { 27 | case Some(command) => 28 | // Note: this callback could be invoked immediately 29 | // and on our thread - I hope sbt can cope with that... 30 | command.command.cancelStatus.onCancel { () => 31 | state.cancel() 32 | } 33 | case None => 34 | warn(s"cancel strategy for task engine does not know current command; will not be able to cancel") 35 | } 36 | state 37 | } 38 | override def onTaskEngineFinish(state: State): Unit = { 39 | state.enabled = false 40 | debug(s"cancel strategy finish enabled=${state.enabled} lastCommand=${state.lastCommand}") 41 | } 42 | } 43 | object ServerTaskCancellation { 44 | def getShims(logSink: MessageSink[protocol.LogEvent]): Seq[Setting[_]] = { 45 | Seq( 46 | Keys.taskCancelStrategy in Global := { (state: State) => 47 | val sstate = server.ServerState.extract(state) 48 | new ServerTaskCancellation(sstate, logSink) 49 | }) 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/TaskIdRecorder.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import sbt.Task 5 | 6 | /** this is the read-only face of TaskIdRecorder which is used outside of ServerExecuteProgress */ 7 | trait TaskIdFinder { 8 | /** 9 | * guess task ID from the key and/or the current thread. 10 | * this returns 0 (invalid task ID) if we can't come up with any guess; 11 | * doesn't return an Option because normally we want to just send the event 12 | * anyway with a 0 task ID. 13 | */ 14 | final def bestGuessTaskId(taskIfKnown: Option[Task[_]] = None): Long = 15 | bestGuessTaskIdOption(taskIfKnown).getOrElse(0L) 16 | 17 | def bestGuessTaskIdOption(taskIfKnown: Option[Task[_]] = None): Option[Long] 18 | 19 | /** just look up the task ID by key, don't use any thread info. */ 20 | def taskId(task: Task[_]): Option[Long] 21 | } 22 | 23 | /** 24 | * This class actually records tasks registered via the task engine. 25 | */ 26 | class TaskIdRecorder extends TaskIdFinder { 27 | // This is always modified from the engine thread, and we will 28 | // add the ID for each task in register() before 29 | // the corresponding task runs. So while a task thread might 30 | // get an old map that's missing a newly-registered ID, that 31 | // task thread should not care about or need to access said 32 | // newly-registered ID. The thread which needs a task ID 33 | // should run post-register. In theory, of course. If this 34 | // theory is wrong not sure what we'll have to do. 35 | // 36 | // Note: This is a single-producer of changed values, with multiple thread consumers. 37 | // If that assumption ever changes, this will have to change 38 | // from a volatile into an Atomic Reference and use CAS operations 39 | // for writing. 40 | @volatile private var taskIds: Map[Task[_], Long] = Map.empty 41 | private var nextTaskId = 1L // start with 1 so 0 is invalid 42 | 43 | /** this one is used from the task threads and thus we have to synchronize. */ 44 | private var runningTasks: Set[Task[_]] = Set.empty 45 | 46 | private object taskIdThreadLocal extends ThreadLocal[Long] { 47 | override def initialValue(): Long = 0 48 | } 49 | 50 | // This is only ever called from one thread at a time (we assume). 51 | // NOTE: We should validate this is the case even in the presence of 52 | // custom commands. 53 | def register(task: Task[_]): Unit = { 54 | if (taskIds.contains(task)) 55 | throw new RuntimeException(s"registered more than once? ${task}") 56 | // clear system streams of anything which isn't from the new task 57 | flushSystemStreams() 58 | taskIds += (task -> nextTaskId) 59 | nextTaskId += 1 60 | } 61 | 62 | def unregister(task: Task[_]): Unit = { 63 | // clear system streams of anything which is from the previous tasks 64 | flushSystemStreams() 65 | taskIds -= task 66 | } 67 | 68 | def clear(): Unit = { 69 | // clear system streams of anything which is from the previous tasks 70 | flushSystemStreams() 71 | taskIds = Map.empty 72 | } 73 | 74 | // TODO we want to replace this with *requiring* all event senders 75 | // to know their key, which means we need to pass the task key 76 | // through to the UIContext and the EventLogger. This can probably 77 | // be done with a streamsManager plus somehow relating UIContext to 78 | // the streams, or handling UIContext in a similar way to streams 79 | // where it's a dummy value replaced by sbt before invoking each 80 | // task. Exact details TBD and may require sbt ABI break. 81 | // The problem with this hack is that if a task spawns its 82 | // own threads, we won't have the task ID. 83 | def setThreadTask(task: Task[_]): Unit = 84 | taskId(task) match { 85 | case Some(id) => 86 | // clear system streams of anything which isn't from the new task 87 | flushSystemStreams() 88 | taskIdThreadLocal.set(id) 89 | synchronized { 90 | runningTasks += task 91 | } 92 | case None => 93 | throw new RuntimeException(s"Running a task which was never ExecuteProgress#registered? ${task}") 94 | } 95 | 96 | def clearThreadTask(task: Task[_]): Unit = { 97 | // clear system streams of anything which is from the new task 98 | flushSystemStreams() 99 | taskIdThreadLocal.remove() 100 | synchronized { 101 | runningTasks -= task 102 | } 103 | } 104 | 105 | override def bestGuessTaskIdOption(taskIfKnown: Option[Task[_]] = None): Option[Long] = { 106 | taskIfKnown flatMap { key => 107 | taskIds.get(key) 108 | } orElse { 109 | taskIdThreadLocal.get match { 110 | // if we don't have anything in the thread local, if we have 111 | // only one task running we can guess that one. 112 | case 0L => synchronized { 113 | if (runningTasks.size == 1) 114 | Some(taskIds.get(runningTasks.head).getOrElse(throw new RuntimeException("running task has no ID?"))) 115 | else 116 | None 117 | } 118 | case other => Some(other) 119 | } 120 | } 121 | } 122 | 123 | // because these are buffered and then forwarded to log events 124 | // that get task IDs, we want to flush whenever our bestGuessTaskId 125 | // might change. 126 | private def flushSystemStreams(): Unit = { 127 | System.out.flush() 128 | System.err.flush() 129 | } 130 | 131 | override def taskId(task: Task[_]): Option[Long] = { 132 | taskIds.get(task) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /server/src/main/scala/sbt/server/TestShims.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package server 3 | 4 | import sbt.testing.{ SuiteSelector, TestWildcardSelector, TestSelector } 5 | 6 | object TestShims { 7 | 8 | val serverTestListener = taskKey[TestReportListener]("Global test listener for the sbt server.") 9 | val testShimSettings: Seq[Setting[_]] = 10 | Seq( 11 | serverTestListener in Global := { 12 | val sendEventService = SendEventServiceKeys.sendEventService.value 13 | new ServerTestListener(sendEventService) 14 | }, 15 | sbt.Keys.testListeners in Test in Global += (serverTestListener in Global).value) 16 | 17 | // Don't pass anything in here that's "internal" because we 18 | // should be moving this code into the default sbt test task, 19 | // and it won't be able to use internals. You probably have to 20 | // add anything you need to UIContext. 21 | def makeShims(state: State): Seq[Setting[_]] = 22 | testShimSettings 23 | } 24 | 25 | import testing.{ Logger => TLogger, Event => TEvent, Status => TStatus } 26 | 27 | class ServerTestListener(val sendEventService: SendEventService) extends TestReportListener { 28 | 29 | override def startGroup(name: String): Unit = { 30 | sendEventService.sendEvent(protocol.TestGroupStarted(name)) 31 | } 32 | 33 | override def testEvent(event: TestEvent): Unit = { 34 | for (detail <- event.detail) { 35 | val outcome = detail.status match { 36 | case TStatus.Success => protocol.TestPassed 37 | case TStatus.Error => protocol.TestError 38 | case TStatus.Failure => protocol.TestFailed 39 | case TStatus.Skipped => protocol.TestSkipped 40 | case TStatus.Canceled => protocol.TestCanceled 41 | case TStatus.Ignored => protocol.TestIgnored 42 | // TODO - Handle this correctly... (which means what?) 43 | case TStatus.Pending => protocol.TestPending 44 | } 45 | val testName = detail.selector() match { 46 | case s: TestSelector => s.testName() 47 | case _ => detail.fullyQualifiedName() 48 | } 49 | sendEventService.sendEvent( 50 | protocol.TestEvent( 51 | testName, 52 | None, 53 | outcome, 54 | if (detail.throwable.isDefined) Some(detail.throwable.get) else None, 55 | detail.duration())) 56 | } 57 | } 58 | 59 | override def endGroup(name: String, t: Throwable): Unit = { 60 | sendEventService.sendEvent(protocol.TestGroupFinished(name, protocol.TestGroupError, Option(t))) 61 | } 62 | 63 | override def endGroup(name: String, result: TestResult.Value): Unit = { 64 | val res = result match { 65 | case TestResult.Passed => protocol.TestGroupPassed 66 | case TestResult.Failed => protocol.TestGroupFailed 67 | case TestResult.Error => protocol.TestGroupError 68 | } 69 | sendEventService.sendEvent(protocol.TestGroupFinished(name, res, None)) 70 | } 71 | 72 | override def contentLogger(test: TestDefinition): Option[ContentLogger] = None 73 | } 74 | -------------------------------------------------------------------------------- /server/src/test/scala/sbt/server/ByteDecoderTest.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Typesafe Inc. 3 | */ 4 | package sbt.server 5 | 6 | import org.junit.Assert._ 7 | import org.junit._ 8 | import java.io.File 9 | 10 | class ByteDecoderTest { 11 | 12 | val utf8Bytes = { 13 | val in = getClass.getResourceAsStream("/UTF-8-demo.txt") 14 | if (in == null) 15 | throw new Exception("Missing UTF-8 test file") 16 | val size = in.available() 17 | val array = new Array[Byte](size) 18 | if (in.read(array) != size) 19 | throw new Exception("did not read " + size + " files") 20 | array 21 | } 22 | 23 | val utf8String = new String(utf8Bytes, "UTF-8") 24 | 25 | @Test 26 | def testDecoderSingleChunk(): Unit = { 27 | val decoder = new ByteDecoder() 28 | decoder.feed(utf8Bytes) 29 | decoder.finish() 30 | val result = decoder.read.mkString 31 | assertEquals(utf8String.size, result.size) 32 | assertEquals(utf8String, result) 33 | } 34 | 35 | @Test 36 | def testDecoderOneByteChunks(): Unit = { 37 | val decoder = new ByteDecoder() 38 | for (i <- 0 until utf8Bytes.length) { 39 | decoder.feed(utf8Bytes(i): Byte) 40 | } 41 | decoder.finish() 42 | val result = decoder.read.mkString 43 | assertEquals(utf8String.size, result.size) 44 | assertEquals(utf8String, result) 45 | } 46 | 47 | @Test 48 | def testDecoderVariousByteChunks(): Unit = { 49 | for (chunkSize <- 1 until 16) { 50 | val decoder = new ByteDecoder() 51 | for (chunk <- utf8Bytes.sliding(chunkSize, chunkSize)) { 52 | decoder.feed(chunk: Array[Byte]) 53 | } 54 | decoder.finish() 55 | val result = decoder.read.mkString 56 | assertEquals(utf8String.size, result.size) 57 | assertEquals(utf8String, result) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /server/src/test/scala/sbt/server/TestSbtDiscovery.scala: -------------------------------------------------------------------------------- 1 | package sbt.server 2 | 3 | import org.junit.Assert._ 4 | import org.junit._ 5 | import SbtDiscovery._ 6 | import sbt.ConfigKey.configurationToKey 7 | import sbt.Scoped.inputScopedToKey 8 | import sbt.Scoped.taskScopedToKey 9 | 10 | class TestSbtDiscovery { 11 | 12 | @Test 13 | def canSortOfDetermineInputTaskKeys(): Unit = { 14 | val name = sbt.Keys.name in sbt.Compile 15 | val sources = sbt.Keys.sources in sbt.Compile 16 | val run = sbt.Keys.run in sbt.Compile 17 | assertFalse("Could not figure out `name` is not an input task!", isInputKey(name.scopedKey)) 18 | assertFalse("Could not figure out spirces is not an input task!", isInputKey(sources)) 19 | assertTrue("Could not figure out run is an input task!", isInputKey(run)) 20 | } 21 | 22 | @Test 23 | def canSortOfDetermineTaskKeys(): Unit = { 24 | val name = sbt.Keys.name in sbt.Compile 25 | val sources = sbt.Keys.sources in sbt.Compile 26 | val run = sbt.Keys.run in sbt.Compile 27 | assertFalse("Could not figure out `name` is not a task!", isInputKey(name.scopedKey)) 28 | assertTrue("Could not figure out `sources` is a task!", isTaskKey(sources)) 29 | assertFalse("Could not figure out `run` is not a task!", isTaskKey(run)) 30 | } 31 | } -------------------------------------------------------------------------------- /server/src/test/scala/sbt/server/TestSbtToProtocolUtils.scala: -------------------------------------------------------------------------------- 1 | package sbt.server 2 | 3 | import org.junit.Assert._ 4 | import org.junit._ 5 | import sbt.protocol 6 | import sbt.ConfigKey.configurationToKey 7 | import sbt.Scoped.inputScopedToKey 8 | import sbt.Scoped.taskScopedToKey 9 | import sbt.serialization.TypeExpression 10 | 11 | class TestSbtToProtocolUtils { 12 | 13 | @Test 14 | def testProjectScopeMapping(): Unit = { 15 | val ref = sbt.ProjectRef( 16 | new java.net.URI("file:///home/"), "test-project") 17 | val nameInRef = sbt.Keys.name in ref 18 | val string = TypeExpression("java.lang.String", Nil) 19 | val expectedKey = 20 | protocol.AttributeKey("name", string) 21 | val expectedScope = 22 | protocol.SbtScope( 23 | project = Some(protocol.ProjectReference( 24 | new java.net.URI("file:///home/"), 25 | "test-project")), 26 | build = Some(new java.net.URI("file:///home/"))) 27 | val expected = protocol.ScopedKey(expectedKey, expectedScope) 28 | val actual = SbtToProtocolUtils.scopedKeyToProtocol(nameInRef.scopedKey) 29 | assertEquals("Failed to convert sbt key:", expected, actual) 30 | } 31 | 32 | @Test 33 | def testKeyMapping(): Unit = { 34 | val sbtName = sbt.Keys.name 35 | val string = TypeExpression("java.lang.String", Nil) 36 | val expected = 37 | protocol.AttributeKey("name", string) 38 | 39 | val actual = SbtToProtocolUtils.keyToProtocol(sbtName.key) 40 | assertEquals("Failed to covnert sbt key:", expected, actual) 41 | } 42 | 43 | @Test 44 | def testScopedInputKeyMapping(): Unit = { 45 | val sbtRunInCompile = sbt.Keys.run in sbt.Compile 46 | val unit = TypeExpression("void", Nil) 47 | //val inputTaskType = TypeExpression("sbt.InputTask", Seq(unit)) 48 | 49 | val runInputKey = 50 | protocol.AttributeKey("run", unit) 51 | val scope = protocol.SbtScope(config = Some("compile")) 52 | val expected = protocol.ScopedKey(runInputKey, scope) 53 | 54 | val actual = SbtToProtocolUtils.scopedKeyToProtocol(sbtRunInCompile) 55 | assertEquals("Failed to covnert sbt key:", expected, actual) 56 | } 57 | 58 | @Test 59 | def testScopedTaskKeyMapping(): Unit = { 60 | // Here we ensure the Task[_] part is stripped from the key in the remote API since we cannot (nor should) 61 | // serialize tasks. 62 | val sbtSourcesInCompile = sbt.Keys.sources in sbt.Compile 63 | val file = TypeExpression("java.io.File", Nil) 64 | val seqFile = TypeExpression("scala.collection.Seq", List(file)) 65 | val sourcesTaskKey = 66 | protocol.AttributeKey("sources", seqFile) 67 | val scope = protocol.SbtScope(config = Some("compile")) 68 | val expected = protocol.ScopedKey(sourcesTaskKey, scope) 69 | 70 | val actual = SbtToProtocolUtils.scopedKeyToProtocol(sbtSourcesInCompile) 71 | assertEquals("Failed to covnert sbt key:", expected, actual) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /terminal/src/main/scala/sbt/terminal/JLineReader.scala: -------------------------------------------------------------------------------- 1 | package sbt 2 | package terminal 3 | 4 | import java.io.File 5 | import sbt.client.SbtClient 6 | import sbt.protocol.Completion 7 | 8 | // This class blocks on response from the server for autocompletion options. 9 | final class RemoteJLineReader( 10 | historyPath: Option[File], 11 | client: SbtClient, 12 | val handleCONT: Boolean) extends JLine { 13 | 14 | private val reading = new java.util.concurrent.atomic.AtomicBoolean(false) 15 | 16 | // We synchronize output... 17 | def printLineAndRedrawPrompt(line: String): Unit = reader.synchronized { 18 | // TODO - Is this the correct mechanism? 19 | reader.println(s"\r$line") 20 | if (reading.get) reader.redrawLine() 21 | } 22 | 23 | override def readLine(prompt: String, mask: Option[Char]): Option[String] = { 24 | reading.set(true) 25 | try super.readLine(prompt, mask) 26 | finally reading.lazySet(false) 27 | } 28 | 29 | protected[this] val reader = { 30 | val r = JLine.createReader(historyPath) 31 | // TODO - Install custom completions if the client requests autocompletion... 32 | sbt.complete.JLineCompletion.installCustomCompletor(r)(blockingServerCompleter) 33 | r 34 | } 35 | 36 | private def blockingServerCompleter(line: String, level: Int): (Seq[String], Seq[String]) = { 37 | import concurrent.ExecutionContext.Implicits.global // for "map" below 38 | // TODO - configurable duration here.. 39 | val timeout = concurrent.duration.Duration(2, java.util.concurrent.TimeUnit.SECONDS) 40 | concurrent.Await.result(client.possibleAutocompletions(line, level).map(convertProtocolCompletions), timeout) 41 | } 42 | 43 | private def convertProtocolCompletions(cs: Vector[Completion]): (Seq[String], Seq[String]) = 44 | if (cs.isEmpty) (Nil, "{invalid input}" :: Nil) 45 | else { 46 | import sbt.complete.JLineCompletion.appendNonEmpty 47 | val initial = Set.empty[String] -> Set.empty[String] 48 | val (insert, display) = 49 | (initial /: cs) { 50 | case (t @ (insert, display), comp) => 51 | if (comp.isEmpty) t 52 | else (insert + comp.append, appendNonEmpty(display, comp.display)) 53 | } 54 | (insert.toSeq, display.toSeq.sorted) 55 | } 56 | 57 | } --------------------------------------------------------------------------------