mirror of https://github.com/sbt/sbt.git
commit
36c3215320
15
build.sbt
15
build.sbt
|
|
@ -239,6 +239,8 @@ lazy val testingProj = (project in file("testing"))
|
||||||
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestItemEvent.copy$default$*"),
|
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestItemEvent.copy$default$*"),
|
||||||
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestStringEvent.copy"),
|
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestStringEvent.copy"),
|
||||||
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestStringEvent.copy$default$1"),
|
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestStringEvent.copy$default$1"),
|
||||||
|
//no reason to use
|
||||||
|
exclude[DirectMissingMethodProblem]("sbt.JUnitXmlTestsListener.testSuite"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.configure(addSbtIO, addSbtCompilerClasspath, addSbtUtilLogging)
|
.configure(addSbtIO, addSbtCompilerClasspath, addSbtUtilLogging)
|
||||||
|
|
@ -357,6 +359,9 @@ lazy val actionsProj = (project in file("main-actions"))
|
||||||
exclude[MissingClassProblem]("sbt.Doc$Scaladoc"),
|
exclude[MissingClassProblem]("sbt.Doc$Scaladoc"),
|
||||||
// Removed no longer used private[sbt] method
|
// Removed no longer used private[sbt] method
|
||||||
exclude[DirectMissingMethodProblem]("sbt.Doc.generate"),
|
exclude[DirectMissingMethodProblem]("sbt.Doc.generate"),
|
||||||
|
|
||||||
|
exclude[DirectMissingMethodProblem]("sbt.compiler.Eval.filesModifiedBytes"),
|
||||||
|
exclude[DirectMissingMethodProblem]("sbt.compiler.Eval.fileModifiedBytes"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.configure(
|
.configure(
|
||||||
|
|
@ -564,13 +569,15 @@ lazy val sbtProj = (project in file("sbt"))
|
||||||
BuildInfoPlugin.buildInfoDefaultSettings,
|
BuildInfoPlugin.buildInfoDefaultSettings,
|
||||||
buildInfoObject in Test := "TestBuildInfo",
|
buildInfoObject in Test := "TestBuildInfo",
|
||||||
buildInfoKeys in Test := Seq[BuildInfoKey](
|
buildInfoKeys in Test := Seq[BuildInfoKey](
|
||||||
|
version,
|
||||||
// WORKAROUND https://github.com/sbt/sbt-buildinfo/issues/117
|
// WORKAROUND https://github.com/sbt/sbt-buildinfo/issues/117
|
||||||
BuildInfoKey.map((fullClasspath in Compile).taskValue) { case (ident, cp) => ident -> cp.files },
|
BuildInfoKey.map((fullClasspath in Compile).taskValue) { case (ident, cp) => ident -> cp.files },
|
||||||
|
classDirectory in Compile,
|
||||||
|
classDirectory in Test,
|
||||||
),
|
),
|
||||||
connectInput in run in Test := true,
|
Test / run / connectInput := true,
|
||||||
outputStrategy in run in Test := Some(StdoutOutput),
|
Test / run / outputStrategy := Some(StdoutOutput),
|
||||||
fork in Test := true,
|
Test / run / fork := true,
|
||||||
parallelExecution in Test := false,
|
|
||||||
)
|
)
|
||||||
.configure(addSbtCompilerBridge)
|
.configure(addSbtCompilerBridge)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ package sbt
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import sbt.internal.inc.AnalyzingCompiler
|
import sbt.internal.inc.AnalyzingCompiler
|
||||||
|
import sbt.internal.util.JLine
|
||||||
import sbt.util.Logger
|
import sbt.util.Logger
|
||||||
|
|
||||||
import xsbti.compile.{ Inputs, Compilers }
|
import xsbti.compile.{ Inputs, Compilers }
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
|
|
@ -46,11 +46,13 @@ final class Console(compiler: AnalyzingCompiler) {
|
||||||
)(loader: Option[ClassLoader], bindings: Seq[(String, Any)])(implicit log: Logger): Try[Unit] = {
|
)(loader: Option[ClassLoader], bindings: Seq[(String, Any)])(implicit log: Logger): Try[Unit] = {
|
||||||
def console0() =
|
def console0() =
|
||||||
compiler.console(classpath, options, initialCommands, cleanupCommands, log)(loader, bindings)
|
compiler.console(classpath, options, initialCommands, cleanupCommands, log)(loader, bindings)
|
||||||
// TODO: Fix JLine
|
JLine.usingTerminal { t =>
|
||||||
//JLine.withJLine(Run.executeTrapExit(console0, log))
|
t.init
|
||||||
Run.executeTrapExit(console0, log)
|
Run.executeTrapExit(console0, log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object Console {
|
object Console {
|
||||||
def apply(conf: Inputs): Console =
|
def apply(conf: Inputs): Console =
|
||||||
conf.compilers match {
|
conf.compilers match {
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,10 @@ import ast.parser.Tokens
|
||||||
import reporters.{ ConsoleReporter, Reporter }
|
import reporters.{ ConsoleReporter, Reporter }
|
||||||
import scala.reflect.internal.util.{ AbstractFileClassLoader, BatchSourceFile }
|
import scala.reflect.internal.util.{ AbstractFileClassLoader, BatchSourceFile }
|
||||||
import Tokens.{ EOF, NEWLINE, NEWLINES, SEMI }
|
import Tokens.{ EOF, NEWLINE, NEWLINES, SEMI }
|
||||||
import java.io.File
|
import java.io.{ File, FileNotFoundException }
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
|
import java.security.MessageDigest
|
||||||
import Eval.{ getModule, getValue, WrapValName }
|
import Eval.{ getModule, getValue, WrapValName }
|
||||||
|
|
||||||
import sbt.io.{ DirectoryFilter, FileFilter, GlobFilter, Hash, IO, Path }
|
import sbt.io.{ DirectoryFilter, FileFilter, GlobFilter, Hash, IO, Path }
|
||||||
|
|
@ -171,17 +172,31 @@ final class Eval(
|
||||||
// TODO - We also encode the source of the setting into the hash to avoid conflicts where the exact SAME setting
|
// TODO - We also encode the source of the setting into the hash to avoid conflicts where the exact SAME setting
|
||||||
// is defined in multiple evaluated instances with a backing. This leads to issues with finding a previous
|
// is defined in multiple evaluated instances with a backing. This leads to issues with finding a previous
|
||||||
// value on the classpath when compiling.
|
// value on the classpath when compiling.
|
||||||
val hash = Hash.toHex(
|
|
||||||
Hash(
|
// This is a hot path.
|
||||||
bytes(
|
val digester = MessageDigest.getInstance("SHA")
|
||||||
stringSeqBytes(content) :: optBytes(backing)(fileExistsBytes) :: stringSeqBytes(options) ::
|
content foreach { c =>
|
||||||
seqBytes(classpath)(fileModifiedBytes) :: stringSeqBytes(imports.strings.map(_._1)) :: optBytes(
|
digester.update(bytes(c))
|
||||||
tpeName
|
}
|
||||||
)(bytes) ::
|
backing foreach { x =>
|
||||||
bytes(ev.extraHash) :: Nil
|
digester.update(fileExistsBytes(x))
|
||||||
)
|
}
|
||||||
)
|
options foreach { o =>
|
||||||
)
|
digester.update(bytes(o))
|
||||||
|
}
|
||||||
|
classpath foreach { f =>
|
||||||
|
fileModifiedHash(f, digester)
|
||||||
|
}
|
||||||
|
imports.strings.map(_._1) foreach { x =>
|
||||||
|
digester.update(bytes(x))
|
||||||
|
}
|
||||||
|
tpeName foreach { x =>
|
||||||
|
digester.update(bytes(x))
|
||||||
|
}
|
||||||
|
digester.update(bytes(ev.extraHash))
|
||||||
|
val d = digester.digest()
|
||||||
|
|
||||||
|
val hash = Hash.toHex(d)
|
||||||
val moduleName = makeModuleName(hash)
|
val moduleName = makeModuleName(hash)
|
||||||
|
|
||||||
lazy val unit = {
|
lazy val unit = {
|
||||||
|
|
@ -514,13 +529,26 @@ private[sbt] object Eval {
|
||||||
def seqBytes[T](s: Seq[T])(f: T => Array[Byte]): Array[Byte] = bytes(s map f)
|
def seqBytes[T](s: Seq[T])(f: T => Array[Byte]): Array[Byte] = bytes(s map f)
|
||||||
def bytes(b: Seq[Array[Byte]]): Array[Byte] = bytes(b.length) ++ b.flatten.toArray[Byte]
|
def bytes(b: Seq[Array[Byte]]): Array[Byte] = bytes(b.length) ++ b.flatten.toArray[Byte]
|
||||||
def bytes(b: Boolean): Array[Byte] = Array[Byte](if (b) 1 else 0)
|
def bytes(b: Boolean): Array[Byte] = Array[Byte](if (b) 1 else 0)
|
||||||
def filesModifiedBytes(fs: Array[File]): Array[Byte] =
|
|
||||||
if (fs eq null) filesModifiedBytes(Array[File]()) else seqBytes(fs)(fileModifiedBytes)
|
// fileModifiedBytes is a hot method, taking up 0.85% of reload time
|
||||||
def fileModifiedBytes(f: File): Array[Byte] =
|
// This is a procedural version
|
||||||
(if (f.isDirectory)
|
def fileModifiedHash(f: File, digester: MessageDigest): Unit = {
|
||||||
filesModifiedBytes(f listFiles classDirFilter)
|
if (f.isDirectory)
|
||||||
else
|
(f listFiles classDirFilter) foreach { x =>
|
||||||
bytes(IO.getModifiedTimeOrZero(f))) ++ bytes(f.getAbsolutePath)
|
fileModifiedHash(x, digester)
|
||||||
|
} else digester.update(bytes(getModifiedTimeOrZero(f)))
|
||||||
|
|
||||||
|
digester.update(bytes(f.getAbsolutePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
// This uses NIO instead of the JNA-based IO.getModifiedTimeOrZero for speed
|
||||||
|
def getModifiedTimeOrZero(f: File): Long =
|
||||||
|
try {
|
||||||
|
sbt.io.JavaMilli.getModifiedTime(f.getPath)
|
||||||
|
} catch {
|
||||||
|
case _: FileNotFoundException => 0L
|
||||||
|
}
|
||||||
|
|
||||||
def fileExistsBytes(f: File): Array[Byte] =
|
def fileExistsBytes(f: File): Array[Byte] =
|
||||||
bytes(f.exists) ++
|
bytes(f.exists) ++
|
||||||
bytes(f.getAbsolutePath)
|
bytes(f.getAbsolutePath)
|
||||||
|
|
|
||||||
|
|
@ -142,14 +142,14 @@ object Watched {
|
||||||
AttributeKey[Watched]("watched-configuration", "Configures continuous execution.")
|
AttributeKey[Watched]("watched-configuration", "Configures continuous execution.")
|
||||||
|
|
||||||
def createWatchService(): WatchService = {
|
def createWatchService(): WatchService = {
|
||||||
|
def closeWatch = new MacOSXWatchService()
|
||||||
sys.props.get("sbt.watch.mode") match {
|
sys.props.get("sbt.watch.mode") match {
|
||||||
case Some("polling") =>
|
case Some("polling") =>
|
||||||
new PollingWatchService(PollDelay)
|
new PollingWatchService(PollDelay)
|
||||||
case Some("nio") =>
|
case Some("nio") =>
|
||||||
FileSystems.getDefault.newWatchService()
|
FileSystems.getDefault.newWatchService()
|
||||||
case _ if Properties.isMac =>
|
case Some("closewatch") => closeWatch
|
||||||
// WatchService is slow on macOS - use old polling mode
|
case _ if Properties.isMac => closeWatch
|
||||||
new PollingWatchService(PollDelay)
|
|
||||||
case _ =>
|
case _ =>
|
||||||
FileSystems.getDefault.newWatchService()
|
FileSystems.getDefault.newWatchService()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,12 +91,12 @@ object TaskMacro {
|
||||||
final val InputTaskCreateDynName = "createDyn"
|
final val InputTaskCreateDynName = "createDyn"
|
||||||
final val InputTaskCreateFreeName = "createFree"
|
final val InputTaskCreateFreeName = "createFree"
|
||||||
final val append1Migration =
|
final val append1Migration =
|
||||||
"`<+=` operator is removed. Try `lhs += { x.value }`\n or see http://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html#Migrating+from+sbt+0.12+style."
|
"`<+=` operator is removed. Try `lhs += { x.value }`\n or see http://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html."
|
||||||
final val appendNMigration =
|
final val appendNMigration =
|
||||||
"`<++=` operator is removed. Try `lhs ++= { x.value }`\n or see http://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html#Migrating+from+sbt+0.12+style."
|
"`<++=` operator is removed. Try `lhs ++= { x.value }`\n or see http://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html."
|
||||||
final val assignMigration =
|
final val assignMigration =
|
||||||
"""`<<=` operator is removed. Use `key := { x.value }` or `key ~= (old => { newValue })`.
|
"""`<<=` operator is removed. Use `key := { x.value }` or `key ~= (old => { newValue })`.
|
||||||
|See http://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html#Migrating+from+sbt+0.12+style""".stripMargin
|
|See http://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html""".stripMargin
|
||||||
|
|
||||||
import LinterDSL.{ Empty => EmptyLinter }
|
import LinterDSL.{ Empty => EmptyLinter }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,6 @@ object TestUtil {
|
||||||
val mainClassesDir = buildinfo.TestBuildInfo.classDirectory
|
val mainClassesDir = buildinfo.TestBuildInfo.classDirectory
|
||||||
val testClassesDir = buildinfo.TestBuildInfo.test_classDirectory
|
val testClassesDir = buildinfo.TestBuildInfo.test_classDirectory
|
||||||
val depsClasspath = buildinfo.TestBuildInfo.dependencyClasspath
|
val depsClasspath = buildinfo.TestBuildInfo.dependencyClasspath
|
||||||
mainClassesDir +: testClassesDir +: depsClasspath mkString ":"
|
mainClassesDir +: testClassesDir +: depsClasspath mkString java.io.File.pathSeparator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,9 +167,7 @@ object Defaults extends BuildCommon {
|
||||||
artifactClassifier in packageSrc :== Some(SourceClassifier),
|
artifactClassifier in packageSrc :== Some(SourceClassifier),
|
||||||
artifactClassifier in packageDoc :== Some(DocClassifier),
|
artifactClassifier in packageDoc :== Some(DocClassifier),
|
||||||
includeFilter :== NothingFilter,
|
includeFilter :== NothingFilter,
|
||||||
includeFilter in unmanagedSources :== ("*.java" | "*.scala") && new SimpleFileFilter(
|
includeFilter in unmanagedSources :== ("*.java" | "*.scala") -- DirectoryFilter,
|
||||||
_.isFile
|
|
||||||
),
|
|
||||||
includeFilter in unmanagedJars :== "*.jar" | "*.so" | "*.dll" | "*.jnilib" | "*.zip",
|
includeFilter in unmanagedJars :== "*.jar" | "*.so" | "*.dll" | "*.jnilib" | "*.zip",
|
||||||
includeFilter in unmanagedResources :== AllPassFilter,
|
includeFilter in unmanagedResources :== AllPassFilter,
|
||||||
bgList := { bgJobService.value.jobs },
|
bgList := { bgJobService.value.jobs },
|
||||||
|
|
@ -1440,11 +1438,9 @@ object Defaults extends BuildCommon {
|
||||||
val sc = (scalacOptions in task).value
|
val sc = (scalacOptions in task).value
|
||||||
val ic = (initialCommands in task).value
|
val ic = (initialCommands in task).value
|
||||||
val cc = (cleanupCommands in task).value
|
val cc = (cleanupCommands in task).value
|
||||||
JLine.usingTerminal { _ =>
|
|
||||||
(new Console(compiler))(cpFiles, sc, loader, ic, cc)()(s.log).get
|
(new Console(compiler))(cpFiles, sc, loader, ic, cc)()(s.log).get
|
||||||
println()
|
println()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private[this] def exported(w: PrintWriter, command: String): Seq[String] => Unit =
|
private[this] def exported(w: PrintWriter, command: String): Seq[String] => Unit =
|
||||||
args => w.println((command +: args).mkString(" "))
|
args => w.println((command +: args).mkString(" "))
|
||||||
|
|
@ -2269,6 +2265,7 @@ object Classpaths {
|
||||||
).withScalaOrganization(scalaOrganization.value)
|
).withScalaOrganization(scalaOrganization.value)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
dependencyResolution := IvyDependencyResolution(ivyConfiguration.value),
|
||||||
updateSbtClassifiers in TaskGlobal := (Def.task {
|
updateSbtClassifiers in TaskGlobal := (Def.task {
|
||||||
val lm = dependencyResolution.value
|
val lm = dependencyResolution.value
|
||||||
val s = streams.value
|
val s = streams.value
|
||||||
|
|
@ -2281,6 +2278,9 @@ object Classpaths {
|
||||||
val log = s.log
|
val log = s.log
|
||||||
val out = is.withIvy(log)(_.getSettings.getDefaultIvyUserDir)
|
val out = is.withIvy(log)(_.getSettings.getDefaultIvyUserDir)
|
||||||
val uwConfig = (unresolvedWarningConfiguration in update).value
|
val uwConfig = (unresolvedWarningConfiguration in update).value
|
||||||
|
val depDir = dependencyCacheDirectory.value
|
||||||
|
val ivy = scalaModuleInfo.value
|
||||||
|
val st = state.value
|
||||||
withExcludes(out, mod.classifiers, lock(app)) {
|
withExcludes(out, mod.classifiers, lock(app)) {
|
||||||
excludes =>
|
excludes =>
|
||||||
// val noExplicitCheck = ivy.map(_.withCheckExplicit(false))
|
// val noExplicitCheck = ivy.map(_.withCheckExplicit(false))
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ private[sbt] object LanguageServerProtocol {
|
||||||
else throw LangServerError(ErrorCodes.InvalidRequest, "invalid token")
|
else throw LangServerError(ErrorCodes.InvalidRequest, "invalid token")
|
||||||
} else ()
|
} else ()
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
appendExec(Exec(s"collectAnalyses", Some(r.id), Some(CommandSource(name))))
|
appendExec(Exec(s"collectAnalyses", None, Some(CommandSource(name))))
|
||||||
jsonRpcRespond(InitializeResult(serverCapabilities), Option(r.id))
|
jsonRpcRespond(InitializeResult(serverCapabilities), Option(r.id))
|
||||||
|
|
||||||
case r: JsonRpcRequestMessage if r.method == "textDocument/definition" =>
|
case r: JsonRpcRequestMessage if r.method == "textDocument/definition" =>
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ import Prop._
|
||||||
import Gen._
|
import Gen._
|
||||||
|
|
||||||
object Delegates extends Properties("delegates") {
|
object Delegates extends Properties("delegates") {
|
||||||
property("generate non-empty configs") = forAll { (c: Seq[Configuration]) =>
|
property("generate non-empty configs") = forAll { (c: Vector[Configuration]) =>
|
||||||
c.nonEmpty
|
c.nonEmpty
|
||||||
}
|
}
|
||||||
property("generate non-empty tasks") = forAll { (t: Seq[Taskk]) =>
|
property("generate non-empty tasks") = forAll { (t: Vector[Taskk]) =>
|
||||||
t.nonEmpty
|
t.nonEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,6 +46,7 @@ object Delegates extends Properties("delegates") {
|
||||||
global forall { _ == Zero }
|
global forall { _ == Zero }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property("Initial scope present with all combinations of Global axes") = allAxes(
|
property("Initial scope present with all combinations of Global axes") = allAxes(
|
||||||
(s, ds, _) => globalCombinations(s, ds)
|
(s, ds, _) => globalCombinations(s, ds)
|
||||||
)
|
)
|
||||||
|
|
@ -55,12 +56,58 @@ object Delegates extends Properties("delegates") {
|
||||||
ds.head == scope
|
ds.head == scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property("global scope last") = forAll { (keys: Keys) =>
|
property("global scope last") = forAll { (keys: Keys) =>
|
||||||
allDelegates(keys) { (_, ds) =>
|
allDelegates(keys) { (_, ds) =>
|
||||||
ds.last == Scope.GlobalScope
|
ds.last == Scope.GlobalScope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property("Project axis delegates to BuildRef then Zero") = forAll { (keys: Keys) =>
|
||||||
|
allDelegates(keys) { (key, ds) =>
|
||||||
|
key.project match {
|
||||||
|
case Zero => true // filtering out of testing
|
||||||
|
case Select(ProjectRef(uri, _)) =>
|
||||||
|
val buildScoped = key.copy(project = Select(BuildRef(uri)))
|
||||||
|
val idxKey = ds.indexOf(key)
|
||||||
|
val idxB = ds.indexOf(buildScoped)
|
||||||
|
val z = key.copy(project = Zero)
|
||||||
|
val idxZ = ds.indexOf(z)
|
||||||
|
if (z == Scope.GlobalScope) true
|
||||||
|
else {
|
||||||
|
(s"idxKey = $idxKey; idxB = $idxB; idxZ = $idxZ") |:
|
||||||
|
(idxKey < idxB) && (idxB < idxZ)
|
||||||
|
}
|
||||||
|
case Select(BuildRef(_)) =>
|
||||||
|
ds.indexOf(key) < ds.indexOf(key.copy(project = Zero))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property("Config axis delegates to parent configuration") = forAll { (keys: Keys) =>
|
||||||
|
allDelegates(keys) { (key, ds) =>
|
||||||
|
key.config match {
|
||||||
|
case Zero => true
|
||||||
|
case Select(config) if key.project.isSelect =>
|
||||||
|
val p = key.project.toOption.get
|
||||||
|
val r = keys.env.resolve(p)
|
||||||
|
val proj = keys.env.projectFor(r)
|
||||||
|
val inh: Vector[ConfigKey] = keys.env.inheritConfig(r, config)
|
||||||
|
val conf = proj.confMap(config.name)
|
||||||
|
if (inh.isEmpty) true
|
||||||
|
else {
|
||||||
|
val idxKey = ds.indexOf(key)
|
||||||
|
val parent = inh.head
|
||||||
|
val a = key.copy(config = Select(parent))
|
||||||
|
val idxA = ds.indexOf(a)
|
||||||
|
(s"idxKey = $idxKey; a = $a; idxA = $idxA") |:
|
||||||
|
idxKey < idxA
|
||||||
|
}
|
||||||
|
case _ => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def allAxes(f: (Scope, Seq[Scope], Scope => ScopeAxis[_]) => Prop): Prop = forAll {
|
def allAxes(f: (Scope, Seq[Scope], Scope => ScopeAxis[_]) => Prop): Prop = forAll {
|
||||||
(keys: Keys) =>
|
(keys: Keys) =>
|
||||||
allDelegates(keys) { (s, ds) =>
|
allDelegates(keys) { (s, ds) =>
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ abstract class TestBuild {
|
||||||
(taskAxes, zero.toSet, single.toSet, multi.toSet)
|
(taskAxes, zero.toSet, single.toSet, multi.toSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final class Env(val builds: Seq[Build], val tasks: Seq[Taskk]) {
|
final class Env(val builds: Vector[Build], val tasks: Vector[Taskk]) {
|
||||||
override def toString =
|
override def toString =
|
||||||
"Env:\n " + " Tasks:\n " + tasks.mkString("\n ") + "\n" + builds.mkString("\n ")
|
"Env:\n " + " Tasks:\n " + tasks.mkString("\n ") + "\n" + builds.mkString("\n ")
|
||||||
val root = builds.head
|
val root = builds.head
|
||||||
|
|
@ -135,14 +135,15 @@ abstract class TestBuild {
|
||||||
def inheritConfig(ref: ResolvedReference, config: ConfigKey) =
|
def inheritConfig(ref: ResolvedReference, config: ConfigKey) =
|
||||||
projectFor(ref).confMap(config.name).extendsConfigs map toConfigKey
|
projectFor(ref).confMap(config.name).extendsConfigs map toConfigKey
|
||||||
def inheritTask(task: AttributeKey[_]) = taskMap.get(task) match {
|
def inheritTask(task: AttributeKey[_]) = taskMap.get(task) match {
|
||||||
case None => Nil; case Some(t) => t.delegates map getKey
|
case None => Vector()
|
||||||
|
case Some(t) => t.delegates.toVector map getKey
|
||||||
}
|
}
|
||||||
def inheritProject(ref: ProjectRef) = project(ref).delegates
|
def inheritProject(ref: ProjectRef) = project(ref).delegates.toVector
|
||||||
def resolve(ref: Reference) = Scope.resolveReference(root.uri, rootProject, ref)
|
def resolve(ref: Reference) = Scope.resolveReference(root.uri, rootProject, ref)
|
||||||
lazy val delegates: Scope => Seq[Scope] =
|
lazy val delegates: Scope => Seq[Scope] =
|
||||||
Scope.delegates(
|
Scope.delegates(
|
||||||
allProjects,
|
allProjects,
|
||||||
(_: Proj).configurations.map(toConfigKey),
|
(_: Proj).configurations.toVector.map(toConfigKey),
|
||||||
resolve,
|
resolve,
|
||||||
uri => buildMap(uri).root.id,
|
uri => buildMap(uri).root.id,
|
||||||
inheritProject,
|
inheritProject,
|
||||||
|
|
@ -267,13 +268,14 @@ abstract class TestBuild {
|
||||||
implicit lazy val uriGen: Gen[URI] = for (sch <- idGen; ssp <- idGen; frag <- optIDGen)
|
implicit lazy val uriGen: Gen[URI] = for (sch <- idGen; ssp <- idGen; frag <- optIDGen)
|
||||||
yield new URI(sch, ssp, frag.orNull)
|
yield new URI(sch, ssp, frag.orNull)
|
||||||
|
|
||||||
implicit def envGen(implicit bGen: Gen[Build], tasks: Gen[Seq[Taskk]]): Gen[Env] =
|
implicit def envGen(implicit bGen: Gen[Build], tasks: Gen[Vector[Taskk]]): Gen[Env] =
|
||||||
for (i <- MaxBuildsGen; bs <- listOfN(i, bGen); ts <- tasks) yield new Env(bs, ts)
|
for (i <- MaxBuildsGen; bs <- containerOfN[Vector, Build](i, bGen); ts <- tasks)
|
||||||
|
yield new Env(bs, ts)
|
||||||
implicit def buildGen(implicit uGen: Gen[URI], pGen: URI => Gen[Seq[Proj]]): Gen[Build] =
|
implicit def buildGen(implicit uGen: Gen[URI], pGen: URI => Gen[Seq[Proj]]): Gen[Build] =
|
||||||
for (u <- uGen; ps <- pGen(u)) yield new Build(u, ps)
|
for (u <- uGen; ps <- pGen(u)) yield new Build(u, ps)
|
||||||
|
|
||||||
def nGen[T](igen: Gen[Int])(implicit g: Gen[T]): Gen[List[T]] = igen flatMap { ig =>
|
def nGen[T](igen: Gen[Int])(implicit g: Gen[T]): Gen[Vector[T]] = igen flatMap { ig =>
|
||||||
listOfN(ig, g)
|
containerOfN[Vector, T](ig, g)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit def genProjects(build: URI)(
|
implicit def genProjects(build: URI)(
|
||||||
|
|
@ -294,7 +296,7 @@ abstract class TestBuild {
|
||||||
implicit genName: Gen[String],
|
implicit genName: Gen[String],
|
||||||
maxDeps: Gen[Int],
|
maxDeps: Gen[Int],
|
||||||
count: Gen[Int]
|
count: Gen[Int]
|
||||||
): Gen[Seq[Configuration]] =
|
): Gen[Vector[Configuration]] =
|
||||||
genAcyclicDirect[Configuration, String](maxDeps, genName, count)(
|
genAcyclicDirect[Configuration, String](maxDeps, genName, count)(
|
||||||
(key, deps) =>
|
(key, deps) =>
|
||||||
Configuration
|
Configuration
|
||||||
|
|
@ -302,30 +304,36 @@ abstract class TestBuild {
|
||||||
.withExtendsConfigs(deps.toVector)
|
.withExtendsConfigs(deps.toVector)
|
||||||
)
|
)
|
||||||
|
|
||||||
def genTasks(implicit genName: Gen[String], maxDeps: Gen[Int], count: Gen[Int]): Gen[Seq[Taskk]] =
|
def genTasks(
|
||||||
|
implicit genName: Gen[String],
|
||||||
|
maxDeps: Gen[Int],
|
||||||
|
count: Gen[Int]
|
||||||
|
): Gen[Vector[Taskk]] =
|
||||||
genAcyclicDirect[Taskk, String](maxDeps, genName, count)(
|
genAcyclicDirect[Taskk, String](maxDeps, genName, count)(
|
||||||
(key, deps) => new Taskk(AttributeKey[String](key), deps)
|
(key, deps) => new Taskk(AttributeKey[String](key), deps)
|
||||||
)
|
)
|
||||||
|
|
||||||
def genAcyclicDirect[A, T](maxDeps: Gen[Int], keyGen: Gen[T], max: Gen[Int])(
|
def genAcyclicDirect[A, T](maxDeps: Gen[Int], keyGen: Gen[T], max: Gen[Int])(
|
||||||
make: (T, Seq[A]) => A
|
make: (T, Vector[A]) => A
|
||||||
): Gen[Seq[A]] =
|
): Gen[Vector[A]] =
|
||||||
genAcyclic[A, T](maxDeps, keyGen, max) { t =>
|
genAcyclic[A, T](maxDeps, keyGen, max) { t =>
|
||||||
Gen.const { deps =>
|
Gen.const { deps =>
|
||||||
make(t, deps)
|
make(t, deps.toVector)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def genAcyclic[A, T](maxDeps: Gen[Int], keyGen: Gen[T], max: Gen[Int])(
|
def genAcyclic[A, T](maxDeps: Gen[Int], keyGen: Gen[T], max: Gen[Int])(
|
||||||
make: T => Gen[Seq[A] => A]
|
make: T => Gen[Vector[A] => A]
|
||||||
): Gen[Seq[A]] =
|
): Gen[Vector[A]] =
|
||||||
max flatMap { count =>
|
max flatMap { count =>
|
||||||
listOfN(count, keyGen) flatMap { keys =>
|
containerOfN[Vector, T](count, keyGen) flatMap { keys =>
|
||||||
genAcyclic(maxDeps, keys.distinct)(make)
|
genAcyclic(maxDeps, keys.distinct)(make)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def genAcyclic[A, T](maxDeps: Gen[Int], keys: List[T])(make: T => Gen[Seq[A] => A]): Gen[Seq[A]] =
|
def genAcyclic[A, T](maxDeps: Gen[Int], keys: Vector[T])(
|
||||||
genAcyclic(maxDeps, keys, Nil) flatMap { pairs =>
|
make: T => Gen[Vector[A] => A]
|
||||||
|
): Gen[Vector[A]] =
|
||||||
|
genAcyclic(maxDeps, keys, Vector()) flatMap { pairs =>
|
||||||
sequence(pairs.map { case (key, deps) => mapMake(key, deps, make) }) flatMap { inputs =>
|
sequence(pairs.map { case (key, deps) => mapMake(key, deps, make) }) flatMap { inputs =>
|
||||||
val made = new collection.mutable.HashMap[T, A]
|
val made = new collection.mutable.HashMap[T, A]
|
||||||
for ((key, deps, mk) <- inputs)
|
for ((key, deps, mk) <- inputs)
|
||||||
|
|
@ -334,27 +342,27 @@ abstract class TestBuild {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def mapMake[A, T](key: T, deps: Seq[T], make: T => Gen[Seq[A] => A]): Gen[Inputs[A, T]] =
|
def mapMake[A, T](key: T, deps: Vector[T], make: T => Gen[Vector[A] => A]): Gen[Inputs[A, T]] =
|
||||||
make(key) map { (mk: Seq[A] => A) =>
|
make(key) map { (mk: Vector[A] => A) =>
|
||||||
(key, deps, mk)
|
(key, deps, mk)
|
||||||
}
|
}
|
||||||
|
|
||||||
def genAcyclic[T](
|
def genAcyclic[T](
|
||||||
maxDeps: Gen[Int],
|
maxDeps: Gen[Int],
|
||||||
names: List[T],
|
names: Vector[T],
|
||||||
acc: List[Gen[(T, Seq[T])]]
|
acc: Vector[Gen[(T, Vector[T])]]
|
||||||
): Gen[Seq[(T, Seq[T])]] =
|
): Gen[Vector[(T, Vector[T])]] =
|
||||||
names match {
|
names match {
|
||||||
case Nil => sequence(acc)
|
case Vector() => sequence(acc)
|
||||||
case x :: xs =>
|
case Vector(x, xs @ _*) =>
|
||||||
val next = for (depCount <- maxDeps; d <- pick(depCount min xs.size, xs))
|
val next = for (depCount <- maxDeps; d <- pick(depCount min xs.size, xs))
|
||||||
yield (x, d.toList)
|
yield (x, d.toVector)
|
||||||
genAcyclic(maxDeps, xs, next :: acc)
|
genAcyclic(maxDeps, xs.toVector, next +: acc)
|
||||||
}
|
}
|
||||||
def sequence[T](gs: Seq[Gen[T]]): Gen[Seq[T]] = Gen.parameterized { prms =>
|
def sequence[T](gs: Vector[Gen[T]]): Gen[Vector[T]] = Gen.parameterized { prms =>
|
||||||
delay(gs map { g =>
|
delay(gs map { g =>
|
||||||
g(prms, seed) getOrElse sys.error("failed generator")
|
g(prms, seed) getOrElse sys.error("failed generator")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
type Inputs[A, T] = (T, Seq[T], Seq[A] => A)
|
type Inputs[A, T] = (T, Vector[T], Vector[A] => A)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ import sbt.contraband.ContrabandPlugin.autoImport._
|
||||||
|
|
||||||
object Dependencies {
|
object Dependencies {
|
||||||
// WARNING: Please Scala update versions in PluginCross.scala too
|
// WARNING: Please Scala update versions in PluginCross.scala too
|
||||||
val scala212 = "2.12.4"
|
val scala212 = "2.12.5"
|
||||||
val baseScalaVersion = scala212
|
val baseScalaVersion = scala212
|
||||||
|
|
||||||
// sbt modules
|
// sbt modules
|
||||||
private val ioVersion = "1.1.4"
|
private val ioVersion = "1.1.6"
|
||||||
private val utilVersion = "1.1.3"
|
private val utilVersion = "1.1.3"
|
||||||
private val lmVersion = "1.1.4"
|
private val lmVersion = "1.1.4"
|
||||||
private val zincVersion = "1.1.3"
|
private val zincVersion = "1.1.5"
|
||||||
|
|
||||||
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
||||||
|
|
||||||
|
|
@ -26,8 +26,9 @@ object Dependencies {
|
||||||
private val libraryManagementCore = "org.scala-sbt" %% "librarymanagement-core" % lmVersion
|
private val libraryManagementCore = "org.scala-sbt" %% "librarymanagement-core" % lmVersion
|
||||||
private val libraryManagementIvy = "org.scala-sbt" %% "librarymanagement-ivy" % lmVersion
|
private val libraryManagementIvy = "org.scala-sbt" %% "librarymanagement-ivy" % lmVersion
|
||||||
|
|
||||||
val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.3"
|
val launcherVersion = "1.0.4"
|
||||||
val rawLauncher = "org.scala-sbt" % "launcher" % "1.0.3"
|
val launcherInterface = "org.scala-sbt" % "launcher-interface" % launcherVersion
|
||||||
|
val rawLauncher = "org.scala-sbt" % "launcher" % launcherVersion
|
||||||
val testInterface = "org.scala-sbt" % "test-interface" % "1.0"
|
val testInterface = "org.scala-sbt" % "test-interface" % "1.0"
|
||||||
val ipcSocket = "org.scala-sbt.ipcsocket" % "ipcsocket" % "1.0.0"
|
val ipcSocket = "org.scala-sbt.ipcsocket" % "ipcsocket" % "1.0.0"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,7 @@ object Util {
|
||||||
"-Yno-adapted-args",
|
"-Yno-adapted-args",
|
||||||
"-Ywarn-dead-code",
|
"-Ywarn-dead-code",
|
||||||
"-Ywarn-numeric-widen",
|
"-Ywarn-numeric-widen",
|
||||||
//"-Ywarn-value-discard",
|
"-Ywarn-unused:-patvars,-implicits,_",
|
||||||
"-Ywarn-unused",
|
|
||||||
"-Ywarn-unused-import"
|
"-Ywarn-unused-import"
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ scalaVersion := "2.12.4"
|
||||||
scalacOptions ++= Seq("-feature", "-language:postfixOps")
|
scalacOptions ++= Seq("-feature", "-language:postfixOps")
|
||||||
|
|
||||||
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.2.0")
|
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.2.0")
|
||||||
addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.5")
|
addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.6")
|
||||||
addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.4.0")
|
addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.4.0")
|
||||||
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2")
|
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2")
|
||||||
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.8.0")
|
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.8.0")
|
||||||
|
|
|
||||||
|
|
@ -1 +1,9 @@
|
||||||
scalaVersion := "2.11.11"
|
scalaVersion := "2.11.11"
|
||||||
|
|
||||||
|
ivyConfiguration := {
|
||||||
|
throw new RuntimeException("updateSbtClassifiers should use updateSbtClassifiers / ivyConfiguration")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyResolution := {
|
||||||
|
throw new RuntimeException("updateSbtClassifiers should use updateSbtClassifiers / dependencyResolution")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,24 @@
|
||||||
import sbt.internal.ServerHandler
|
// import sbt.internal.ServerHandler
|
||||||
|
|
||||||
lazy val root = (project in file("."))
|
lazy val root = (project in file("."))
|
||||||
.settings(
|
.settings(
|
||||||
Global / serverLog / logLevel := Level.Debug,
|
Global / serverLog / logLevel := Level.Debug,
|
||||||
|
|
||||||
// custom handler
|
// custom handler
|
||||||
Global / serverHandlers += ServerHandler({ callback =>
|
// Global / serverHandlers += ServerHandler({ callback =>
|
||||||
import callback._
|
// import callback._
|
||||||
import sjsonnew.BasicJsonProtocol._
|
// import sjsonnew.BasicJsonProtocol._
|
||||||
import sbt.internal.protocol.JsonRpcRequestMessage
|
// import sbt.internal.protocol.JsonRpcRequestMessage
|
||||||
ServerIntent(
|
// ServerIntent(
|
||||||
{
|
// {
|
||||||
case r: JsonRpcRequestMessage if r.method == "lunar/helo" =>
|
// case r: JsonRpcRequestMessage if r.method == "lunar/helo" =>
|
||||||
jsonRpcNotify("lunar/oleh", "")
|
// jsonRpcNotify("lunar/oleh", "")
|
||||||
()
|
// ()
|
||||||
},
|
// },
|
||||||
PartialFunction.empty
|
// PartialFunction.empty
|
||||||
)
|
// )
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
name := "handshake",
|
name := "handshake",
|
||||||
scalaVersion := "2.12.3",
|
scalaVersion := "2.12.3",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,34 @@
|
||||||
|
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
import sbt.util.LogExchange
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
import buildinfo.TestBuildInfo
|
||||||
import xsbti._
|
import xsbti._
|
||||||
|
|
||||||
object RunFromSourceMain {
|
object RunFromSourceMain {
|
||||||
private val sbtVersion = "1.1.0" // "dev"
|
private val sbtVersion = "1.1.4" // TestBuildInfo.version
|
||||||
private val scalaVersion = "2.12.4"
|
private val scalaVersion = "2.12.4"
|
||||||
|
|
||||||
|
def fork(workingDirectory: File): Try[Unit] = {
|
||||||
|
val fo = ForkOptions()
|
||||||
|
.withOutputStrategy(OutputStrategy.StdoutOutput)
|
||||||
|
fork(fo, workingDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
def fork(fo0: ForkOptions, workingDirectory: File): Try[Unit] = {
|
||||||
|
val fo = fo0
|
||||||
|
.withWorkingDirectory(workingDirectory)
|
||||||
|
implicit val runner = new ForkRun(fo)
|
||||||
|
val cp = {
|
||||||
|
TestBuildInfo.test_classDirectory +: TestBuildInfo.fullClasspath
|
||||||
|
}
|
||||||
|
val options = Vector(workingDirectory.toString)
|
||||||
|
val log = LogExchange.logger("RunFromSourceMain.fork", None, None)
|
||||||
|
Run.run("sbt.RunFromSourceMain", cp, options, log)
|
||||||
|
}
|
||||||
|
|
||||||
def main(args: Array[String]): Unit = args match {
|
def main(args: Array[String]): Unit = args match {
|
||||||
case Array() => sys.error(s"Must specify working directory as the first argument")
|
case Array() => sys.error(s"Must specify working directory as the first argument")
|
||||||
case Array(wd, args @ _*) => run(file(wd), args)
|
case Array(wd, args @ _*) => run(file(wd), args)
|
||||||
|
|
|
||||||
|
|
@ -1,206 +0,0 @@
|
||||||
/*
|
|
||||||
* sbt
|
|
||||||
* Copyright 2011 - 2017, Lightbend, Inc.
|
|
||||||
* Copyright 2008 - 2010, Mark Harrah
|
|
||||||
* Licensed under BSD-3-Clause license (see LICENSE)
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sbt
|
|
||||||
|
|
||||||
import org.scalatest._
|
|
||||||
import scala.concurrent._
|
|
||||||
import scala.annotation.tailrec
|
|
||||||
import java.io.{ InputStream, OutputStream }
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
import java.util.concurrent.{ ThreadFactory, ThreadPoolExecutor }
|
|
||||||
import sbt.protocol.ClientSocket
|
|
||||||
|
|
||||||
class ServerSpec extends AsyncFlatSpec with Matchers {
|
|
||||||
import ServerSpec._
|
|
||||||
|
|
||||||
"server" should "start" in {
|
|
||||||
withBuildSocket("handshake") { (out, in, tkn) =>
|
|
||||||
writeLine(
|
|
||||||
"""{ "jsonrpc": "2.0", "id": 3, "method": "sbt/setting", "params": { "setting": "handshake/name" } }""",
|
|
||||||
out
|
|
||||||
)
|
|
||||||
Thread.sleep(100)
|
|
||||||
assert(waitFor(in, 10) { s =>
|
|
||||||
s contains """"id":3"""
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@tailrec
|
|
||||||
private[this] def waitFor(in: InputStream, num: Int)(f: String => Boolean): Boolean = {
|
|
||||||
if (num < 0) false
|
|
||||||
else
|
|
||||||
readFrame(in) match {
|
|
||||||
case Some(x) if f(x) => true
|
|
||||||
case _ =>
|
|
||||||
waitFor(in, num - 1)(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object ServerSpec {
|
|
||||||
private val serverTestBase: File = new File(".").getAbsoluteFile / "sbt" / "src" / "server-test"
|
|
||||||
private val nextThreadId = new AtomicInteger(1)
|
|
||||||
private val threadGroup = Thread.currentThread.getThreadGroup()
|
|
||||||
val readBuffer = new Array[Byte](4096)
|
|
||||||
var buffer: Vector[Byte] = Vector.empty
|
|
||||||
var bytesRead = 0
|
|
||||||
private val delimiter: Byte = '\n'.toByte
|
|
||||||
private val RetByte = '\r'.toByte
|
|
||||||
|
|
||||||
private val threadFactory = new ThreadFactory() {
|
|
||||||
override def newThread(runnable: Runnable): Thread = {
|
|
||||||
val thread =
|
|
||||||
new Thread(
|
|
||||||
threadGroup,
|
|
||||||
runnable,
|
|
||||||
s"sbt-test-server-threads-${nextThreadId.getAndIncrement}"
|
|
||||||
)
|
|
||||||
// Do NOT setDaemon because then the code in TaskExit.scala in sbt will insta-kill
|
|
||||||
// the backgrounded process, at least for the case of the run task.
|
|
||||||
thread
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val executor = new ThreadPoolExecutor(
|
|
||||||
0, /* corePoolSize */
|
|
||||||
1, /* maxPoolSize, max # of servers */
|
|
||||||
2,
|
|
||||||
java.util.concurrent.TimeUnit.SECONDS,
|
|
||||||
/* keep alive unused threads this long (if corePoolSize < maxPoolSize) */
|
|
||||||
new java.util.concurrent.SynchronousQueue[Runnable](),
|
|
||||||
threadFactory
|
|
||||||
)
|
|
||||||
|
|
||||||
def backgroundRun(baseDir: File, args: Seq[String]): Unit = {
|
|
||||||
executor.execute(new Runnable {
|
|
||||||
def run(): Unit = {
|
|
||||||
RunFromSourceMain.run(baseDir, args)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
def shutdown(): Unit = executor.shutdown()
|
|
||||||
|
|
||||||
def withBuildSocket(
|
|
||||||
testBuild: String
|
|
||||||
)(f: (OutputStream, InputStream, Option[String]) => Future[Assertion]): Future[Assertion] = {
|
|
||||||
IO.withTemporaryDirectory { temp =>
|
|
||||||
IO.copyDirectory(serverTestBase / testBuild, temp / testBuild)
|
|
||||||
withBuildSocket(temp / testBuild)(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def sendJsonRpc(message: String, out: OutputStream): Unit = {
|
|
||||||
writeLine(s"""Content-Length: ${message.size + 2}""", out)
|
|
||||||
writeLine("", out)
|
|
||||||
writeLine(message, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
def readFrame(in: InputStream): Option[String] = {
|
|
||||||
val l = contentLength(in)
|
|
||||||
println(l)
|
|
||||||
readLine(in)
|
|
||||||
readLine(in)
|
|
||||||
readContentLength(in, l)
|
|
||||||
}
|
|
||||||
|
|
||||||
def contentLength(in: InputStream): Int = {
|
|
||||||
readLine(in) map { line =>
|
|
||||||
line.drop(16).toInt
|
|
||||||
} getOrElse (0)
|
|
||||||
}
|
|
||||||
|
|
||||||
def readLine(in: InputStream): Option[String] = {
|
|
||||||
if (buffer.isEmpty) {
|
|
||||||
val bytesRead = try {
|
|
||||||
in.read(readBuffer)
|
|
||||||
} catch {
|
|
||||||
case _: java.io.IOException => 0
|
|
||||||
}
|
|
||||||
if (bytesRead > 0) {
|
|
||||||
buffer = buffer ++ readBuffer.toVector.take(bytesRead)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val delimPos = buffer.indexOf(delimiter)
|
|
||||||
if (delimPos > 0) {
|
|
||||||
val chunk0 = buffer.take(delimPos)
|
|
||||||
buffer = buffer.drop(delimPos + 1)
|
|
||||||
// remove \r at the end of line.
|
|
||||||
val chunk1 = if (chunk0.lastOption contains RetByte) chunk0.dropRight(1) else chunk0
|
|
||||||
Some(new String(chunk1.toArray, "utf-8"))
|
|
||||||
} else None // no EOL yet, so skip this turn.
|
|
||||||
}
|
|
||||||
|
|
||||||
def readContentLength(in: InputStream, length: Int): Option[String] = {
|
|
||||||
if (buffer.isEmpty) {
|
|
||||||
val bytesRead = in.read(readBuffer)
|
|
||||||
if (bytesRead > 0) {
|
|
||||||
buffer = buffer ++ readBuffer.toVector.take(bytesRead)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (length <= buffer.size) {
|
|
||||||
val chunk = buffer.take(length)
|
|
||||||
buffer = buffer.drop(length)
|
|
||||||
Some(new String(chunk.toArray, "utf-8"))
|
|
||||||
} else None // have not read enough yet, so skip this turn.
|
|
||||||
}
|
|
||||||
|
|
||||||
def writeLine(s: String, out: OutputStream): Unit = {
|
|
||||||
def writeEndLine(): Unit = {
|
|
||||||
val retByte: Byte = '\r'.toByte
|
|
||||||
val delimiter: Byte = '\n'.toByte
|
|
||||||
out.write(retByte.toInt)
|
|
||||||
out.write(delimiter.toInt)
|
|
||||||
out.flush
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s != "") {
|
|
||||||
out.write(s.getBytes("UTF-8"))
|
|
||||||
}
|
|
||||||
writeEndLine
|
|
||||||
}
|
|
||||||
|
|
||||||
def withBuildSocket(
|
|
||||||
baseDirectory: File
|
|
||||||
)(f: (OutputStream, InputStream, Option[String]) => Future[Assertion]): Future[Assertion] = {
|
|
||||||
backgroundRun(baseDirectory, Nil)
|
|
||||||
|
|
||||||
val portfile = baseDirectory / "project" / "target" / "active.json"
|
|
||||||
|
|
||||||
def waitForPortfile(n: Int): Unit =
|
|
||||||
if (portfile.exists) ()
|
|
||||||
else {
|
|
||||||
if (n <= 0) sys.error(s"Timeout. $portfile is not found.")
|
|
||||||
else {
|
|
||||||
println(s" waiting for $portfile...")
|
|
||||||
Thread.sleep(1000)
|
|
||||||
waitForPortfile(n - 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
waitForPortfile(20)
|
|
||||||
val (sk, tkn) = ClientSocket.socket(portfile)
|
|
||||||
val out = sk.getOutputStream
|
|
||||||
val in = sk.getInputStream
|
|
||||||
|
|
||||||
sendJsonRpc(
|
|
||||||
"""{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { } } }""",
|
|
||||||
out
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
f(out, in, tkn)
|
|
||||||
} finally {
|
|
||||||
sendJsonRpc(
|
|
||||||
"""{ "jsonrpc": "2.0", "id": 9, "method": "sbt/exec", "params": { "commandLine": "exit" } }""",
|
|
||||||
out
|
|
||||||
)
|
|
||||||
// shutdown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2017, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under BSD-3-Clause license (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package testpkg
|
||||||
|
|
||||||
|
import org.scalatest._
|
||||||
|
import scala.concurrent._
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
import sbt.protocol.ClientSocket
|
||||||
|
import TestServer.withTestServer
|
||||||
|
import java.io.File
|
||||||
|
import sbt.io.syntax._
|
||||||
|
import sbt.io.IO
|
||||||
|
import sbt.RunFromSourceMain
|
||||||
|
|
||||||
|
class ServerSpec extends AsyncFreeSpec with Matchers {
|
||||||
|
"server" - {
|
||||||
|
"should start" in withTestServer("handshake") { p =>
|
||||||
|
p.writeLine(
|
||||||
|
"""{ "jsonrpc": "2.0", "id": "3", "method": "sbt/setting", "params": { "setting": "root/name" } }"""
|
||||||
|
)
|
||||||
|
assert(p.waitForString(10) { s =>
|
||||||
|
s contains """"id":"3""""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
"return number id when number id is sent" in withTestServer("handshake") { p =>
|
||||||
|
p.writeLine(
|
||||||
|
"""{ "jsonrpc": "2.0", "id": 3, "method": "sbt/setting", "params": { "setting": "root/name" } }"""
|
||||||
|
)
|
||||||
|
assert(p.waitForString(10) { s =>
|
||||||
|
s contains """"id":3"""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object TestServer {
|
||||||
|
private val serverTestBase: File = new File(".").getAbsoluteFile / "sbt" / "src" / "server-test"
|
||||||
|
|
||||||
|
def withTestServer(testBuild: String)(f: TestServer => Future[Assertion]): Future[Assertion] = {
|
||||||
|
IO.withTemporaryDirectory { temp =>
|
||||||
|
IO.copyDirectory(serverTestBase / testBuild, temp / testBuild)
|
||||||
|
withTestServer(temp / testBuild)(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def withTestServer(baseDirectory: File)(f: TestServer => Future[Assertion]): Future[Assertion] = {
|
||||||
|
val testServer = TestServer(baseDirectory)
|
||||||
|
try {
|
||||||
|
f(testServer)
|
||||||
|
} finally {
|
||||||
|
testServer.bye()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def hostLog(s: String): Unit = {
|
||||||
|
println(s"""[${scala.Console.MAGENTA}build-1${scala.Console.RESET}] $s""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TestServer(baseDirectory: File) {
|
||||||
|
import TestServer.hostLog
|
||||||
|
|
||||||
|
val readBuffer = new Array[Byte](4096)
|
||||||
|
var buffer: Vector[Byte] = Vector.empty
|
||||||
|
var bytesRead = 0
|
||||||
|
private val delimiter: Byte = '\n'.toByte
|
||||||
|
private val RetByte = '\r'.toByte
|
||||||
|
|
||||||
|
hostLog("fork to a new sbt instance")
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
Future {
|
||||||
|
RunFromSourceMain.fork(baseDirectory)
|
||||||
|
()
|
||||||
|
}
|
||||||
|
lazy val portfile = baseDirectory / "project" / "target" / "active.json"
|
||||||
|
|
||||||
|
hostLog("wait 30s until the server is ready to respond")
|
||||||
|
def waitForPortfile(n: Int): Unit =
|
||||||
|
if (portfile.exists) ()
|
||||||
|
else {
|
||||||
|
if (n <= 0) sys.error(s"Timeout. $portfile is not found.")
|
||||||
|
else {
|
||||||
|
Thread.sleep(1000)
|
||||||
|
waitForPortfile(n - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
waitForPortfile(30)
|
||||||
|
|
||||||
|
// make connection to the socket described in the portfile
|
||||||
|
val (sk, tkn) = ClientSocket.socket(portfile)
|
||||||
|
val out = sk.getOutputStream
|
||||||
|
val in = sk.getInputStream
|
||||||
|
|
||||||
|
// initiate handshake
|
||||||
|
sendJsonRpc(
|
||||||
|
"""{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { } } }"""
|
||||||
|
)
|
||||||
|
|
||||||
|
def test(f: TestServer => Future[Assertion]): Future[Assertion] = {
|
||||||
|
f(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
def bye(): Unit = {
|
||||||
|
hostLog("sending exit")
|
||||||
|
sendJsonRpc(
|
||||||
|
"""{ "jsonrpc": "2.0", "id": 9, "method": "sbt/exec", "params": { "commandLine": "exit" } }"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def sendJsonRpc(message: String): Unit = {
|
||||||
|
writeLine(s"""Content-Length: ${message.size + 2}""")
|
||||||
|
writeLine("")
|
||||||
|
writeLine(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
def writeLine(s: String): Unit = {
|
||||||
|
def writeEndLine(): Unit = {
|
||||||
|
val retByte: Byte = '\r'.toByte
|
||||||
|
val delimiter: Byte = '\n'.toByte
|
||||||
|
out.write(retByte.toInt)
|
||||||
|
out.write(delimiter.toInt)
|
||||||
|
out.flush
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s != "") {
|
||||||
|
out.write(s.getBytes("UTF-8"))
|
||||||
|
}
|
||||||
|
writeEndLine
|
||||||
|
}
|
||||||
|
|
||||||
|
def readFrame: Option[String] = {
|
||||||
|
def getContentLength: Int = {
|
||||||
|
readLine map { line =>
|
||||||
|
line.drop(16).toInt
|
||||||
|
} getOrElse (0)
|
||||||
|
}
|
||||||
|
|
||||||
|
val l = getContentLength
|
||||||
|
readLine
|
||||||
|
readLine
|
||||||
|
readContentLength(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
@tailrec
|
||||||
|
final def waitForString(num: Int)(f: String => Boolean): Boolean = {
|
||||||
|
if (num < 0) false
|
||||||
|
else
|
||||||
|
readFrame match {
|
||||||
|
case Some(x) if f(x) => true
|
||||||
|
case _ =>
|
||||||
|
waitForString(num - 1)(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def readLine: Option[String] = {
|
||||||
|
if (buffer.isEmpty) {
|
||||||
|
val bytesRead = in.read(readBuffer)
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
buffer = buffer ++ readBuffer.toVector.take(bytesRead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val delimPos = buffer.indexOf(delimiter)
|
||||||
|
if (delimPos > 0) {
|
||||||
|
val chunk0 = buffer.take(delimPos)
|
||||||
|
buffer = buffer.drop(delimPos + 1)
|
||||||
|
// remove \r at the end of line.
|
||||||
|
val chunk1 = if (chunk0.lastOption contains RetByte) chunk0.dropRight(1) else chunk0
|
||||||
|
Some(new String(chunk1.toArray, "utf-8"))
|
||||||
|
} else None // no EOL yet, so skip this turn.
|
||||||
|
}
|
||||||
|
|
||||||
|
def readContentLength(length: Int): Option[String] = {
|
||||||
|
if (buffer.isEmpty) {
|
||||||
|
val bytesRead = in.read(readBuffer)
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
buffer = buffer ++ readBuffer.toVector.take(bytesRead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (length <= buffer.size) {
|
||||||
|
val chunk = buffer.take(length)
|
||||||
|
buffer = buffer.drop(length)
|
||||||
|
Some(new String(chunk.toArray, "utf-8"))
|
||||||
|
} else None // have not read enough yet, so skip this turn.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,8 @@ import java.util.concurrent.{
|
||||||
CompletionService => JCompletionService,
|
CompletionService => JCompletionService,
|
||||||
Executor,
|
Executor,
|
||||||
Executors,
|
Executors,
|
||||||
ExecutorCompletionService
|
ExecutorCompletionService,
|
||||||
|
RejectedExecutionException,
|
||||||
}
|
}
|
||||||
|
|
||||||
object CompletionService {
|
object CompletionService {
|
||||||
|
|
@ -33,7 +34,9 @@ object CompletionService {
|
||||||
def take() = completion.take().get()
|
def take() = completion.take().get()
|
||||||
}
|
}
|
||||||
def submit[T](work: () => T, completion: JCompletionService[T]): () => T = {
|
def submit[T](work: () => T, completion: JCompletionService[T]): () => T = {
|
||||||
val future = completion.submit { new Callable[T] { def call = work() } }
|
val future = try completion.submit { new Callable[T] { def call = work() } } catch {
|
||||||
|
case _: RejectedExecutionException => throw Incomplete(None, message = Some("cancelled"))
|
||||||
|
}
|
||||||
() =>
|
() =>
|
||||||
future.get()
|
future.get()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
import sbt.internal.util.ErrorHandling.wideConvert
|
import sbt.internal.util.ErrorHandling.wideConvert
|
||||||
import sbt.internal.util.{ DelegatingPMap, PMap, RMap, IDSet, ~> }
|
import sbt.internal.util.{ DelegatingPMap, IDSet, PMap, RMap, ~> }
|
||||||
import sbt.internal.util.Types._
|
import sbt.internal.util.Types._
|
||||||
import Execute._
|
import Execute._
|
||||||
|
|
||||||
|
|
@ -82,6 +82,7 @@ private[sbt] final class Execute[A[_] <: AnyRef](
|
||||||
|
|
||||||
def run[T](root: A[T])(implicit strategy: Strategy): Result[T] =
|
def run[T](root: A[T])(implicit strategy: Strategy): Result[T] =
|
||||||
try { runKeep(root)(strategy)(root) } catch { case i: Incomplete => Inc(i) }
|
try { runKeep(root)(strategy)(root) } catch { case i: Incomplete => Inc(i) }
|
||||||
|
|
||||||
def runKeep[T](root: A[T])(implicit strategy: Strategy): RMap[A, Result] = {
|
def runKeep[T](root: A[T])(implicit strategy: Strategy): RMap[A, Result] = {
|
||||||
assert(state.isEmpty, "Execute already running/ran.")
|
assert(state.isEmpty, "Execute already running/ran.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import java.net.InetAddress
|
||||||
import java.util.Hashtable
|
import java.util.Hashtable
|
||||||
|
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
import scala.util.DynamicVariable
|
|
||||||
import scala.xml.{ Elem, Node => XNode, XML }
|
import scala.xml.{ Elem, Node => XNode, XML }
|
||||||
import testing.{
|
import testing.{
|
||||||
Event => TEvent,
|
Event => TEvent,
|
||||||
|
|
@ -125,21 +124,28 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**The currently running test suite*/
|
/**The currently running test suite*/
|
||||||
val testSuite = new DynamicVariable(null: TestSuite)
|
private val testSuite = new InheritableThreadLocal[Option[TestSuite]] {
|
||||||
|
override def initialValue(): Option[TestSuite] = None
|
||||||
|
}
|
||||||
|
|
||||||
|
private def withTestSuite[T](f: TestSuite => T) =
|
||||||
|
testSuite.get().map(f).getOrElse(sys.error("no test suite"))
|
||||||
|
|
||||||
/**Creates the output Dir*/
|
/**Creates the output Dir*/
|
||||||
override def doInit() = { targetDir.mkdirs(); () }
|
override def doInit() = {
|
||||||
|
val _ = targetDir.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a new, initially empty Suite with the given name.
|
* Starts a new, initially empty Suite with the given name.
|
||||||
*/
|
*/
|
||||||
override def startGroup(name: String): Unit = testSuite.value_=(new TestSuite(name))
|
override def startGroup(name: String): Unit = testSuite.set(Some(new TestSuite(name)))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds all details for the given even to the current suite.
|
* Adds all details for the given even to the current suite.
|
||||||
*/
|
*/
|
||||||
override def testEvent(event: TestEvent): Unit = for (e <- event.detail) {
|
override def testEvent(event: TestEvent): Unit = for (e <- event.detail) {
|
||||||
testSuite.value.addEvent(e)
|
withTestSuite(_.addEvent(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -175,7 +181,7 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener {
|
||||||
def selector = null
|
def selector = null
|
||||||
def throwable = new OptionalThrowable(t)
|
def throwable = new OptionalThrowable(t)
|
||||||
}
|
}
|
||||||
testSuite.value.addEvent(event)
|
withTestSuite(_.addEvent(event))
|
||||||
writeSuite()
|
writeSuite()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,10 +198,11 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener {
|
||||||
private[this] def normalizeName(s: String) = s.replaceAll("""\s+""", "-")
|
private[this] def normalizeName(s: String) = s.replaceAll("""\s+""", "-")
|
||||||
|
|
||||||
private def writeSuite() = {
|
private def writeSuite() = {
|
||||||
val file = new File(targetDir, s"${normalizeName(testSuite.value.name)}.xml").getAbsolutePath
|
val file = new File(targetDir, s"${normalizeName(withTestSuite(_.name))}.xml").getAbsolutePath
|
||||||
// TODO would be nice to have a logger and log this with level debug
|
// TODO would be nice to have a logger and log this with level debug
|
||||||
// System.err.println("Writing JUnit XML test report: " + file)
|
// System.err.println("Writing JUnit XML test report: " + file)
|
||||||
XML.save(file, testSuite.value.stop(), "UTF-8", true, null)
|
XML.save(file, withTestSuite(_.stop()), "UTF-8", true, null)
|
||||||
|
testSuite.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Does nothing, as we write each file after a suite is done.*/
|
/**Does nothing, as we write each file after a suite is done.*/
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -52,7 +52,8 @@
|
||||||
"postinstall": "node ./node_modules/vscode/bin/install"
|
"postinstall": "node ./node_modules/vscode/bin/install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vscode": "^1.1.5",
|
"vscode": "^1.1.16",
|
||||||
"vscode-languageclient": "^3.4.2"
|
"vscode-languageclient": "^3.4.2",
|
||||||
|
"hoek": "^4.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,30 @@
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
let fs = require('fs');
|
let fs = require('fs');
|
||||||
|
import * as vscode from 'vscode';
|
||||||
import { ExtensionContext, workspace } from 'vscode'; // workspace,
|
import { ExtensionContext, workspace } from 'vscode'; // workspace,
|
||||||
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
|
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
|
||||||
|
|
||||||
export function activate(context: ExtensionContext) {
|
export async function activate(context: ExtensionContext) {
|
||||||
|
// Start sbt
|
||||||
|
const terminal = vscode.window.createTerminal(`sbt`);
|
||||||
|
terminal.show();
|
||||||
|
terminal.sendText("sbt");
|
||||||
|
|
||||||
|
function delay(ms: number) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for SBT server to start
|
||||||
|
let retries = 30;
|
||||||
|
while (retries > 0) {
|
||||||
|
retries--;
|
||||||
|
await delay(1000);
|
||||||
|
if (fs.existsSync(path.join(workspace.rootPath, 'project', 'target', 'active.json'))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The server is implemented in node
|
// The server is implemented in node
|
||||||
let serverModule = context.asAbsolutePath(path.join('server', 'server.js'));
|
let serverModule = context.asAbsolutePath(path.join('server', 'server.js'));
|
||||||
// The debug options for the server
|
// The debug options for the server
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,15 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "6.0.88",
|
"version": "7.0.61",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.88.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.61.tgz",
|
||||||
"integrity": "sha512-bYDPZTX0/s1aihdjLuAgogUAT5M+TpoWChEMea2p0yOcfn5bu3k6cJb9cp6nw268XeSNIGGr+4+/8V5K6BGzLQ==",
|
"integrity": "sha512-X4MNN+Z36OmVPv7n08wxq46/t61/rauW4+xeyxGPueDQ9t7SetHnuEPS0p9n6wU/15HvJLGjzfLTc/RwN7id3A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "2.5.2",
|
"version": "2.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.3.tgz",
|
||||||
"integrity": "sha1-A4qV99m7tCCxvzW6MdTFwd0//jQ=",
|
"integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mocha": "^2.2.42",
|
"@types/mocha": "^2.2.42",
|
||||||
"@types/node": "^6.0.88",
|
"@types/node": "^7.0.10",
|
||||||
"typescript": "^2.5.2"
|
"typescript": "^2.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,38 +4,40 @@
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vscode-jsonrpc": {
|
|
||||||
"version": "3.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.4.0.tgz",
|
|
||||||
"integrity": "sha1-qpWsWDvzHYD3JdV8J8CfTCz+n6k="
|
|
||||||
},
|
|
||||||
"vscode-languageserver": {
|
"vscode-languageserver": {
|
||||||
"version": "3.4.2",
|
"version": "3.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.5.1.tgz",
|
||||||
"integrity": "sha1-CMvlDuJpAdN91LXcUsJbkJNjwfE=",
|
"integrity": "sha512-RYUKn0DgHTFcS8kS4VaNCjNMaQXYqiXdN9bKrFjXzu5RPKfjIYcoh47oVWwZj4L3R/DPB0Se7HPaDatvYY2XgQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"vscode-languageserver-protocol": "3.4.1",
|
"vscode-languageserver-protocol": "3.5.1",
|
||||||
"vscode-uri": "1.0.1"
|
"vscode-uri": "1.0.3"
|
||||||
}
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-jsonrpc": {
|
||||||
|
"version": "3.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz",
|
||||||
|
"integrity": "sha1-hyOdnhZrLXNSJFuKgTWXgEwdY6o="
|
||||||
},
|
},
|
||||||
"vscode-languageserver-protocol": {
|
"vscode-languageserver-protocol": {
|
||||||
"version": "3.4.1",
|
"version": "3.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.1.tgz",
|
||||||
"integrity": "sha1-lrfIo+1opOvTCkB7BCenY3k1/h8=",
|
"integrity": "sha512-1fPDIwsAv1difCV+8daOrJEGunClNJWqnUHq/ncWrjhitKWXgGmRCjlwZ3gDUTt54yRcvXz1PXJDaRNvNH6pYA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"vscode-jsonrpc": "3.4.0",
|
"vscode-jsonrpc": "3.5.0",
|
||||||
"vscode-languageserver-types": "3.4.0"
|
"vscode-languageserver-types": "3.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vscode-languageserver-types": {
|
"vscode-languageserver-types": {
|
||||||
"version": "3.4.0",
|
"version": "3.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz",
|
||||||
"integrity": "sha1-UEOuR+5KwWrwe7PQylYSNeDA0vo="
|
"integrity": "sha1-5I15li8LjgLelV4/UkkI4rGcA3Q="
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"vscode-uri": {
|
"vscode-uri": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.3.tgz",
|
||||||
"integrity": "sha1-Eahr7+rDxKo+wIYjZRo8gabQu8g="
|
"integrity": "sha1-Yxvb9xbcyrDmUpGo3CXCMjIIWlI="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue