Commit Graph

275 Commits

Author SHA1 Message Date
Ethan Atkins 3216848c77 Fix client argument parsing for commands containing `-`
When the user ran a command like `testOnly foo -- bar`, the client was
incorrectly treating the `--` as an sbt argument. The assumption is that
once an argument is found that does not start with a `-`, then
everything following that argument is part of the command arguments.
2020-06-26 20:59:18 -07:00
Ethan Atkins ba0d97c9ac Use 'FastTrack' instead of 'Maintenance'
FastTrack may better convey the intent.
2020-06-25 15:12:41 -07:00
Ethan Atkins 4eedaea49e Synchronize ServerConnection.sendString
Without synchronization, it is possible to interlace liens from multiple
messages which causes parsing to fail on the server side.
2020-06-25 10:38:08 -07:00
Ethan Atkins a2047a0b2c Refactor watch
The existing implementation of watch did not work with the thin client.
In sbt 1.3.0, watch was changed to be a blocking command that performed
manual task evaluation. This commit makes the implementation more
similar to < 1.3.0 where watch modifies the state and after running the
user specified command(s), it enters a blocking command. The new
blocking command is very similar to the shell command.

As part of this change, I also reworked some of the internals of watch
so that a number of threads are spawned for reading file and input
events. By using background threads that write to a single event queue,
we are able to block on the file events and terminal input stream rather
than polling. After this change, the cpu utilization as measured by ps
drops from roughly 2% of a cpu to 0.

To integrate with the network client, we introduce a new UITask that is
similar to the AskUserTask but instead of reading lines and adding execs
to the command queue, it reads characters and converts them into watch
commands that we also append to the command queue.

With this new implementation, the watch task that was added in 1.3.0 no
longer works. My guess is that no one was really using it. It wasn't
documented anywhere. The motivation for the task implementation was that
it could be called within another task which would let users define a
task that monitors for file changes before running. Since this had never
been advertised and is only of limited utility anyway, I think it's fine
to break it.

I also had to disable the input-parser and symlinks tests. I'm not 100%
sure why the symlinks test was failing. It would tend to work on my
machine but fail in CI. I gave up on debugging it. The input-parser test
also fails but would be a good candidate to be moved to the client test
in the serverTestProj. At any rate, it was testing a code path that was
only exercised if the user changed the watchInputStream method which is
highly unlikely to have been done in any user builds.

The WatchSpec had become a nuisance and wasn't really preventing from
any regressions so I removed it. The scripted tests are how we test
watch.
2020-06-25 10:05:59 -07:00
Ethan Atkins d5cbc43075 Add tab completion support to thin client
The sbtc client can provide a ux very similar to using the sbt shell
when combined with tab completions. In fact, since some shells have a
better tab completion engine than that provided by jilne2, the
experience can be even better. To make this work, we add another entry
point to the thin client that is capable of generating completions for
an input string. It queries sbt for the completions and prints the
result to stdout, where they are consumed by the shell and fed into its
completion engine.

In addition to providing tab completions, if there is no server running
or if the user is completing `runMain`, `testOnly` or `testQuick`, the
thin client will prompt the user to ask if they would like to start an
sbt server or if they would like to compile to generate the main class
or test names. Neither powershell nor zsh support forwarding input to
the tab completion script. Zsh will print output to stderr so we
opportunistically start the server or complete the test class names.
Powershell does not print completion output at all, so we do not start a
server or fill completions in that case*. For fish and bash, we prompt
the user that they can take these actions so that they can avoid the
expensive operation if desired.

* Powershell users can set the environment variable SBTC_AUTO_COMPLETE
if they want to automatically start a server of compile for run and test
names. No output will be displayed so there can be a long latency
between pressing <tab> and seeing completion results if this variable is
set.
2020-06-24 20:08:14 -07:00
Ethan Atkins ea823f1051 Add server idle timeout
This commit adds the ability for sbt to automatically shut itself down
if it has been idle for some duration of time. The motivation is that
if the user may not realize they have an sbt server running in the
background that is using resources. We don't want to be too aggressive
with the idle timeout because that can reduce the efficacy of the thin
client. A value of one week is chosen so that users can enjoy a long
weekend and when they return to their computer, they won't have to
restart sbt. If they haven't used the server in at least a week, it
seems prudent to just kill it.
2020-06-24 20:04:13 -07:00
Ethan Atkins f8e06def74 Add win32 named pipe security level option
The sbtipcsocket by default restricts win32 named pipes to only allow
connections from the same login session. This makes connecting to a
remote server not work over ssh. We relax the default slightly in sbt to
allow the owner of the pipe to connect over any logon shell. The user
could restore the old behavior with:
```
Global / windowsServerSecurityLevel := Win32SecurityLevel.LOGON_DACL
```
or, if YOLO
```
Global / windowsServerSecurityLevel := Win32SecurityLevel.NO_SECURITY
```
2020-06-24 20:04:13 -07:00
Ethan Atkins 65599f2f84 Add sbt client project
This project is used to create client executables. The implementation is
pure java but we can build graalvm native-images from the java main
class. There are two versions of the client. One of them uses the
ipcsocket jni implementation to connect to the sbt server while the
other uses jna. It is necessary to use jni for the graalvm native-image
tool to work. Otherwise the two approaches should be identical.
2020-06-24 20:04:12 -07:00
Ethan Atkins a7cb186924 Add --close-io-streams flag to sbt
When we start sbt with the thin client, we want to close the server io
streams after it loads so that the client exiting won't crash the
server. When we are running the server as part of the server tests, it
is nice to have the server output. By setting the --close-io-streams
flag when we launch the server in the client, we are able to achieve
both.
2020-06-24 20:03:44 -07:00
Ethan Atkins 18cb839c47 Wrap network commands for reporting
Running multi commands (input commands delimited by semi-colons) did not
work with the thin client. The commands would actually run on the
server, but the thin client would exit immediately without displaying
the output. The reason was that MainLoop would report the exec complete
when all it had done was split the original command into its constituent
parts and prepended them to the state command list. To work around this,
when we detect a network source command, we can remap its exec id to a
different id and only report the original exec id after the commands
complete. We also have to keep track of whether or not the command
succeeded or failed so that the reporting command reports the correct
result.

The way its implemented is with the the following steps:
1. set the terminal to the network terminal
2. stash the current onFailure so that we can properly report failures
3. add the new exec id to a map of the original exec id to the generated
   id
4. actually run the command
5. if the command succeeds, add the original exec id to a result map
6. pop the onFailure
7. restore the terminal to console
8. report the result -- if the original exec id is in the result map we
   report success. Otherwise we report failure.

There is also logic in NetworkChannel for finding the original exec id
if reporting one of the artificially generated exec ids because the
client will not be aware of that id.
2020-06-24 20:03:43 -07:00
Ethan Atkins 43e4fa85e3 Add ctrl+c support thin client
When the user presses ctrl+c, we want to cancel any running tasks that
were initiated by that client. This is a bit tricky because we may not
be sure what is running if the client is in interactive mode. To work
around this, we send a cancellation request with the special id
__CancelAll. When the NetworkChannel receives this request, it cancels
the active task if was initiated by the client that sent the
cancellation request. The result it returns to the client indicates if
there were any tasks to be cancelled. If there were and the client was
in interactive mode, we do not exit. Otherwise we exit.
2020-06-24 19:40:18 -07:00
Ethan Atkins d0842711e4 Rework NetworkClient
This commit integrates the NetworkClient with the server side rendered
ui. Rather than implementing its own shell method, it will now connect
to the server and register itself as a virtual terminal. If there are
command arguments, those will be sent to the server as execs. Otherwise
it will enter a shell mode where it just acts as a relay for io.

In batch mode, it will return the exit code of the last exec sent to the
server. If the server disconnects, the client will exit with an error code.
2020-06-24 19:40:17 -07:00
Ethan Atkins ba345dd797 Add multi-client ui to server
This commit makes it possible for the sbt server to render the same ui
to multiple clients. The network client ui should look nearly identical
to the console ui except for the log messages about the experimental
client.

The way that it works is that it associates a ui thread with each
terminal. Whenever a command starts or completes, callbacks are invoked
on the various channels to update their ui state. For example, if there
are two clients and one of them runs compile, then the prompt is changed
from AskUser to Running for the terminal that initiated the command
while the other client remains in the AskUser state. Whenever the client
changes uses ui states, the existing thread is terminated if it is
running and a new thread is begun.

The UITask formalizes this process. It is based on the AskUser class
from older versions of sbt. In fact, there is an AskUserTask which is
very similar. It uses jline to read input from the terminal (which could
be a network terminal). When it gets a line, it submits it to the
CommandExchange and exits. Once the next command is run (which may or
may not be the command it submitted), the ui state will be reset.

The debug, info, warn and error commands should work with the multi
client ui. When run, they set the log level globally, not just for the
client that set the level.
2020-06-24 19:40:17 -07:00
Ethan Atkins f0815edc7a Improve network client server forking
In the previous version of the NetworkClient, there was no feedback
while the client was starting up. It was also possible that if the
server had exited abruptly and there was a dead active.json portfile
left over, that the client wouldn't be able to start the server.

This commit reworks things so that we launch the server with a java
process and we print out the stdout, stderr streams from the process. We
also forward the client's stdin in case the server couldn't be started
and the user wants to retry or print the stacktrace.
2020-06-24 19:40:17 -07:00
Ethan Atkins 734a1e7641 Add virtual terminal support for network clients
This commit adds support for remote clients to connect to the sbt server
and attach themselves as a virtual terminal. In order to make this work,
each connection must send a json rpc request to attach to the server.
When this is received, the server will periodically query the remote
client to get the terminal properties and capabilities that allow the
remote client to act as a jline terminal proxy. There is also support
for json messages with ids sbt/systemIn and sbt/systemOut that allow io
to be relayed from the remote terminal to the sbt server and back.

Certain commands such as `exit` should be evaluated immediately. To make
this work, we add the concept of a MaintenanceTask. The CommandExchange
has a background thread that reads MaintenanceTasks and evaluates them
on demand. This allows maintenance tasks to be evaluated even when sbt
is evaluating an exec. If it weren't done this way, when the user typed
exit while a different remote connection was running a command, they
wouldn't be able to exit until the command completed.

The ServerIntents in ServerHandler did not handle
JsonRpcResponseMessage because prior to this commit, sbt clients were
primarily making requests to the server. But now the server sends
requests to the client for the terminal properties and terminal
capabilities so it was necessary to add an onResponse handler to
ServerIntent.

I had to move the network channel publishBytes method to run on a
background thread because there were scenarios in which the client
socket would get blocked because the server was trying to write on the
same thread that the read the bytes from the client.

To make the console command work, it is necessary to hijack the
classloader for JLine. In MetaBuildLoader, we put a custom forked JLine
that has a setter for the TerminalFactory singleton. This allows us to
change the terminal that is used by JLine in ConsoleReader. Without this
hack, the scala console would not work for remote clients.
2020-06-24 19:38:42 -07:00
Ethan Atkins e77906445d Refactor network client
Neither The ConsoleAppender class nor the jni based ClientSocket can be
used in a graalvm native image. This commit reworks the NetworkClient so
that we can avoid those limitations. It also adds some additional
command line argument parsing and changes the value of the run method to
return Int rather than Unit for exit code support.
2020-06-24 19:22:57 -07:00
Ethan Atkins 1b03c9b1a9 Make Terminal a trait to support multiple clients
In order to support a multi-client sbt server ux, we need to factor
`Terminal` out into a class instead of a singleton. Each terminal provides
and outputstream and inputstream. In all of the places where we were
previously relying on the `Terminal` singleton we need to update the
code to use `Terminal.get`, which will redirect io to the terminal whose
command is currently running.

This commit does not implement the server side ui for network clients.
It is just preparatory work for the multi-client ui.

The Terminal implementations have thread safe access to the output
stream. For this reason, I had to remove the sychronization on the
ConsoleOut lockObject. There were code paths that led to deadlock when
synchronizing on the lockObject.
2020-06-24 19:22:57 -07:00
Ethan Atkins fcfe4333fe Consolidate and optimize input stream json reading
We had similar code for reading json frames from an input stream in
NetworkChannel and ServerConnection. I reworked and consolidated this
logic into a shared method in ReadJsonFromInputStream.

This commit also removes the ObjectMessage reporting methods that
weren't doing anything.
2020-06-24 19:19:06 -07:00
Ethan Atkins af5afef271 Add option to skip collectAnalysis on network init
The collectAnalysis task an be a bit slow and delays client connections
from running commands. This commit adds an option to skip the analysis
if it isn't needed. The default behavior is left as it was.
2020-06-24 19:19:06 -07:00
adpi2 eac9328db7 Replace -jar with -classpath in BuildServerConnectionDetails 2020-06-08 15:36:41 +02:00
Adrien Piquerez 0789fd7be6 Use java command in BspConnectionDetails 2020-05-25 13:32:48 +02:00
Adrien Piquerez b184be860f Add headers 2020-05-25 10:43:54 +02:00
Adrien Piquerez 6bce0a7b07 update NetworkClient 2020-05-22 11:17:33 +02:00
Adrien Piquerez a31747758c Create BSP connection file at server startup 2020-05-18 09:35:14 +02:00
Adrien Piquerez c80fe525c6 add BspClient 2020-05-16 09:52:21 +02:00
Adrien Piquerez 454ee61289 separate BSP and LSP handlers + add bspWorkspace task 2020-05-16 09:52:21 +02:00
Eugene Yokota cb93d20492 build server protocol
Initial draft for bsp support.

This shows two communication pattern around BSP.
First, if the request can be handled with the build knowledge is readily available in `NetworkChannel` we can reply immediately. `BuildServerImpl#onBspBuildTargets` is an example for that.

Second, if the request requires `State`, then we can forward the parameter into a custom command, and reply back from a command. `BuildServerProtocol.bspBuildTargetSources` is an example of that since it needs to invoke tasks to generate sources.
2020-05-16 09:52:20 +02:00
Adrien Piquerez 8df754eeb1 rename publish to either respond or notify 2020-05-12 16:26:33 +02:00
Adrien Piquerez 255a0a6ea6 send response to the source channel only 2020-05-12 14:44:10 +02:00
Adrien Piquerez 781584d137 id is mandatory in json rpc responses 2020-05-11 16:51:34 +02:00
Ethan Atkins 079cf2178c Add ClearScreenAfterCursor
This communicates intent better than clearScreen(0).
2020-05-01 13:02:48 -07:00
Ethan Atkins a449b1ff2d Move JLine apis into LineReader
It is better that sbt not expose the implementation detail that
LineReader is implemented by JLine. Other terminal related apis should
be handled by sbt.internal.util.Terminal.
2020-05-01 12:35:43 -07:00
Ethan Atkins 9218d3c087 Redraw command prompt after network command
Presently if a server command comes in while in the shell, the client
output can appear on the same line as the command prompt and the command
prompt will not appear again until the user hits enter. This is a
confusing ux. For example, if I start an sbt server and type
the partial command "comp" and then start up a client and run the clean
command followed by a compile, the output looks like:

[info] sbt server started at local:///Users/ethanatkins/.sbt/1.0/server/51cfad3281b3a8a1820a/sock
sbt:scala-compile> comp[info] new client connected: network-1
[success] Total time: 0 s, completed Dec 12, 2019, 7:23:24 PM
[success] Total time: 0 s, completed Dec 12, 2019, 7:23:27 PM
[success] Total time: 2 s, completed Dec 12, 2019, 7:23:31 PM

Now, if I type "ile\n", I get:
[info] sbt server started at local:///Users/ethanatkins/.sbt/1.0/server/51cfad3281b3a8a1820a/sock
ile
[success] Total time: 0 s, completed Dec 12, 2019, 7:23:34 PM
sbt:scala-compile>

Following the same set of inputs after this change, I get:
[info] sbt server started at local:///Users/ethanatkins/.sbt/1.0/server/51cfad3281b3a8a1820a/sock
sbt:scala-compile> comp
[info] new client connected: network-1
[success] Total time: 0 s, completed Dec 12, 2019, 7:25:58 PM
sbt:scala-compile> comp
[success] Total time: 0 s, completed Dec 12, 2019, 7:26:14 PM
sbt:scala-compile> comp
[success] Total time: 1 s, completed Dec 12, 2019, 7:26:17 PM
sbt:scala-compile> compile
[success] Total time: 0 s, completed Dec 12, 2019, 7:26:19 PM
sbt:scala-compile>

To implement this change, I added the redraw() method to LineReader
which is a wrapper around ConsoleReader.drawLine; ConsoleReader.flush().
We invoke LineReader.redraw whenever the ConsoleChannel receives a
ConsolePromptEvent and there is a running thread.

To prevent log lines from being appended to the prompt line, in the
CommandExchange we print a newline character whenever a new command is
received from the network or a network client connects and we believe
that there is an active prompt.
2020-05-01 12:35:43 -07:00
Ethan Atkins 293e83ef9f Add Delete line to terminal prompt 2020-05-01 12:35:43 -07:00
Ethan Atkins 2e3a1e767d Don't poll System.in in ConsoleChannel
The ask user thread is a background thread so it's fine for it to block
on System.in. By blocking rather than polling, the cpu utilization of
sbt drops to 0 on idle. We have to explicitly handle <ctrl+d> if we
block though because the JLine console reader will return null both if
the input stream returns -1
2020-05-01 12:35:43 -07:00
Ethan Atkins 7902ec3b7d Add Terminal abstraction
This commit aims to centralize all of the terminal interactions
throughout sbt. It also seeks to hide the jline implementation details
and only expose the apis that sbt needs for interacting with the
terminal.

In general, we should be able to assume that the terminal is in
canonical (line buffered) mode with echo enabled. To switch to raw mode
or to enable/disable echo, there are apis: Terminal.withRawSystemIn and
Terminal.withEcho that take a thunk as parameter to ensure that the
terminal is reset back to the canonical mode afterwards.
2020-05-01 12:35:43 -07:00
Ethan Atkins cd65543d10 Deprecate unused ConsoleUnpromptEvent 2020-05-01 12:28:44 -07:00
Eugene Yokota 2396b449fe Contraband 0.4.6 2020-04-24 17:44:15 -04:00
Eugene Yokota 3ce4d22b84 integrate with VirtualFile changes
Ref https://github.com/sbt/zinc/pull/712
2020-04-24 17:44:14 -04:00
Ethan Atkins 3c54559236 Revert accidental debug logging commit
This was accidentally included in a formatting commit
cf745255e8.
2020-01-17 17:09:52 -08:00
Ethan Atkins cf745255e8 Apply javafmt in sbt project 2020-01-14 14:38:08 -08:00
Eugene Yokota 36a16673c0 reduce compiler warnings 2020-01-08 09:41:29 -05:00
Ethan Atkins aecdc44909 Update contraband sources
In 8bfae66b9d I upgraded contraband but
not all of the sources were regenerated because I didn't run clean
before recompiling.
2019-12-12 11:34:53 -08:00
Eugene Yokota 54b3405f42 apply -Yno-lub
To demonstrate [-Yno-lub](http://eed3si9n.com/stricter-scala-with-ynolub), this shows the code changes that removes lubing (Not all subprojects are done).

After I made the changes, I switched the Scala back to normal 2.12.10.
2019-10-13 23:46:23 -04:00
Ethan Atkins 955547e5bd Update deprecation warnings for api changes
During refactoring, these warnings got out of date. I also added
scaladoc to the watchTriggeredMessage key.

Ref: https://github.com/sbt/sbt/issues/5051.
2019-09-06 12:10:59 -07:00
xuwei-k dfe789d7c6 avoid deprecated /: and :\
use foldLeft and foldRight

https://github.com/scala/scala/blob/v2.13.0/src/library/scala/collection/IterableOnce.scala#L682-L686
2019-08-30 11:20:53 +09:00
Ethan Atkins 556a9384f3
Merge branch 'develop' into parser-fix 2019-07-29 21:57:44 -07:00
eugene yokota 5b0d0122af
Merge pull request #4906 from eatkins/turbo-resource-loader
Turbo resource loader
2019-07-29 16:21:17 -04:00
Ethan Atkins be489e05ca Clear expired loaders
Sometimes turbo mode didn't work correctly for projects where resources
were modified. This was because it was possible for the resource
classloader to inadvertently evict the dependency classloader from the
classloader cache because they had the same file stamps. There were two
fixes:
1) remove expired entries from the cache based on the
    (Parent, Classpath) pair rather than just classpath
2) do not close the classloaders during cache eviction. They may still
   be in use when we evict them so we need to wait until they are
   explicitly closed elsewhere or until the go out of scope and are
   collected by the CleanupThread

I tested this change with a spark project in which I kept modifying the
resources. Prior to this change, I could get into a state where if I
modified the resources, the dependency layer would get evicted every
time so the benefits of turbo mode were not realized.
2019-07-29 12:30:42 -07:00
Ethan Atkins c7ec97d18f Rework multi parser to exclude 'alias'
There have been numerous issues with the multi parser incorrectly
splitting commands like `alias foo = ; bar` into
`"alias foo =" :: "bar" :: Nil`. To fix this, I update the multi parser
implementation to accept a list of commands that cannot be part of a
multi command. For now, the only excluded command is "alias", but if
other issues come up, we can add more. I also thought about adding a
system property for excluding more commands but it didn't seem worth the
maintenance cost at this point.

In addition to adding a filter for the excluded commands, I also
reworked the multi parser so that I think its more clear (and should
hopefully have more predictable performance). I changed the cmdPart
parser to accept empty strings. Prior to this, the parser explicitly
handled the non-leading semicolon and leading semicolon cases
separately. With the relaxed cmdPart, we can handle both cases with a
single parser. We just have to strip any empty commands at the beginning
or end of the command list.
2019-07-28 12:35:13 -07:00