On linux and mac, entering ctrl+c will automatically kill any forked
processes that were created by the sbt server because sigint is
automatically forwarded to the child process. This is not the case on
windows where it is necessary to forcibly kill these processes.
The commit 388ed641fb added an autoplugin
that was compiled for every scripted test. Compiling autoplugins
introduces a fair bit of overhead because it can easily take 3-4 seconds
to compile with a cold compiler and even a warn compiler takes a second
or so. Removing the instrumentation caused 3 tests to fail:
1. genereated-root-no-publish relied on setUpScripted modifing the project
name. Explicitly setting the name in the project build.sbt fixed it.
2. cp-order I'm not sure why this change broke that test, but changing
the coursier classpath ordering setting does not automatically
trigger a reload on the next update. I have a more involved change
that makes changing coursier settings invalidate the update cache
but I'm markign the test as pending for now. It could be fixed by
adding a call to `update` after
`set csrConfiguration ~= (_.withClasspathOrder(false))` but I think
it's better that the test actually reflect the expected behavior
until I push the fix.
3. auto-plugins there was a hack that seemed added to address
https://github.com/sbt/sbt/issues/3164. I cannot tell from either the
issue or the linked PR what was going on and since removing the
lines that were explicitly commented as being temporary fixed it,
I figured it was ok to remove them.
This reverts commit e01f5f5ef1.
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.
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.
This commit makes it so that the scalaVersion, sbtVersion and classpath
are always passed in as parameters to any method that creates an sbt
server -- either for scripted or for the sbt server tests. By making
that change, I was able to change the implementation of scripted in the
sbt project to use publishLocalBin instead of publishLocal. This makes
the scripted tests start much faster (doc alone can easily take 30
second) with messing with the build to exclude slow tasks from
publishLocal.
As part of this change, I removed the test dependency on scriptedSbtRedux for
sbtProj and instead had scriptedSbtRedux depend on sbtProj. This allowed
me to remove some messy LocalProject logic in the resourceGenerators for
scriptedSbtReduxProj. I also had to remove a number of imports in the
scriptedSbtReduxProj because the definitions available in the sbt
package object became available.
I also removed the dependency on sbt-buildinfo and instead pass the
values from the build into test classes using scalatest properties. I
ran into a number of minor issues with the build info plugin, namely
that I couldn't get fullClasspathAsJars to reliably run as a BuildInfo
key. It also is somewhat more clear to me to just rely on the built in
scalatest functionality. The big drawback is that the scalatest
properties can only be strings, but that restriction isn't really a
problem here (strangely the TestData structure has a field configMap
which is effectively Map[String, Any] but Any is actually always String
given how the TestData is created as part of framework initialization.
Since scripted no longer publishes, scriptedUnpublished is now
effectively an alias for scripted.
To get publishLocalBin working, I had to copy private code from
IvyXml.scala into PublishBinPlugin. Once we publish a new version of
sbt, we can remove the copied code and invoke IvyXml.makeIvyXmlBefore
directly.
After the changes to `RunFromSourceMain` and the addition of
`Compile / exportJars` to the build, all of the tests pass with
RunFromSourceMain so I decided to remove the launcher based ones. The
RunFromSourceMain based tests are faster because they don't recompile
the various compiler bridges every time.
Scripted automatically adding quotation marks when an argument contains
spaces breaks arguments that already were wrapped in quotations. The
scripted test should be responsible for its own escaping. Since this is
part of scripted-sbt-redux, we don't have to worry about downstream
builds relying on the old escaping behavior.
Stripping quotation marks makes it impossible to cleanly test certain
sbt features without resorting to weird hacks. For example:
> set Compile / scalacOptions += "-Xfatal-warnings"
did not work while
> set Compile / scalacOptions += '"-Xfatal-warnings"'
did.
I leave the single quote parser unchanged since single quotes are not
really used in sbt and so there is utility in leaving them as a way to
group arguments that should not be split apart.
This change should only affect the scripted tests in the sbt repo. We
can consider making stripQuotes = false the default for the plugin as
well.
sbt should not by default create files in the location specified by
java.io.tmpdir (which is the default behavior of apis like
IO.createTemporaryDirectory or Files.createTempFile) because they have a
tendency to leak and it also isn't even guaranteed that the user has
write permissions there (though this is unlikely). Doing so creates the
possibility for leaks
I git grepped for `createTemp` and found these apis. After this change,
the files created by sbt should largely be localized to the project and
sbt global base directories.
This can be very noisy, especially for tests that are marked pending
because they fail to load the build. These induce a lengthy and largely
unhelpful "Reload for batch scripted failed..." error message.
Sometimes scripted tasks hang in ci and they are effectively impossible
to debug. To workaround this, I add a five minute timeout to any
scripted test and print the log if the test fails.
https://github.com/sbt/sbt/issues/4986 reported that +compile would
always recompile everything in the project even when the sources hadn't
changed. This was because the dependency classpath was changing between
calls to compile, which caused the external hooks cache introduced in
32a6d0d5d7 to invalidate the scala
library. To fix this, I cache the file stamps on a per scala version
basis. I added a scripted test that checks that there is no
recompilation in two consecutive calls to `+compile` in a multi scala
version build. It failed prior to these changes.
The sbt-coursier plugin now uses a shaded jansi and it seems to cause
problems with scripted tests if they aren't run using the launcher. On
my vm, all of the watch tests succeeded after this change and they all
crashed the jvm before.
When running the scripted tests on my vm, I noticed that one of the
watch tests warned that the InstrumentScripted.scala file had changed
after the project had initialized. The test then hung and I had to kill
it. To try and prevent this from happening, I moved the creation of the
plugin file to right before the call to ";reload;setUpScripted".
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.
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.
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.
I had previously not though there was much reason to support commands in
continuous builds. This was primarily because there were a number of
questions regarding semantics. Commands cannot have fileInputs
specifically assigned to them because they don't have an associated
scope. They also can arbitrarily modify state so what is the expectation
when running ~foo where foo is a command that, for example, replaces
itself. I settled on the following semantics:
1) Commands run in a continuous build cannot modify the sbt execution
state which is to say that the state that is returned by continuous
is the same that was passed in (unless a reload occurred or we exited
the build with an exception)
2) Any global watchTriggers or fileInputs apply to a watched command.
They automatically inherit any fileInputs that are queried when
running tasks in a command. So, for example, ~+compile does what
you'd expect.
The implementation is fairly straightforward. If we can successfully
parse a command, but we cannot parse a scopedKey from it, we assign it a
private ScopedKey. When computing the watch settings for that key, we
will select the global settings through delegation. This is how it picks
up the global watchTriggers.
To run the command, I had to rework the task evaluation portion because
a command may return a state with additional commands to run. The cross
build command works this way. We recursively run all of the commands
starting with the original until we run out of commands to run. As part
of this work, I was able to remove the three argument version of
Command.processCommand that I'd previously added to support my old
approach to evaluating commands. This was a nice bonus.
I added scripted tests that check that global watchTriggers are picked
up and that commands that delegate to a command that uses fileInputs
automatically pick up those inputs during the watch. I also added a test
that approximates the ~+compile use case and ensures that the failure
semantics are what we expect and that the task runs for all defined
scala versions.
@olegych reported in https://github.com/sbt/sbt/issues/4722 that
sometimes even when a build was triggered during watch that no
recompilation would occur. The cause of this was that we never
invalidated the file stamp cache for managed sources or output files.
The optimization of persisting the source file stamps between task
evaluations in a continuous build only really makes sense for unmanaged
sources. We make the implicit assumption that unmanaged sources are
infrequently updated and generally one at a time. That assumption does
not hold for unmanaged source or output files.
To fix this, I split the fileStampCache into two caches: one for
unmanaged sources and one for everything else. We only persist the
unmanagedFileStampCache during continuous builds. The
managedFileStampCache gets invalidated every time.
I added a scripted test that simulates changing a generated source file.
Prior to this change, the test would fail because the file stamp was not
invalidated for the new source file content.
Fixes#4722
Given that there are io differences between windows and posix systems,
we should aim to run the tests that do a lot of io on windows. There are
a few tests that don't work because of some platform specific issues so
I added a filter that excludes these tests on windows in ScriptedTests.
* 1.2.x: (28 commits)
More bumping up the 2.12 version to 2.12.8 in 1.2.x
Bump the 2.12 version to 2.12.8 in 1.2.x
define whitesourceOnPush
lm 1.2.4
1.2.7-SNAPSHOT
implement TestConsoleLogger
bump util, lm, and zinc
Bump scalatest to 3.0.6-SNAP5
Bump log4j2 to 2.11.1
drop notification override
Ignore files in scripted group dirs
Fix '~' for dependent projects with a broken parent
util 1.2.3, zinc 1.2.4
lm 1.2.2
Adjust the tests
Set withMetadataDirectory by default
Fix single repo emulation script
add onLoadMessage
check PluginCross.scala consisntency
Bump modules
...
It was reported in https://github.com/sbt/sbt/issues/4608 that there was
a regression that tests run against scala 2.11 would fail. This was
because the interface loader incorrectly contained the scala library. To
fix this, I needed to find the xsbt.boot.BootFilteredLoader in the
classloading hierarchy and put the sbt testing interface library in
between that loader and the scala library loader.
It is possible with the new layering strategies that tests may fail if a
java package private class is accessed across classloader layers. This
will result in an IllegalAccessError that is hard to debug. With this
commit, I add an error message that will be displayed if run throws an
IllegalAccessError that suggests that the user try the
ScalaInstance layering strategy or the flat layering strategy.
I'm not sure if this is a huge benefit or not, but it's nice to have the
option to run the scripted tests in parallel. The default behavior
should be the same as before.
Previously, the ClassLoaderLayeringStrategy was set globally. This
didn't really make sense because the Runtime and Test configs had
different strategies available (Test being a superset of Runtime).
Instead, we now set the layering strategy in the Runtime and Test
configurations directly. In doing this, we can eliminate the Default
ClassLoaderLayeringStrategy. Previously this had existed so that we
could set the layering strategy globally and have it do the right thing
in both test and runtime.
To implement this, I factored out the logic for generating the layered
classloader in the test task and shared it with the runtime task. I did
this because I realized that Test / run is a thing. Previously I had
been operating under the assumption that the runner would never include
the test dependencies. Once I realized this, it made sense to combine
the logic in both tasks.
As a bonus, I only allow the layering strategies that explicitly make
sense to be set in each configuration. If the user sets an invalid
strategy, an error will be thrown that specifies the valid strategies
for the task.
I also added ScalaInstance as an option for the runtime layer. It was an
oversight that this was left out.
Normally I'd include these with the previous commit, but the diff is so
large that I put them in their own commit. The tests handle 5 scenarios:
1) akka-actor-system -- a project that has Akka as a dependency and a
simple main method that creates and terminates an ActorSystem. What
is interesting about this test is that if scriptedBufferLog := false,
we notice that the first call to run is slow, but subsequent calls to
run and test are fast. The test does at least ensure that recycling
the runtime layer in test works ok.
2) jni -- verifies that a project with native libraries will be able to
load the library with each run. It actually swaps out the underlying
library so that the it really ensures that the library is reloaded
between runs.
3) library-mismatch -- verifies that the layered classloaders can work
when the test dependencies are incompatible with the runtime
dependencies. In this test, the test dependencies use an api in a
library called foo-lib that isn't available in the version used by
the runtime dependencies. Because of this incompatibility, the test
will not work if Test / layeringStrategy := LayeringStrategy.Full.
4) scalatest -- verifies that a test runs using the scalatest framework
5) utest -- verifies that a test runs using the utest framework
The reason for (4) and (5) is to ensure that both the in sourced test
frameworks and external frameworks work with the new loaders.
Fixes#4241Fixes#4242
This introduces a new subproject named scripted-sbt-redux. The purpose of this new subproject is to workaround the 'sbt.test` package vs `Keys.test` key confusion (#4242) while maintaining the forward compatibility of 0.13.17's sbt cross testing ^^ (#4241).
The new subproject uses `sbt.scriptedtest` package name, and that's the one that will be used by the mothership.
Meanwhile "scripted-sbt" subproject will also be published for compatibility purpose.