Commit Graph

364 Commits

Author SHA1 Message Date
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
Ethan Atkins 196318e619 Fix multi parser performance regression
It was reported in https://github.com/sbt/sbt/issues/4890 that cosmetic
white space could cause problems for the paser. I tracked this down to
primarily being because of the
`val semi = token(OptSpace ~> ';' ~> OptSpace)` line. This would cause
excessive backtracking. I added a test for a multi line command with a
lot of cosmetic whitespace that was adapted from #4890 except that I
made it even more taxing by running adding 100 commands instead of the
roughly 10 in the report. Before the parser changes, the test would
more or less block indefinitely. I never saw it successfully complete.
After these changes, it completes in 30-50ms (which drops to about 2-3
ms if the number of commands is dropped from 100 to 3).

I verified manually in a different project that a number of different
multi command completions still worked. In particular, I tested that
`~foo/test; foo/tes` would expand to `~foo/test; foo/test` which is one
of the hardest cases to get right.

I also added a few extra test cases for the parser since I wasn't sure
what the impact of removing the OptSpace ~> from the semi parser would
be.
2019-07-28 12:35:03 -07:00
Ethan Atkins f5c8b8aad5 Don't use exception for reloading
I completely forgot about the StateTransform class which allows a task
to modify the state through its return value.
2019-07-26 15:03:32 -07:00
Nafer Sanabria 3f3d7d47e3 Minor clarification of logging message 2019-07-19 06:01:15 -05:00
Ethan Atkins 125c4ba532 Don't validate multi commands
We tried to prevent users from doing something like running a multi
command "foo; bar" where foo is valid but bar is invalid so that we
wouldn't run foo only to discover bar was an invalid key. It isn't
possible to know in general if any command other than the first command
in a multi command is valid because it might update the state and add
the initially invalid command.

The validation caused the intellij plugin to not work with 1.3.0-RC3.
2019-07-17 19:27:07 -07:00
Ethan Atkins a93d9e77ad Relax strict commands
The recent changes to make the multi parser strict broke any multi
command, or alias, where the multi command contained a command or task
that was not yet defined, but was possibly added by reload. This was
reported as #4869. I had had to work around this issue in ScriptedTests
by running `reload` and `setUpScripted` separately instead of as a multi
command. This workaround doesn't work for aliasing boot, which has been
a recommended approach by Mark Harrah since 2011.

To fix this, I relax the strict parser. We don't require that the parser
be valid to create a multi command string. In the multiApplied state
transformation, however, we validate all of the commands up to 'reload'.
Since there is no way to validate any commands to the right of 'reload,
we optimistically allow those commands to run.

So long as there is no 'reload' in the multi commands, all of the
commands will be validated.
2019-07-16 15:17:21 -07:00
Ethan Atkins 1b0159c547 Remove case in flatMap
I didn't notice that this was triggering a warning. I think at some
point I was actually doing something in the pattern match.
2019-07-13 10:46:25 -07:00
Ethan Atkins 60b1ac7ac4 Improve multi parser performance
The multi parser had very poor performance if there were many commands.
Evaluating the expansion of something like "compile;" * 30 could cause
sbt to hang indefinitely. I believe this was due to excessive
backtracking due to the optional `(parser <~ semi.?).?` part of the
parser in the non-leading semicolon case.

I also reworked the implementation so that the multi command now has a
name. This allows us to partition the commands into multi and non-multi
commands more easily in State while still having multi in the command
list. With this change, builds and plugins can exclude the multi parser
if they wish.

Using the partitioned parsers, I removed the high/priority low priority
distinction. Instead, I made it so that the multi command will actually
check if the first command is a named command, like '~'. If it is, it
will pass the raw command argument with the named command stripped out
into the parser for the named command. If that is parseable, then we
directly apply the effect. Otherwise we prefix each multi command to the
state.
2019-06-25 13:45:09 -07:00
Ethan Atkins ff16d76ad3 Remove matchers from MultiParserSpec
We've been trying to move away from the wordy dsl and stick with bare
assertions.
2019-06-19 16:12:45 -07:00
Ethan Atkins 4c814752fb Support braces in multi command parser
We run into issues if we naively split the command input on ';' and
treat each part as a separate command unless the ';' is inside of a
string because it is also valid to have ';'s inside of braced
expressions, e.g. `set foo := { val x = 1; x + 1 }`. There was no parser
for expressions enclosed in braces. I add one that should parse any
expression wrapped in braces so long as each opening brace is matched by a
closing brace. The parser returns the original expression. This allows
the multi parser to ignore ';' inside of '{...}'.

I had to rework the scripted tests to individually run 'reload' and
'setUpScripted' because the new parser rejects setUpScripted because it
isn't a valid command until reload has run.
2019-06-19 16:12:45 -07:00
Ethan Atkins ccfc3d7bc7 Validate commands in multiparser
It was reported in https://github.com/sbt/sbt/issues/4808 that compared
to 1.2.8, sbt 1.3.0-RC2 will truncate the command args of an input task
that contains semicolons. This is actually intentional, but not
completely robust. For sbt >= 1.3.0, we are making ';' syntactically
meaningful. This means that it always represents a command separator
_unless_ it is inside of a quoted string. To enforce this, the multi parser
will effectively split the input on ';', it will then validate that each
command that it extracted is valid. If not, it throws an exception. If
the input is not a multi command, then parsing fails with a normal
failure.

I removed the multi command from the state's defined commands and reworked
State.combinedParser to explicitly first try multi parsing and fall back
to the regular combined parser if it is a regular command. If the multi
parser throws an uncaught exception, parsing fails even if the regular
parser could have successfully parsed the command. The reason is so that
we do not ever allow the user to evaluate, say 'run a;b'. Otherwise the
behavior would be inconsitent when the user runs 'compile; run a;b'
2019-06-19 16:12:45 -07:00
Ethan Atkins 27fc4e57e3 Add missing match case
There was an incomplete pattern match that assumed that the jars in the
scala provider included one with the name  "scala-library.jar". In
practice, I think this is always true, but it's safer to have a fallback
case and it also removes the compiler warning.
2019-06-11 15:52:23 -07:00
Ethan Atkins f1698d2bf2 Re-use metabuild scala instance layer
At some point I noticed that projects with no scala sources in the build
loaded significantly faster than projects that had even a single scala
file -- no matter how simple that file was. This didn't really make
sense to me because *.sbt files _do_ have to be compiled. I finally
realized that classloading was a likely bottle neck because *.sbt
files are compiled on the sbt classpath while *.scala files are compiled
with a different classloader generated by the classloader cache. It then
occurred to me that we could pre-fill the classloader cache with the
scala layer of the sbt metabuild classloader.

I found that compared to 1.3.0-M5, a project with a simple scala file in
the project directory loaded about 2 seconds faster after this change.
Even if there are no scala sources in the build.sbt, there is a similar
performance improvement for running "sbt compile", which I found exited
2-3 seconds faster after this change.
2019-06-06 21:02:24 -07:00
eugene yokota e31fd3f082
Merge pull request #4765 from eatkins/watch-docs
Watch docs
2019-06-03 22:36:46 -04:00
Ethan Atkins 70899e5cad Switch private[sbt] status of Reload objects
The Reload exception that I added in the sbt package really wasn't
intended to be public. It's only meant to be used by
checkMetaBuildSources, which the users shouldn't override. I put it in
the top package though because I wanted it to be next to FullReload. I
also am not sure why the Reload object in Watch was private[sbt], but
while writing documentation, I realized that users couldn't access it.
2019-06-03 17:35:01 -07:00
Ethan Atkins 4193cc323d Remove leading semicolon from multi command help 2019-06-03 17:35:01 -07:00
Ethan Atkins 625470cdd5 Make LayeredClassLoaders parallel capable
The docs for ClassLoader,
https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html
say that all non-hierarchical custom classloaders should be registered
as parallel capable. The docs also suggest that custom classloaders
should try to only override findClass so I reworked LayerdClassLoader to
only override findClass. I also added locking to the class loading to
make it safe for concurrent loading.

All of the custom classloaders besides LayeredClassLoader either
subclass URLClassLoader or LayeredClassLoader but don't override
loadClass. Because those two classloaders are parallel capable, the
subclasses should be as well. It isn't possible to make classloaders
that are implemented in scala parallel capable because scala 2 doesn't
support jvm static blocks (dotty does support this with an annotation).
To work around this, I re-worked some of the classloaders so that they
are either directly implemented in java or I subclassed a scala
implementation class in java.
2019-06-03 17:26:14 -07:00
Ethan Atkins 6f7a824478 Reduce idle cpu usage
I noticed that sbt 1.3.0 was using more cpu when idling (either at the
shell or while waiting for file events) than 1.2.8. This was because I'd
reduced a number of timeouts to 2 milliseconds which was causing a
thread to keep waking up every 2 milliseconds to poll a queue. I thought
that this was cheaper than it actually is and drove the cpu utilization
to O(10%) of a cpu on my mac.

To address this, I consolidated a number of queues into a single queue
in CommandExchange and Continuous. In the CommandExchange case, I
reworked CommandChannel to have a register method that passes in a Queue
of CommandChannels. Whenever it appends an exec, it adds itself to the
queue. CommandExchange can then poll that queue directly and poll the
returned CommandChannel for the actual exec. Since the main thread is
blocking on this queue, it does not need to frequently wake up and can
just poll more or less indefinitely until a message is received. This
also reduces average latency compared to older versions of sbt since
messages will be processed almost as soon as they are received.

The continuous case is slightly more complicated because we are polling
from two sources, stdin and FileEventMonitor. In my ideal world, I'd
have a reactive api for both of those sources and they would just write
events to a shared queue that we could block on. That is nontrivial to
implement, so instead I consolidated the FileEventMonitor instances into
a single FileEventMonitor. Since there is now only one FileEventMonitor
queue, we can block on that queue for 30 milliseconds and the poll
stdin. This reduces cpu utilization to O(2%) on my machine while still
having reasonably low latency for key input events (the latency of file
events should be close to zero since we are usually polling the
FileEventMonitor queue when waiting).

I actually had a TODO about the FileEventMonitor change that this
resolves.
2019-05-31 09:34:04 -07:00
Ethan Atkins dc903bb4d8 Don't check parents in ClassLoaderCacheTest
This check doesn't actually make sense anymore with the new
ClassLoaderCache. In the old ClassLoaderCache, there were separate
layers for the snapshots and regular jars.  The test was verifying that
only the snapshot layer was invalidated but now there is just one layer.
2019-05-28 11:53:13 -07:00
Ethan Atkins 7b870d647a Add missing header 2019-05-28 10:36:44 -07:00
Ethan Atkins e73b10fd89 Add comment explaining metaspace argument behavior 2019-05-28 09:53:36 -07:00
Ethan Atkins 5f94252ff8 Fix dotty plugin
The dotty sbt-bridge module assumes that it's going to get a
URLClassLoader from which it can extract all of the classpath urls. That
doesn't work with the old wrapped classloader because its classpath was
empty. As a nasty workaround, I override the getURLs method, which is
where it gets the URLs from. After this change the
compiler-project/dotty-compiler-plugin test passes.
2019-05-28 09:53:36 -07:00
Ethan Atkins 03bf539ce9 Add new ClassLoaderCache implementation
This commit adds a new ClassLoaderCache that builds on the
ClassLoaderCache that is present in zinc (and can be used to build an
instance of the zinc ClassLoaderCache to preserve compatibility). It
differs from the zinc classloader cache that it does not use direct
SoftReferences to classloaders. Instead, we create a wrapper loader
that can't load any classes and just delegates to its parent. This
allows us to add a thread that reaps the soft reference to the wrapper
loader. Crucially, we add a custom SoftReference class that has a strong
reference to the underlying classloader. This allows us to call close on
the strong reference.

The one issue with this approach is that we can't
rescue the jvm from crashing with an OOM: metaspace because the jvm
doesn't give us a chance to close and dereference the underlying
classloaders before it crashes. It WILL collect classloaders under
normal memory pressure, just not metaspace pressure. To fix this, I
check if the MaxMetaspaceSize is set via an MxBean and, if it is, we
fill the cache with regular soft references. We are going to change the
bash script to not set -XX:MaxMetaspaceSize by default so most builds
should probably end up correctly closing the classloaders after this
change. But we should break existing builds that set MaxMetaspaceSize
but don't crash.

As part of this commit, I audited all of the places where we were
instantiating ClassLoaderCache instances and instead pass in the
state's ClassLoaderCache instance. This reduces the total number of
classloaders created.
2019-05-28 09:53:35 -07:00
xuwei-k 9b1f3e542a disable "Reload" exception stack trace 2019-05-13 12:15:19 +09:00
Ethan Atkins 8f54ecd536 Check meta build sources before task evaluation
This commit finally fixes #241 by adding support for sbt to either
print a warning or automatically reload the project if the metabuild
sources have changed. To facilitate this, I introduce a new key,
metaBuildSourceOption which has three options:
1) IgnoreSourceChanges
2) WarnOnSourceChanges
3) ReloadOnSourceChanges

When the former is set, sbt will not check if the meta build sources
have changed. Otherwise, sbt will use the buildStructure / fileInputs to
get the ChangedFiles for the metabuild. If there are any changes, it
will either warn or reload the build depending on the value of
metaBuildSourceOption.

The mechanism for diffing the files is that I add a step to EvaluateTask
where, if the project has been loaded and
metaBuildSourceOption != IgnoreSourceChanges, we evaluate the needReload
task. If we need a reload, we return an error that indicates that a
Reload is necessary. When that error is detected, the MainLoop will
prepend "reload" to the pending commands for the state. Otherwise we
just print a warning and continue.

I benchmarked the overhead of this and it wasn't too bad. I generally
saw it taking 5-20ms to perform the check. Since this is only done once
per task evaluation run, I don't think it's a big deal. When
IgnoreSourceChanges is set, there is O(10us) overhead. If performance
does become a problem, we could add a global watch service and skip the
needReload evaluation if no files have been modified.

I removed the watchTrackMetaBuild key and made it so that the continuous
builds only track the meta build when
metaBuildSourceOption == ReloadOnSourceChanges
2019-05-11 22:01:49 -07:00
Ethan Atkins 2deac62b00 Bump io
The newest version of io repackages a number of classes into the
sbt.nio.* packages. It also changes some of the semantics of glob
related apis. This commit updates all of the usages of the updated apis
within sbt but should have no functional difference.
2019-05-02 14:33:01 -07:00
Ethan Atkins 20b0ef786b Undeprecate WatchSource
Since the new watch implementation has yet to be widely deployed, we
should hold off on deprecating the old keys. They could still be
deprecated in a patch release or in 1.4.0.
2019-05-02 09:41:53 -07:00
Ethan Atkins 247d242008 Improve watch messages
This commit reworks the watch start message so that instead of printing
something like:

[info] [watch] 1. Waiting for source changes... (press 'r' to re-run the command, 'x' to exit sbt or 'enter' to return to the shell)

it instead prints something like:

[info] 1. Monitoring source files for updates...
[info] Project: filesJVM
[info] Command: compile
[info] Options:
[info]   <enter>: return to the shell
[info]   'r': repeat the current command
[info]   'x': exit sbt

It will also print which path triggered the build.
2019-03-30 16:39:10 -07:00
Ethan Atkins 40d8d8876d Create Watch.scala
I decided that it makes sense to move all of the new watch code out of
the Watched companion object since the Watched trait itself is now
deprecated. I don't really like having the new code in Watched.scala
mixed with the legacy code, so I pulled it all out and moved it into the
Watch object. Since we have to put all of the logic for the Continuous
object in main in order to access the sbt.Keys object, it makes sense to
move the logic out of main-command and into command so that most of the
watch related logic is in the same subproject.
2019-03-30 16:39:10 -07:00
Ethan Atkins e868c43fcc Refactor Watched
This is a huge refactor of Watched. I produced this through multiple
rewrite iterations and it was too difficult to separate all of the
changes into small individual commits so I, unfortunately, had to make a
massive commit. In general, I have tried to document the source code
extensively both to facilitate reading this commit and to help with
future maintenance.

These changes are quite complicated because they provided a built-in
like api to a feature that is implemented like a plugin. In particular,
we have to manually do a lot of parsing as well as roll our own
task/setting evaluation because we cannot infer the watch settings at
project build time because we do not know a priori what commands the
user may watch in a given session. The dynamic setting and task
evaluation is mostly confined to the WatchSettings class in Continuous.
It feels dirty to do all of this extraction by hand, but it does seem to
work correctly with scopes.

At a high level this commit does four things:
1) migrate the watch implementation to using the InputGraph to collect
   the globs that it needs to monitor during the watch
2) simplify WatchConfig to make it easier for plugin authors to write
   their own custom watch implementations
3) allow configuration of the watch settings based on the task(s) that
   is/are being run
4) adds an InputTask implemenation of watch.

Point #1 is mostly handled by Point #3 since I had to overhaul how _all_
of the watch settings are generated. InputGraph already handles both
transitive inputs and triggers as well as legacy watchSources so not
much additional logic is needed beyond passing the correct scoped keys
into InputGraph.

Point #3 require some structural changes. The watch settings cannot in
general be defined statically because we don't know a priori what tasks
the user will try and watch. To address this, I added code that will
extract the task keys for all of the commands that we are running. I
then manually extract the relevant settings for each command. Finally, I
aggregate those settings into a single WatchConfig that can be used to
actually implement the watch. The aggregation is generally
straightforward: we run all of the callbacks for each task and choose
the next watch state based on the highest priority Action that is
returned by any of the callbacks.

Because I needed Extracted to pull out the necessary settings, I was
forced to move a lot of logic out of Watched and into a new singleton,
Continuous, that exists in the main project (Watched is in the command
project). The public footprint of Continuous is tiny. Even though I want
to make the watch feature flexible for plugin authors, the
implementation and api remain a moving target so I do not want to be
limited by future binary compatibility requirements. Anyone who wants to
live dangerously can access the private[sbt] apis via reflection or by
adding custom code to the sbt package in their plugin (a technique I've
used in CloseWatch).

Point #2 is addressed by removing the count and lastStatus from the
WatchConfig callbacks. While these parameters can be useful, they are
not necessary to implement the semantics of a watch. Moreover, a status
boolean isn't really that useful and the sbt task engine makes it very
difficult to actually extract the previous result of the tasks that were
run. After this refactor, WatchConfig has a simpler api. There are fewer
callbacks to implement and the signatures are simpler. To preserve the
_functionality_ of making the count accessible to the user specifiable
callbacks, I still provided settings like watchOnInputEvent that accept
a count parameter, but the count is actually tracked externally to
Watched.watch and incremented every time the task is run.

Moreover, there are a few parameters of the watch: the logger and
transitive globs, that cannot be provided via settings. I provide
callback settings like watchOnStart that mirror the WatchConfig
callbacks except that they return a function from Continuous.Arguments
to the needed callback. The Continuous.aggregate function will check if
the watchOnStart setting is set and if it is, will pass in the needed
arguments. Otherwise it will use the default watchOnStart implementation
which simulates the existing behavior by tracking the iteration count in
an AtomicInteger and passing the current count into the user provided
callback. In this way, we are able to provide a number of apis to the
watch process while preserving the default behavior.

To implement #4, I had to change the label of the `watch` attribute key
from "watch" to "watched". This allows `watch compile` to work at the
sbt command line even thought it maps to the watchTasks key. The actual
implementation is almost trivial. The difference between an
InputTask[Unit] and a command is very small. The tricky part is that the
actual implementation requires applying mapTask to a delegate task that
overrides the Task's info.postTransform value (which is used to
transform the state after task evaluation). The actual postTransform
function can be shared by the continuous task and continuous command.
There is just a slightly different mechanism for getting to the state
transformation function.
2019-03-30 16:38:56 -07:00
Eugene Yokota db45b456ef switch to official sbt-scalafmt 2019-03-22 17:47:48 -04:00
Ethan Atkins f26afe6681 Return (Path, FileAttributes) instead of Stamped.File
I realized that Stamped.File was a bad interface that was really just an
implementation detail of external hooks. I updated the
GlobLister.{ all, unique } methods to return Seq[(Path, FileAttributes)]
rather than Stamped.File which is a much more natural api and one I
could see surviving the switch to nio based apis planned for
1.4.0/2.0.0. I also added a simple scripted test for glob listing. The
GlobLister.all method is implicitly tested all over the place since the
compile task uses it, but it's good to have an explicit test.
2019-03-22 09:32:36 -07:00
Ethan Atkins d231d7d9ec Rename FileCacheEntry to FileAttributes
I decided that FileCacheEntry was a bad name because the methods did not
necessarily have anything to do with caching. Moreover, because it is
exposed in a public interface, it shouldn't be in the internal package.
2019-03-22 09:32:36 -07:00
Ethan Atkins be94b25d68 Add Event trait to FileCacheEntry
Rather than exposing the FileEventMonitor.Event types, which are under
active development in the io repo, I am adding a new event trait to
FileCacheEntry. This trait doesn't expose any internal implementation
details.
2019-03-22 09:32:36 -07:00
Ethan Atkins 86200345e1 Don't expose TypedPath to users
I've decided I don't like the TypedPath interface so I'm not going to
expose it publicly.
2019-03-22 09:32:36 -07:00
Ethan Atkins 6a5f0f2af2 Make Stamped.file private[sbt] 2019-03-22 09:32:36 -07:00
Ethan Atkins fac6e0d9a0 Make file repository apis private[sbt]
I haven't fully settled on these interfaces yet so they shouldn't be publicly
exposed in the sbt api.
2019-03-22 09:32:36 -07:00
Ethan Atkins 1489879b80 Fix equals for FileCacheEntry
The equals method didn't work exactly the way I thought. By delegating
to the equivStamp object in sbt we can be more confident that it is
actually comparing the stamp values and not object references or
some other equals implementation.
2019-03-22 09:32:35 -07:00
Ethan Atkins 16afe41cc1 Don't try to stamp files that don't exist
This was causing slowdowns in windows.
2019-03-22 09:32:35 -07:00
Ethan Atkins 571b179574 Add dsl for collecting globs
Right now, the sbt.internal.io.Source is something of a second class
citizen within sbt. Since sbt 0.13, there have been extension classes
defined that can convert a file to a PathFinder but no analog has been
introduced for sbt.internal.io.Source.

Given that sbt.internal.io.Source was not really intended to be part of
the public api (just look at its package), I think it makes sense to
just replace it with Glob. In this commit, I add extension
methods to Glob and Seq[Glob] that make it possible to easily
retrieve all of the files for a particular Glob within a task. The
upshot is that where previously, we'd have had to write something like:

watchSources += Source(baseDirectory.value / "src" / "main" / "proto", "*.proto", NothingFilter)

now we can write

watchGlobs += baseDirectory.value / "src" / "main" / "proto" * "*.proto"

Moreover, within a task, we can now do something like:
foo := {
  val allWatchGlobs: Seq[File] = watchGlobs.value.all
  println(allWatchSources.mkString("all watch source files:\n", "\n", ""))
}
Before we would have had to manually retrieve the files.

The implementation of the dsl uses the new GlobExtractor class which
proxies file look ups through a FileTree.Repository. This makes it so
that, by default, all file i/o using Sources will use the default
FileTree.Repository. The default is a macro that returns
`sbt.Keys.fileTreeRepository.value: @sbtUnchecked`. By doing it this
way, the default repository can only be used within a task definition
(since it delegates to `fileTreeRepository.value`). It does not,
however, prevent the user from explicitly providing a
FileTree.Repository instance which the user is free to instantiate
however they wish.

Bonus: optimize imports in Def.scala and Defaults.scala
2019-03-22 07:53:41 -07:00
Ethan Atkins d0310cc866 Rework FileTreeRepository configuration
The FileTreeViewConfig abstraction that I added was somewhat unwieldy
and confusing. The original intention was to provide users with a lot of
flexibility in configuring the global file tree repository used by sbt.
I don't think that flexibility is necessary and it was both conceptually
complicated and made the implementation complex. In this commit, I add a
new boolean flag enableGlobalCachingFileTreeRepository that toggles
which kind of FileTreeRepository to use globally.

There are actually three kinds of repositories that could be returned:
1) FileTreeRepository.default -- this caches the entire file system
   tree it hooks into the cache's event callbacks to create a file event
   monitor. It will be used if enableGlobalCachingFileTreeRepository is
   true and Global / pollingGlobs := Nil
2) FileTreeRepository.hybrid -- similar to FileTreeRepository.default
   except that it will not cache any files that are included in
   Global / pollingGlobs. It will be used if
   enableGlobalCachingFileTreeRepository is true and
   Global / pollingGlobs is non empty
3) FileTreeRepository.legacy -- does not cache any of the file system
   tree, but does maintain a persistent file monitoring process that is
   implemented with a WatchServiceBackedObservable. Because it doesn't
   poll, in general, it's ok to leave the monitoring on in the
   background. One reason to use this is that if there are any issues
   with the cache being unable to accurately mirror the underlying file
   system tree, this repository will always poll the file system
   whenever sbt requests the entries for a given glob. Moreover, the
   file system tree implementation is very similar to the implementation
   that was used in 1.2.x so this gives users a way to almost fully opt
   back in to the old behavior.
2019-03-22 07:53:41 -07:00
Ethan Atkins f7f7addff7 Bump io
This new version of io breaks source and binary compatibility everywhere
that uses the register(path: Path, depth: Int) method that is defined on
a few interfaces because I changed the signature to register(glob:
Glob). I had to convert to using a glob everywhere that register was
called.

I also noticed a number of places where we were calling .asFile on a
file. This is redundant because asFile is an extension method on File
that just returns the underlying file.

Finally, I share the IOSyntax trait from io in AllSyntax. There was more
or less a TODO suggesting this change. The one hairy part is the
existence of the Alternative class. This class has unfortunately somehow
made it into the sbt package object. While I doubt many plugins are
using this, it doesn't seem worth breaking binary compatibility to get
rid of it. The issue is that while Alternative is defined private[sbt],
the alternative method in IOSyntax is public, so I can't get rid of
Alternative without breaking binary compatibility.

I'm not deprecating Alternative for now because the sbtProj still has
xfatal warnings on. I think in many, if not most, cases, the Alternative
class makes the code more confusing as is often the case with custom
operators. The confusion is mitigated if the abstraction is used only in
the file in which it's defined.
2019-03-22 07:53:41 -07:00
Ethan Atkins e8af828c73 Add FileCacheEntry
Previously, we were leaking the internal details of incremental
compilation to users by defining FileTree(DataView|Repository)[Stamp].
To avoid this, I introduce the new class FileCacheEntry that is quite
similar to Stamp except defined using scala Options rather than java
Optionals. The implementation class just delegates to an actual Stamp
and I provided a private[sbt] ops class that adds a
method `stamp` to FileCacheEntry. This will usually just extract the
stamp from the implementation class. This allows us to use
FileCacheEntry almost interchangeably with Stamp while still avoiding
exposing users to Stamp.
2019-02-02 16:03:59 -08:00
Ethan Atkins ba0494df14 Stop Stamped from inheriting File and TypedPath
In the FileTreeDataView use case, we were previously working with
FileTreeDataView[Stamped], which actually contained a lot of redundant
information because FileTreeDataView.Entry[_] has a toTypedPath method
that could be used to read the path related fields in Stamped. Instead,
we can just return the Stamp itself in FileTreeDataView.list* methods
and convert to Stamped.File where needed (i.e. in ExternalHooks).

Also move BasicKeys.globalFileTreeView to Keys since it isn't actually
used in the main-command project.
2019-02-02 12:22:57 -08:00
Ethan Atkins 0bdc30b60b Rename StampedFile to Stamped 2019-02-02 12:22:57 -08:00
Ethan Atkins d39bb96c41 Move StampedFile into its own file 2019-02-02 12:22:57 -08:00
Ethan Atkins 602554a411 Add scaladoc for StampedFile 2019-02-02 12:22:57 -08:00
Ethan Atkins 5fc5846737 Add TaskRepository to manage ClassLoaderCache
We want the user to be able to invalidate the classloader cache in the
event that it somehow gets in a bad state. The cache is, however,
defined in multiple configurations, so there are in fact many
ClassLoaderCache instances that are managed by sbt. To make this sane, I
add a global cache that is keyed by a TaskKey[_] and can return
arbitrary data back. Invalidating all of the ClassLoaderCache instances
is then as straightforward as just replacing the TaskRepository
instance.

I also went ahead and unified the management of the global file tree
repository. Instead of having to specifically clear the file tree
repository or the classloader cache, the user can now invalidate both
with the new clearCaches command.
2019-01-30 08:55:22 -08:00
Ethan Atkins 161dfd77f2 Update TypedPath apis
I renamed getPath to toPath in io so we need to update sbt to using the
correct api.
2019-01-16 13:22:56 -08:00
Ethan Atkins 2b831e5988 Add LabeledFunctions to repo
I noticed that debugging settings that return functions is annoying
because often the setting is initialized as an anonymous function with a
useless toString method. To improve the debugging for users, I'm adding
a number of wrapper classes for functions that override the default
toString with a provided label.

I then used these functions to label all of the anonymous functions in
Watched.scala.
2019-01-08 09:45:06 -08:00
Ethan Atkins 9634a872cd Allow trailing semicolon in multi commands
It was a mistake to disallow trailing semicolons for multi commands.
Firstly this was a mistake because previous versions of sbt supported a
trailing semi colon. It was also inconsistent with how commands work in
a regular shell (e.g. bash or zsh).
2018-12-27 13:22:45 -08:00
Ethan Atkins 80eb76da36 Annotate type signature of public methods and fields
This file was littered with intellij warnings due to public members and
fields not having their types annotated. Although in this case it didn't
really matter, it is good practice to always annotate public methods and
fields so that they can evolve in a binary compatible way.
2018-12-03 12:57:28 -08:00
Ethan Atkins c00cc37953 Do not require leading semicolon for multi command
It has long been a frustration of mine that it is necessary to prepend
multiple commands with a ';'. In this commit, I relax that restriction.
I had to reorder the command definitions so that multi comes before act.
This was because if the multi command did not have a leading semicolon,
then it would be handled by the action parser before the multi command
parser had a shot at it. Sadness ensued.
2018-11-19 10:42:51 -08:00
Ethan Atkins 51d986d751 Make multi command parser work with string literals
Presently the multi command parser doesn't work correctly if one of the
commands includes a string literal. For example, suppose that there is
an input task defined name "bash" that shells out and runs the input.
Then the following does not work with the current multi command parser:
; bash "rm target/classes/Foo.class; touch src/main/scala/Foo.scala"; comple
Note that this is a real use case that has caused me issues in the past.

The problem is that the semicolon inside of the quote gets interpreted
as a command separator token. To fix this, I rework the parser so that
it consumes string literals and doesn't modify them. By using
StringEscapable, I allow the string to contain quotation marks itself.

I couldn't write a scripted test for this because in a command like
`; foo "bar"; baz`, the quotes around bar seem to get stripped. This
could be fixed by adding an alternative to StringEscapable that matches
an escaped string, but that is more work than I'm willing to do right
now.
2018-11-19 10:38:22 -08:00
Ethan Atkins 05e3a8609b Fix watch command parser
I discovered that when I ran multi-commands with '~' that if there was a
space between the ';' and the command, then the parsing of the command
would fail and the watch would abort. To fix this, I refactor
Watched.watch to use the multi command parser and, if that parser fails,
we fallback on a single command.
2018-11-19 10:38:22 -08:00
Ethan Atkins 4281972f1a Refactor multi parser
Prior to this commit, there was no unit testing of the parser for
multiple commands. I wanted to make some improvements to the parser, so
I reworked the implementation to be testable. This change also allows
the multiParserImpl method to be shared with Watched.watch, which I will
also update in a subsequent commit.

There also were no explicit scripted tests for multiple commands, so I
added one that I will augment in later commits.
2018-11-19 10:38:22 -08:00
Andrea Peruffo a2607f1da6
Merge branch 'develop' into refactorCancellations 2018-10-11 18:57:57 +01:00
eugene yokota e759d17b2e
Merge pull request #4397 from andreaTP/lspCompletions
[sbt-server] LSP completions support
2018-10-11 12:49:23 -04:00
andrea 02b19752eb refactoring of server cancellation request 2018-10-11 14:03:41 +01:00
andrea 34e0fc159c [sbt-server] LSP completions support 2018-10-11 13:34:40 +01:00
Ethan Atkins f579b89577 Fix windows reload loop
On windows* it was possible to get into a loop where the build would
continually restart because for some reason the build.sbt file would get
touched during test (I did not see this behavior on osx). Thankfully,
the repository keeps track of the file hash and when we detect that the
build file has been updated, we check the file hash to see if it
actually changed.

Note that had this bug shipped, it would have been fixable by overriding
the watchOnEvent task in user builds.

The loop would occur if I ran ~filesJVM/test in
https://github.com/swoval/swoval. It would not occur if I ran
test:compile, so the fact that the build file is being touched seems
to be related to the test run itself.
2018-10-10 20:16:29 -07:00
Ethan Atkins 2cfbfcc842 Disable re-run feature on windows
For whatever reason, I couldn't get jline to work on windows, so I'm
disabling the re-run with 'r' feature. This can almost surely be fixed,
but the way I was invoking jline was blocking the continuous build from
exiting when the user pressed enter.
2018-10-10 18:35:30 -07:00
Ethan Atkins a1580bafbf Improve error message
Previously, the invalid commands would be wrapped in 'Left($CMD)'.
2018-10-10 18:35:30 -07:00
Ethan Atkins dc4f705500 Add support to rebuild a '~' task by pressing 'r'
Sometimes a user may want to rerun their task even if the source files
haven't changed. Presently this is a little annoying because you have to
hit enter to stop the build and then up arrow or <ctrl+r> plus enter to
rebuild. It's more convenient to just be able to press the 'r' key to
re-run the task.

To implement this, I had to make the watch task set up a jline terminal
so that System.in would be character buffered instead of line buffered.
Furthermore, I took advantage of the NonBlockingInputStream
implementation provided by jline to wrap System.in. This was necessary
because even with the jline terminal, System.in.available doesn't return
> 0 until a newline character is entered. Instead, the
NonBlockingInputStream does provide a peek api with a timeout that will
return the next unread key off of System.in if there is one available.
This can be use to proxy available in the WrappedNonBlockingInputStream.

To ensure maximum user flexibility, I also update the watchHandleInput Key to
take an InputStream and return an Action. This setting will now receive
the wrapped System.in, which will allow the user to create their own
keybindings for watch actions without needing to use jline themselves.

Future work might make it more straightforward to go back to a line
buffered input if that is what the user desires.
2018-10-09 12:09:42 -07:00
Ethan Atkins b155ffb77b Add support for polling some directories
It is not always possible to monitor a directory using OS file system
events. For example, inotify does not work with nfs. To work around
this, I add support for a hybrid FileTreeViewConfig that caches a
portion of the file system and monitors it with os file system
notification, but that polls a subset of the directories. When we query
the view using list or listEntries, we will actually query the file
system for the polling directories while we will read from the cache for
the remainder. When we are not in a continuous build (~ *), there is no
polling of the pollingDirectories but the cache will continue to update
the regular directories in the background. When we are in a continuous
build, we use a PollingWatchService to poll the pollingDirectories and
continue to use the regular repository callbacks for the other
directories.

I suspect that #4179 may be resolved by adding the directories for which
monitoring is not working to the pollingDirectories task.
2018-10-09 12:09:42 -07:00
Ethan Atkins 2b2b84f589 Use FileTreeDataView to collect files
Now that we have the fileTreeView task, we can generalized the process
of collecting files from the view (which may or may not actually cache
the underlying file tree). I moved the implementation of collectFiles
and addBaseSources into the new FileManagement object because Defaults
is already too large of a file. When we query the view, we also need to
register the directory we're listing because if the underlying view is a
cache, we must call register before any entries will be available.
Because FileTreeDataView doesn't have a register method, I implement
registration with a simple implicit class that pattern matches on the
underlying type and only calls register if it is actually a
FileRepository.

A side effect of this change is that the underlying files returned by
collectFiles and appendBaseSources are StampedFile instances. This is so
that in a subsequent commit, I can add a Zinc external hook that will
read these stamps from the files in the source input array rather than
compute the stamp on the fly. This leads to a substantial reduction in
Zinc startup time for projects with many source files. The file filters
also may be applied more quickly because the isDirectory property (which
we check for all source files) is read from a cached value rather than
requiring a stat.

I had to update a few of the scripted tests to use the `1.2.0`
FileTreeViewConfig because those tests would copy a file and then
immediately re-compile. The latency of cache invalidation is O(1-10ms),
but not instantaneous so it's necessary to either use a non-caching
FileTreeView or add a sleep between updates and compilation. I chose the
former.
2018-10-09 12:09:42 -07:00
Ethan Atkins d31fae59f7 Add global file repository task
Every time that the compile task is run, there are potentially a large
number of iops that must occur in order for sbt to generate the source
file list as well as for zinc to check which files have changed since
the last build. This can lead to a noticeable delay between when a build
is started (either manually or by triggered execution) and when
compilation actually begins. To reduce this latency, I am adding a
global view of the file system that will be stored in
BasicKeys.globalFileTreeView.

To make this work, I introduce the StampedFile trait, which augments the
java.io.File class with a stamp method that returns the zinc stamp for
the file. For source files, this will be a hash of the file, while for
binaries, it is just the last modified time. In order to gain access to
the sbt.internal.inc.Stamper class, I had to append addSbtZinc to the
commandProj configurations.

This view may or may not use an in-memory cache of the file system tree
to return the results. Because there is always the risk of the cache
getting out of sync with the actual file system, I both make it optional
to use a cache and provide a mechanism for flushing the cache. Moreover,
the in-memory cache implementation in sbt.io, which is backed by a
swoval FileTreeRepository, has the property that touching a monitored
directory invalidates the entire directory within the cache, so the
flush command isn't even strictly needed in general.

Because caching is optional, the global is of a FileTreeDataView, which
doesn't specify a caching strategy. Subsequent commits will make use of
this to potentially speed up incremental compilation by caching the
Stamps of the source files so that zinc does not need to compute the
hashes itself and will allow for continuous builds to use the cache to
monitor events instead of creating a new, standalone FileEventMonitor.
2018-10-09 12:09:42 -07:00
Ethan Atkins 4347d21248 Add support for user defined Actions
It may be useful for users to be able to return their own custom
Action types in the Config callbacks. For a contrived example, a user
could add a jar file in the .ivy2 directory to the watch sources and
trigger a reboot full when that jar changes.
2018-10-09 12:09:42 -07:00
Ethan Atkins 28fd4a1e61 Add the ability to halt watch on failure
There may be instances where the user may wish to stop the watch if an
error occurs running the task. To facilitate this, I add boolean
parameter, lastStatus, to watchShouldTerminate. The value is computed by
modifying the state used to run the task to have a custom onFailure
command. If the task fails, the returned state will have the onFailure
command will be enqueued at the head of the remaining commands. The
result of the task then becomes true if the custom onFailure is not
present in the remaining commands and false if it is. We don't actually
run this command, so it's just implemented with the identity function.

I also updated Watched.watch to return an Action instead of Unit. This
enables us to return a failed state if Watched.watch returns
HandleError.
2018-10-09 12:09:41 -07:00
Ethan Atkins 7d3d3c71d6 Refactor Watched
This commit reworks Watched to be more testable and extensible. It also
adds some small features. The previous implementation presented a number
of challenges:

1) It relied on external side effects to terminate the watch, which was
   difficult to test
2) It exposed irrelevant implementation details to the user in the
   methods that exposed the WatchState as a parameter.
3) It spun up two worker threads. One was to monitor System.in for user
   input. The other was to poll the watch service for events and write
   them to a queue. The user input thread actually broke '~console'
   because nearly every console session will hit the <enter> key, which
   would eventually cause the watch to stop when the user exited the
   console.

To address (1), I add the shouldTerminate method to WatchConfig. This
takes the current watch iteration is input and if the function returns
true, the watch will stop.

To address (2), I replace the triggeredMessage and watchingMessage keys
with watchTriggeredMessage and watchStartMessage. The latter two keys
are functions that do not take the WatchState as parameters. Both
functions take the current iteration count as a parameter and the
watchTriggeredMessage also has a parameter for the path that triggered
the build.

To address (3), I stop using the sbt.internal.io.EventMonitor and
instead use the sbt.io.FileEventMonitor. The latter class is similar to
the former except that it's polling method accepts a duration, which may
be finite or infinite) and returns all of the events that occurred since
it was last polled. By adding the ability to poll for a finite amount of
time, we can interleave polling for events with polling System.in for
user input, all on the main thread. This eliminates the two extraneous
threads and fixes the '~console' use case I described before.

I also let the user configure the function that reads from System.in via
the watchHandleInput method. In fact, this method need not read from
System.in at all since it's just () => Watched.Action. The reason that
it isn't () => Boolean is that I'd like to leave open the option for the
ability to trigger a build via user input, not just terminating the
watch. My initial idea was to add the ability to type 'r' to re-build in
addition to <enter> to exit. This doesn't work without integrating
jline though because the input is buffered. Regardless, for testing
purposes, it gives us the ability to add a timeout to the watch by
making handleInput return true when a deadline expires.

The tests are a bit wonky because I still need to rely on side effects
in the logging methods to orchestrate the sequence of file events that
I'd like to test. While I could move some of this logic into a
background thread, there still needs to be coordination between the
state of the watch and the background thread. I think it's easier to
reason about when all of the work occurs on the same thread, even if it
makes these user provided functions impure.

I deprecated all of the previous watch related keys that are no longer
used with the new infrastructure. To avoid breaking existing builds, I
make the watchConfig task use the deprecated logging methods if they are
defined in the user's builds, but sbt will not longer set the default
values. For the vast majority of users, it should be straightforward to
migrate their builds to use the new keys.  My hunch is that the of the
deprecated keys, only triggeredMessage is widely used (in conjunction
with the clear screen method) and it is dead simple to replace it with
watchTriggeredMessage.

Note: The FileTreeViewConfig class is not really necessary for this commit.
It will become more important in a subsequent commit which introduces an
optional global file system cache.
2018-10-08 22:00:50 -07:00
Ethan Atkins 7764dc42ae Move executeContinuously into LegacyWatched
This helps keep Watched.scala more manageable until we can potentially
remove this code.
2018-10-08 22:00:50 -07:00
Ethan Atkins 28aa1de32a Refactor continuous execution
This commit makes watch event logging work in the '~' command. The
previous design of the command made this difficult, so there is a
significant re-design of the implementation of '~'. I believe that this
redesign will allow the feature to be maintained and improved more
easily moving forward. With the redesign, it is now possible to test the
business logic of the watch command (and I add a rudimentary test that I
will build upon in subsequent commits).

A bonus of this redesign is that now if the user tries to watch an
invalid command, the watch will immediately terminate with an error
rather than get stuck waiting for events when the task can never
possibly succeed.

The previous implementation of the '~' command makes it difficult
to dynamically control the implementation arguments because it is
implemented in the command project which makes it unable to depend on
any task keys that are defined in the build. It works around this by
putting all of it's configuration in the Watched attribute which is
stored globally. This would not have been necessary if the function had
been defined in the main project where it could just extract the value
of the watched task rather than relying on the global attribute value.
Moreover, because it cannot depend on tasks, it makes it nigh impossible
to use the logging framework within the '~' command.

Another issue with the previous implementation is that it's somewhat
difficult to reason about. The executeContinuously has effectively two
entry points: one for the first time the command is run and one for each
subsequent invocation when a new build is triggered. The successive
invocations are triggered by prepending commands to run to the previous
state. This is made recursive by prepending the initial command (that
was prefixed with '~'. Which branch we're in is determined by checking
for the existence of a temporary attribute, that we must ensure that we
remove when the build is stopped. This makes a lot of behavior non-local and
difficult for an outsider who is less familiar with sbt to understand.

Broadly, this refactor does two things:
1) Move the definition of continuous from BasicCommands to BuiltInCommands
2) Re-work the implementation to be executed in code rather than using
   the sbt dsl.

The first part is simple. We just add an implementation of continuous to
BuiltInCommands and remove it from the list of BasicCommands. We need to
leave in the legacy implementation for binary compatibility. I also
moved all of the actual implementation logic into Watched, which makes
maintenance easier since most of the logic is in one place.

The second part is more complicated. Rather than rely on the sbt dsl
(e.g. `(ClearOnFailure :: next :: FailureWall :: repeat :: s)`) to
parse and run the command. We manually parse the command and generate a
task of type `() => State`. We don't actually need to do anything with
the generated state because we're going to return the original state at
the end of the command no matter what. With this task, we can then
create a tail recursive function that repeatedly executes the task until
the watch is terminated.

The parsing is handled in the Watch.command method (which is where I
moved the refactored BasicCommands.continuous implementation). The
actual task running and monitoring is handled in Watched.watch. This
method has no reference to the sbt state, which makes it testable. It sets
up an event monitor and then delegates the recursive monitoring to a
small nested function, Watched.watch.impl. One nice thing about this
approach is that it is very easy to reason about the life cycle of the
EventMonitor. The recursive call is within a try { } finally { } where
the monitor and stdin are guaranteed to be cleared at the end.

Adding support for a custom (and default) watch logger is trivial with
the new infrastructure and is done via the watchLogger TaskKey.

There was a small reporting race condition that was introduced by the
change to (2). Because the new implementation is able to bypass command
parsing for triggered builds, the watch message would usually end up
being printed before the task outcome was fully logged. To work around
this, I made the watch and triggered messages be logged rather than
printed directly to stdout. As a result, the only user visible result of
this change should be that instead of seeing:
"1. Waiting for source changes in project foo... (press enter to interrupt)",
users will now see:
"[info] 1. Waiting for source changes in project foo... (press enter to interrupt)".
2018-10-08 22:00:50 -07:00
Ethan Atkins da54e2fbd3 Deprecate unused method 2018-10-08 22:00:50 -07:00
Ethan Atkins 3e62b983a2 Remove intellij warnings 2018-10-08 22:00:50 -07:00
eugene yokota a943543291
Merge pull request #4334 from eatkins/state-ops
Add an implicit class for StateOps
2018-09-16 02:40:12 -04:00
Eugene Yokota 4ff4f6e45e Update header 2018-09-14 04:53:36 -04:00
Ethan Atkins 88aa60ea49 Add an implicit class for StateOps
The State file in intellij was littered with red squiggly lines wherever
the extension methods of State called a different extension method of
State.  These went away when I switched to an implicit class, which is
the preferred way of adding extension methods since scala 2.10. As a
bonus, I was able to switch the implicit class to be a value class, so
it should not actually make a new object in most use cases.

I had to re-implement the stateOps method to delegate to the implicit
class for binary compatibility.
2018-08-28 15:45:54 -07:00
eugene yokota cf31a11b69
Merge branch '1.x' into help-sbt-new 2018-06-27 22:15:02 -04:00
eugene yokota 773d35dadd
Merge pull request #4231 from steinybot/fix/3432
Add warning for unknown configurations
2018-06-27 20:59:43 -04:00
Jason Pickens c9aa0c5285 Add warning for unknown project configurations. 2018-06-27 18:25:10 +12:00
Eugene Yokota f3038167a5 Fork server if it's not running
Fixes https://github.com/sbt/sbt/issues/3508

This forks an instance of sbt in the background when it's not running already.

```
$ time sbt -client compile
Getting org.scala-sbt sbt 1.2.0-SNAPSHOT  (this may take some time)...
:: retrieving :: org.scala-sbt#boot-app
	confs: [default]
	79 artifacts copied, 0 already retrieved (28214kB/130ms)
[info] entering *experimental* thin client - BEEP WHIRR
[info] server was not detected. starting an instance
[info] waiting for the server...
[info] waiting for the server...
[info] server found
> compile
[success] completed
sbt -client compile  9.25s user 2.39s system 33% cpu 34.893 total
$ time sbt -client compile
[info] entering *experimental* thin client - BEEP WHIRR
> compile
[success] completed
sbt -client compile  3.55s user 1.68s system 107% cpu 4.889 total
```
2018-06-25 22:37:22 -04:00
Eugene Yokota 3eb76125e0 implement batch mode 2018-06-25 22:26:13 -04:00
Eugene Yokota 5c202a564b skip debug log 2018-06-25 22:26:13 -04:00
Eugene Yokota 1a1f530985 implement -client option 2018-06-25 22:26:13 -04:00
Eugene Yokota 18c6b04b47 Fix thin client to use LSP
Fixes https://github.com/sbt/sbt/issues/2798
2018-06-25 22:26:13 -04:00
Eugene Yokota 932f911483 addPluginSbtFile command
Fixes https://github.com/sbt/sbt/issues/1502

This adds `--addPluginSbtFile=<file>` command, which adds the given .sbt file to the plugin build.
Using this mechanism editors or IDEs can start a build with required plugin.

```
$ cat /tmp/extra.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")

$ sbt --addPluginSbtFile=/tmp/extra.sbt
...
sbt:helloworld> plugins
In file:/xxxx/hellotest/
  ...
  sbtassembly.AssemblyPlugin: enabled in root
```
2018-06-18 01:50:36 -04:00
Eugene Yokota 86427c7ce7 Merge branch '1.1.x' into wip/merge-1.1.x 2018-06-12 23:33:47 -04:00
Holden Karau 6214408783 Add support for --error. 2018-05-15 05:31:58 -07:00
Ethan Atkins d3ac5274b3 Revert back to non-blocking watch termination condition
A thread blocking on System.in.read() cannot be interrupted, so check
System.in.available before blocking. This is how it used to work. It
requires https://github.com/sbt/io/pull/149 or else a cpu will be
pegged by the EventMonitor user input thread spinning on
System.in.available.
2018-05-08 13:26:58 -07:00
Eugene Yokota d8fe09f007 Adjust to upstream change 2018-05-05 05:02:53 -04:00
Eugene Yokota 28e90ea09b Merge branch '1.1.x' into wip/merge-1.1.x 2018-04-29 14:31:30 -04:00
Dale Wijnand 3a4bc8dc0b
Dropped deprecated "compat" commands & strings
Fixes #4089
2018-04-26 14:04:05 +01:00
Eugene Yokota d90f273420 Remove deprecated commands -, --, and ---
86ae3c8c59 deprecated -, --, and ---.
This removes the deprecated commands.
2018-04-26 05:02:21 -04:00
Dale Wijnand 8f4b8abb7b
Run scalafmt & test:scalafmt 2018-04-24 16:12:10 +01:00
Ethan Atkins 754385125a Use new EventMonitor in executeContinuously
In https://github.com/sbt/io/pull/142, I add a new api for watching for
source file events. This commit updates sbt to use the new EventMonitor
based api. The EventMonitor has an anti-entropy parameter, so that
multiple events on the same file in a short window of time do not
trigger a build. I add a key to tune it.

The implementation of executeContinuously is pretty similar. The main
changes are that shouldTerminate now blocks (EventMonitor spins up a
thread to check the termination condition) and that the
EventMonitor.watch method only returns a Boolean. This is because
the event monitor contains mutable state. It does, however, have a
state() method that returns an immutable snapshot of the state.
2018-04-23 10:02:29 -07:00
Eugene Yokota 707bf08c4e Add new closewatch mode 2018-04-05 20:29:26 -04:00
Ethan Atkins fff32db7ce Use MacOSXWatchService instead of PollingWatchService
This watch service should be more responsive and significantly reduce
the disk overhead of the polling based service for large repos.
2018-03-28 17:11:30 -07:00
Dale Wijnand 289077a405
Re-introduce Command.process
This was an unnecessary removal in
e83564a6b7.
2018-03-28 16:37:07 +01:00
Eugene Yokota c2837c7714 Merge branch 'wip/bumpsbt' into wip/merge-1.1.x 2018-03-27 10:16:10 -04:00
Dale Wijnand 00ce32f102
Cleanup CommandChannel 2018-03-20 09:17:55 +00:00
Dale Wijnand 9c0ac90ee9
Make Watched use State#handleError 2018-03-19 15:21:15 +00:00
eugene yokota 4efce7e877
Merge pull request #3991 from dwijnand/ExecStatusEvent-exitCode
Add an optional exitCode to ExecStatusEvent so clients can use it
2018-03-16 00:27:17 +09:00
Eugene Yokota 0433440c59 move ServerHandler to internal per review 2018-03-13 23:42:40 +09:00
Eugene Yokota f13465246c include the full body in debug message 2018-03-13 23:02:45 +09:00
Eugene Yokota cd9f0d2711 make sbt server extensible
Fixes #3890

Here's an example:

```scala
    Global / serverHandlers += ServerHandler({ callback =>
      import callback._
      import sjsonnew.BasicJsonProtocol._
      import sbt.internal.protocol.JsonRpcRequestMessage
      ServerIntent(
        {
          case r: JsonRpcRequestMessage if r.method == "lunar/helo" =>
            jsonRpcNotify("lunar/oleh", "")
            ()
        },
        PartialFunction.empty
      )
```
2018-03-13 23:02:45 +09:00
Dale Wijnand dd4de14593
Upgrade to contraband 0.4.0 2018-03-12 15:39:07 +00:00
Ethan Atkins 5df1d8e23f Cache watch service
I noticed that my custom WatchService was never cleaned up by sbt and
realized that after every build we were making a new WatchService. At
the same time, we were reusing the WatchState from the previous run,
which was using the original WatchService. This was particularly
problematic because it prevented us from registering any paths with the
new watch service. This may have prevented some of the file updates
from being seen by the watch service. Moreover, because we lost the
reference to the original WatchService, there was no way to clean it up,
which was a resource leak.

May be related to #3775, #3695
2018-03-09 06:11:52 -05:00
Dale Wijnand ed5a8c118b
Upgrade to contraband 0.3.3 2018-03-08 12:49:38 +00:00
eugene yokota 92c95ce290
Merge pull request #3997 from eed3si9n/wip/test-status-thread-safety
Fix race condition in non-forked, parallel tests.
2018-03-07 16:18:55 -05:00
Dale Wijnand 7b11729f8c
Cleanup State#process 2018-03-06 11:59:26 +00:00
Dale Wijnand 2effe0845f
Cleanup IPC 2018-03-06 11:59:26 +00:00
Jason Zaugg 0bea7e9e18 Fix race condition in non-forked, parallel tests.
Non forked tests that are run in parallel groups can call into
a single instance of TestStatusReporter concurrently.

This seems to be limited to the startGroup/endGroup/testEvent
methods called in:

  a41727fb17/testing/src/main/scala/sbt/TestFramework.scala (L100-L124)

Which itself is called within:

  a41727fb17/testing/src/main/scala/sbt/TestFramework.scala (L203-L229)

Creating the `runnables` that are run in parallel (in builds so configured):

  a6eb1260c8/main-actions/src/main/scala/sbt/Tests.scala (L222-L230)

We believe this to be the cause of the hang witnessed in
the a suite of Scalacheck-framework tests in the Scala
build:

  https://github.com/scala/scala-jenkins-infra/issues/249

This commit uses a concurrent map to support concurrent
status updates.
2018-03-05 18:23:03 +10:00