Commit Graph

11744 Commits

Author SHA1 Message Date
eugene yokota ca660f25da
Merge pull request #5747 from eatkins/watch-threads
Ensure watch threads are joined
2020-08-14 05:09:37 -04:00
Eugene Yokota 0b94b68fb5 Rename to sbtn 2020-08-13 22:56:02 -04:00
eugene yokota cd47b5c47b
Merge pull request #5746 from eatkins/virtual-file-cache
Make virtual file value cache global
2020-08-13 22:20:11 -04:00
Ethan Atkins 415341ec8b Add reload to watch commands
I have this in my global git config and think it's useful enough to
promote to the default.
2020-08-13 18:03:55 -07:00
Ethan Atkins 59c3d73f83 Ensure watch threads are joined
While dogfooding the latest sbt, I noticed that sometimes the watch
input threads leak. I suspect this happens when a build is immediately
triggered by a file that was modified during compilation. Though I
didn't fully verify this, it's likely that we interrupted the input
reading thread before it actually started reading. When it started
reading after the interrupt, it would block until the user entered
another input character. The result was that the zombie thread would
effectively steal the next character from the input stream which
manifested as the first character being ignored when the user tried to
enter a watch input option. If more than one thread leaked, then it may
take a number of keystrokes before the user regained control.

To fix this, we can ensure that all watch related threads are joined
before we exit watch. To avoid completely blocking the ui, we only try
to join the threads for a second and print an error if the join fails.
This shouldn't be the case so if users see this error, then we need to
fix the bug.
2020-08-13 18:03:26 -07:00
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 ef34a33ac9
Merge pull request #5744 from eed3si9n/wip/converter
Use mapped VirtualFile for classpath
2020-08-13 10:00:15 -04:00
Eugene Yokota 9937230f0c Fixes semicolon showing up in parser errors
Fixes https://github.com/sbt/sbt/issues/5039
Fixes https://github.com/sbt/sbt/issues/4989

This is take 2 on the semicolon fix by emptying out the completion examples in the multi parser.

```
> set scalaV
```

would complete to

```
> set scalaVersion
```

and more importantly

```
coursierUseSbtCredentials := true
```

errors to

```
sbt:hello> coursierUseSbtCredentials := true
[error] Expected ID character
[error] Not a valid command: coursierUseSbtCredentials
[error] Expected project ID
[error] Expected configuration
[error] Expected ':'
[error] Expected key
[error] Not a valid key: coursierUseSbtCredentials (similar: csrExtraCredentials, credentials)
[error] coursierUseSbtCredentials := true
[error]                          ^
```
2020-08-13 01:41:19 -04:00
Eugene Yokota 8521be5ac8 Revert "Filter single semicolons from tab completions"
This reverts commit dcb8e18f0e.
2020-08-13 01:41:19 -04:00
Johannes Rudolph 4948c54486
Update README.md 2020-08-12 12:25:53 +02: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 5419c3a0db
Merge pull request #5740 from eed3si9n/wip/coursier_initialization
Delay the initialization of Coursier cache
2020-08-11 20:55:40 -04:00
Ethan Atkins dcc5c6df0f
Merge pull request #5614 from eed3si9n/wip/npe_in_log
Fix NullPointerException in appendLog
2020-08-11 09:52:18 -07: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 64335567ac Fix NullPointerException in appendLog 2020-08-11 09:34:48 -04:00
Eugene Yokota b111704620 Move Coursier cache check 2020-08-11 02:10:40 -04:00
eugene yokota 3c5b88a83a
Merge pull request #5737 from eatkins/reprompt
Add reprompt fast track command
2020-08-10 20:00:53 -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
eugene yokota 31bea086aa
Merge pull request #5711 from eatkins/jline3-console
Support scala 2.13 and dotty console in thin client
2020-08-09 23:33:58 -04: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 e4cd6a38fc Hold lock while writing bytes to stdout
We should always hold the print stream lock when calling
progressState.write because otherwise the task progress thread could
concurrently write to stdout.
2020-08-09 16:33:46 -07:00
Ethan Atkins 44a605198b Add toString implementation to ConsoleOut instances
This makes it easier to debug which ConsoleOut is printing output.
2020-08-09 16:33:46 -07:00
Ethan Atkins 9c0c51971e
Merge pull request #5731 from eatkins/logging-rework
Add internal multi logger implementation
2020-08-09 16:33:23 -07:00
Ethan Atkins 07acb2aa0e
Merge branch 'develop' into logging-rework 2020-08-09 15:55:46 -07:00
eugene yokota df1eae1a87
Merge pull request #5734 from eatkins/defines-class-cache
Cache jar classpath entries between commands
2020-08-09 15:04:59 -04: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 46724159da Stop filling json codec cache
These were not actually used as far as I could tell. The json codecs
cache showed up as taking up 30MB in a heap dump that I took after
running compile 30 times in a clone of the repro project in
https://github.com/sbt/sbt/issues/5508.
2020-08-08 09:02:38 -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
eugene yokota 20f725c73c
Merge pull request #5707 from eatkins/native-install
Add installNativeExecutable task
2020-08-08 11:57:40 -04:00
Ethan Atkins b9cda2e713
Merge pull request #5729 from eatkins/scalacache
Use java caffeine library rather than scalacache
2020-08-07 18:06:11 -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 53299b79bd
Merge pull request #5724 from eed3si9n/wip/versionscheme
versionScheme setting + better eviction warning
2020-08-06 23:24:41 -04:00
eugene yokota a59d9fbb97
Merge branch 'develop' into wip/versionscheme 2020-08-06 21:04:21 -04:00
Ethan Atkins 48fa28e566 Add installNativeThinClient task
This allows a user to install the native thin client into a particular
directory (e.g. /usr/local/bin). I also made buildNativeThinClient have
a file dependency on the classpath so that it can be incremental if the
classpath hasn't changed. This is useful if the user has run
buildNativeThinClient for testing and then decides to install it once
it's been validated without having to rebuild (which takes a minimum of
about 30 seconds on my laptop).
2020-08-06 07:58:53 -07:00
eugene yokota 26d7331283
Merge pull request #5703 from eed3si9n/wip/pipelining
Build pipelining
2020-08-06 10:47:39 -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
eugene yokota d1cd24078d
Merge pull request #5721 from eatkins/windows-appveyor
Attempt to make CI less flaky
2020-08-06 02:03:20 -04:00
eugene yokota f2d42264b6
Merge pull request #5708 from eatkins/internal-commands
Add fast path for parsing commands
2020-08-05 22:14:02 -04:00
Ethan Atkins 3a9d349065 Check server test project scalafmt on travis 2020-08-05 18:14:11 -07:00
Ethan Atkins 5e2fe77434 Disable server tests on windows ci
The server tests fail often which makes CI very painful.
2020-08-05 18:14:11 -07:00