Commit Graph

2853 Commits

Author SHA1 Message Date
Ethan Atkins adf28877bc Make virtual file value cache global
In 0d2b00c7e9, I introduced a setting for
the virtual file defines class cache to avoid ooms coming from zinc
stamping the project jar files. I introduced that cache at the compile
level though rather than global level and crashes were still occurring
in the sbt build. It was very easy to induce a crash on my computer by
running compile a few times, reload and then compile again. After making
the cache global, the crashes went away.
2020-08-13 18:02:57 -07:00
Eugene Yokota c85e5b8bc0 Upgrade scala version to 2.12.12
This introduces `Def.unit(...)` to workaround the pure expression does nothing warning.
2020-08-11 23:33:12 -04:00
Eugene Yokota 541235e024 Use mapped VirtualFile for classpath 2020-08-11 22:24:05 -04:00
Eugene Yokota d2ff2edf87 SemanticDB 4.3.20 2020-08-11 21:59:33 -04:00
Eugene Yokota c8b9283a74 Delay the initialization of Coursier cache
This attempts to delay the initialization of Coursier cache, such that it will not trigger Coursier directory related code if `ThisBuild / useCoursier` or `-Dsbt.coursier` is set to `false`.
2020-08-11 10:24:12 -04:00
Eugene Yokota b111704620 Move Coursier cache check 2020-08-11 02:10:40 -04:00
Ethan Atkins adc8d5ee6e Add reprompt fast track command
With the latest sbt snapshot, the ui would get stuck if the user entered
an empty command. They would be presented with an empty prompt and could
not input any commands. This was caused by the change in
d569abe70a that reset the prompt after a
line was read. I had tried to optimize line reading by ignoring empty
commands in UITask.readline so we wouldn't have to make a new thread.
This optimization wasn't really buying much since it only affects how
quickly the user is reprompted after entering an empty command. Unless a
user is spamming the <enter> key, they shouldn't notice a difference.
2020-08-10 14:39:38 -07:00
Ethan Atkins d58aab5d84 Add super shell options
This commit adds a few options to supershell:
1. Max items -- sets the max number of tasks to display in the progress
   reports. It is pretty hard to read more than a few items in the
   progress reports so I set the default limit to 8 and made that
   configurable via the superShellMaxTasks parameter. If there are more
   than the limit, there is an additional line telling how many additional
   tasks are running
2. sleep -- sets how long to sleep between reports. The default is 500ms
   to ensure that it updates at least once per second but the previous
   value of 100ms is more frequent than necessary
3. threshold -- sets the minimum duration a task has to run before being
   printed in the progress reports. The default threshold is increased
   from 10ms to 100ms. This introduces a delay of threshold milliseconds
   before any progress lines appear and also means that if no tasks ever
   exceed the threshold, then no progress is ever displayed.
2020-08-09 19:04:03 -07:00
Ethan Atkins 102e3d1969 Improve supershell performance
It turns out that task progress actually introduces a fair bit of
overhead. The biggest issue is that the task progress callbacks block
the Execute main thread. This means that time in those callbacks
delays task evaluation, slowing down sbt. This was not negligible, I was
seeing a lot of the total time of a no-op compile in
https://github.com/jtjeferreira/sbt-multi-module-sample was spent in
TaskProgress callbacks. Prior to these changes, I ran 30 no-op compiles
in that project and the average time was about 570ms. This number got
worse and worse because there were memory leaks in the TaskProgress
object. After these changes, it dropped to 250ms and after jit-ing, it
would drop to about 200ms. I also successfully ran 5000 consecutive
no-op compiles without leaking any memory.

A lot of the overhead of task progress was in adding tasks to the
timings map in AbstractTaskProgress. Tasks were never removed and
ConcurrentHashMap insertion time is proportional to the size of the map
(not sure if it's linear, quadratic or other) which was why sbt actually
got slower and slower the longer it ran. Much of the time was spent
adding tasks to the progress timings.

To fix this, I did something similar to what I did to manage logger
state in https://github.com/jtjeferreira/sbt-multi-module-sample. In
MainLoop, we create a new TaskProgress instance before command
evaluation and clean it up after. Earlier I made TaskProgress an object
to try to ensure there was only one progress thread at a time, and that
introduced the memory leak. In addition to removing the leak, I was able
to improve performance by removing tasks from the timings map when they
completed. Unlike TaskTimings and TaskTraceEvent, we don't care about
tasks that have completed for TaskProgress so it is safe to remove them.

In addition to the memory leaks, I also reworked how the background
threads work. Instead of having one thread that sleeps and prints
progress reports, we now use two single threaded executors. One is a
scheduled executor that is used to schedule progress reports and the
other is the actual thread on which the report is generated. When
progress starts, we schedule a recurring report that is generated every
sleep interval until task evaluation completes. Whenever we add a new
task, if we have haven't previously generated a progress report, we
schedule a report in threshold milliseconds. If the task completes
before the threshold period has elapsed, we just cancel the schedule
report. By doing things this way, we reduce the total number of reports
that are generated. Because reports need to effectively lock System.out,
the less we generate them, the better.

I also modified the internal data structures of AbstractTaskProgress so
that there is a single task map of timings instead of one map for
timings and one for active tasks.
2020-08-09 19:04:03 -07:00
Ethan Atkins d569abe70a Consolidate terminal prompt management
It was a bit tricky to reason about the state of the prompt for a
terminal. To help make things more clear, I reworked things so that the
LineReader always sets the prompt to Pending after it reads a command.
In MainLoop, we cache the prompt value and temporarily set it to Running
while the command is running, which is really how it should have always
been.
2020-08-09 17:18:47 -07:00
Ethan Atkins 90dacc339c Support scala 2.13 console in thin client
In order to make the console task work with scala 2.13 and the thin
client, we need to provide a way for the scala repl to use an sbt
provided jline3 terminal instead of the default terminal typically built
by the repl. We also need to put jline 3 higher up in the classloading
hierarchy to ensure that two versions of jline 3 are not loaded (which
makes it impossible to share the sbt terminal with the scala terminal).

One impact of this change is the decoupling of the version of
jline-terminal used by the in process scala console and the version
of jline-terminal specified by the scala version itself. It is possible
to override this by setting the `useScalaReplJLine` flag to true. When
that is set, the scala REPL will run in a fully isolated classloader. That
will ensure that the versions are consistent. It will, however, for sure
break the thin client and may interfere with the embedded shell ui.

As part of this work, I also discovered that jline 3 Terminal.getSize is
very slow. In jline 2, the terminal attributes were automatically cached with a
timeout of, I think, 1 second so it wasn't a big deal to call
Terminal.getAttributes. The getSize method in jline 3 is not cached and
it shells out to run a tty command. This caused a significant
performance regression in sbt because when progress is enabled, we call
Terminal.getSize whenever we log any messages. I added caching of
getSize at the TerminalImpl level to address this. The timeout is 1
second, which seems responsive enough for most use cases. We could also
move the calculation onto a background thread and have it periodically
updated, but that seems like overkill.
2020-08-09 17:12:15 -07:00
Ethan Atkins 6dd69a54ae Close line reader when interrupted
There are cases where if the ui state is changing rapidly, that an
AskUserThread can be created and cancelled in a short time windows. This
could cause problems if the AskUserThread is interrupted during
`LineReader.createReader` which I think can shell out to run some
commands so it is relatively slow. If the thread was interrupted during
the call to `LineReader.createReader` and the interruption was not
handled, then the thread would go into `LineReader.readLine`, which
wouldn't exit until the user pressed enter. This ultimately caused the
ui to break until enter because this zombie line reader would be holding
the lock on the terminal input stream.
2020-08-09 16:33:46 -07:00
Ethan Atkins 07acb2aa0e
Merge branch 'develop' into logging-rework 2020-08-09 15:55:46 -07:00
Ethan Atkins 2e9805b9d0 Add internal multi logger implementation
Prior to these changes, sbt was leaking large amounts of memory via
log4j appenders. sbt has an unusual use case for log4j because it
creates many ephemeral loggers while also having a global logger that is
supposed to work for the duration of the sbt session. There is a lot of
shared global state in log4j and properly cleaning up the ephemeral task
appenders would break global logging. This commit fixes the behavior by
introducing an alternate logging implementation. Users can still use the
old log4j logging implementation but it will be off by default. The
internal implementation is very simple: it just blocks the current
thread and writes to all of the appenders. Nevertheless, I found the
performance to be roughly identical to that of log4j in my sample
project. As an experiment, I did the appending on a thread pool and got
a significant performance improvement but I'll defer that to a later PR
since parallel io is harder to reason about.

Background: I was testing sbt performance in
https://github.com/jtjeferreira/sbt-multi-module-sample and noticed that
performance rapidly degraded after I ran compile a few times. I took a
heap dump and it became obvious that sbt was leaking console appenders.
Further investigation revealed that all of the leaking appenders in the
project were coming from task streams. This made me think that the fix
would be to track what loggers were created during task evaluation and
clear them out when task evaluation completed. That almost worked except
that log4j has an internal append only data structure containing logger
names. Since we create unique logger names for each run, that internal
data structure grew without bound. It looked like this could be worked
around by creating a new log4j Configuration (where that data structure
was stored) but while creating new configurations with each task runs
did fix the leak, it also broke global logging, which was using a
different configuration. At this point, I decided to write an alternate
implementation of the appender api where I could be sure that the
appenders were cleaned up without breaking global logging.

Implementation: I made ConsoleAppender a trait and made it no longer
extends log4j AbstractAppender. To do this, I had to remove the one
log4j specific method, append(LogEvent). ConsoleAppender now has a
method toLog4J that, in most cases, will return a log4j Appender that is
almost identical to the Appenders that we previously used. To manage
the loggers created during task evaluation, I introduce a new class,
LoggerContext. The LoggerContext determines which logging backend to use
and keeps track of what appenders and loggers have been created. We can
create a fresh LoggerContext before each task evaluation and clear it
out, cleaning up all of its resources after task evaluation concludes.

In order to make this work, there were many places where we need to
either pass in a LoggerContext or create a new one. The main magic is
happening in the `next(State)` method in Main. This is where we create a
new LoggerContext prior to command evaluation and clean it up after the
evaluation completes.

Users can toggle log4j using the new useLog4J key. They also can set the
system property, sbt.log.uselog4j. The global logger will use the sbt
internal implementation unless the system property is set.

There are a fairly significant number of mima issues since I changed the
type of ConsoleAppender. All of the mima changes were in the
sbt.internal package so I think this should be ok.

Effects: the memory leaks are gone. I successfully ran 5000 no-op
compiles in the sbt-multi-module-sample above with no degradation of
performace. There was a noticeable degradation after 30 no-op compiles
before.

During the refactor, I had to work on TestLogger and in doing so I also
fixed https://github.com/sbt/sbt/issues/4480.

This also should fix https://github.com/sbt/sbt/issues/4773
2020-08-09 11:20:34 -07:00
Ethan Atkins 0d2b00c7e9 Cache jar classpath entries between commands
Zinc frequently needs to check the library classpath to ensure that
class names are defined in a given jar. There is a cost to looking up
the class names in the jar so it's a benefit to cache this across runs
so that we don't have to redo the same work every time. More
importantly, in testing with the latest sbt HEAD, I found that sbt would
crash fairly frequently because it ran out of direct memory, which is
used by nio to read and write to native memory without copying. The
direct memory area is shared with the java heap and if it reaches the
limit, the jvm crashes hard as though kill -9 was invoked. After caching
the entries, I stopped seeing crashes.
2020-08-09 10:24:26 -07:00
Ethan Atkins 841b6dbfd8 Remove SetTerminal command
Rather than relying on a command, I realized it makes more sense to
explicitly set the terminal for the calling channel in MainLoop. By
doing it this way, we can also ensure that we always reset it to the
previous value.
2020-08-08 09:48:48 -07:00
Ethan Atkins 6c50a85f93 Use a macro to create StringTypeTag instances
Using the scala reflect library always introduces significant
classloading overhead. We can eliminate the classloading overhead by
generating StringTypeTags at compile time instead.

This sped up average project loading time by a few hundred milliseconds
on my computer. The ManagedLoggedReporter in zinc is still using the
type tag based apis but after the next sbt release, we can upgrade the
zinc apis. We also could consider breaking binary compatibility.
2020-08-08 09:02:38 -07:00
Ethan Atkins 525cff7fd7 Use java caffeine library rather than scalacache
sbt depends on scalacache (which hasn't been updated in about a year)
and we really don't need the functionality provided by scalacache. In
fact, the java api is somewhat easier to work with for our use case. The
motivation is that scalacache uses slf4j for logging which meant that it
was implicitly loading log4j. This caused some noisy logs during
shutdown when the previously unused cache was initialized just to be
cleaned up.

This commit also upgrades caffeine and moving forward we can always
upgrade caffeine (and potentially shade it) without any conflict with
the scalacache version.
2020-08-07 17:18:09 -07:00
eugene yokota 9beecf98e0
Merge pull request #5728 from eatkins/observer-memory-leak
Close file tree repository registrations
2020-08-07 18:29:16 -04:00
Ethan Atkins 05407cea55 Close file tree repository registrations
Upon successful registration with a FileTreeRepository, an Observable is
returned by the FileTreeRepository that can be used to observer the
specific globs that were registered. The FileTreeRepository also has a
global Observable that can be used to monitor _all_ events. In order to
implement this feature, internally the FileTreeRepository needs to hold
a reference to the registered Observable so that it forwards relevant
file change events. If we do not close the Observable, it leaks memory
inside of FileTreeRepository. There were a number of places within sbt
where we registered globs and did nothing with the returned Observable.
It was thus straightforward to fix the leak by just closing the returned
Observables.

This came up because I was looking at a heap dump of
https://github.com/jtjeferreira/sbt-multi-module-sample after running
1000 no-op compiles and noticed that the FileTreeRepository.observables
were taking up 75MB out of a total heap of about 300MB.

As a side note, it would be nice if sbt had a warning for unused return
values when a statement is not the last in a block. It's possible that
these leaks wouldn't have happened if we were forced to handle the
returned Observables.
2020-08-07 10:50:22 -07:00
eugene yokota a59d9fbb97
Merge branch 'develop' into wip/versionscheme 2020-08-06 21:04:21 -04:00
Eugene Yokota 002f97cae7 Build pipelining
Ref https://github.com/sbt/zinc/pull/744

This implements `ThisBuild / usePipelining`, which configures subproject pipelining available from Zinc 1.4.0.

The basic idea is to start subproject compilation as soon as pickle JARs (early output) becomes available. This is in part enabled by Scala compiler's new flags `-Ypickle-java` and `-Ypickle-write`.

The other part of magic is the use of `Def.promise`:

```
earlyOutputPing := Def.promise[Boolean],
```

This notifies `compileEarly` task, which to the rest of the tasks would look like a normal task but in fact it is promise-blocked. In other words, without calling full `compile` task together, `compileEarly` will never return, forever waiting for the `earlyOutputPing`.
2020-08-06 02:31:01 -04:00
Ethan Atkins caccba7112 Add fast path for parsing commands
It can easily take 2ms or more to parse a command depending on state's
combined parser. There are some commands that sbt requires to work that
we can handle in microseconds instead of milliseconds by special casing
them.

After this change, I saw the performance of
https://github.com/eatkins/scala-build-watch-performance improve by
a consistent 4-5ms in the 3 source file example which was a drop from
120ms to 115ms. While not necessarily earth shattering, this difference
could theoretically be much worse in other projects that have a lot of
plugins and custom tasks/commands. I think it's worth the modest
maintenance cost.
2020-08-05 17:19:05 -07:00
Eugene Yokota f947970247 versionScheme setting + better eviction warning
Ref https://github.com/sbt/sbt/issues/5710
Ref https://github.com/sbt/librarymanagement/pull/339

This adds `versionScheme` setting. When set, it is included into POM, and gets picked up on the other side as an extra attribute of ModuleID. That information in turn is used to inform the eviction warning.

This should reduce the false positives associated with SemVer'ed libraries showing up in the eviction warning.
2020-08-05 18:11:02 -04:00
eugene yokota 6c89d7416d
Merge pull request #5723 from eatkins/watch-state-map
Make watch implementation more sbt idiomatic
2020-08-04 22:17:31 -04:00
eugene yokota 34b74d0a14
Merge pull request #5718 from eatkins/force-flush
Allow sbt to force flush of remote output
2020-08-04 22:14:45 -04:00
Ethan Atkins eb48f24f3a Make watch implementation more sbt idiomatic
The 1.4.0 implementation of watch uses a concurrent hash map to maintain
the global watch state which manages the state for an arbitrary number
of clients. Using a mutable map is not idiomatic sbt and I found it
difficult to reason about when the map was updated. This commit reworks
the feature so that the global state is instead stored in an immutable
map that is only modified during the internal watch commands, which is
easier to reason about.
2020-08-04 17:19:51 -07:00
Ethan Atkins 284ed4de5f Apply miscellaneous whitespace changes
The EventsTest changes kept appearing. I'm not sure why scalafmt check
was allowing it before. My vim status bar warns me about trailing spaces
and I noticed the two in Keys.scala and removed them.
2020-08-04 11:54:46 -07:00
Ethan Atkins b656d599e1 Allow sbt to force flush of remote output
In eb688c9ecd, we started buffering output
to the remote client to reduce flickering. This was causing problems
with the output for the thin client in batch mode. With the delay, it
was possible for the client to exit before all of its output had been
displayed.

Bonus: only display aggregation error message if terminal has success
enabled (the thin client displays its own timing message so the message
in aggregation ended up being a duplicate).
2020-08-04 10:44:43 -07:00
eugene yokota e76f61bec5
Merge pull request #5575 from eed3si9n/wip/bump
update to lm-coursier-shaded 2.0.0-RC6-8
2020-08-01 17:13:01 -04:00
Eugene Yokota 2bebee4dfe lm-coursier-shaded 2.0.0-RC6-8
https://github.com/coursier/sbt-coursier/releases/tag/v2.0.0-RC6-5
https://github.com/coursier/sbt-coursier/releases/tag/v2.0.0-RC6-6
https://github.com/coursier/sbt-coursier/releases/tag/v2.0.0-RC6-7
https://github.com/coursier/sbt-coursier/releases/tag/v2.0.0-RC6-8

Ref https://github.com/coursier/sbt-coursier/pull/235
Ref https://github.com/coursier/sbt-coursier/pull/262
2020-08-01 15:51:19 -04:00
Ethan Atkins 7ba5fd79f8 Use time wrapped stamper for dependency classpath
It is expensive to compute the the hash of every jar on the classpath so
we can try to avoid that by using the timeWrappedStamper which only
computes the hash if the last modified time has changed.
2020-07-28 13:52:57 -07:00
Ethan Atkins c9dc041643 Don't use managedCache for library stamps
Using the managedCached introduced an unintended performance regression
because it ensured that we always computed the hash of each jar on the
dependency classpath. The backing ReadStamps only computes the stamp if
the timestamp of the jar has changed.
2020-07-28 13:50:49 -07:00
Ethan Atkins 45a940a080 Add lintUnusedKeysOnLoad to lint ignore keys
I noticed that if lintUnusedKeysOnLoad := true is set, it emits a lint
warning.

As a side note, Project linting takes about 300-400ms in the sbt project
so we might want to consider disabling it by default in batch mode at
least.
2020-07-27 08:57:29 -07:00
Ethan Atkins 38d67cfdf0 Improve sbt build load time by 25%
The sbt project load made a number of relatively inefficient
transformations of scala collecitons. I went through and found the slow
parts during project loading and made my best attempt at fixing them.
The most significant changes I made were in places using IMap. An IMap
is more or less a wrapper around an immutable Map. It can be much faster
to construct an IMap by creating a java mutable hashmap, wrapping it a
scala Map that delegates to the underlying java hashmap (with a copy on
write if the map is modified) and constructing the IMap from the wrapped
map. It was also in many cases to parallelize some transformations
wherever the order didn't matter.

After applying all of these changes, I found that loading the sbt
project took generally between 8.5 and 9 seconds on my laptop. With
1.3.13, it hovered around 11.5 seconds. I saw a similar speedup in zinc.
The biggest specific improvement was that generating the compiled map
dropped from between 3.5-4 seconds to pretty consistently being around
1.5 seconds.
2020-07-26 19:52:26 -07:00
Ethan Atkins eb88d523e0 Update sbt for latest zinc apis 2020-07-22 15:00:56 -07:00
Ethan Atkins 7b39118214
Merge branch 'develop' into client-system-err 2020-07-22 09:16:10 -07:00
Ethan Atkins 12112741cb Revert "Unprompt channels during project load"
This reverts commit b1dcf031a5.

I found that b1dcf031a5 had some
unintended consequences that seemed to mess up the prompt state. The
real problem that it was trying to address was that the prompt was being
interleaved with log messages in some scenarios. There was a different
way to fix that in ProgressState that was both simpler and more
reliable.
2020-07-21 13:27:44 -07:00
Ethan Atkins 901c8ee5df Use new history file path for jline3
The jline2 history file format is incompatible with jline3 and jline3
prints a very noisy warning if it detects such a file. History will also
not work with jline3 until you remove or reformat the old file.
2020-07-21 13:27:44 -07:00
Ethan Atkins 761c926944 Catch ClosedChannelException in TaskTraceEvent
sbt would print a stack trace on exit when run with
-Dsbt.task.timings=true. This removes that annoying stack trace.
2020-07-21 13:27:44 -07:00
Ethan Atkins cd26abd656 Print ClearScreenAfterCursor on shutdown
There are scenarios in which sbt can leave behind stray progress lines
after it exits. This attempts to delete them.
2020-07-21 13:27:44 -07:00
Ethan Atkins e82c3405b9 Support System.err in thin client
I noticed that when reloading the build, that certain errors are logged
by sbt to System.err. These were not shown to a thin client because we
weren't forwarding System.err. This change remedies that.

System.err is handled more simply than System.out. We do not put
System.err through the progress state because generally System.err is
tends to be unbuffered. I had hesitated to add System.err to the
Terminal interface at all to give users an escape hatch but I couldn't
get project loading to work well with the thin client without it.
2020-07-21 13:27:32 -07:00
eugene yokota d53ebaa686
Merge pull request #5680 from adpi2/develop
Make the BuildServerReporter stop blinking
2020-07-21 15:35:32 -04:00
adpi2 6c694fb04d Rework BuildServerReporter
Make it stop blinking
Use `reset` field to reduce verbosity
2020-07-20 09:17:12 +02:00
Ethan Atkins f7f25bea81
Merge branch 'develop' into task-progress 2020-07-19 21:42:00 -07:00
Ethan Atkins c6ef4c477d
Merge branch 'develop' into parallel-stamping 2020-07-19 18:31:21 -07:00
Ethan Atkins d227b6423f Clear singleton TaskProgress between runs
In db4878c786, TaskProgress became an
object which mostly made things easier to reason about. The one problem
was that it started leaking tasks with every run because the timings map
would accumulate tasks that weren't cleared. To fix this, we can clear
the timings and activeTasksMap in the TaskProgress object in the
afterAllCompleted callback. Some extra null checks needed to be added
since it's possible for the maps to not contain a previously added key
after reset has been called.
2020-07-19 13:08:56 -07:00
Ethan Atkins 1b5cbd8ead Stamp source files in parallel
I noticed a modest performance increase could be achieved by stamping
the source files in parallel.
2020-07-19 12:21:25 -07:00
Ethan Atkins e81d459f69 Remove custom ExternalHooks
This is now handled by passing a custom instance of ReadStamps to
CompileOptions instead.
2020-07-19 12:16:37 -07:00
Ethan Atkins 329d989e73 Use sbt source file stamps in zinc
This fixes the nio/external-hooks test and also restores the performance
of the benchmarks for the latest sbt version in
https://github.com/eatkins/scala-build-watch-performance which had
regressed when the custom ExternalHooks were disabled in
7c4b01d9f7.

The main change is that it changes the ReadStamps object that is passed
into the compiler options to one that uses the unmanagedFileStampCache
and managedFileStampCache for source files and falls back to the default
stamper otherwise. This improves the performance quite significantly
since we only hash the files once. It also makes it so that the analysis
file will contain the source file stamps of the files when compilation
began, rather than when compilation ended. That is what
nio/external-hooks was testing. In the real world what could happen was
that one modified a source file during compilation but then no
incremental re-compilation would occur because after the initial
compilation completed, zinc wrote the stamp of the modified source file
in the analysis file even though it may have actually compiled a
different version of the source file.
2020-07-19 12:16:37 -07:00
Ethan Atkins 4acf3d14c2 Handle closed executor in network channel flush
I noticed some RejectedExecutionExceptions in travis failures of
ClientTest. This could happen if we try to write output to the
network channel after it has been closed. To avoid this problem, we can
catch RejectedExecutionExceptions and do an immediate flush if the
executor has been shutdown.
2020-07-19 10:09:54 -07:00
adpi2 2c0d09dfb3 Specify full java path in BSP connection details
Use System properties to add java path and classpath to BSP connection details
2020-07-13 13:13:54 +02:00
Ethan Atkins 464ea1b97e Add improved classloader construction short circuit
In order for sbt to function well, it needs the test interface, jansi
and forked jline jars provided by a classloader that is parent to all
other sbt classloaders. To do this for just the test interface jar, I
just checked if the top loader in the app configuration had the correct
name. Now that there are three jars, this is more complicated so I
updated the launcher to create a top loader with the method getEarlyJars
implemented and returning the three needed jars. This is a much more
scalable design.

If sbt is entered with a configuration that does not have a top loader
with the getEarlyJars method defined, then we just fall back on
constructing the default layered classlaoder from the configuration
classpath.

The motivation for this change is that I discovered that sbt immediately
crashed when I tried to run a non-snapshot version. After this change, I
verified that both snapshot and non-snapshot versions of the latest sbt
code could load with both an obsolete and up-to-date launcher.
2020-07-12 17:49:01 -07:00
eugene yokota 6110734383
Merge pull request #5673 from eatkins/client-watch-fixes
UI fixes for remote client in watch
2020-07-12 00:55:03 -04:00
Ethan Atkins baa96be6dc Use unprompt in watch to better manage ui state
The unprompt method will actually kill the ui thread if its running. If
we don't to this, we can get into a weird state where after watch is
exited by <enter>, the ask user thread spins up but before it can print
the prompt, the terminal prompt is changed to Running, which has an
empty prompt. The end result is that jline3 never displays the prompt
even though the line reader is active and reading bytes. When the user
typed <enter> a prompt would appear. They also could input a command and
it would run but it wasn't obvious what would happen since the prompt
was missing.
2020-07-10 14:26:16 -07:00
Ethan Atkins 3ddc7bd744 Remove unneeded println
While I'm sure this was added for a reason, it presently is creating
unnecessary and ugly newlines to the console during `~`.
2020-07-10 14:26:14 -07:00
Ethan Atkins 9dc3c6b17f Use terminal printstream in CheckBuildSources
The build source check is evaluated at times when we can't be completely
sure that global logger is pointing at the terminal that initiated the
reload (which may be a passive watch client). To work around this, we
can inspect the exec to determine which terminal initiated the check and
write any output directly to that terminal.
2020-07-10 13:37:54 -07:00
Ethan Atkins 25e83d8fec Add Terminal.withRawOutput api
In the scala console, it's essential that we not process the bytes that
are written to the terminal by jline.
2020-07-10 13:37:54 -07:00
Ethan Atkins 2ecf5967ee Upgrade LineReader to JLine3
This commit upgrades sbt to using jline3. The advantage to jline3 is
that it has a significantly better tab completion engine that is more
similar to what you get from zsh or fish.

The diff is bigger than I'd hoped because there are a number of
behaviors that are different in jline3 vs jline2 in how the library
consumes input streams and implements various features. I also was
unable to remove jline2 because we need it for older versions of the
scala console to work correctly with the thin client. As a result, the
changes are largely additive.

A good amount of this commit was in adding more protocol so that the
remote client can forward its jline3 terminal information to the server.

There were a number of minor changes that I made that either fixed
outstanding ui bugs from #5620 or regressions due to differences between
jline3 and jline2.

The number one thing that caused problems is that the jline3 LineReader
insists on using a NonBlockingInputStream. The implementation ofo
NonBlockingInputStream seems buggy. Moreover, sbt internally uses a
non blocking input stream for system in so jline is adding non blocking
to an already non blocking stream, which is frustrating.

A long term solution might be to consider insourcing LineReader.java
from jline3 and just adapting it to use an sbt terminal rather than
fighting with the jline3 api. This would also have the advantage of not
conflicting with other versions of jline3. Even if we don't, we may want to
shade jline3 if that is possible.
2020-07-10 13:37:53 -07:00
Ethan Atkins 366c49a764 Aggregate watch events
It is possible for sbt to get into a weird state when in a continuous
build when the auto reload feature is on and a source file and a build
file are changed in a small window of time. If sbt detects the source
file first, it will start running the command but then it will
autoreload when it runs the command because of the build file change.
This causes the watch to get into a broken state because it is necessary
to completely restart the watch after sbt exits.

To fix this, we can aggregate the detected events in a 100ms window. The
idea is to handle bursts of file events so we poll in 5ms increments and
as soon as no events are detected, we trigger a build.
2020-07-10 13:04:08 -07:00
Ethan Atkins eb688c9ecd Buffer output to the remote client
Remote clients sometimes flicker when updating progress. This is especially
noticeable when there are two clients and one of them is running a command,
the other will tend to have some visible flickering and character ghosting.
As an experiment, I buffered calls to flush in the NetworkChannel output
stream and the artifacts went away.
2020-07-09 17:17:15 -07:00
Ethan Atkins 6aa1333adb Don't log systemOut messages in jsonRpcNotify
Whatever debugging utility these may have is not worth spamming the task
logs with these.
2020-07-09 16:54:29 -07:00
Ethan Atkins d826e93ddf Only trigger reload if sources have changed
Running a `~` command in a local build off the latest develop branch
will cause the build to reload even if the build sources were only
touched and not actually modified.
2020-07-09 16:54:29 -07:00
Ethan Atkins de1423d662 Clarify boolean flag
I found this difficult to read.
2020-07-09 16:54:29 -07:00
Ethan Atkins d74a10aad1 Set terminal before watch triggered reload
Without setting the terminal, a remote client is unable to reload the
project if it fails.
2020-07-09 16:54:29 -07:00
Ethan Atkins b1dcf031a5 Unprompt channels during project load
In the situation where sbt was started in server mode and a client is
running a `~` command and a project reload is triggered by a change to
a build source, the console terminal looks like

sbt:foo>
[info] received remote command: ~compile
sbt:foo>
[info] welcome to sbt 1.4.0-SNAPSHOT (Azul Systems, Inc. Java 1.8.0_252)
sbt:foo>
[info] loading global plugins from ~/.sbt/1.0/plugins
sbt:foo>
[info] loading settings for project foo-build from metals.sbt ...
sbt:foo>
[info] loading project definition from
~/foo/project
sbt:foo>
[info] loading settings for project root from build.sbt ...
sbt:foo>
[info] loading settings for project macros from build.sbt ...
sbt:foo>
[info] loading settings for project main from build.sbt ...
sbt:foo>
[info] set current project to foo (in build file:~/foo)
sbt:foo>

This change fixes that by unprompting all channels during project
loading and reprompting them when it completes.
2020-07-09 16:54:29 -07:00
Ethan Atkins 80cd0d5e6b Rename SettingsGraph WatchTransitiveDependencies
This is a more descriptive name and differentiates the object from
`SettingGraph`.
2020-07-03 14:08:26 -07:00
Ethan Atkins 4a8e7c0734 Used cached compiled map in LintUnused
Linting unused keys was adding a significant overhead to sbt project
loading because Def.compiled is so slow. It was around 4 seconds in the
sbt project on my computer.
2020-07-03 14:08:26 -07:00
Ethan Atkins 6565618a15 Cache compiled map during build load
The continuous command recompiles the setting graph into a CompiledMap
data structure so that it can determine which files it needs to
transitively monitor during watch. Generating the CompiledMap can be
very slow for large projects (5 seconds or so on my computer in the sbt
project) and this startup cost is paid every time the user enters a
watch with `~`. To avoid this, we can cache the compile map that is
generated during the initial settings evaluation.

The only real drawback I can see is that the compiled map is guaranteed
to remain in memory so long as the BuildStructure instance that holds it
is alive. Given the performance benefit, this seems like a worthwhile
tradeoff.
2020-07-03 14:08:26 -07:00
Ethan Atkins e4c7747570 Add retry to BspCompileTask.compute
There have been occasional failures on appveyor where an
AccessDeniedException was thrown at this point. AccessDeniedExceptions
thrown during scripted tests can often by resolved with a Retry.
2020-07-03 12:15:55 -07:00
Ethan Atkins f9d5fbf29b Support reboot from remote client
Reboot is a bit tricky for the remote client because the sbt server is
actually shut down during reboot. When sbt shuts down the client, it can
notify the client that the reason is a reboot. The client can then
connect to the recently introduced boot control socket to display the
reboot output and supply input in case the build fails to load. Once the
server has brought back up the server, the client can reconnect. When
the client session is interactive, we're done once we reconnect. When
it's a batch session, the client needs to resend the remaing commands
that have submitted that it hasn't yet run.
2020-06-29 16:41:33 -07:00
Ethan Atkins 77b1e38e41 Add shutdown command
Shutdown was being handled as a special case in CommandExchange. This
promotes it to a full fledged command. Also replace instance of
hard-coded strings with constants.
2020-06-29 16:41:33 -07:00
Ethan Atkins 261084bbb2 Avoid leaking sbt processes
On windows, it is sometimes possible to leak an sbt process if two
processes are started simultaneously by a remote client at the same
time. When this happens, the second process is unable to create a
server because of the first process and it also has no io streams
because the the client detaches its streams. We can detect this
in the shell command and prevent the process from persisting as a
zombie.
2020-06-29 16:41:33 -07:00
Ethan Atkins ae2899baae Notify initiating client before shutdown
When a remote client sent the command `shutdown` through the shell, the
client would log an error and exit with a nonzero exit code because
before shutting down, the server would notify the client that it was
disconnecting it due to shutdown. In this scenario, we actually do not
want the client to log an error since they initiated the shutdown, so
before doing the full shutdown, we shutdown the client that inititated
the shutdown with the flag that tells the client not to log the shutdown
or return a nonzero exit code.
2020-06-29 16:41:33 -07:00
Ethan Atkins 267918958d Prevent simultaneous server booting
One issue with the remote client approach is that it is possible for
multiple clients to start multiple servers concurrently. I encountered
this in testing where in one tmux pane I'd start an sbt server and in
another I might run sbtc before the server had finished loading. This
can actually cause java processes to leak because the second process is
unable to start a server but it doesn't necessarily die after the client
that spawned it exits. This commit prevents this scenario by creating a
server socket before it loads the build and closes once the build is
complete. The client can then receive output bytes and forward input to
the booting server.

The socket that is created during boot is always a local socket, either
a UnixDomainServerSocket or a Win32NamedPipeServerSocket. At the moment,
I don't see any reason to support TCP. This socket also has no impact at
all on the normal sbt server that is started up after the project has
loaded.

The socket is hardcoded to be located at the relative path
project/target/$SOCK_NAME or the named pipe $SOCK_NAME where SOCK_NAME
is a farm hash of the absolute path of the project base directory. There
is no portfile json since there is no need since we don't support TCP.

After the socket is created it listens for clients to whom it relays
input to the console's input stream and relays the process output back
to the client. See the javadoc in BootServerSocket.java for further
details.

The process for forking the server is also a bit more complicated after
this change because the client will read the process output and error
streams until the socket is created and thereafter will only read output
from the socket, not the process.
2020-06-29 16:41:33 -07:00
Ethan Atkins db4878c786 Make progress an object
This commit reworks TaskProgress so that it is a singleton object. By
using a singleton, we ensure that there is at most one progress thread
running at a time. With multiple threads, there can be flickering in the
progress reports.

This fixes https://github.com/sbt/sbt/issues/5547. There also was a bug
that the reference to the progress thread was not reset when the thread
itself exited. As a result, it was possible for progress reporting to
stop while tasks were still running. This seemed to primarily happen in
multi-project builds. It should be fixed by this change.
2020-06-29 09:44:24 -07:00
Ethan Atkins d102c47415 fixup! Add multi-client ui to server
The absence of this case was causing match errors. I think this was
introduced during rebasing.
2020-06-29 08:59:52 -07:00
Ethan Atkins 6796f43ab1 Merge remote-tracking branch 'origin/develop' into wip-sbt-instant-startup 2020-06-26 21:51:58 -07:00
eugene yokota 638aa5b8e6
Merge pull request #5653 from eed3si9n/wip/bumpgiter8
Giter8 0.13.1
2020-06-26 19:44:41 -04:00
eugene yokota e7c3b675e9
Merge pull request #5645 from eatkins/watch-excludes
Filter all watch settings for unused key check
2020-06-26 17:20:12 -04:00
eugene yokota 836134d0aa
Merge pull request #5642 from eatkins/zinc-real-paths
Use real paths for zinc roots
2020-06-26 17:19:36 -04:00
Eugene Yokota 9437fcf2c0 Giter8 0.13.1 2020-06-26 17:09:33 -04:00
adpi2 15178b3ef7 trim internal dependency configs in BSP 2020-06-26 10:10:08 +02:00
Ethan Atkins aaee092c96 Revert "Use print + flush instead of println"
This reverts commit b0a859acb5.
2020-06-25 15:16:21 -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 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 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 ab362397ba Add remote cancellation support
This commit makes it possible for a remote client to cancel a running
task initiated by a different client by typing `cancel` into the shell.
It can be useful if the remote client has run something blocking like
console.

The console task can't safely be interrupted, so instead we write some
newlines filed by ctrl+d to exit the console.
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 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 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 120e6eb63d Route TaskProgress through CommandExchange
Rather than going through the console appender logging to make
TaskProgress work, we can instead use the CommandExchange. This will be
useful in future commits where there are multiple terminals that all
need to receive progress. By organizing the TaskProgress this way, we
can store a separate progress state for each terminal and update the
progress for all of the active terminals. We also can set the current
running command in command exchange which will be useful in future
commits to show what command is currently running.

This commit also reworks TaskProgress to always kill its thread when
there are no active tasks. It will start a new thread as soon as there
is another active task.
2020-06-24 19:19:06 -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 b0a859acb5 Use print + flush instead of println
With println, it is possible for lines to get interleaved because
another thread may call flush before the println appends a newline.
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