mirror of https://github.com/sbt/sbt.git
Merge pull request #7517 from adpi2/sbt2-fix-bsp-reporter
[sbt 2] Restore server tests
This commit is contained in:
commit
b921230836
|
|
@ -126,7 +126,7 @@ jobs:
|
|||
./sbt -v --client "Test/compile"
|
||||
./sbt -v --client publishLocal
|
||||
./sbt -v --client test
|
||||
# ./sbt -v --client "serverTestProj/test"
|
||||
./sbt -v --client "serverTestProj/test"
|
||||
# ./sbt -v --client doc
|
||||
./sbt -v --client "all $UTIL_TESTS"
|
||||
./sbt -v --client ++2.13.x
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
target/
|
||||
__pycache__
|
||||
out
|
||||
node_modules
|
||||
vscode-sbt-scala/client/server
|
||||
npm-debug.log
|
||||
*.vsix
|
||||
*_pid*.log
|
||||
!sbt/src/server-test/completions/target
|
||||
!server-test/src/server-test/completions/target
|
||||
.big
|
||||
.idea
|
||||
.bloop
|
||||
|
|
|
|||
|
|
@ -995,7 +995,6 @@ lazy val serverTestProj = (project in file("server-test"))
|
|||
.dependsOn(sbtProj % "compile->test", scriptedSbtReduxProj % "compile->test")
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
bspEnabled := false,
|
||||
publish / skip := true,
|
||||
// make server tests serial
|
||||
Test / watchTriggers += baseDirectory.value.toGlob / "src" / "server-test" / **,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import dotty.tools.dotc.CompilationUnit
|
|||
import dotty.tools.dotc.core.Contexts.{ atPhase, Context }
|
||||
import dotty.tools.dotc.core.{ Flags, Names, Phases, Symbols, Types }
|
||||
import dotty.tools.dotc.Driver
|
||||
import dotty.tools.dotc.reporting.Reporter
|
||||
import dotty.tools.dotc.Run
|
||||
import dotty.tools.dotc.util.SourceFile
|
||||
import dotty.tools.io.{ PlainDirectory, Directory, VirtualDirectory, VirtualFile }
|
||||
|
|
@ -31,7 +30,7 @@ class Eval(
|
|||
nonCpOptions: Seq[String],
|
||||
classpath: Seq[Path],
|
||||
backingDir: Option[Path],
|
||||
mkReporter: Option[() => Reporter]
|
||||
mkReporter: Option[() => EvalReporter]
|
||||
):
|
||||
import Eval.*
|
||||
|
||||
|
|
@ -46,9 +45,9 @@ class Eval(
|
|||
.map(_.toString)
|
||||
.mkString(":")
|
||||
private lazy val driver: EvalDriver = new EvalDriver
|
||||
private lazy val reporter = mkReporter match
|
||||
case Some(fn) => fn()
|
||||
case None => EvalReporter.store
|
||||
private lazy val reporter: EvalReporter = mkReporter match
|
||||
case Some(f) => f()
|
||||
case None => EvalReporter.store
|
||||
|
||||
final class EvalDriver extends Driver:
|
||||
val compileCtx0 = initCtx.fresh
|
||||
|
|
@ -83,6 +82,7 @@ class Eval(
|
|||
line: Int
|
||||
): EvalResult =
|
||||
val ev = new EvalType[String]:
|
||||
override def sourceName: String = srcName
|
||||
override def makeSource(moduleName: String): SourceFile =
|
||||
val returnType = tpeName match
|
||||
case Some(tpe) => s": $tpe"
|
||||
|
|
@ -148,6 +148,7 @@ class Eval(
|
|||
require(definitions.nonEmpty, "definitions to evaluate cannot be empty.")
|
||||
val extraHash0 = extraHash
|
||||
val ev = new EvalType[Seq[String]]:
|
||||
override def sourceName: String = srcName
|
||||
override def makeSource(moduleName: String): SourceFile =
|
||||
val header =
|
||||
imports.strings.mkString("\n") +
|
||||
|
|
@ -176,7 +177,6 @@ class Eval(
|
|||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING
|
||||
)
|
||||
|
||||
override def extraHash: String = extraHash0
|
||||
|
||||
val inter = evalCommon[Seq[String]](definitions.map(_._1), imports, tpeName = Some(""), ev)
|
||||
|
|
@ -203,13 +203,16 @@ class Eval(
|
|||
val d = digester.digest()
|
||||
val hash = Hash.toHex(d)
|
||||
val moduleName = makeModuleName(hash)
|
||||
val (extra, loader) = backingDir match
|
||||
case Some(backing) if classExists(backing, moduleName) =>
|
||||
val loader = (parent: ClassLoader) =>
|
||||
(new URLClassLoader(Array(backing.toUri.toURL), parent): ClassLoader)
|
||||
val extra = ev.read(cacheFile(backing, moduleName))
|
||||
(extra, loader)
|
||||
case _ => compileAndLoad(ev, moduleName)
|
||||
val (extra, loader) =
|
||||
try
|
||||
backingDir match
|
||||
case Some(backing) if classExists(backing, moduleName) =>
|
||||
val loader = (parent: ClassLoader) =>
|
||||
(new URLClassLoader(Array(backing.toUri.toURL), parent): ClassLoader)
|
||||
val extra = ev.read(cacheFile(backing, moduleName))
|
||||
(extra, loader)
|
||||
case _ => compileAndLoad(ev, moduleName)
|
||||
finally reporter.finalReport(ev.sourceName)
|
||||
val generatedFiles = getGeneratedFiles(moduleName)
|
||||
EvalIntermediate(
|
||||
extra = extra,
|
||||
|
|
@ -277,19 +280,19 @@ object Eval:
|
|||
def apply(): Eval =
|
||||
new Eval(Nil, currentClasspath, None, None)
|
||||
|
||||
def apply(mkReporter: () => Reporter): Eval =
|
||||
def apply(mkReporter: () => EvalReporter): Eval =
|
||||
new Eval(Nil, currentClasspath, None, Some(mkReporter))
|
||||
|
||||
def apply(
|
||||
backingDir: Path,
|
||||
mkReporter: () => Reporter,
|
||||
mkReporter: () => EvalReporter,
|
||||
): Eval =
|
||||
new Eval(Nil, currentClasspath, Some(backingDir), Some(mkReporter))
|
||||
|
||||
def apply(
|
||||
nonCpOptions: Seq[String],
|
||||
backingDir: Path,
|
||||
mkReporter: () => Reporter,
|
||||
mkReporter: () => EvalReporter,
|
||||
): Eval =
|
||||
new Eval(nonCpOptions, currentClasspath, Some(backingDir), Some(mkReporter))
|
||||
|
||||
|
|
@ -326,6 +329,7 @@ object Eval:
|
|||
end EvalSourceFile
|
||||
|
||||
trait EvalType[A]:
|
||||
def sourceName: String
|
||||
def makeSource(moduleName: String): SourceFile
|
||||
|
||||
/** Extracts additional information after the compilation unit is evaluated. */
|
||||
|
|
|
|||
|
|
@ -7,7 +7,14 @@ import dotty.tools.dotc.reporting.Diagnostic
|
|||
import dotty.tools.dotc.reporting.Reporter
|
||||
import dotty.tools.dotc.reporting.StoreReporter
|
||||
|
||||
abstract class EvalReporter extends Reporter
|
||||
abstract class EvalReporter extends Reporter:
|
||||
/**
|
||||
* Send a final report to clear out the outdated diagnostics.
|
||||
* @param sourceName the source path of a build.sbt file
|
||||
*/
|
||||
def finalReport(sourceName: String): Unit
|
||||
|
||||
def log(msg: String): Unit = ()
|
||||
|
||||
object EvalReporter:
|
||||
def console: EvalReporter = ForwardingReporter(ConsoleReporter())
|
||||
|
|
@ -16,4 +23,6 @@ end EvalReporter
|
|||
|
||||
class ForwardingReporter(delegate: Reporter) extends EvalReporter:
|
||||
def doReport(dia: Diagnostic)(using Context): Unit = delegate.doReport(dia)
|
||||
|
||||
def finalReport(sourceName: String): Unit = ()
|
||||
end ForwardingReporter
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import sbt.SlashSyntax0.*
|
|||
import sbt.internal.parser.SbtParser
|
||||
import sbt.io.IO
|
||||
import scala.jdk.CollectionConverters.*
|
||||
import xsbti.PathBasedFile
|
||||
import xsbti.VirtualFile
|
||||
import xsbti.VirtualFileRef
|
||||
|
||||
|
|
@ -151,7 +152,9 @@ private[sbt] object EvaluateConfigurations {
|
|||
): LazyClassLoaded[LoadedSbtFile] = {
|
||||
// TODO - Store the file on the LoadedSbtFile (or the parent dir) so we can accurately do
|
||||
// detection for which project project manipulations should be applied.
|
||||
val name = file.id
|
||||
val name = file match
|
||||
case file: PathBasedFile => file.toPath.toString
|
||||
case file => file.id
|
||||
val parsed = parseConfiguration(file, lines, imports, offset)
|
||||
val (importDefs, definitions) =
|
||||
if (parsed.definitions.isEmpty) (Nil, DefinedSbtValues.empty)
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ object Command {
|
|||
def combine(cmds: Seq[Command]): State => Parser[() => State] = {
|
||||
val (simple, arbs) = separateCommands(cmds)
|
||||
state =>
|
||||
arbs.map(_ parser state).foldLeft(simpleParser(simple)(state))(_ | _)
|
||||
arbs.map(_.parser(state)).foldLeft(simpleParser(simple)(state))(_ | _)
|
||||
}
|
||||
|
||||
private[this] def separateCommands(
|
||||
|
|
@ -188,7 +188,7 @@ object Command {
|
|||
else parse(command, state.nonMultiParser)) match {
|
||||
case Right(s) => s() // apply command. command side effects happen here
|
||||
case Left(errMsg) =>
|
||||
state.log error errMsg
|
||||
state.log.error(errMsg)
|
||||
state.fail
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -403,8 +403,6 @@ object Scoped:
|
|||
)((thisTask, deps) => thisTask.dependsOn(deps: _*))
|
||||
def failure: Initialize[Task[Incomplete]] = init(_.failure)
|
||||
def result: Initialize[Task[Result[A1]]] = init(_.result)
|
||||
def xtriggeredBy[A2](tasks: Initialize[Task[A2]]*): Initialize[Task[A1]] =
|
||||
nonLocal(tasks.toSeq.asInstanceOf[Seq[AnyInitTask]], Def.triggeredBy)
|
||||
def triggeredBy[A2](tasks: Initialize[Task[A2]]*): Initialize[Task[A1]] =
|
||||
nonLocal(tasks.toSeq.asInstanceOf[Seq[AnyInitTask]], Def.triggeredBy)
|
||||
def runBefore[A2](tasks: Initialize[Task[A2]]*): Initialize[Task[A1]] =
|
||||
|
|
|
|||
|
|
@ -106,9 +106,11 @@ import sbt.internal.inc.{
|
|||
MixedAnalyzingCompiler,
|
||||
ScalaInstance
|
||||
}
|
||||
import xsbti.{ CrossValue, HashedVirtualFileRef, VirtualFile, VirtualFileRef }
|
||||
import sbt.internal.io.Retry
|
||||
import xsbti.{ CompileFailed, CrossValue, HashedVirtualFileRef, VirtualFile, VirtualFileRef }
|
||||
import xsbti.compile.{
|
||||
AnalysisContents,
|
||||
AnalysisStore,
|
||||
ClassFileManagerType,
|
||||
ClasspathOptionsUtil,
|
||||
CompileAnalysis,
|
||||
|
|
@ -1005,7 +1007,7 @@ object Defaults extends BuildCommon {
|
|||
discoveredMainClasses := compile
|
||||
.map(discoverMainClasses)
|
||||
.storeAs(discoveredMainClasses)
|
||||
.xtriggeredBy(compile)
|
||||
.triggeredBy(compile)
|
||||
.value,
|
||||
discoveredSbtPlugins := discoverSbtPluginNames.value,
|
||||
// This fork options, scoped to the configuration is used for tests
|
||||
|
|
@ -1266,9 +1268,11 @@ object Defaults extends BuildCommon {
|
|||
testFrameworks.value.flatMap(f => f.create(loader, log).map(x => (f, x))).toMap
|
||||
},
|
||||
definedTests := detectTests.value,
|
||||
definedTestNames := (definedTests map (_.map(
|
||||
_.name
|
||||
).distinct) storeAs definedTestNames triggeredBy compile).value,
|
||||
definedTestNames := definedTests
|
||||
.map(_.map(_.name).distinct)
|
||||
.storeAs(definedTestNames)
|
||||
.triggeredBy(compile)
|
||||
.value,
|
||||
testQuick / testFilter := testQuickFilter.value,
|
||||
executeTests := {
|
||||
import sbt.TupleSyntax.*
|
||||
|
|
@ -2393,11 +2397,10 @@ object Defaults extends BuildCommon {
|
|||
*/
|
||||
private[sbt] def compileScalaBackendTask: Initialize[Task[CompileResult]] = Def.task {
|
||||
val setup: Setup = compileIncSetup.value
|
||||
val useBinary: Boolean = enableBinaryCompileAnalysis.value
|
||||
val _ = compileIncremental.value
|
||||
val exportP = exportPipelining.value
|
||||
// Save analysis midway if pipelining is enabled
|
||||
val store = MixedAnalyzingCompiler.staticCachedStore(setup.cachePath, !useBinary)
|
||||
val store = analysisStore
|
||||
val contents = store.unsafeGet()
|
||||
if (exportP) {
|
||||
// this stores the eary analysis (again) in case the subproject contains a macro
|
||||
|
|
@ -2422,9 +2425,7 @@ object Defaults extends BuildCommon {
|
|||
.debug(s"${name.value}: compileEarly: blocking on earlyOutputPing")
|
||||
earlyOutputPing.await.value
|
||||
}) {
|
||||
val useBinary: Boolean = enableBinaryCompileAnalysis.value
|
||||
val store =
|
||||
MixedAnalyzingCompiler.staticCachedStore(earlyCompileAnalysisFile.value.toPath, !useBinary)
|
||||
val store = earlyAnalysisStore
|
||||
store.get.toOption match {
|
||||
case Some(contents) => contents.getAnalysis
|
||||
case _ => Analysis.empty
|
||||
|
|
@ -2436,13 +2437,11 @@ object Defaults extends BuildCommon {
|
|||
|
||||
def compileTask: Initialize[Task[CompileAnalysis]] = Def.task {
|
||||
val setup: Setup = compileIncSetup.value
|
||||
val useBinary: Boolean = enableBinaryCompileAnalysis.value
|
||||
val store = analysisStore
|
||||
val c = fileConverter.value
|
||||
// TODO - expose bytecode manipulation phase.
|
||||
val analysisResult: CompileResult = manipulateBytecode.value
|
||||
if (analysisResult.hasModified) {
|
||||
val store =
|
||||
MixedAnalyzingCompiler.staticCachedStore(setup.cacheFile.toPath, !useBinary)
|
||||
val contents = AnalysisContents.create(analysisResult.analysis(), analysisResult.setup())
|
||||
store.set(contents)
|
||||
}
|
||||
|
|
@ -2455,73 +2454,72 @@ object Defaults extends BuildCommon {
|
|||
analysis
|
||||
}
|
||||
|
||||
def compileIncrementalTaskSettings =
|
||||
inTask(compileIncremental)(
|
||||
Seq(
|
||||
(TaskZero / compileIncremental) := (Def
|
||||
.cachedTask {
|
||||
val s = streams.value
|
||||
val ci = (compile / compileInputs).value
|
||||
// This is a cacheable version
|
||||
val ci2 = (compile / compileInputs2).value
|
||||
val ping = (TaskZero / earlyOutputPing).value
|
||||
val reporter = (compile / bspReporter).value
|
||||
val setup: Setup = (TaskZero / compileIncSetup).value
|
||||
val useBinary: Boolean = enableBinaryCompileAnalysis.value
|
||||
val c = fileConverter.value
|
||||
val analysisResult: CompileResult =
|
||||
BspCompileTask
|
||||
.compute(bspTargetIdentifier.value, thisProjectRef.value, configuration.value) {
|
||||
bspTask =>
|
||||
// TODO - Should readAnalysis + saveAnalysis be scoped by the compile task too?
|
||||
compileIncrementalTaskImpl(bspTask, s, ci, ping, reporter)
|
||||
}
|
||||
val analysisOut = c.toVirtualFile(setup.cachePath())
|
||||
val store =
|
||||
MixedAnalyzingCompiler.staticCachedStore(setup.cachePath, !useBinary)
|
||||
val contents =
|
||||
AnalysisContents.create(analysisResult.analysis(), analysisResult.setup())
|
||||
store.set(contents)
|
||||
Def.declareOutput(analysisOut)
|
||||
val dir = classDirectory.value
|
||||
if (dir / "META-INF" / "MANIFEST.MF").exists then
|
||||
IO.delete(dir / "META-INF" / "MANIFEST.MF")
|
||||
// inline mappings
|
||||
val mappings = Path
|
||||
.allSubpaths(dir)
|
||||
.filter(_._1.isFile())
|
||||
.map { case (p, path) =>
|
||||
val vf = c.toVirtualFile(p.toPath())
|
||||
(vf: HashedVirtualFileRef) -> path
|
||||
}
|
||||
.toSeq
|
||||
// inlined to avoid caching mappings
|
||||
val pkgConfig = Pkg.Configuration(
|
||||
mappings,
|
||||
artifactPath.value,
|
||||
packageOptions.value,
|
||||
)
|
||||
val out = Pkg(
|
||||
pkgConfig,
|
||||
c,
|
||||
s.log,
|
||||
Pkg.timeFromConfiguration(pkgConfig)
|
||||
)
|
||||
s.log.debug(s"wrote $out")
|
||||
Def.declareOutput(out)
|
||||
analysisResult.hasModified() -> (out: HashedVirtualFileRef)
|
||||
})
|
||||
.tag(Tags.Compile, Tags.CPU)
|
||||
.value,
|
||||
packagedArtifact := {
|
||||
val (hasModified, out) = compileIncremental.value
|
||||
artifact.value -> out
|
||||
},
|
||||
artifact := artifactSetting.value,
|
||||
artifactClassifier := Some("noresources"),
|
||||
artifactPath := artifactPathSetting(artifact).value,
|
||||
)
|
||||
def compileIncrementalTaskSettings = inTask(compileIncremental)(
|
||||
Seq(
|
||||
(TaskZero / compileIncremental) := {
|
||||
val bspTask = (compile / bspCompileTask).value
|
||||
val result = cachedCompileIncrementalTask.result.value
|
||||
val reporter = (compile / bspReporter).value
|
||||
val store = analysisStore
|
||||
val ci = (compile / compileInputs).value
|
||||
result match
|
||||
case Result.Value(res) =>
|
||||
val analysis = store.unsafeGet().getAnalysis()
|
||||
reporter.sendSuccessReport(analysis)
|
||||
bspTask.notifySuccess(analysis)
|
||||
res
|
||||
case Result.Inc(cause) =>
|
||||
val compileFailed = cause.directCause.collect { case c: CompileFailed => c }
|
||||
reporter.sendFailureReport(ci.options.sources)
|
||||
bspTask.notifyFailure(compileFailed)
|
||||
throw cause
|
||||
},
|
||||
packagedArtifact := {
|
||||
val (hasModified, out) = compileIncremental.value
|
||||
artifact.value -> out
|
||||
},
|
||||
artifact := artifactSetting.value,
|
||||
artifactClassifier := Some("noresources"),
|
||||
artifactPath := artifactPathSetting(artifact).value,
|
||||
)
|
||||
)
|
||||
|
||||
private val cachedCompileIncrementalTask = Def
|
||||
.cachedTask {
|
||||
val s = streams.value
|
||||
val ci = (compile / compileInputs).value
|
||||
val bspTask = (compile / bspCompileTask).value
|
||||
// This is a cacheable version
|
||||
val ci2 = (compile / compileInputs2).value
|
||||
val ping = (TaskZero / earlyOutputPing).value
|
||||
val setup: Setup = (TaskZero / compileIncSetup).value
|
||||
val store = analysisStore
|
||||
val c = fileConverter.value
|
||||
// TODO - Should readAnalysis + saveAnalysis be scoped by the compile task too?
|
||||
val analysisResult = Retry(compileIncrementalTaskImpl(bspTask, s, ci, ping))
|
||||
val analysisOut = c.toVirtualFile(setup.cachePath())
|
||||
val contents = AnalysisContents.create(analysisResult.analysis(), analysisResult.setup())
|
||||
store.set(contents)
|
||||
Def.declareOutput(analysisOut)
|
||||
val dir = classDirectory.value
|
||||
if (dir / "META-INF" / "MANIFEST.MF").exists then IO.delete(dir / "META-INF" / "MANIFEST.MF")
|
||||
// inline mappings
|
||||
val mappings = Path
|
||||
.allSubpaths(dir)
|
||||
.filter(_._1.isFile())
|
||||
.map { case (p, path) =>
|
||||
val vf = c.toVirtualFile(p.toPath())
|
||||
(vf: HashedVirtualFileRef) -> path
|
||||
}
|
||||
.toSeq
|
||||
// inlined to avoid caching mappings
|
||||
val pkgConfig = Pkg.Configuration(mappings, artifactPath.value, packageOptions.value)
|
||||
val out = Pkg(pkgConfig, c, s.log, Pkg.timeFromConfiguration(pkgConfig))
|
||||
s.log.debug(s"wrote $out")
|
||||
Def.declareOutput(out)
|
||||
analysisResult.hasModified() -> (out: HashedVirtualFileRef)
|
||||
}
|
||||
.tag(Tags.Compile, Tags.CPU)
|
||||
|
||||
private val incCompiler = ZincUtil.defaultIncrementalCompiler
|
||||
private[sbt] def compileJavaTask: Initialize[Task[CompileResult]] = Def.task {
|
||||
|
|
@ -2549,8 +2547,7 @@ object Defaults extends BuildCommon {
|
|||
task: BspCompileTask,
|
||||
s: TaskStreams,
|
||||
ci: Inputs,
|
||||
promise: PromiseWrap[Boolean],
|
||||
reporter: BuildServerReporter,
|
||||
promise: PromiseWrap[Boolean]
|
||||
): CompileResult = {
|
||||
lazy val x = s.text(ExportStream)
|
||||
def onArgs(cs: Compilers) =
|
||||
|
|
@ -2565,16 +2562,12 @@ object Defaults extends BuildCommon {
|
|||
val compilers: Compilers = ci.compilers
|
||||
val setup: Setup = ci.setup
|
||||
val i = ci.withCompilers(onArgs(compilers)).withSetup(onProgress(setup))
|
||||
try
|
||||
val result = incCompiler.compile(i, s.log)
|
||||
reporter.sendSuccessReport(result.getAnalysis)
|
||||
result
|
||||
try incCompiler.compile(i, s.log)
|
||||
catch
|
||||
case e: Throwable =>
|
||||
if !promise.isCompleted then
|
||||
promise.failure(e)
|
||||
ConcurrentRestrictions.cancelAllSentinels()
|
||||
reporter.sendFailureReport(ci.options.sources)
|
||||
throw e
|
||||
finally x.close() // workaround for #937
|
||||
}
|
||||
|
|
@ -2591,12 +2584,8 @@ object Defaults extends BuildCommon {
|
|||
override def definesClass(classpathEntry: VirtualFile): DefinesClass =
|
||||
cachedPerEntryDefinesClassLookup(classpathEntry)
|
||||
val extra = extraIncOptions.value.map(t2)
|
||||
val useBinary: Boolean = enableBinaryCompileAnalysis.value
|
||||
val eapath = earlyCompileAnalysisFile.value.toPath
|
||||
val eaOpt =
|
||||
if exportPipelining.value then
|
||||
Some(MixedAnalyzingCompiler.staticCachedStore(eapath, !useBinary))
|
||||
else None
|
||||
val store = earlyAnalysisStore
|
||||
val eaOpt = if exportPipelining.value then Some(store) else None
|
||||
Setup.of(
|
||||
lookup,
|
||||
(compile / skip).value,
|
||||
|
|
@ -2672,6 +2661,8 @@ object Defaults extends BuildCommon {
|
|||
javacOptions.value.toVector,
|
||||
)
|
||||
},
|
||||
bspCompileTask :=
|
||||
BspCompileTask.start(bspTargetIdentifier.value, thisProjectRef.value, configuration.value)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -2698,8 +2689,7 @@ object Defaults extends BuildCommon {
|
|||
def compileAnalysisSettings: Seq[Setting[_]] = Seq(
|
||||
previousCompile := {
|
||||
val setup = compileIncSetup.value
|
||||
val useBinary: Boolean = enableBinaryCompileAnalysis.value
|
||||
val store = MixedAnalyzingCompiler.staticCachedStore(setup.cacheFile.toPath, !useBinary)
|
||||
val store = analysisStore
|
||||
val prev = store.get().toOption match {
|
||||
case Some(contents) =>
|
||||
val analysis = Option(contents.getAnalysis).toOptional
|
||||
|
|
@ -2711,6 +2701,18 @@ object Defaults extends BuildCommon {
|
|||
}
|
||||
)
|
||||
|
||||
private inline def analysisStore: AnalysisStore = {
|
||||
val setup = compileIncSetup.value
|
||||
val useBinary = enableBinaryCompileAnalysis.value
|
||||
MixedAnalyzingCompiler.staticCachedStore(setup.cacheFile.toPath, !useBinary)
|
||||
}
|
||||
|
||||
private inline def earlyAnalysisStore: AnalysisStore = {
|
||||
val earlyAnalysisPath = earlyCompileAnalysisFile.value.toPath
|
||||
val useBinary = enableBinaryCompileAnalysis.value
|
||||
MixedAnalyzingCompiler.staticCachedStore(earlyAnalysisPath, !useBinary)
|
||||
}
|
||||
|
||||
def printWarningsTask: Initialize[Task[Unit]] =
|
||||
Def.task {
|
||||
val analysis = compile.value match { case a: Analysis => a }
|
||||
|
|
@ -4828,7 +4830,7 @@ trait BuildExtra extends BuildCommon with DefExtra {
|
|||
}
|
||||
}
|
||||
})
|
||||
.value
|
||||
.evaluated
|
||||
) ++ inTask(scoped)((config / forkOptions) := forkOptionsTask.value)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import sbt.internal.io.WatchState
|
|||
import sbt.internal.librarymanagement.{ CompatibilityWarningOptions, IvySbt }
|
||||
import sbt.internal.remotecache.RemoteCacheArtifact
|
||||
import sbt.internal.server.BuildServerProtocol.BspFullWorkspace
|
||||
import sbt.internal.server.{ BuildServerReporter, ServerHandler }
|
||||
import sbt.internal.server.{ BspCompileTask, BuildServerReporter, ServerHandler }
|
||||
import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition }
|
||||
import sbt.internal.util.StringAttributeKey
|
||||
import sbt.io._
|
||||
|
|
@ -44,6 +44,7 @@ import xsbti.compile.analysis.ReadStamps
|
|||
import scala.concurrent.duration.{ Duration, FiniteDuration }
|
||||
import scala.xml.{ NodeSeq, Node => XNode }
|
||||
|
||||
|
||||
// format: off
|
||||
|
||||
object Keys {
|
||||
|
|
@ -438,6 +439,7 @@ object Keys {
|
|||
val bspBuildTargetOutputPathsItem = taskKey[OutputPathsItem]("").withRank(DTask)
|
||||
val bspBuildTargetCompile = inputKey[Unit]("").withRank(DTask)
|
||||
val bspBuildTargetCompileItem = taskKey[Int]("").withRank(DTask)
|
||||
@cacheLevel(include = Array.empty) private[sbt] val bspCompileTask = taskKey[BspCompileTask]("").withRank(DTask)
|
||||
val bspBuildTargetTest = inputKey[Unit]("Corresponds to buildTarget/test request").withRank(DTask)
|
||||
val bspBuildTargetRun = inputKey[Unit]("Corresponds to buildTarget/run request").withRank(DTask)
|
||||
val bspBuildTargetCleanCache = inputKey[Unit]("Corresponds to buildTarget/cleanCache request").withRank(DTask)
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ object RemoteCache {
|
|||
Def._outputDirectory = Some(outDir)
|
||||
val caches = s.get(BasicKeys.cacheStores)
|
||||
caches match
|
||||
case Some(xs) => Def._cacheStore = AggregateActionCacheStore(xs)
|
||||
case None =>
|
||||
case Some(xs) if xs.nonEmpty => Def._cacheStore = AggregateActionCacheStore(xs)
|
||||
case _ =>
|
||||
val tempDiskCache = (s.baseDir / "target" / "bootcache").toPath()
|
||||
Def._cacheStore = DiskActionCacheStore(tempDiskCache)
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ object Act {
|
|||
keyMap: Map[String, AttributeKey[_]],
|
||||
data: Settings[Scope]
|
||||
): Parser[ParsedKey] =
|
||||
scopedKeyFull(index, current, defaultConfigs, keyMap) flatMap { choices =>
|
||||
scopedKeyFull(index, current, defaultConfigs, keyMap).flatMap { choices =>
|
||||
select(choices, data)(showRelativeKey2(current))
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +355,7 @@ object Act {
|
|||
val normKeys = taskKeys(_.label)
|
||||
val valid = allKnown ++ normKeys
|
||||
val suggested = normKeys.map(_._1).toSet
|
||||
val keyP = filterStrings(examples(ID, suggested, "key"), valid.keySet, "key") map valid
|
||||
val keyP = filterStrings(examples(ID, suggested, "key"), valid.keySet, "key").map(valid)
|
||||
|
||||
((token(
|
||||
value(keyP).map(_ -> slashSeq)
|
||||
|
|
@ -515,7 +515,7 @@ object Act {
|
|||
}
|
||||
}
|
||||
action match {
|
||||
case SingleAction => akp flatMap evaluate
|
||||
case SingleAction => akp.flatMap(evaluate)
|
||||
case ShowAction | PrintAction | MultiAction =>
|
||||
rep1sep(akp, token(Space)) flatMap { pairs =>
|
||||
val flat: mutable.ListBuffer[(ScopedKey[_], Seq[String])] = mutable.ListBuffer.empty
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import sbt.internal.inc.{ MappedFileConverter, ScalaInstance, ZincLmUtil, ZincUt
|
|||
import sbt.internal.util.Attributed.data
|
||||
import sbt.internal.util.Types.const
|
||||
import sbt.internal.util.{ Attributed, Settings }
|
||||
import sbt.internal.server.BuildServerEvalReporter
|
||||
import sbt.io.{ GlobFilter, IO, Path }
|
||||
import sbt.librarymanagement.ivy.{ InlineIvyConfiguration, IvyDependencyResolution, IvyPaths }
|
||||
import sbt.librarymanagement.{ Configuration, Configurations, Resolver }
|
||||
|
|
@ -33,7 +34,6 @@ import java.net.URI
|
|||
import java.nio.file.{ Path, Paths }
|
||||
import scala.annotation.{ nowarn, tailrec }
|
||||
import scala.collection.mutable
|
||||
// import scala.tools.nsc.reporters.ConsoleReporter
|
||||
|
||||
private[sbt] object Load {
|
||||
// note that there is State passed in but not pulled out
|
||||
|
|
@ -78,7 +78,7 @@ private[sbt] object Load {
|
|||
"JAVA_HOME" -> javaHome,
|
||||
)
|
||||
val loader = getClass.getClassLoader
|
||||
val converter = MappedFileConverter(rootPaths, false)
|
||||
val converter = MappedFileConverter(rootPaths, true)
|
||||
val cp0 = provider.mainClasspath.toIndexedSeq ++ scalaProvider.jars.toIndexedSeq
|
||||
val classpath = Attributed.blankSeq(
|
||||
cp0.map(_.toPath).map(p => converter.toVirtualFile(p): HashedVirtualFileRef)
|
||||
|
|
@ -469,7 +469,7 @@ private[sbt] object Load {
|
|||
nonCpOptions = options,
|
||||
classpath = classpath,
|
||||
backingDir = Option(evalOutputDirectory(base).toPath()),
|
||||
mkReporter = Option(() => (mkReporter(): dotty.tools.dotc.reporting.Reporter)),
|
||||
mkReporter = Option(() => mkReporter()),
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -749,14 +749,10 @@ private[sbt] object Load {
|
|||
|
||||
// NOTE - because we create an eval here, we need a clean-eval later for this URI.
|
||||
lazy val eval = timed("Load.loadUnit: mkEval", log) {
|
||||
def mkReporter() = EvalReporter.console
|
||||
// todo:
|
||||
// def mkReporter(settings: scala.tools.nsc.Settings): EvalReporter =
|
||||
// plugs.pluginData.buildTarget match {
|
||||
// case None => EvalReporter.console // (settings)
|
||||
// case Some(buildTarget) =>
|
||||
// new BuildServerEvalReporter(buildTarget, new ConsoleReporter(settings))
|
||||
// }
|
||||
def mkReporter(): EvalReporter = plugs.pluginData.buildTarget match {
|
||||
case None => EvalReporter.console
|
||||
case Some(buildTarget) => new BuildServerEvalReporter(buildTarget, EvalReporter.console)
|
||||
}
|
||||
mkEval(
|
||||
classpath = plugs.classpath.map(converter.toPath),
|
||||
defDir,
|
||||
|
|
|
|||
|
|
@ -9,57 +9,23 @@ package sbt.internal.server
|
|||
|
||||
import sbt._
|
||||
import sbt.internal.bsp._
|
||||
import sbt.internal.io.Retry
|
||||
import sbt.internal.server.BspCompileTask.{ compileReport, exchange }
|
||||
import sbt.librarymanagement.Configuration
|
||||
import sjsonnew.support.scalajson.unsafe.Converter
|
||||
import xsbti.compile.CompileResult
|
||||
import xsbti.compile.CompileAnalysis
|
||||
import xsbti.{ CompileFailed, Problem, Severity }
|
||||
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
object BspCompileTask {
|
||||
private lazy val exchange = StandardMain.exchange
|
||||
|
||||
def compute(targetId: BuildTargetIdentifier, project: ProjectRef, config: Configuration)(
|
||||
compile: BspCompileTask => CompileResult
|
||||
): CompileResult = {
|
||||
val task = BspCompileTask(targetId, project, config)
|
||||
try {
|
||||
task.notifyStart()
|
||||
val result = Retry(compile(task))
|
||||
task.notifySuccess(result)
|
||||
result
|
||||
} catch {
|
||||
case NonFatal(cause) =>
|
||||
val compileFailed = cause match {
|
||||
case failed: CompileFailed => Some(failed)
|
||||
case _ => None
|
||||
}
|
||||
task.notifyFailure(compileFailed)
|
||||
throw cause
|
||||
}
|
||||
}
|
||||
|
||||
private def apply(
|
||||
def start(
|
||||
targetId: BuildTargetIdentifier,
|
||||
project: ProjectRef,
|
||||
config: Configuration
|
||||
): BspCompileTask = {
|
||||
val taskId = TaskId(BuildServerTasks.uniqueId, Vector())
|
||||
val targetName = BuildTargetName.fromScope(project.project, config.name)
|
||||
BspCompileTask(targetId, targetName, taskId, System.currentTimeMillis())
|
||||
}
|
||||
|
||||
private def compileReport(
|
||||
problems: Seq[Problem],
|
||||
targetId: BuildTargetIdentifier,
|
||||
elapsedTimeMillis: Long
|
||||
): CompileReport = {
|
||||
val countBySeverity = problems.groupBy(_.severity).view.mapValues(_.size)
|
||||
val warnings = countBySeverity.getOrElse(Severity.Warn, 0)
|
||||
val errors = countBySeverity.getOrElse(Severity.Error, 0)
|
||||
CompileReport(targetId, None, errors, warnings, Some(elapsedTimeMillis.toInt))
|
||||
val task = BspCompileTask(targetId, targetName, taskId, System.currentTimeMillis())
|
||||
task.notifyStart()
|
||||
task
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,16 +41,16 @@ case class BspCompileTask private (
|
|||
val message = s"Compiling $targetName"
|
||||
val data = Converter.toJsonUnsafe(CompileTask(targetId))
|
||||
val params = TaskStartParams(id, startTimeMillis, message, "compile-task", data)
|
||||
exchange.notifyEvent("build/taskStart", params)
|
||||
StandardMain.exchange.notifyEvent("build/taskStart", params)
|
||||
}
|
||||
|
||||
private[sbt] def notifySuccess(result: CompileResult): Unit = {
|
||||
private[sbt] def notifySuccess(analysis: CompileAnalysis): Unit = {
|
||||
import scala.jdk.CollectionConverters.*
|
||||
val endTimeMillis = System.currentTimeMillis()
|
||||
val elapsedTimeMillis = endTimeMillis - startTimeMillis
|
||||
val sourceInfos = result.analysis().readSourceInfos().getAllSourceInfos.asScala
|
||||
val sourceInfos = analysis.readSourceInfos().getAllSourceInfos.asScala
|
||||
val problems = sourceInfos.values.flatMap(_.getReportedProblems).toSeq
|
||||
val report = compileReport(problems, targetId, elapsedTimeMillis)
|
||||
val report = compileReport(problems, elapsedTimeMillis)
|
||||
val params = TaskFinishParams(
|
||||
id,
|
||||
endTimeMillis,
|
||||
|
|
@ -93,7 +59,7 @@ case class BspCompileTask private (
|
|||
"compile-report",
|
||||
Converter.toJsonUnsafe(report)
|
||||
)
|
||||
exchange.notifyEvent("build/taskFinish", params)
|
||||
StandardMain.exchange.notifyEvent("build/taskFinish", params)
|
||||
}
|
||||
|
||||
private[sbt] def notifyProgress(percentage: Int, total: Int): Unit = {
|
||||
|
|
@ -110,14 +76,14 @@ case class BspCompileTask private (
|
|||
Some("compile-progress"),
|
||||
Some(data)
|
||||
)
|
||||
exchange.notifyEvent("build/taskProgress", params)
|
||||
StandardMain.exchange.notifyEvent("build/taskProgress", params)
|
||||
}
|
||||
|
||||
private[sbt] def notifyFailure(cause: Option[CompileFailed]): Unit = {
|
||||
val endTimeMillis = System.currentTimeMillis()
|
||||
val elapsedTimeMillis = endTimeMillis - startTimeMillis
|
||||
val problems = cause.map(_.problems().toSeq).getOrElse(Seq.empty[Problem])
|
||||
val report = compileReport(problems, targetId, elapsedTimeMillis)
|
||||
val report = compileReport(problems, elapsedTimeMillis)
|
||||
val params = TaskFinishParams(
|
||||
id,
|
||||
endTimeMillis,
|
||||
|
|
@ -126,6 +92,13 @@ case class BspCompileTask private (
|
|||
"compile-report",
|
||||
Converter.toJsonUnsafe(report)
|
||||
)
|
||||
exchange.notifyEvent("build/taskFinish", params)
|
||||
StandardMain.exchange.notifyEvent("build/taskFinish", params)
|
||||
}
|
||||
|
||||
private def compileReport(problems: Seq[Problem], elapsedTimeMillis: Long): CompileReport = {
|
||||
val countBySeverity = problems.groupBy(_.severity).view.mapValues(_.size)
|
||||
val warnings = countBySeverity.getOrElse(Severity.Warn, 0)
|
||||
val errors = countBySeverity.getOrElse(Severity.Error, 0)
|
||||
CompileReport(targetId, None, errors, warnings, Some(elapsedTimeMillis.toInt))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,35 +8,34 @@
|
|||
package sbt.internal.server
|
||||
|
||||
import dotty.tools.dotc.core.Contexts.Context
|
||||
import dotty.tools.dotc.reporting.{ Diagnostic => ScalaDiagnostic }
|
||||
import dotty.tools.dotc.reporting.Reporter
|
||||
// import sbt.StandardMain.exchange
|
||||
import sbt.internal.ForwardingReporter
|
||||
import dotty.tools.dotc.reporting.{ Diagnostic => ScalaDiagnostic }
|
||||
import dotty.tools.dotc.util.SourcePosition
|
||||
import sbt.StandardMain.exchange
|
||||
import sbt.internal.EvalReporter
|
||||
import sbt.internal.bsp
|
||||
import sbt.internal.bsp.{
|
||||
BuildTargetIdentifier,
|
||||
Diagnostic,
|
||||
// DiagnosticSeverity,
|
||||
// PublishDiagnosticsParams,
|
||||
// Range,
|
||||
// TextDocumentIdentifier
|
||||
}
|
||||
import sbt.internal.bsp.BuildTargetIdentifier
|
||||
import sbt.internal.bsp.Diagnostic
|
||||
import sbt.internal.bsp.DiagnosticSeverity
|
||||
import sbt.internal.bsp.PublishDiagnosticsParams
|
||||
import sbt.internal.bsp.Range
|
||||
import sbt.internal.bsp.TextDocumentIdentifier
|
||||
import sbt.internal.bsp.codec.JsonProtocol._
|
||||
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import scala.collection.mutable
|
||||
|
||||
class BuildServerEvalReporter(buildTarget: BuildTargetIdentifier, delegate: Reporter)
|
||||
extends ForwardingReporter(delegate):
|
||||
extends EvalReporter:
|
||||
private val problemsByFile = mutable.Map[Path, Vector[Diagnostic]]()
|
||||
|
||||
override def doReport(dia: ScalaDiagnostic)(using Context): Unit = {
|
||||
/*
|
||||
for {
|
||||
filePath <- if (pos.source.file.exists) Some(Paths.get(pos.source.file.path)) else None
|
||||
range <- convertToRange(pos)
|
||||
} {
|
||||
val bspSeverity = convertToBsp(severity)
|
||||
val diagnostic = Diagnostic(range, bspSeverity, None, Option("sbt"), msg)
|
||||
if (dia.pos.exists) {
|
||||
val filePath = Paths.get(dia.pos.source.file.path)
|
||||
val range = convertToRange(dia.pos)
|
||||
val bspSeverity = convertToBsp(dia.level)
|
||||
val diagnostic = Diagnostic(range, bspSeverity, None, Option("sbt"), dia.msg.message)
|
||||
problemsByFile(filePath) = problemsByFile.getOrElse(filePath, Vector()) :+ diagnostic
|
||||
val params = PublishDiagnosticsParams(
|
||||
TextDocumentIdentifier(filePath.toUri),
|
||||
|
|
@ -46,51 +45,40 @@ class BuildServerEvalReporter(buildTarget: BuildTargetIdentifier, delegate: Repo
|
|||
reset = false
|
||||
)
|
||||
exchange.notifyEvent("build/publishDiagnostics", params)
|
||||
delegate.doReport(dia)
|
||||
}
|
||||
*/
|
||||
super.doReport(dia)
|
||||
}
|
||||
|
||||
/*
|
||||
def finalReport(sourceName: String): Unit = {
|
||||
override def finalReport(sourceName: String): Unit = {
|
||||
val filePath = Paths.get(sourceName)
|
||||
if (Files.exists(filePath)) {
|
||||
val diagnostics = problemsByFile.getOrElse(filePath, Vector())
|
||||
val params = PublishDiagnosticsParams(
|
||||
textDocument = TextDocumentIdentifier(filePath.toUri),
|
||||
buildTarget,
|
||||
originId = None,
|
||||
diagnostics,
|
||||
reset = true
|
||||
)
|
||||
exchange.notifyEvent("build/publishDiagnostics", params)
|
||||
}
|
||||
val diagnostics = problemsByFile.getOrElse(filePath, Vector())
|
||||
val params = PublishDiagnosticsParams(
|
||||
textDocument = TextDocumentIdentifier(filePath.toUri),
|
||||
buildTarget,
|
||||
originId = None,
|
||||
diagnostics,
|
||||
reset = true
|
||||
)
|
||||
exchange.notifyEvent("build/publishDiagnostics", params)
|
||||
}
|
||||
|
||||
private def convertToBsp(severity: Severity): Option[Long] = {
|
||||
private def convertToBsp(severity: Int): Option[Long] = {
|
||||
val result = severity match {
|
||||
case Reporter.INFO => DiagnosticSeverity.Information
|
||||
case Reporter.WARNING => DiagnosticSeverity.Warning
|
||||
case Reporter.ERROR => DiagnosticSeverity.Error
|
||||
case dotty.tools.dotc.interfaces.Diagnostic.INFO => DiagnosticSeverity.Information
|
||||
case dotty.tools.dotc.interfaces.Diagnostic.WARNING => DiagnosticSeverity.Warning
|
||||
case dotty.tools.dotc.interfaces.Diagnostic.ERROR => DiagnosticSeverity.Error
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
|
||||
private def convertToRange(pos: Position): Option[Range] = {
|
||||
pos match {
|
||||
case _: DefinedPosition =>
|
||||
val startLine = pos.source.offsetToLine(pos.start)
|
||||
val startChar = pos.start - pos.source.lineToOffset(startLine)
|
||||
val endLine = pos.source.offsetToLine(pos.end)
|
||||
val endChar = pos.end - pos.source.lineToOffset(endLine)
|
||||
Some(
|
||||
Range(
|
||||
bsp.Position(startLine.toLong, startChar.toLong),
|
||||
bsp.Position(endLine.toLong, endChar.toLong)
|
||||
)
|
||||
)
|
||||
case _ => None
|
||||
}
|
||||
private def convertToRange(pos: SourcePosition): Range = {
|
||||
val startLine = pos.source.offsetToLine(pos.start)
|
||||
val startChar = pos.start - pos.source.lineToOffset(startLine)
|
||||
val endLine = pos.source.offsetToLine(pos.end)
|
||||
val endChar = pos.end - pos.source.lineToOffset(endLine)
|
||||
Range(
|
||||
bsp.Position(startLine.toLong, startChar.toLong),
|
||||
bsp.Position(endLine.toLong, endChar.toLong)
|
||||
)
|
||||
}
|
||||
*/
|
||||
end BuildServerEvalReporter
|
||||
|
|
|
|||
|
|
@ -38,9 +38,8 @@ import java.io.File
|
|||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import scala.collection.mutable
|
||||
|
||||
// import scala.annotation.nowarn
|
||||
import scala.util.control.NonFatal
|
||||
import scala.util.{ Failure, Success }
|
||||
import scala.util.{ Try, Failure, Success }
|
||||
import scala.annotation.nowarn
|
||||
import sbt.testing.Framework
|
||||
|
||||
|
|
@ -62,14 +61,10 @@ object BuildServerProtocol {
|
|||
|
||||
private val bspReload = "bspReload"
|
||||
|
||||
private lazy val targetIdentifierParser: Parser[Seq[BuildTargetIdentifier]] =
|
||||
private val targetIdentifierParser: Parser[Seq[BuildTargetIdentifier]] =
|
||||
Def
|
||||
.spaceDelimited()
|
||||
.map { xs =>
|
||||
xs.map { uri =>
|
||||
BuildTargetIdentifier(URI.create(uri))
|
||||
}
|
||||
}
|
||||
.map(xs => xs.map(uri => BuildTargetIdentifier(URI.create(uri))))
|
||||
|
||||
lazy val commands: Seq[Command] = Seq(
|
||||
Command.single(bspReload) { (state, reqId) =>
|
||||
|
|
@ -103,7 +98,7 @@ object BuildServerProtocol {
|
|||
bspSbtEnabled := true,
|
||||
bspFullWorkspace := bspFullWorkspaceSetting.value,
|
||||
bspWorkspace := bspFullWorkspace.value.scopes,
|
||||
bspWorkspaceBuildTargets := (Def
|
||||
bspWorkspaceBuildTargets := Def
|
||||
.task {
|
||||
val workspace = Keys.bspFullWorkspace.value
|
||||
val state = Keys.state.value
|
||||
|
|
@ -121,186 +116,137 @@ object BuildServerProtocol {
|
|||
state.respondEvent(WorkspaceBuildTargetsResult(successfulBuildTargets.toVector))
|
||||
successfulBuildTargets
|
||||
}
|
||||
})
|
||||
}
|
||||
.value,
|
||||
// https://github.com/build-server-protocol/build-server-protocol/blob/master/docs/specification.md#build-target-sources-request
|
||||
bspBuildTargetSources := (Def
|
||||
.input((s: State) => targetIdentifierParser)
|
||||
.flatMapTask { targets =>
|
||||
val s = state.value
|
||||
// val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri)))
|
||||
val workspace = bspFullWorkspace.value.filter(targets)
|
||||
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||
// run the worker task concurrently
|
||||
Def.task {
|
||||
val items = bspBuildTargetSourcesItem.result.all(filter).value
|
||||
val buildItems = workspace.builds.map { case (id, loadedBuildUnit) =>
|
||||
val base = loadedBuildUnit.localBase
|
||||
val sbtFiles = configurationSources(base)
|
||||
val pluginData = loadedBuildUnit.unit.plugins.pluginData
|
||||
val dirs = pluginData.unmanagedSourceDirectories
|
||||
val sourceFiles = getStandaloneSourceFiles(pluginData.unmanagedSources, dirs)
|
||||
val managedDirs = pluginData.managedSourceDirectories
|
||||
val managedSourceFiles =
|
||||
getStandaloneSourceFiles(pluginData.managedSources, managedDirs)
|
||||
val items =
|
||||
dirs.map(toSourceItem(SourceItemKind.Directory, generated = false)) ++
|
||||
sourceFiles.map(toSourceItem(SourceItemKind.File, generated = false)) ++
|
||||
managedDirs.map(toSourceItem(SourceItemKind.Directory, generated = true)) ++
|
||||
managedSourceFiles.map(toSourceItem(SourceItemKind.File, generated = true)) ++
|
||||
sbtFiles.map(toSourceItem(SourceItemKind.File, generated = false))
|
||||
Result.Value(SourcesItem(id, items.toVector))
|
||||
}
|
||||
val successfulItems = anyOrThrow(items ++ buildItems)
|
||||
val result = SourcesResult(successfulItems.toVector)
|
||||
s.respondEvent(result)
|
||||
}
|
||||
})
|
||||
.value,
|
||||
bspBuildTargetSources := bspInputTask { (workspace, filter) =>
|
||||
val items = bspBuildTargetSourcesItem.result.all(filter).value
|
||||
val buildItems = workspace.builds.map { case (id, loadedBuildUnit) =>
|
||||
val base = loadedBuildUnit.localBase
|
||||
val sbtFiles = configurationSources(base)
|
||||
val pluginData = loadedBuildUnit.unit.plugins.pluginData
|
||||
val dirs = pluginData.unmanagedSourceDirectories
|
||||
val sourceFiles = getStandaloneSourceFiles(pluginData.unmanagedSources, dirs)
|
||||
val managedDirs = pluginData.managedSourceDirectories
|
||||
val managedSourceFiles =
|
||||
getStandaloneSourceFiles(pluginData.managedSources, managedDirs)
|
||||
val items =
|
||||
dirs.map(toSourceItem(SourceItemKind.Directory, generated = false)) ++
|
||||
sourceFiles.map(toSourceItem(SourceItemKind.File, generated = false)) ++
|
||||
managedDirs.map(toSourceItem(SourceItemKind.Directory, generated = true)) ++
|
||||
managedSourceFiles.map(toSourceItem(SourceItemKind.File, generated = true)) ++
|
||||
sbtFiles.map(toSourceItem(SourceItemKind.File, generated = false))
|
||||
Result.Value(SourcesItem(id, items.toVector))
|
||||
}
|
||||
val successfulItems = anyOrThrow(items ++ buildItems)
|
||||
val result = SourcesResult(successfulItems.toVector)
|
||||
state.value.respondEvent(result)
|
||||
}.evaluated,
|
||||
bspBuildTargetSources / aggregate := false,
|
||||
bspBuildTargetResources := (Def
|
||||
.input((s: State) => targetIdentifierParser)
|
||||
.flatMapTask { targets =>
|
||||
val s = state.value
|
||||
val workspace = bspFullWorkspace.value.filter(targets)
|
||||
workspace.warnIfBuildsNonEmpty(Method.Resources, s.log)
|
||||
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||
// run the worker task concurrently
|
||||
Def.task {
|
||||
val items = bspBuildTargetResourcesItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = ResourcesResult(successfulItems.toVector)
|
||||
s.respondEvent(result)
|
||||
}
|
||||
})
|
||||
.value,
|
||||
bspBuildTargetResources := bspInputTask { (_, filter) =>
|
||||
val items = bspBuildTargetResourcesItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = ResourcesResult(successfulItems.toVector)
|
||||
state.value.respondEvent(result)
|
||||
}.evaluated,
|
||||
bspBuildTargetResources / aggregate := false,
|
||||
bspBuildTargetDependencySources := (Def
|
||||
.input((s: State) => targetIdentifierParser)
|
||||
.flatMapTask { targets =>
|
||||
val s = state.value
|
||||
val workspace = bspFullWorkspace.value.filter(targets)
|
||||
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||
// run the worker task concurrently
|
||||
Def.task {
|
||||
import sbt.internal.bsp.codec.JsonProtocol._
|
||||
val items = bspBuildTargetDependencySourcesItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = DependencySourcesResult(successfulItems.toVector)
|
||||
s.respondEvent(result)
|
||||
}
|
||||
})
|
||||
.value,
|
||||
bspBuildTargetDependencySources := bspInputTask { (_, filter) =>
|
||||
val items = bspBuildTargetDependencySourcesItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = DependencySourcesResult(successfulItems.toVector)
|
||||
state.value.respondEvent(result)
|
||||
}.evaluated,
|
||||
bspBuildTargetDependencySources / aggregate := false,
|
||||
bspBuildTargetCompile := (Def
|
||||
.input((s: State) => targetIdentifierParser)
|
||||
.flatMapTask { targets =>
|
||||
val s: State = state.value
|
||||
val workspace = bspFullWorkspace.value.filter(targets)
|
||||
workspace.warnIfBuildsNonEmpty(Method.Compile, s.log)
|
||||
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||
Def.task {
|
||||
val statusCodes = Keys.bspBuildTargetCompileItem.result.all(filter).value
|
||||
val aggregatedStatusCode = allOrThrow(statusCodes) match {
|
||||
case Seq() => StatusCode.Success
|
||||
case codes => codes.max
|
||||
}
|
||||
s.respondEvent(BspCompileResult(None, aggregatedStatusCode))
|
||||
}
|
||||
})
|
||||
.value,
|
||||
bspBuildTargetCompile := bspInputTask { (workspace, filter) =>
|
||||
val s = state.value
|
||||
workspace.warnIfBuildsNonEmpty(Method.Compile, s.log)
|
||||
val statusCodes = Keys.bspBuildTargetCompileItem.result.all(filter).value
|
||||
val aggregatedStatusCode = allOrThrow(statusCodes) match {
|
||||
case Seq() => StatusCode.Success
|
||||
case codes => codes.max
|
||||
}
|
||||
s.respondEvent(BspCompileResult(None, aggregatedStatusCode))
|
||||
}.evaluated,
|
||||
bspBuildTargetOutputPaths := bspInputTask { (_, filter) =>
|
||||
val items = bspBuildTargetOutputPathsItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = OutputPathsResult(successfulItems.toVector)
|
||||
state.value.respondEvent(result)
|
||||
}.evaluated,
|
||||
bspBuildTargetOutputPaths / aggregate := false,
|
||||
bspBuildTargetCompile / aggregate := false,
|
||||
bspBuildTargetTest := bspTestTask.evaluated,
|
||||
bspBuildTargetTest / aggregate := false,
|
||||
bspBuildTargetCleanCache := (Def
|
||||
.input((s: State) => targetIdentifierParser)
|
||||
.flatMapTask { targets =>
|
||||
val s: State = state.value
|
||||
val workspace = bspFullWorkspace.value.filter(targets)
|
||||
workspace.warnIfBuildsNonEmpty(Method.CleanCache, s.log)
|
||||
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||
Def.task {
|
||||
val results = Keys.clean.result.all(filter).value
|
||||
val successes = anyOrThrow(results).size
|
||||
bspBuildTargetCleanCache := bspInputTask { (workspace, filter) =>
|
||||
val s = state.value
|
||||
workspace.warnIfBuildsNonEmpty(Method.CleanCache, s.log)
|
||||
val results = Keys.clean.result.all(filter).value
|
||||
val successes = anyOrThrow(results).size
|
||||
|
||||
// When asking to Rebuild Project, IntelliJ sends the root build as an additional target, however it is
|
||||
// not returned as part of the results. In this case, there's 1 build entry in the workspace, and we're
|
||||
// checking that the executed results plus this entry is equal to the total number of targets.
|
||||
// When rebuilding a single module, the root build isn't sent, just the requested targets.
|
||||
val cleaned = successes + workspace.builds.size == targets.size
|
||||
s.respondEvent(CleanCacheResult(None, cleaned))
|
||||
}
|
||||
})
|
||||
.value,
|
||||
// When asking to rebuild Project, IntelliJ sends the root build as an additional target,
|
||||
// however it is not returned as part of the results. We're checking that the number of
|
||||
// results equals the number of scopes (not the root build).
|
||||
// When rebuilding a single module, the root build isn't sent, just the requested targets.
|
||||
val cleaned = successes == workspace.scopes.size
|
||||
s.respondEvent(CleanCacheResult(None, cleaned))
|
||||
}.evaluated,
|
||||
bspBuildTargetCleanCache / aggregate := false,
|
||||
bspBuildTargetScalacOptions := (Def
|
||||
.input((s: State) => targetIdentifierParser)
|
||||
.flatMapTask { targets =>
|
||||
val s = state.value
|
||||
val workspace = bspFullWorkspace.value.filter(targets)
|
||||
val builds = workspace.builds
|
||||
|
||||
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||
Def.task {
|
||||
val items = bspBuildTargetScalacOptionsItem.result.all(filter).value
|
||||
val appProvider = appConfiguration.value.provider()
|
||||
val sbtJars = appProvider.mainClasspath()
|
||||
val buildItems = builds.map { build =>
|
||||
val plugins: LoadedPlugins = build._2.unit.plugins
|
||||
val scalacOptions = plugins.pluginData.scalacOptions
|
||||
val pluginClasspath = plugins.classpath
|
||||
val converter = plugins.pluginData.converter
|
||||
val classpath =
|
||||
pluginClasspath.map(converter.toPath).map(_.toFile).map(_.toURI).toVector ++
|
||||
(sbtJars).map(_.toURI).toVector
|
||||
val item = ScalacOptionsItem(
|
||||
build._1,
|
||||
scalacOptions.toVector,
|
||||
classpath,
|
||||
new File(build._2.localBase, "project/target").toURI
|
||||
)
|
||||
Result.Value(item)
|
||||
}
|
||||
val successfulItems = anyOrThrow(items ++ buildItems)
|
||||
val result = ScalacOptionsResult(successfulItems.toVector)
|
||||
s.respondEvent(result)
|
||||
}
|
||||
})
|
||||
.value,
|
||||
bspBuildTargetScalacOptions := bspInputTask { (workspace, filter) =>
|
||||
val items = bspBuildTargetScalacOptionsItem.result.all(filter).value
|
||||
val appProvider = appConfiguration.value.provider()
|
||||
val sbtJars = appProvider.mainClasspath()
|
||||
val buildItems = workspace.builds.map { build =>
|
||||
val plugins: LoadedPlugins = build._2.unit.plugins
|
||||
val scalacOptions = plugins.pluginData.scalacOptions
|
||||
val pluginClasspath = plugins.classpath
|
||||
val converter = plugins.pluginData.converter
|
||||
val classpath =
|
||||
pluginClasspath.map(converter.toPath).map(_.toFile).map(_.toURI).toVector ++
|
||||
(sbtJars).map(_.toURI).toVector
|
||||
val item = ScalacOptionsItem(
|
||||
build._1,
|
||||
scalacOptions.toVector,
|
||||
classpath,
|
||||
new File(build._2.localBase, "project/target").toURI
|
||||
)
|
||||
Result.Value(item)
|
||||
}
|
||||
val successfulItems = anyOrThrow(items ++ buildItems)
|
||||
val result = ScalacOptionsResult(successfulItems.toVector)
|
||||
state.value.respondEvent(result)
|
||||
}.evaluated,
|
||||
bspBuildTargetScalacOptions / aggregate := false,
|
||||
bspScalaTestClasses := (Def
|
||||
.input((s: State) => targetIdentifierParser)
|
||||
.flatMapTask { targets =>
|
||||
val s = state.value
|
||||
val workspace = bspFullWorkspace.value.filter(targets)
|
||||
workspace.warnIfBuildsNonEmpty(Method.ScalaTestClasses, s.log)
|
||||
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||
Def.task {
|
||||
val items = bspScalaTestClassesItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow[Seq[ScalaTestClassesItem]](items).flatten
|
||||
val result = ScalaTestClassesResult(
|
||||
items = successfulItems.toVector,
|
||||
originId = None: Option[String]
|
||||
)
|
||||
s.respondEvent(result)
|
||||
}
|
||||
})
|
||||
.value,
|
||||
bspScalaMainClasses := (Def
|
||||
.input((s: State) => targetIdentifierParser)
|
||||
.flatMapTask { targets =>
|
||||
val s = state.value
|
||||
val workspace = bspFullWorkspace.value.filter(targets)
|
||||
workspace.warnIfBuildsNonEmpty(Method.ScalaMainClasses, s.log)
|
||||
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||
Def.task {
|
||||
val items = bspScalaMainClassesItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = ScalaMainClassesResult(successfulItems.toVector, None)
|
||||
s.respondEvent(result)
|
||||
}
|
||||
})
|
||||
.value,
|
||||
bspBuildTargetJVMRunEnvironment := bspInputTask { (_, filter) =>
|
||||
val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = JvmRunEnvironmentResult(successfulItems.toVector, None)
|
||||
state.value.respondEvent(result)
|
||||
}.evaluated,
|
||||
bspBuildTargetJVMRunEnvironment / aggregate := false,
|
||||
bspBuildTargetJVMTestEnvironment := bspInputTask { (_, filter) =>
|
||||
val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = JvmTestEnvironmentResult(successfulItems.toVector, None)
|
||||
state.value.respondEvent(result)
|
||||
}.evaluated,
|
||||
bspBuildTargetJVMTestEnvironment / aggregate := false,
|
||||
bspScalaTestClasses := bspInputTask { (workspace, filter) =>
|
||||
val s = state.value
|
||||
val items = bspScalaTestClassesItem.result.all(filter).value
|
||||
workspace.warnIfBuildsNonEmpty(Method.ScalaTestClasses, s.log)
|
||||
val successfulItems = anyOrThrow[Seq[ScalaTestClassesItem]](items).flatten
|
||||
val result = ScalaTestClassesResult(
|
||||
items = successfulItems.toVector,
|
||||
originId = None: Option[String]
|
||||
)
|
||||
s.respondEvent(result)
|
||||
}.evaluated,
|
||||
bspScalaMainClasses := bspInputTask { (_, filter) =>
|
||||
val items = bspScalaMainClassesItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = ScalaMainClassesResult(successfulItems.toVector, None)
|
||||
state.value.respondEvent(result)
|
||||
}.evaluated,
|
||||
bspScalaMainClasses / aggregate := false
|
||||
)
|
||||
|
||||
|
|
@ -344,22 +290,6 @@ object BuildServerProtocol {
|
|||
bspBuildTargetCompileItem := bspCompileTask.value,
|
||||
bspBuildTargetRun := bspRunTask.evaluated,
|
||||
bspBuildTargetScalacOptionsItem := scalacOptionsTask.value,
|
||||
bspBuildTargetJVMRunEnvironment := bspInputTask { (state, _, _, filter) =>
|
||||
Def.task {
|
||||
val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = JvmRunEnvironmentResult(successfulItems.toVector, None)
|
||||
state.respondEvent(result)
|
||||
}
|
||||
}.evaluated,
|
||||
bspBuildTargetJVMTestEnvironment := bspInputTask { (state, _, _, filter) =>
|
||||
Def.task {
|
||||
val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value
|
||||
val successfulItems = anyOrThrow(items)
|
||||
val result = JvmTestEnvironmentResult(successfulItems.toVector, None)
|
||||
state.respondEvent(result)
|
||||
}
|
||||
}.evaluated,
|
||||
bspBuildTargetJvmEnvironmentItem := jvmEnvironmentItem().value,
|
||||
bspInternalDependencyConfigurations := internalDependencyConfigurationsSetting.value,
|
||||
bspScalaTestClassesItem := scalaTestClassesTask.value,
|
||||
|
|
@ -755,21 +685,16 @@ object BuildServerProtocol {
|
|||
)
|
||||
}
|
||||
|
||||
private def bspInputTask[T](
|
||||
taskImpl: (
|
||||
State,
|
||||
Seq[BuildTargetIdentifier],
|
||||
BspFullWorkspace,
|
||||
ScopeFilter
|
||||
) => Def.Initialize[Task[T]]
|
||||
private inline def bspInputTask[T](
|
||||
inline taskImpl: (BspFullWorkspace, ScopeFilter) => T
|
||||
): Def.Initialize[InputTask[T]] =
|
||||
Def
|
||||
.input((s: State) => targetIdentifierParser)
|
||||
.input(_ => targetIdentifierParser)
|
||||
.flatMapTask { targets =>
|
||||
val s = state.value
|
||||
val workspace: BspFullWorkspace = bspFullWorkspace.value.filter(targets)
|
||||
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||
taskImpl(s, targets, workspace, filter)
|
||||
Def.task(taskImpl(workspace, filter))
|
||||
}
|
||||
|
||||
private def jvmEnvironmentItem(): Initialize[Task[JvmEnvironmentItem]] = Def.task {
|
||||
|
|
@ -865,12 +790,12 @@ object BuildServerProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
private val jsonParser: Parser[JValue] = (Parsers.any.*).map(_.mkString)
|
||||
.map(JsonParser.parseUnsafe)
|
||||
private val jsonParser: Parser[Try[JValue]] = Parsers.any.*.map(_.mkString)
|
||||
.map(JsonParser.parseFromString)
|
||||
|
||||
private def bspRunTask: Def.Initialize[InputTask[Unit]] =
|
||||
Def.input((s: State) => jsonParser).flatMapTask { json =>
|
||||
val runParams = Converter.fromJson[RunParams](json).get
|
||||
Def.input(_ => jsonParser).flatMapTask { json =>
|
||||
val runParams = json.flatMap(Converter.fromJson[RunParams]).get
|
||||
val defaultClass = Keys.mainClass.value
|
||||
val defaultJvmOptions = Keys.javaOptions.value
|
||||
|
||||
|
|
@ -912,8 +837,8 @@ object BuildServerProtocol {
|
|||
}
|
||||
|
||||
private def bspTestTask: Def.Initialize[InputTask[Unit]] =
|
||||
Def.input((s: State) => jsonParser).flatMapTask { json =>
|
||||
val testParams = Converter.fromJson[TestParams](json).get
|
||||
Def.input(_ => jsonParser).flatMapTask { json =>
|
||||
val testParams = json.flatMap(Converter.fromJson[TestParams]).get
|
||||
val workspace = bspFullWorkspace.value
|
||||
|
||||
val resultTask: Def.Initialize[Task[Result[Seq[Unit]]]] = testParams.dataKind match {
|
||||
|
|
|
|||
|
|
@ -39,9 +39,7 @@ sealed trait BuildServerReporter extends Reporter {
|
|||
|
||||
protected def publishDiagnostic(problem: Problem): Unit
|
||||
|
||||
def sendSuccessReport(
|
||||
analysis: CompileAnalysis,
|
||||
): Unit
|
||||
def sendSuccessReport(analysis: CompileAnalysis): Unit
|
||||
|
||||
def sendFailureReport(sources: Array[VirtualFile]): Unit
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ private[sbt] object LanguageServerProtocol {
|
|||
onCancellationRequest(Option(r.id), param)
|
||||
|
||||
case r: JsonRpcRequestMessage if r.method == "sbt/completion" =>
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
val param = Converter.fromJson[CP](json(r)).get
|
||||
onCompletionRequest(Option(r.id), param)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
ThisBuild / scalaVersion := "2.13.8"
|
||||
|
||||
Global / serverLog / logLevel := Level.Debug
|
||||
Global / cacheStores := Seq.empty
|
||||
|
||||
lazy val runAndTest = project.in(file("run-and-test"))
|
||||
.settings(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ TaskKey[Unit]("willSucceed") := println("success")
|
|||
|
||||
TaskKey[Unit]("willFail") := { throw new Exception("failed") }
|
||||
|
||||
scalaVersion := "2.12.19"
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test"
|
||||
|
||||
TaskKey[Unit]("fooBar") := { () }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
val hello = taskKey[Unit]("Say hello")
|
||||
|
||||
scalaVersion := "3.3.1"
|
||||
hello := {}
|
||||
|
|
|
|||
|
|
@ -48,26 +48,28 @@ lazy val fooClasspath = taskKey[Unit]("")
|
|||
lazy val root = (project in file("."))
|
||||
.settings(
|
||||
name := "response",
|
||||
commands += Command.command("fooExport") { s0: State =>
|
||||
commands += Command.command("fooExport") { (s0: State) =>
|
||||
val (s1, cp) = s0.unsafeRunTask(Compile / fullClasspath)
|
||||
s0.respondEvent(cp.map(_.data))
|
||||
val converter = s1.setting(fileConverter)
|
||||
s1.respondEvent(cp.map(a => converter.toPath(a.data)))
|
||||
s1
|
||||
},
|
||||
commands += Command.command("fooFail") { s0: State =>
|
||||
commands += Command.command("fooFail") { (s0: State) =>
|
||||
sys.error("fail message")
|
||||
},
|
||||
commands += Command.command("fooCustomFail") { s0: State =>
|
||||
commands += Command.command("fooCustomFail") { (s0: State) =>
|
||||
import sbt.internal.protocol.JsonRpcResponseError
|
||||
throw JsonRpcResponseError(500, "some error")
|
||||
},
|
||||
commands += Command.command("fooNotification") { s0: State =>
|
||||
commands += Command.command("fooNotification") { (s0: State) =>
|
||||
import CacheImplicits._
|
||||
s0.notifyEvent("foo/something", "something")
|
||||
s0
|
||||
},
|
||||
fooClasspath := {
|
||||
val s = state.value
|
||||
val converter = fileConverter.value
|
||||
val cp = (Compile / fullClasspath).value
|
||||
s.respondEvent(cp.map(_.data))
|
||||
s.respondEvent(cp.map(a => converter.toPath(a.data)))
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,27 +7,27 @@
|
|||
|
||||
package testpkg
|
||||
|
||||
import sbt.internal.bsp._
|
||||
import sbt.internal.bsp.*
|
||||
import sbt.internal.langserver.ErrorCodes
|
||||
import sbt.IO
|
||||
import sbt.internal.protocol.JsonRpcRequestMessage
|
||||
import sbt.internal.protocol.codec.JsonRPCProtocol._
|
||||
import sbt.internal.protocol.codec.JsonRPCProtocol.*
|
||||
import sjsonnew.JsonWriter
|
||||
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.nio.file.Paths
|
||||
import scala.concurrent.duration._
|
||||
import java.nio.file.Files
|
||||
import scala.concurrent.duration.*
|
||||
|
||||
// starts svr using server-test/buildserver and perform custom server tests
|
||||
object BuildServerTest extends AbstractServerTest {
|
||||
class BuildServerTest extends AbstractServerTest {
|
||||
|
||||
import sbt.internal.bsp.codec.JsonProtocol._
|
||||
|
||||
override val testDirectory: String = "buildserver"
|
||||
|
||||
test("build/initialize") { _ =>
|
||||
test("build/initialize") {
|
||||
initializeRequest()
|
||||
assert(svr.waitForString(10.seconds) { s =>
|
||||
(s contains """"id":"8"""") &&
|
||||
|
|
@ -36,7 +36,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("workspace/buildTargets") { _ =>
|
||||
test("workspace/buildTargets") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "16", "method": "workspace/buildTargets", "params": {} }"""
|
||||
)
|
||||
|
|
@ -50,7 +50,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
assert(!result.targets.exists(_.displayName.contains("badBuildTarget")))
|
||||
}
|
||||
|
||||
test("buildTarget/sources") { _ =>
|
||||
test("buildTarget/sources") {
|
||||
val buildTarget = buildTargetUri("util", "Compile")
|
||||
val badBuildTarget = buildTargetUri("badBuildTarget", "Compile")
|
||||
svr.sendJsonRpc(buildTargetSources(24, Seq(buildTarget, badBuildTarget)))
|
||||
|
|
@ -59,7 +59,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
val sources = s.items.head.sources.map(_.uri)
|
||||
assert(sources.contains(new File(svr.baseDirectory, "util/src/main/scala").toURI))
|
||||
}
|
||||
test("buildTarget/sources: base sources") { _ =>
|
||||
test("buildTarget/sources: base sources") {
|
||||
val buildTarget = buildTargetUri("buildserver", "Compile")
|
||||
svr.sendJsonRpc(buildTargetSources(25, Seq(buildTarget)))
|
||||
assert(processing("buildTarget/sources"))
|
||||
|
|
@ -73,7 +73,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
assert(sources.contains(expectedSource))
|
||||
}
|
||||
|
||||
test("buildTarget/sources: sbt") { _ =>
|
||||
test("buildTarget/sources: sbt") {
|
||||
val x = new URI(s"${svr.baseDirectory.getAbsoluteFile.toURI}#buildserver-build")
|
||||
svr.sendJsonRpc(buildTargetSources(26, Seq(x)))
|
||||
assert(processing("buildTarget/sources"))
|
||||
|
|
@ -83,16 +83,15 @@ object BuildServerTest extends AbstractServerTest {
|
|||
"build.sbt",
|
||||
"project/A.scala",
|
||||
"project/src/main/java",
|
||||
"project/src/main/scala-2",
|
||||
"project/src/main/scala-2.12",
|
||||
"project/src/main/scala-sbt-1.0",
|
||||
"project/src/main/scala-3",
|
||||
s"project/src/main/scala-sbt-${TestProperties.version}",
|
||||
"project/src/main/scala/",
|
||||
"project/target/scala-2.12/sbt-1.0/src_managed/main"
|
||||
"target/out/jvm/scala-3.3.1/buildserver-build/src_managed/main"
|
||||
).map(rel => new File(svr.baseDirectory.getAbsoluteFile, rel).toURI).sorted
|
||||
assert(sources == expectedSources)
|
||||
}
|
||||
|
||||
test("buildTarget/compile") { _ =>
|
||||
test("buildTarget/compile") {
|
||||
val buildTarget = buildTargetUri("util", "Compile")
|
||||
|
||||
compile(buildTarget, id = 32)
|
||||
|
|
@ -102,33 +101,31 @@ object BuildServerTest extends AbstractServerTest {
|
|||
assert(res.statusCode == StatusCode.Success)
|
||||
}
|
||||
|
||||
test("buildTarget/compile - reports compilation progress") { _ =>
|
||||
test("buildTarget/compile - reports compilation progress") {
|
||||
val buildTarget = buildTargetUri("runAndTest", "Compile")
|
||||
|
||||
compile(buildTarget, id = 33)
|
||||
|
||||
// This doesn't always come back in 10s on CI.
|
||||
assert(svr.waitForString(60.seconds) { s =>
|
||||
assert(svr.waitForString(20.seconds) { s =>
|
||||
s.contains("build/taskStart") &&
|
||||
s.contains(""""message":"Compiling runAndTest"""")
|
||||
})
|
||||
|
||||
assert(svr.waitForString(60.seconds) { s =>
|
||||
assert(svr.waitForString(20.seconds) { s =>
|
||||
s.contains("build/taskProgress") &&
|
||||
s.contains(""""message":"Compiling runAndTest (15%)"""")
|
||||
})
|
||||
|
||||
assert(svr.waitForString(60.seconds) { s =>
|
||||
assert(svr.waitForString(20.seconds) { s =>
|
||||
s.contains("build/taskProgress") &&
|
||||
s.contains(""""message":"Compiling runAndTest (100%)"""")
|
||||
})
|
||||
|
||||
assert(svr.waitForString(60.seconds) { s =>
|
||||
assert(svr.waitForString(20.seconds) { s =>
|
||||
s.contains("build/publishDiagnostics")
|
||||
s.contains(""""diagnostics":[]""")
|
||||
})
|
||||
|
||||
assert(svr.waitForString(60.seconds) { s =>
|
||||
assert(svr.waitForString(20.seconds) { s =>
|
||||
s.contains("build/taskFinish") &&
|
||||
s.contains(""""message":"Compiled runAndTest"""")
|
||||
})
|
||||
|
|
@ -136,7 +133,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
|
||||
test(
|
||||
"buildTarget/compile [diagnostics] don't publish unnecessary for successful compilation case"
|
||||
) { _ =>
|
||||
) {
|
||||
val buildTarget = buildTargetUri("diagnostics", "Compile")
|
||||
val mainFile = new File(svr.baseDirectory, "diagnostics/src/main/scala/Diagnostics.scala")
|
||||
|
||||
|
|
@ -199,7 +196,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
)
|
||||
}
|
||||
|
||||
test("buildTarget/compile [diagnostics] clear stale warnings") { _ =>
|
||||
test("buildTarget/compile [diagnostics] clear stale warnings") {
|
||||
val buildTarget = buildTargetUri("diagnostics", "Compile")
|
||||
val testFile = new File(svr.baseDirectory, s"diagnostics/src/main/scala/PatternMatch.scala")
|
||||
|
||||
|
|
@ -240,7 +237,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
|
||||
}
|
||||
|
||||
test("buildTarget/scalacOptions") { _ =>
|
||||
test("buildTarget/scalacOptions") {
|
||||
val buildTarget = buildTargetUri("util", "Compile")
|
||||
val badBuildTarget = buildTargetUri("badBuildTarget", "Compile")
|
||||
svr.sendJsonRpc(
|
||||
|
|
@ -255,20 +252,14 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/cleanCache") { _ =>
|
||||
def targetDir =
|
||||
Paths
|
||||
.get(
|
||||
svr.baseDirectory.getAbsoluteFile.toString,
|
||||
"run-and-test/target/scala-2.13/classes/main"
|
||||
)
|
||||
.toFile
|
||||
|
||||
test("buildTarget/cleanCache") {
|
||||
def classFile = svr.baseDirectory.toPath.resolve(
|
||||
"target/out/jvm/scala-2.13.8/runandtest/classes/main/Main.class"
|
||||
)
|
||||
val buildTarget = buildTargetUri("runAndTest", "Compile")
|
||||
compile(buildTarget, id = 43)
|
||||
svr.waitFor[BspCompileResult](10.seconds)
|
||||
assert(targetDir.list().contains("Main.class"))
|
||||
|
||||
assert(Files.exists(classFile))
|
||||
svr.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": "44", "method": "buildTarget/cleanCache", "params": {
|
||||
| "targets": [{ "uri": "$buildTarget" }]
|
||||
|
|
@ -277,10 +268,10 @@ object BuildServerTest extends AbstractServerTest {
|
|||
assert(processing("buildTarget/cleanCache"))
|
||||
val res = svr.waitFor[CleanCacheResult](10.seconds)
|
||||
assert(res.cleaned)
|
||||
assert(targetDir.list().isEmpty)
|
||||
assert(Files.notExists(classFile))
|
||||
}
|
||||
|
||||
test("buildTarget/cleanCache: rebuild project") { _ =>
|
||||
test("buildTarget/cleanCache: rebuild project") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "45", "method": "workspace/buildTargets", "params": {} }"""
|
||||
)
|
||||
|
|
@ -300,7 +291,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
assert(res.cleaned)
|
||||
}
|
||||
|
||||
test("workspace/reload") { _ =>
|
||||
test("workspace/reload") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "48", "method": "workspace/reload"}"""
|
||||
)
|
||||
|
|
@ -311,23 +302,22 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("workspace/reload: send diagnostic and respond with error") { _ =>
|
||||
test("workspace/reload: send diagnostic and respond with error") {
|
||||
// write an other-build.sbt file that does not compile
|
||||
val otherBuildFile = new File(svr.baseDirectory, "other-build.sbt")
|
||||
IO.write(
|
||||
val otherBuildFile = svr.baseDirectory.toPath.resolve("other-build.sbt")
|
||||
Files.writeString(
|
||||
otherBuildFile,
|
||||
"""
|
||||
|val someSettings = Seq(
|
||||
| scalacOptions ++= "-deprecation"
|
||||
|)
|
||||
|""".stripMargin
|
||||
"""|val someSettings = Seq(
|
||||
| scalacOptions ++= "-deprecation"
|
||||
|)
|
||||
|""".stripMargin
|
||||
)
|
||||
// reload
|
||||
reloadWorkspace(id = 52)
|
||||
assert(
|
||||
svr.waitForString(10.seconds) { s =>
|
||||
s.contains(s""""buildTarget":{"uri":"$metaBuildTarget"}""") &&
|
||||
s.contains(s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""") &&
|
||||
s.contains(s""""textDocument":{"uri":"${otherBuildFile.toUri}"}""") &&
|
||||
s.contains(""""severity":1""") &&
|
||||
s.contains(""""reset":true""")
|
||||
}
|
||||
|
|
@ -337,32 +327,31 @@ object BuildServerTest extends AbstractServerTest {
|
|||
s.contains(""""id":"52"""") &&
|
||||
s.contains(""""error"""") &&
|
||||
s.contains(s""""code":${ErrorCodes.InternalError}""") &&
|
||||
s.contains("Type error in expression")
|
||||
s.contains("No Append.Values[Seq[String], String] found")
|
||||
}
|
||||
)
|
||||
// fix the other-build.sbt file and reload again
|
||||
IO.write(
|
||||
Files.writeString(
|
||||
otherBuildFile,
|
||||
"""
|
||||
|val someSettings = Seq(
|
||||
| scalacOptions += "-deprecation"
|
||||
|)
|
||||
|""".stripMargin
|
||||
"""|val someSettings = Seq(
|
||||
| scalacOptions += "-deprecation"
|
||||
|)
|
||||
|""".stripMargin
|
||||
)
|
||||
reloadWorkspace(id = 52)
|
||||
// assert received an empty diagnostic
|
||||
assert(
|
||||
svr.waitForString(10.seconds) { s =>
|
||||
s.contains(s""""buildTarget":{"uri":"$metaBuildTarget"}""") &&
|
||||
s.contains(s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""") &&
|
||||
s.contains(s""""textDocument":{"uri":"${otherBuildFile.toUri}"}""") &&
|
||||
s.contains(""""diagnostics":[]""") &&
|
||||
s.contains(""""reset":true""")
|
||||
}
|
||||
)
|
||||
IO.delete(otherBuildFile)
|
||||
Files.delete(otherBuildFile)
|
||||
}
|
||||
|
||||
test("buildTarget/scalaMainClasses") { _ =>
|
||||
test("buildTarget/scalaMainClasses") {
|
||||
val buildTarget = buildTargetUri("runAndTest", "Compile")
|
||||
val badBuildTarget = buildTargetUri("badBuildTarget", "Compile")
|
||||
svr.sendJsonRpc(
|
||||
|
|
@ -377,7 +366,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/run") { _ =>
|
||||
test("buildTarget/run") {
|
||||
val buildTarget = buildTargetUri("runAndTest", "Compile")
|
||||
svr.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": "64", "method": "buildTarget/run", "params": {
|
||||
|
|
@ -397,7 +386,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/jvmRunEnvironment") { _ =>
|
||||
test("buildTarget/jvmRunEnvironment") {
|
||||
val buildTarget = buildTargetUri("runAndTest", "Compile")
|
||||
svr.sendJsonRpc(
|
||||
s"""|{ "jsonrpc": "2.0",
|
||||
|
|
@ -418,7 +407,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
}
|
||||
}
|
||||
|
||||
test("buildTarget/jvmTestEnvironment") { _ =>
|
||||
test("buildTarget/jvmTestEnvironment") {
|
||||
val buildTarget = buildTargetUri("runAndTest", "Test")
|
||||
svr.sendJsonRpc(
|
||||
s"""|{ "jsonrpc": "2.0",
|
||||
|
|
@ -441,7 +430,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
}
|
||||
}
|
||||
|
||||
test("buildTarget/scalaTestClasses") { _ =>
|
||||
test("buildTarget/scalaTestClasses") {
|
||||
val buildTarget = buildTargetUri("runAndTest", "Test")
|
||||
val badBuildTarget = buildTargetUri("badBuildTarget", "Test")
|
||||
svr.sendJsonRpc(
|
||||
|
|
@ -458,7 +447,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/test: run all tests") { _ =>
|
||||
test("buildTarget/test: run all tests") {
|
||||
val buildTarget = buildTargetUri("runAndTest", "Test")
|
||||
svr.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": "80", "method": "buildTarget/test", "params": {
|
||||
|
|
@ -472,7 +461,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/test: run one test class") { _ =>
|
||||
test("buildTarget/test: run one test class") {
|
||||
val buildTarget = buildTargetUri("runAndTest", "Test")
|
||||
svr.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": "84", "method": "buildTarget/test", "params": {
|
||||
|
|
@ -495,7 +484,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/compile: report error") { _ =>
|
||||
test("buildTarget/compile: report error") {
|
||||
val buildTarget = buildTargetUri("reportError", "Compile")
|
||||
compile(buildTarget, id = 88)
|
||||
assert(svr.waitForString(10.seconds) { s =>
|
||||
|
|
@ -505,7 +494,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/compile: report warning") { _ =>
|
||||
test("buildTarget/compile: report warning") {
|
||||
val buildTarget = buildTargetUri("reportWarning", "Compile")
|
||||
compile(buildTarget, id = 90)
|
||||
assert(svr.waitForString(10.seconds) { s =>
|
||||
|
|
@ -515,7 +504,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/compile: respond error") { _ =>
|
||||
test("buildTarget/compile: respond error") {
|
||||
val buildTarget = buildTargetUri("respondError", "Compile")
|
||||
compile(buildTarget, id = 92)
|
||||
assert(svr.waitForString(10.seconds) { s =>
|
||||
|
|
@ -526,7 +515,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/resources") { _ =>
|
||||
test("buildTarget/resources") {
|
||||
val buildTarget = buildTargetUri("util", "Compile")
|
||||
val badBuildTarget = buildTargetUri("badBuildTarget", "Compile")
|
||||
svr.sendJsonRpc(
|
||||
|
|
@ -540,7 +529,7 @@ object BuildServerTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("buildTarget/outputPaths") { _ =>
|
||||
test("buildTarget/outputPaths") {
|
||||
val buildTarget = buildTargetUri("util", "Compile")
|
||||
val badBuildTarget = buildTargetUri("badBuildTarget", "Compile")
|
||||
svr.sendJsonRpc(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import sbt.internal.client.NetworkClient
|
|||
import sbt.internal.util.Util
|
||||
import scala.collection.mutable
|
||||
|
||||
object ClientTest extends AbstractServerTest {
|
||||
class ClientTest extends AbstractServerTest {
|
||||
override val testDirectory: String = "client"
|
||||
object NullInputStream extends InputStream {
|
||||
override def read(): Int = {
|
||||
|
|
@ -88,28 +88,28 @@ object ClientTest extends AbstractServerTest {
|
|||
)
|
||||
cps.lines
|
||||
}
|
||||
test("exit success") { c =>
|
||||
test("exit success") {
|
||||
assert(client("willSucceed") == 0)
|
||||
}
|
||||
test("exit failure") { _ =>
|
||||
test("exit failure") {
|
||||
assert(client("willFail") == 1)
|
||||
}
|
||||
test("two commands") { _ =>
|
||||
test("two commands") {
|
||||
assert(client("compile;willSucceed") == 0)
|
||||
}
|
||||
test("two commands with failing second") { _ =>
|
||||
test("two commands with failing second") {
|
||||
assert(client("compile;willFail") == 1)
|
||||
}
|
||||
test("two commands with leading failure") { _ =>
|
||||
test("two commands with leading failure") {
|
||||
assert(client("willFail;willSucceed") == 1)
|
||||
}
|
||||
test("three commands") { _ =>
|
||||
test("three commands") {
|
||||
assert(client("compile;clean;willSucceed") == 0)
|
||||
}
|
||||
test("three commands with middle failure") { _ =>
|
||||
test("three commands with middle failure") {
|
||||
assert(client("compile;willFail;willSucceed") == 1)
|
||||
}
|
||||
test("compi completions") { _ =>
|
||||
test("compi completions") {
|
||||
val expected = Vector(
|
||||
"compile",
|
||||
"compile:",
|
||||
|
|
@ -131,7 +131,7 @@ object ClientTest extends AbstractServerTest {
|
|||
|
||||
assert(complete("compi").toVector == expected)
|
||||
}
|
||||
test("testOnly completions") { _ =>
|
||||
test("testOnly completions") {
|
||||
val testOnlyExpected = Vector(
|
||||
"testOnly",
|
||||
"testOnly/",
|
||||
|
|
@ -143,7 +143,7 @@ object ClientTest extends AbstractServerTest {
|
|||
val testOnlyOptionsExpected = Vector("--", ";", "test.pkg.FooSpec")
|
||||
assert(complete("testOnly ") == testOnlyOptionsExpected)
|
||||
}
|
||||
test("quote with semi") { _ =>
|
||||
test("quote with semi") {
|
||||
assert(complete("\"compile; fooB") == Vector("compile; fooBar"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ import scala.concurrent.duration._
|
|||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
// starts svr using server-test/events and perform event related tests
|
||||
object EventsTest extends AbstractServerTest {
|
||||
class EventsTest extends AbstractServerTest {
|
||||
override val testDirectory: String = "events"
|
||||
val currentID = new AtomicInteger(1000)
|
||||
|
||||
test("report task failures in case of exceptions") { _ =>
|
||||
test("report task failures in case of exceptions") {
|
||||
val id = currentID.getAndIncrement()
|
||||
svr.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": $id, "method": "sbt/exec", "params": { "commandLine": "hello" } }"""
|
||||
|
|
@ -25,7 +25,7 @@ object EventsTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("return error if cancelling non-matched task id") { _ =>
|
||||
test("return error if cancelling non-matched task id") {
|
||||
val id = currentID.getAndIncrement()
|
||||
svr.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id":$id, "method": "sbt/exec", "params": { "commandLine": "run" } }"""
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ package testpkg
|
|||
import scala.concurrent.duration._
|
||||
|
||||
// starts svr using server-test/handshake and perform basic tests
|
||||
object HandshakeTest extends AbstractServerTest {
|
||||
class HandshakeTest extends AbstractServerTest {
|
||||
override val testDirectory: String = "handshake"
|
||||
|
||||
test("handshake") { _ =>
|
||||
test("handshake") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "3", "method": "sbt/setting", "params": { "setting": "root/name" } }"""
|
||||
)
|
||||
|
|
@ -22,7 +22,7 @@ object HandshakeTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("return number id when number id is sent") { _ =>
|
||||
test("return number id when number id is sent") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": 3, "method": "sbt/setting", "params": { "setting": "root/name" } }"""
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,32 +10,30 @@ package testpkg
|
|||
import scala.concurrent.duration._
|
||||
|
||||
// starts svr using server-test/response and perform custom server tests
|
||||
object ResponseTest extends AbstractServerTest {
|
||||
class ResponseTest extends AbstractServerTest {
|
||||
override val testDirectory: String = "response"
|
||||
|
||||
test("response from a command") { _ =>
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "10", "method": "foo/export", "params": {} }"""
|
||||
)
|
||||
test("response from a command") {
|
||||
svr.sendJsonRpc("""{ "jsonrpc": "2.0", "id": "10", "method": "foo/export", "params": {} }""")
|
||||
assert(svr.waitForString(10.seconds) { s =>
|
||||
if (!s.contains("systemOut")) println(s)
|
||||
(s contains """"id":"10"""") &&
|
||||
(s contains "scala-library.jar")
|
||||
(s contains "scala-library-2.12.17.jar")
|
||||
})
|
||||
}
|
||||
|
||||
test("response from a task") { _ =>
|
||||
test("response from a task") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "11", "method": "foo/rootClasspath", "params": {} }"""
|
||||
)
|
||||
assert(svr.waitForString(10.seconds) { s =>
|
||||
if (!s.contains("systemOut")) println(s)
|
||||
(s contains """"id":"11"""") &&
|
||||
(s contains "scala-library.jar")
|
||||
(s contains "scala-library-2.12.17.jar")
|
||||
})
|
||||
}
|
||||
|
||||
test("a command failure") { _ =>
|
||||
test("a command failure") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "12", "method": "foo/fail", "params": {} }"""
|
||||
)
|
||||
|
|
@ -45,7 +43,7 @@ object ResponseTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("a command failure with custom code") { _ =>
|
||||
test("a command failure with custom code") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "13", "method": "foo/customfail", "params": {} }"""
|
||||
)
|
||||
|
|
@ -55,7 +53,7 @@ object ResponseTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("a command with a notification") { _ =>
|
||||
test("a command with a notification") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "14", "method": "foo/notification", "params": {} }"""
|
||||
)
|
||||
|
|
@ -65,7 +63,7 @@ object ResponseTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("respond concurrently from a task and the handler") { _ =>
|
||||
test("respond concurrently from a task and the handler") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "15", "method": "foo/respondTwice", "params": {} }"""
|
||||
)
|
||||
|
|
@ -84,7 +82,7 @@ object ResponseTest extends AbstractServerTest {
|
|||
}
|
||||
}
|
||||
|
||||
test("concurrent result and error") { _ =>
|
||||
test("concurrent result and error") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "16", "method": "foo/resultAndError", "params": {} }"""
|
||||
)
|
||||
|
|
@ -103,7 +101,7 @@ object ResponseTest extends AbstractServerTest {
|
|||
}
|
||||
}
|
||||
|
||||
test("response to a notification should not be sent") { _ =>
|
||||
test("response to a notification should not be sent") {
|
||||
svr.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "method": "foo/customNotification", "params": {} }"""
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,21 +10,20 @@ package testpkg
|
|||
import scala.concurrent.duration._
|
||||
|
||||
// starts svr using server-test/completions and perform sbt/completion tests
|
||||
object ServerCompletionsTest extends AbstractServerTest {
|
||||
class ServerCompletionsTest extends AbstractServerTest {
|
||||
override val testDirectory: String = "completions"
|
||||
|
||||
test("return basic completions on request") { _ =>
|
||||
test("return basic completions on request") {
|
||||
val completionStr = """{ "query": "" }"""
|
||||
svr.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": 15, "method": "sbt/completion", "params": $completionStr }"""
|
||||
)
|
||||
assert(svr.waitForString(10.seconds) { s =>
|
||||
println(s)
|
||||
s contains """"result":{"items":["""
|
||||
})
|
||||
}
|
||||
|
||||
test("return completion for custom tasks") { _ =>
|
||||
test("return completion for custom tasks") {
|
||||
val completionStr = """{ "query": "hell" }"""
|
||||
svr.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": 16, "method": "sbt/completion", "params": $completionStr }"""
|
||||
|
|
@ -34,7 +33,7 @@ object ServerCompletionsTest extends AbstractServerTest {
|
|||
})
|
||||
}
|
||||
|
||||
test("return completions for user classes") { _ =>
|
||||
test("return completions for user classes") {
|
||||
val completionStr = """{ "query": "testOnly org." }"""
|
||||
svr.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": 17, "method": "sbt/completion", "params": $completionStr }"""
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import java.net.Socket
|
|||
import java.nio.file.{ Files, Path }
|
||||
import java.util.concurrent.{ LinkedBlockingQueue, TimeUnit }
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import verify._
|
||||
import sbt.{ ForkOptions, OutputStrategy, RunFromSourceMain }
|
||||
import sbt.io.IO
|
||||
import sbt.io.syntax._
|
||||
|
|
@ -24,8 +23,10 @@ import scala.annotation.tailrec
|
|||
import scala.concurrent._
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.{ Failure, Success, Try }
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
|
||||
trait AbstractServerTest extends TestSuite[Unit] {
|
||||
trait AbstractServerTest extends AnyFunSuite with BeforeAndAfterAll {
|
||||
private var temp: File = _
|
||||
var svr: TestServer = _
|
||||
def testDirectory: String
|
||||
|
|
@ -38,7 +39,7 @@ trait AbstractServerTest extends TestSuite[Unit] {
|
|||
else p1
|
||||
}
|
||||
|
||||
override def setupSuite(): Unit = {
|
||||
override def beforeAll(): Unit = {
|
||||
val base = Files.createTempDirectory(
|
||||
Files.createDirectories(targetDir.toPath.resolve("test-server")),
|
||||
"server-test"
|
||||
|
|
@ -47,15 +48,13 @@ trait AbstractServerTest extends TestSuite[Unit] {
|
|||
val classpath = TestProperties.classpath.split(File.pathSeparator).map(new File(_))
|
||||
val sbtVersion = TestProperties.version
|
||||
val scalaVersion = TestProperties.scalaVersion
|
||||
svr = TestServer.get(testDirectory, scalaVersion, sbtVersion, classpath, temp)
|
||||
svr = TestServer.get(testDirectory, scalaVersion, sbtVersion, classpath.toSeq, temp)
|
||||
}
|
||||
override def tearDownSuite(): Unit = {
|
||||
override protected def afterAll(): Unit = {
|
||||
svr.bye()
|
||||
svr = null
|
||||
IO.delete(temp)
|
||||
}
|
||||
override def setup(): Unit = ()
|
||||
override def tearDown(env: Unit): Unit = ()
|
||||
}
|
||||
|
||||
object TestServer {
|
||||
|
|
@ -118,7 +117,7 @@ object TestServer {
|
|||
case _ => throw new IllegalStateException("No server scala version was specified.")
|
||||
}
|
||||
// Each test server instance will be executed in a Thread pool separated from the tests
|
||||
val testServer = TestServer(baseDirectory, scalaVersion, sbtVersion, classpath)
|
||||
val testServer = TestServer(baseDirectory, scalaVersion, sbtVersion, classpath.toSeq)
|
||||
// checking last log message after initialization
|
||||
// if something goes wrong here the communication streams are corrupted, restarting
|
||||
val init =
|
||||
|
|
@ -164,7 +163,13 @@ case class TestServer(
|
|||
val forkOptions =
|
||||
ForkOptions()
|
||||
.withOutputStrategy(OutputStrategy.StdoutOutput)
|
||||
.withRunJVMOptions(Vector("-Djline.terminal=none", "-Dsbt.io.virtual=false"))
|
||||
.withRunJVMOptions(
|
||||
Vector(
|
||||
"-Djline.terminal=none",
|
||||
"-Dsbt.io.virtual=false",
|
||||
// "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=1044"
|
||||
)
|
||||
)
|
||||
val process =
|
||||
RunFromSourceMain.fork(forkOptions, baseDirectory, scalaVersion, sbtVersion, classpath)
|
||||
|
||||
|
|
@ -174,6 +179,7 @@ case class TestServer(
|
|||
try IO.read(portfile).isEmpty
|
||||
catch { case _: IOException => true }
|
||||
def waitForPortfile(duration: FiniteDuration): Unit = {
|
||||
hostLog(s"wait $duration until the server is ready to respond")
|
||||
val deadline = duration.fromNow
|
||||
var nextLog = 10.seconds.fromNow
|
||||
while (portfileIsEmpty() && !deadline.isOverdue && process.isAlive) {
|
||||
|
|
@ -186,9 +192,7 @@ case class TestServer(
|
|||
if (deadline.isOverdue) sys.error(s"Timeout. $portfile is not found.")
|
||||
if (!process.isAlive) sys.error(s"Server unexpectedly terminated.")
|
||||
}
|
||||
private val waitDuration: FiniteDuration = 1.minute
|
||||
hostLog(s"wait $waitDuration until the server is ready to respond")
|
||||
waitForPortfile(waitDuration)
|
||||
waitForPortfile(1.minute)
|
||||
|
||||
@tailrec
|
||||
private def connect(attempt: Int): Socket = {
|
||||
|
|
@ -227,9 +231,7 @@ case class TestServer(
|
|||
s"""{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { "skipAnalysis": true } } }"""
|
||||
)
|
||||
|
||||
def test(f: TestServer => Future[Assertion]): Future[Assertion] = {
|
||||
f(this)
|
||||
}
|
||||
def test(f: TestServer => Future[Unit]): Future[Unit] = f(this)
|
||||
|
||||
def bye(): Unit =
|
||||
try {
|
||||
|
|
@ -298,7 +300,7 @@ case class TestServer(
|
|||
}
|
||||
impl()
|
||||
}
|
||||
final def waitFor[T: JsonReader](duration: FiniteDuration): T = {
|
||||
final def waitFor[T: JsonReader](duration: FiniteDuration, debug: Boolean = false): T = {
|
||||
val deadline = duration.fromNow
|
||||
var lastEx: Throwable = null
|
||||
@tailrec def impl(): T =
|
||||
|
|
@ -307,16 +309,17 @@ case class TestServer(
|
|||
if (lastEx != null) throw lastEx
|
||||
else throw new TimeoutException
|
||||
case s =>
|
||||
if debug then println(s)
|
||||
Parser
|
||||
.parseFromString(s)
|
||||
.flatMap(jvalue =>
|
||||
.flatMap { jvalue =>
|
||||
Converter.fromJson[T](
|
||||
jvalue.toStandard
|
||||
.asInstanceOf[sjsonnew.shaded.scalajson.ast.JObject]
|
||||
.value("result")
|
||||
.toUnsafe
|
||||
)
|
||||
) match {
|
||||
} match {
|
||||
case Success(value) =>
|
||||
value
|
||||
case Failure(exception) =>
|
||||
|
|
|
|||
Loading…
Reference in New Issue