From ca7171ed17dc3b862fe79cc8a6e0fe535b861555 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Fri, 1 Dec 2017 12:39:59 -0800 Subject: [PATCH 001/120] Cache watch service I noticed that my custom WatchService was never cleaned up by sbt and realized that after every build we were making a new WatchService. At the same time, we were reusing the WatchState from the previous run, which was using the original WatchService. This was particularly problematic because it prevented us from registering any paths with the new watch service. This may have prevented some of the file updates from being seen by the watch service. Moreover, because we lost the reference to the original WatchService, there was no way to clean it up, which was a resource leak. May be related to #3775, #3695 --- main-command/src/main/scala/sbt/Watched.scala | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/main-command/src/main/scala/sbt/Watched.scala b/main-command/src/main/scala/sbt/Watched.scala index fa1dee03f..8b8385b61 100644 --- a/main-command/src/main/scala/sbt/Watched.scala +++ b/main-command/src/main/scala/sbt/Watched.scala @@ -94,7 +94,7 @@ object Watched { @tailrec def shouldTerminate: Boolean = (System.in.available > 0) && (watched.terminateWatch(System.in.read()) || shouldTerminate) val sources = watched.watchSources(s) - val service = watched.watchService() + val service = s get ContinuousWatchService getOrElse watched.watchService() val watchState = s get ContinuousState getOrElse WatchState.empty(service, sources) if (watchState.count > 0) @@ -115,15 +115,21 @@ object Watched { if (triggered) { printIfDefined(watched triggeredMessage newWatchState) - (ClearOnFailure :: next :: FailureWall :: repeat :: s).put(ContinuousState, newWatchState) + (ClearOnFailure :: next :: FailureWall :: repeat :: s) + .put(ContinuousState, newWatchState) + .put(ContinuousWatchService, service) } else { while (System.in.available() > 0) System.in.read() service.close() - s.remove(ContinuousState) + s.remove(ContinuousState).remove(ContinuousWatchService) } } val ContinuousState = AttributeKey[WatchState]("watch state", "Internal: tracks state for continuous execution.") + + val ContinuousWatchService = + AttributeKey[WatchService]("watch service", + "Internal: tracks watch service for continuous execution.") val Configuration = AttributeKey[Watched]("watched-configuration", "Configures continuous execution.") From bbddb26224f1c9695c570a9142b08408d8e2e232 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 6 Dec 2017 17:35:42 +0000 Subject: [PATCH 002/120] 2.12.4 --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 4cfe0e774..a8a1195dc 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -scalaVersion := "2.12.3" +scalaVersion := "2.12.4" scalacOptions ++= Seq("-feature", "-language:postfixOps") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.17") From 1f1806a4f19a0ec3c926d68cb6c33f85e4a364b1 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 6 Dec 2017 16:45:17 +0000 Subject: [PATCH 003/120] Re-enable unused warnings --- project/Util.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project/Util.scala b/project/Util.scala index 89556cd2e..bcbedcabf 100644 --- a/project/Util.scala +++ b/project/Util.scala @@ -48,7 +48,8 @@ object Util { "-Yno-adapted-args", "-Ywarn-dead-code", "-Ywarn-numeric-widen", - "-Ywarn-unused:-patvars,-params,-implicits,_", + //"-Ywarn-value-discard", + "-Ywarn-unused", "-Ywarn-unused-import" ) }), From b80a6b217b672be669032acf9be0bf78e76904a8 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 15:50:45 +0000 Subject: [PATCH 004/120] Remove all warnings from collectionProj --- build.sbt | 5 +++++ .../src/main/scala/sbt/internal/util/IDSet.scala | 4 +++- .../src/main/scala/sbt/internal/util/KList.scala | 9 +++++---- .../src/main/scala/sbt/internal/util/Signal.scala | 2 +- .../scala/sbt/internal/util/TypeFunctions.scala | 1 + .../src/test/scala/SettingsTest.scala | 13 ++++++------- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/build.sbt b/build.sbt index 9a1e8b923..8625be777 100644 --- a/build.sbt +++ b/build.sbt @@ -157,6 +157,11 @@ val collectionProj = (project in file("internal") / "util-collection") exclude[MissingClassProblem]("sbt.internal.util.Fn1"), exclude[DirectMissingMethodProblem]("sbt.internal.util.TypeFunctions.toFn1"), exclude[DirectMissingMethodProblem]("sbt.internal.util.Types.toFn1"), + + // Instead of defining foldr in KList & overriding in KCons, + // it's now abstract in KList and defined in both KCons & KNil. + exclude[FinalMethodProblem]("sbt.internal.util.KNil.foldr"), + exclude[DirectAbstractMethodProblem]("sbt.internal.util.KList.foldr"), ), ) .configure(addSbtUtilPosition) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala b/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala index 356b6906b..6bd5cff95 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala @@ -7,6 +7,8 @@ package sbt.internal.util +import scala.collection.JavaConverters._ + /** A mutable set interface that uses object identity to test for set membership.*/ trait IDSet[T] { def apply(t: T): Boolean @@ -41,7 +43,7 @@ object IDSet { def +=(t: T) = { backing.put(t, Dummy); () } def ++=(t: Iterable[T]) = t foreach += def -=(t: T) = if (backing.remove(t) eq null) false else true - def all = collection.JavaConverters.collectionAsScalaIterable(backing.keySet) + def all = backing.keySet.asScala def toList = all.toList def isEmpty = backing.isEmpty diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala index a68b61788..51babcae3 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala @@ -18,7 +18,7 @@ sealed trait KList[+M[_]] { def transform[N[_]](f: M ~> N): Transform[N] /** Folds this list using a function that operates on the homogeneous type of the elements of this list. */ - def foldr[B](f: (M[_], B) => B, init: B): B = init // had trouble defining it in KNil + def foldr[B](f: (M[_], B) => B, init: B): B /** Applies `f` to the elements of this list in the applicative functor defined by `ap`. */ def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] @@ -54,13 +54,14 @@ final case class KCons[H, +T <: KList[M], +M[_]](head: M[H], tail: T) extends KL override def foldr[B](f: (M[_], B) => B, init: B): B = f(head, tail.foldr(f, init)) } -sealed abstract class KNil extends KList[Nothing] { +sealed abstract class KNil extends KList[NothingK] { final type Transform[N[_]] = KNil - final def transform[N[_]](f: Nothing ~> N): Transform[N] = KNil + final def transform[N[_]](f: NothingK ~> N): Transform[N] = KNil + final def foldr[B](f: (NothingK[_], B) => B, init: B): B = init final def toList = Nil final def apply[N[x], Z](f: KNil => Z)(implicit ap: Applicative[N]): N[Z] = ap.pure(f(KNil)) - final def traverse[N[_], P[_]](f: Nothing ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KNil] = + final def traverse[N[_], P[_]](f: NothingK ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KNil] = np.pure(KNil) } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala index f1989ae19..661890b88 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala @@ -65,7 +65,7 @@ object Signals { } // Must only be referenced using a -// try { } catch { case e: LinkageError => ... } +// try { } catch { case _: LinkageError => ... } // block to private final class Signals0 { def supported(signal: String): Boolean = { diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala b/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala index 49c29ff11..8ec06890a 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala @@ -9,6 +9,7 @@ package sbt.internal.util trait TypeFunctions { type Id[X] = X + type NothingK[X] = Nothing sealed trait Const[A] { type Apply[B] = A } sealed trait ConstK[A] { type l[L[x]] = A } sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] } diff --git a/internal/util-collection/src/test/scala/SettingsTest.scala b/internal/util-collection/src/test/scala/SettingsTest.scala index 235cf5508..83d444079 100644 --- a/internal/util-collection/src/test/scala/SettingsTest.scala +++ b/internal/util-collection/src/test/scala/SettingsTest.scala @@ -7,8 +7,7 @@ package sbt.internal.util -import org.scalacheck._ -import Prop._ +import org.scalacheck._, Prop._ object SettingsTest extends Properties("settings") { val settingsExample: SettingsExample = SettingsExample() @@ -160,7 +159,7 @@ object SettingsTest extends Properties("settings") { final def checkCircularReferences(intermediate: Int): Prop = { val ccr = new CCR(intermediate) try { evaluate(setting(chk, ccr.top) :: Nil); false } catch { - case e: java.lang.Exception => true + case _: java.lang.Exception => true } } @@ -197,18 +196,18 @@ object SettingsTest extends Properties("settings") { def evaluate(settings: Seq[Setting[_]]): Settings[Scope] = try { make(settings)(delegates, scopeLocal, showFullKey) } catch { - case e: Throwable => e.printStackTrace; throw e + case e: Throwable => e.printStackTrace(); throw e } } // This setup is a workaround for module synchronization issues final class CCR(intermediate: Int) { import SettingsTest.settingsExample._ - lazy val top = iterate(value(intermediate), intermediate) - def iterate(init: Initialize[Int], i: Int): Initialize[Int] = + lazy val top = iterate(value(intermediate)) + def iterate(init: Initialize[Int]): Initialize[Int] = bind(init) { t => if (t <= 0) top else - iterate(value(t - 1), t - 1) + iterate(value(t - 1)) } } From 34136fb70b92f562b98781363f480d2f037f070f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 17:48:44 +0000 Subject: [PATCH 005/120] Remove all warnings from logicProj --- internal/util-logic/src/test/scala/sbt/logic/Test.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/util-logic/src/test/scala/sbt/logic/Test.scala b/internal/util-logic/src/test/scala/sbt/logic/Test.scala index 51d2f2f67..f83835f39 100644 --- a/internal/util-logic/src/test/scala/sbt/logic/Test.scala +++ b/internal/util-logic/src/test/scala/sbt/logic/Test.scala @@ -24,14 +24,14 @@ object LogicTest extends Properties("Logic") { property("Properly orders results.") = secure(expect(ordering, Set(B, A, C, E, F))) property("Detects cyclic negation") = secure( Logic.reduceAll(badClauses, Set()) match { - case Right(res) => false - case Left(err: Logic.CyclicNegation) => true - case Left(err) => sys.error(s"Expected cyclic error, got: $err") + case Right(_) => false + case Left(_: Logic.CyclicNegation) => true + case Left(err) => sys.error(s"Expected cyclic error, got: $err") } ) def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match { - case Left(err) => false + case Left(_) => false case Right(res) => val actual = res.provenSet if (actual != expected) From 4a5ff4fc0db1a0026cacc12606f2d445426da615 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 16:18:08 +0000 Subject: [PATCH 006/120] Remove all warnings from completeProj --- build.sbt | 5 ++ .../internal/util/complete/EditDistance.scala | 3 +- .../sbt/internal/util/complete/History.scala | 10 +-- .../util/complete/JLineCompletion.scala | 4 +- .../scala/sbt/complete/FileExamplesTest.scala | 70 +++++++++---------- .../src/main/scala/sbt/BasicCommands.scala | 5 +- main-command/src/main/scala/sbt/Command.scala | 19 ++--- 7 files changed, 56 insertions(+), 60 deletions(-) diff --git a/build.sbt b/build.sbt index 8625be777..a54dd57a3 100644 --- a/build.sbt +++ b/build.sbt @@ -174,6 +174,11 @@ val completeProj = (project in file("internal") / "util-complete") name := "Completion", libraryDependencies += jline, mimaSettings, + mimaBinaryIssueFilters ++= Seq( + // Changed signature or removed something in the internal pacakge + exclude[DirectMissingMethodProblem]("sbt.internal.*"), + exclude[IncompatibleResultTypeProblem]("sbt.internal.*"), + ), ) .configure(addSbtIO, addSbtUtilControl) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala index 5a8efe1be..cad2e5002 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala @@ -10,7 +10,7 @@ package complete import java.lang.Character.{ toLowerCase => lower } -/** @author Paul Phillips*/ +/** @author Paul Phillips */ object EditDistance { /** @@ -24,7 +24,6 @@ object EditDistance { insertCost: Int = 1, deleteCost: Int = 1, subCost: Int = 1, - transposeCost: Int = 1, matchCost: Int = 0, caseCost: Int = 1, transpositions: Boolean = false diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala index 1decc7197..8c63bf592 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala @@ -11,11 +11,7 @@ package complete import History.number import java.io.File -final class History private ( - val lines: IndexedSeq[String], - val path: Option[File], - error: String => Unit -) { +final class History private (val lines: IndexedSeq[String], val path: Option[File]) { private def reversed = lines.reverse def all: Seq[String] = lines @@ -52,8 +48,8 @@ final class History private ( } object History { - def apply(lines: Seq[String], path: Option[File], error: String => Unit): History = - new History(lines.toIndexedSeq, path, sys.error) + def apply(lines: Seq[String], path: Option[File]): History = + new History(lines.toIndexedSeq, path) def number(s: String): Option[Int] = try { Some(s.toInt) } catch { case _: NumberFormatException => None } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala index 68d03b841..700dafa35 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala @@ -11,7 +11,7 @@ package complete import jline.console.ConsoleReader import jline.console.completer.{ Completer, CompletionHandler } import scala.annotation.tailrec -import scala.collection.JavaConverters +import scala.collection.JavaConverters._ object JLineCompletion { def installCustomCompletor(reader: ConsoleReader, parser: Parser[_]): Unit = @@ -154,7 +154,7 @@ object JLineCompletion { if (line.charAt(line.length - 1) != '\n') reader.println() } - reader.printColumns(JavaConverters.seqAsJavaList(columns.map(_.trim))) + reader.printColumns(columns.map(_.trim).asJava) } def hasNewline(s: String): Boolean = s.indexOf('\n') >= 0 diff --git a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala index 61c6e53b1..173b276fc 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -9,60 +9,64 @@ package sbt.internal.util package complete import java.io.File -import sbt.io.IO._ +import sbt.io.IO class FileExamplesTest extends UnitSpec { "listing all files in an absolute base directory" should "produce the entire base directory's contents" in { - val _ = new DirectoryStructure { - fileExamples().toList should contain theSameElementsAs (allRelativizedPaths) + withDirectoryStructure() { ds => + ds.fileExamples().toList should contain theSameElementsAs (ds.allRelativizedPaths) } } - "listing files with a prefix that matches none" should - "produce an empty list" in { - val _ = new DirectoryStructure(withCompletionPrefix = "z") { - fileExamples().toList shouldBe empty + "listing files with a prefix that matches none" should "produce an empty list" in { + withDirectoryStructure(withCompletionPrefix = "z") { ds => + ds.fileExamples().toList shouldBe empty } } - "listing single-character prefixed files" should - "produce matching paths only" in { - val _ = new DirectoryStructure(withCompletionPrefix = "f") { - fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) + "listing single-character prefixed files" should "produce matching paths only" in { + withDirectoryStructure(withCompletionPrefix = "f") { ds => + ds.fileExamples().toList should contain theSameElementsAs (ds.prefixedPathsOnly) } } - "listing directory-prefixed files" should - "produce matching paths only" in { - val _ = new DirectoryStructure(withCompletionPrefix = "far") { - fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) + "listing directory-prefixed files" should "produce matching paths only" in { + withDirectoryStructure(withCompletionPrefix = "far") { ds => + ds.fileExamples().toList should contain theSameElementsAs (ds.prefixedPathsOnly) } } it should "produce sub-dir contents only when appending a file separator to the directory" in { - val _ = new DirectoryStructure(withCompletionPrefix = "far" + File.separator) { - fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) + withDirectoryStructure(withCompletionPrefix = "far" + File.separator) { ds => + ds.fileExamples().toList should contain theSameElementsAs (ds.prefixedPathsOnly) } } - "listing files with a sub-path prefix" should - "produce matching paths only" in { - val _ = new DirectoryStructure(withCompletionPrefix = "far" + File.separator + "ba") { - fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) + "listing files with a sub-path prefix" should "produce matching paths only" in { + withDirectoryStructure(withCompletionPrefix = "far" + File.separator + "ba") { ds => + ds.fileExamples().toList should contain theSameElementsAs (ds.prefixedPathsOnly) } } - "completing a full path" should - "produce a list with an empty string" in { - val _ = new DirectoryStructure(withCompletionPrefix = "bazaar") { - fileExamples().toList shouldEqual List("") + "completing a full path" should "produce a list with an empty string" in { + withDirectoryStructure(withCompletionPrefix = "bazaar") { ds => + ds.fileExamples().toList shouldEqual List("") } } - // TODO: Remove DelayedInit - https://github.com/scala/scala/releases/tag/v2.11.0-RC1 - class DirectoryStructure(withCompletionPrefix: String = "") extends DelayedInit { + def withDirectoryStructure[A](withCompletionPrefix: String = "")( + thunk: DirectoryStructure => A): Unit = { + IO.withTemporaryDirectory { tempDir => + val ds = new DirectoryStructure(withCompletionPrefix) + ds.createSampleDirStructure(tempDir) + ds.fileExamples = new FileExamples(ds.baseDir, withCompletionPrefix) + thunk(ds) + } + } + + final class DirectoryStructure(withCompletionPrefix: String) { var fileExamples: FileExamples = _ var baseDir: File = _ var childFiles: List[File] = _ @@ -72,22 +76,14 @@ class FileExamplesTest extends UnitSpec { def allRelativizedPaths: List[String] = (childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories) - .map(relativize(baseDir, _).get) + .map(IO.relativize(baseDir, _).get) def prefixedPathsOnly: List[String] = allRelativizedPaths .filter(_ startsWith withCompletionPrefix) .map(_ substring withCompletionPrefix.length) - override def delayedInit(testBody: => Unit): Unit = { - withTemporaryDirectory { tempDir => - createSampleDirStructure(tempDir) - fileExamples = new FileExamples(baseDir, withCompletionPrefix) - testBody - } - } - - private def createSampleDirStructure(tempDir: File): Unit = { + def createSampleDirStructure(tempDir: File): Unit = { childFiles = toChildFiles(tempDir, List("foo", "bar", "bazaar")) childDirectories = toChildFiles(tempDir, List("moo", "far")) nestedFiles = toChildFiles(childDirectories(1), List("farfile1", "barfile2")) diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index 82bb7b883..584e1196e 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -236,10 +236,9 @@ object BasicCommands { def historyParser(s: State): Parser[() => State] = Command.applyEffect(HistoryCommands.actionParser) { histFun => - val logError = (msg: String) => s.log.error(msg) - val hp = s get historyPath getOrElse None + val hp = (s get historyPath).flatten val lines = hp.toList.flatMap(p => IO.readLines(p)).toIndexedSeq - histFun(CHistory(lines, hp, logError)) match { + histFun(CHistory(lines, hp)) match { case Some(commands) => commands foreach println //printing is more appropriate than logging (commands ::: s).continue diff --git a/main-command/src/main/scala/sbt/Command.scala b/main-command/src/main/scala/sbt/Command.scala index 53158daa4..9d9be7a33 100644 --- a/main-command/src/main/scala/sbt/Command.scala +++ b/main-command/src/main/scala/sbt/Command.scala @@ -178,15 +178,16 @@ object Command { bs map (b => (b, distance(a, b))) filter (_._2 <= maxDistance) sortBy (_._2) take (maxSuggestions) map (_._1) def distance(a: String, b: String): Int = - EditDistance.levenshtein(a, - b, - insertCost = 1, - deleteCost = 1, - subCost = 2, - transposeCost = 1, - matchCost = -1, - caseCost = 1, - transpositions = true) + EditDistance.levenshtein( + a, + b, + insertCost = 1, + deleteCost = 1, + subCost = 2, + matchCost = -1, + caseCost = 1, + transpositions = true + ) def spacedAny(name: String): Parser[String] = spacedC(name, any) From f274aaa8111979fd505d5c7338ce3882e8860fe9 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 16:04:20 +0000 Subject: [PATCH 007/120] Remove all warnings from taskProj --- tasks/src/main/scala/sbt/Execute.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasks/src/main/scala/sbt/Execute.scala b/tasks/src/main/scala/sbt/Execute.scala index 267a1383a..bba13f448 100644 --- a/tasks/src/main/scala/sbt/Execute.scala +++ b/tasks/src/main/scala/sbt/Execute.scala @@ -360,8 +360,11 @@ private[sbt] final class Execute[A[_] <: AnyRef]( // cyclic reference checking def snapshotCycleCheck(): Unit = - for ((called: A[c], callers) <- callers.toSeq; caller <- callers) - cycleCheck(caller.asInstanceOf[A[c]], called) + callers.toSeq foreach { + case (called: A[c], callers) => + for (caller <- callers) cycleCheck(caller.asInstanceOf[A[c]], called) + case _ => () + } def cycleCheck[T](node: A[T], target: A[T]): Unit = { if (node eq target) cyclic(node, target, "Cannot call self") From 87dfb2c0f54b11e86633cd2752188ae330849a09 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 16:33:20 +0000 Subject: [PATCH 008/120] Remove all warnings from stdTaskProj --- tasks-standard/src/main/scala/sbt/Action.scala | 2 +- tasks-standard/src/test/scala/TaskRunnerCircular.scala | 3 ++- tasks-standard/src/test/scala/TaskRunnerFork.scala | 4 ++-- tasks-standard/src/test/scala/Test.scala | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tasks-standard/src/main/scala/sbt/Action.scala b/tasks-standard/src/main/scala/sbt/Action.scala index 0be3a8ae5..be5585cbd 100644 --- a/tasks-standard/src/main/scala/sbt/Action.scala +++ b/tasks-standard/src/main/scala/sbt/Action.scala @@ -25,7 +25,7 @@ sealed trait Action[T] { * If `inline` is true, `f` will be evaluated on the scheduler thread without the overhead of normal scheduling when possible. * This is intended as an optimization for already evaluated values or very short pure computations. */ -final case class Pure[T](f: () => T, inline: Boolean) extends Action[T] { +final case class Pure[T](f: () => T, `inline`: Boolean) extends Action[T] { private[sbt] def mapTask(f: Task ~> Task) = this } diff --git a/tasks-standard/src/test/scala/TaskRunnerCircular.scala b/tasks-standard/src/test/scala/TaskRunnerCircular.scala index 1350a22b0..3d542a9db 100644 --- a/tasks-standard/src/test/scala/TaskRunnerCircular.scala +++ b/tasks-standard/src/test/scala/TaskRunnerCircular.scala @@ -42,8 +42,9 @@ object TaskRunnerCircularTest extends Properties("TaskRunner Circular") { } try { tryRun(top, true, workers); false } catch { case i: Incomplete => cyclic(i) } } + def cyclic(i: Incomplete) = Incomplete .allExceptions(i) - .exists(_.isInstanceOf[Execute[({ type A[_] <: AnyRef })#A]#CyclicException[_]]) + .exists(_.isInstanceOf[Execute[({ type A[_] <: AnyRef })#A @unchecked]#CyclicException[_]]) } diff --git a/tasks-standard/src/test/scala/TaskRunnerFork.scala b/tasks-standard/src/test/scala/TaskRunnerFork.scala index b469dc38e..105f4a2de 100644 --- a/tasks-standard/src/test/scala/TaskRunnerFork.scala +++ b/tasks-standard/src/test/scala/TaskRunnerFork.scala @@ -31,8 +31,8 @@ object TaskRunnerForkTest extends Properties("TaskRunner Fork") { true } def runDoubleJoin(a: Int, b: Int, workers: Int): Unit = { - def inner(i: Int) = List.range(0, b).map(j => task(j).named(j.toString)).join - tryRun(List.range(0, a).map(inner).join, false, workers) + def inner = List.range(0, b).map(j => task(j).named(j.toString)).join + tryRun(List.range(0, a).map(_ => inner).join, false, workers) } property("fork and reduce") = forAll(TaskListGen, MaxWorkersGen) { (m: List[Int], workers: Int) => m.nonEmpty ==> { diff --git a/tasks-standard/src/test/scala/Test.scala b/tasks-standard/src/test/scala/Test.scala index 0574118ca..ab4fdb1b3 100644 --- a/tasks-standard/src/test/scala/Test.scala +++ b/tasks-standard/src/test/scala/Test.scala @@ -34,12 +34,12 @@ object Test extends std.TaskExtra { val d2 = t3(a, b2, c) mapR f val f2: Values => Task[Any] = { case (Value(aa), Value(bb), Value(cc)) => task(aa + " " + bb + " " + cc) - case x => d3 + case _ => d3 } lazy val d = t3(a, b, c) flatMapR f2 val f3: Values => Task[Any] = { case (Value(aa), Value(bb), Value(cc)) => task(aa + " " + bb + " " + cc) - case x => d2 + case _ => d2 } lazy val d3 = t3(a, b, c) flatMapR f3 From b4c5d9de31cf2d3a91cf309d7b84458c5ff35d9e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 16:07:40 +0000 Subject: [PATCH 009/120] Remove all warnings from protocolProj --- build.sbt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.sbt b/build.sbt index a54dd57a3..0ed479347 100644 --- a/build.sbt +++ b/build.sbt @@ -303,6 +303,8 @@ lazy val protocolProj = (project in file("protocol")) .enablePlugins(ContrabandPlugin, JsonCodecPlugin) .settings( testedBaseSettings, + scalacOptions -= "-Ywarn-unused", + scalacOptions += "-Xlint:-unused", name := "Protocol", libraryDependencies ++= Seq(sjsonNewScalaJson.value), managedSourceDirectories in Compile += From d99147d18c58997cbd6b6bbf17a142119921572a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 17:45:18 +0000 Subject: [PATCH 010/120] Remove all warnings from commandProj --- .../src/main/scala/sbt/BasicCommands.scala | 21 +++++++++++-------- main-command/src/main/scala/sbt/Watched.scala | 4 ++-- .../scala/sbt/internal/ConsoleChannel.scala | 4 ++-- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index 584e1196e..cd095f0b5 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -94,10 +94,13 @@ object BasicCommands { } def completionsCommand: Command = - Command(CompletionsCommand, CompletionsBrief, CompletionsDetailed)(completionsParser)( + Command(CompletionsCommand, CompletionsBrief, CompletionsDetailed)(_ => completionsParser)( runCompletions(_)(_)) - def completionsParser(state: State): Parser[String] = { + @deprecated("No longer public", "1.1.1") + def completionsParser(state: State): Parser[String] = completionsParser + + private[this] def completionsParser: Parser[String] = { val notQuoted = (NotQuoted ~ any.*) map { case (nq, s) => nq ++ s } val quotedOrUnquotedSingleArgument = Space ~> (StringVerbatim | StringEscapable | notQuoted) token(quotedOrUnquotedSingleArgument ?? "" examples ("", " ")) @@ -175,19 +178,19 @@ object BasicCommands { } def reboot: Command = - Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(rebootOptionParser) { + Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(_ => rebootOptionParser) { case (s, (full, currentOnly)) => s.reboot(full, currentOnly) } @deprecated("Use rebootOptionParser", "1.1.0") - def rebootParser(s: State): Parser[Boolean] = - rebootOptionParser(s) map { case (full, currentOnly) => full } + def rebootParser(s: State): Parser[Boolean] = rebootOptionParser map { case (full, _) => full } - private[sbt] def rebootOptionParser(s: State): Parser[(Boolean, Boolean)] = - token( - Space ~> (("full" ^^^ ((true, false))) | - ("dev" ^^^ ((false, true))))) ?? ((false, false)) + private[sbt] def rebootOptionParser: Parser[(Boolean, Boolean)] = { + val fullOption = "full" ^^^ ((true, false)) + val devOption = "dev" ^^^ ((false, true)) + token(Space ~> (fullOption | devOption)) ?? ((false, false)) + } def call: Command = Command(ApplyCommand, Help.more(ApplyCommand, ApplyDetailed))(_ => callParser) { diff --git a/main-command/src/main/scala/sbt/Watched.scala b/main-command/src/main/scala/sbt/Watched.scala index fa1dee03f..248eedd5b 100644 --- a/main-command/src/main/scala/sbt/Watched.scala +++ b/main-command/src/main/scala/sbt/Watched.scala @@ -23,8 +23,8 @@ import scala.util.Properties trait Watched { - /** The files watched when an action is run with a preceeding ~ */ - def watchSources(s: State): Seq[Watched.WatchSource] = Nil + /** The files watched when an action is run with a proceeding ~ */ + def watchSources(@deprecated("unused", "") s: State): Seq[Watched.WatchSource] = Nil def terminateWatch(key: Int): Boolean = Watched.isEnter(key) /** diff --git a/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala b/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala index 1ac026e2d..a0356234e 100644 --- a/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala +++ b/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala @@ -50,7 +50,7 @@ private[sbt] final class ConsoleChannel(val name: String) extends CommandChannel case _ => val x = makeAskUserThread(e.state) askUserThread = Some(x) - x.start + x.start() } case e: ConsoleUnpromptEvent => e.lastSource match { @@ -70,7 +70,7 @@ private[sbt] final class ConsoleChannel(val name: String) extends CommandChannel def shutdown(): Unit = askUserThread match { case Some(x) if x.isAlive => - x.interrupt + x.interrupt() askUserThread = None case _ => () } From 5d99bea89fff44e0357036fe274404b9725d17e3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 15:56:07 +0000 Subject: [PATCH 011/120] Remove all warnings from coreMacrosProj --- build.sbt | 2 +- .../main/scala/sbt/internal/util/appmacro/ContextUtil.scala | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index 0ed479347..516a4e2d0 100644 --- a/build.sbt +++ b/build.sbt @@ -357,7 +357,7 @@ lazy val commandProj = (project in file("main-command")) lazy val coreMacrosProj = (project in file("core-macros")) .dependsOn(collectionProj) .settings( - commonSettings, + baseSettings, name := "Core Macros", libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value, mimaSettings, diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala index b70461e53..7e627317d 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala @@ -33,9 +33,9 @@ object ContextUtil { f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] = { import c.universe._ c.macroApplication match { - case s @ Select(Apply(_, t :: Nil), tp) => f(c.Expr[Any](t), s.pos) - case a @ Apply(_, t :: Nil) => f(c.Expr[Any](t), a.pos) - case x => unexpectedTree(x) + case s @ Select(Apply(_, t :: Nil), _) => f(c.Expr[Any](t), s.pos) + case a @ Apply(_, t :: Nil) => f(c.Expr[Any](t), a.pos) + case x => unexpectedTree(x) } } From 843210598b60154a598ef32c393c38d77fb8cc75 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 14 Dec 2017 12:25:49 +0000 Subject: [PATCH 012/120] Exclude contraband generated files from diff by default In both local git diff and GitHub diff you can still see the diff if you want. This is just to remove the noise by default. --- .gitattributes | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitattributes b/.gitattributes index a5d9c6403..f3dbe80d8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,7 @@ # to native line endings on checkout. *.scala text *.java text + +# Exclude contraband generated files from diff (by default - you can see it if you want) +**/contraband-scala/**/* -diff merge=ours +**/contraband-scala/**/* linguist-generated=true From 2390fdfac63e24120f2358c80b9bd79bd14fd1e4 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 18:13:10 +0000 Subject: [PATCH 013/120] Remove all warnings from mainSettingsProj --- main-settings/src/main/scala/sbt/Def.scala | 103 +++++++++++++----- .../src/main/scala/sbt/InputTask.scala | 9 +- main-settings/src/main/scala/sbt/Scope.scala | 50 +++++---- .../src/main/scala/sbt/Structure.scala | 15 ++- .../src/main/scala/sbt/std/InputWrapper.scala | 18 +-- .../src/main/scala/sbt/std/KeyMacro.scala | 4 +- .../main/scala/sbt/std/TaskLinterDSL.scala | 2 +- .../src/main/scala/sbt/std/TaskMacro.scala | 90 ++++++++------- .../src/test/scala/sbt/std/TaskPosSpec.scala | 91 +++++++--------- .../src/test/scala/sbt/std/TestUtil.scala | 4 +- main/src/main/scala/sbt/Project.scala | 5 +- main/src/main/scala/sbt/internal/Act.scala | 4 +- main/src/main/scala/sbt/internal/Load.scala | 1 - .../sbt/internal/server/SettingQuery.scala | 4 +- .../test/scala/sbt/internal/TestBuild.scala | 3 +- 15 files changed, 232 insertions(+), 171 deletions(-) diff --git a/main-settings/src/main/scala/sbt/Def.scala b/main-settings/src/main/scala/sbt/Def.scala index cfde273f9..d03b4ba10 100644 --- a/main-settings/src/main/scala/sbt/Def.scala +++ b/main-settings/src/main/scala/sbt/Def.scala @@ -38,30 +38,44 @@ object Def extends Init[Scope] with TaskMacroExtra { def showFullKey(keyNameColor: Option[String]): Show[ScopedKey[_]] = Show[ScopedKey[_]]((key: ScopedKey[_]) => displayFull(key, keyNameColor)) + @deprecated("Use showRelativeKey2 which doesn't take the unused multi param", "1.1.1") def showRelativeKey( current: ProjectRef, multi: Boolean, keyNameColor: Option[String] = None ): Show[ScopedKey[_]] = - Show[ScopedKey[_]]( - key => - Scope.display( - key.scope, - withColor(key.key.label, keyNameColor), - ref => displayRelative(current, multi, ref) - )) + showRelativeKey2(current, keyNameColor) - def showBuildRelativeKey( - currentBuild: URI, - multi: Boolean, - keyNameColor: Option[String] = None + def showRelativeKey2( + current: ProjectRef, + keyNameColor: Option[String] = None, ): Show[ScopedKey[_]] = Show[ScopedKey[_]]( key => Scope.display( key.scope, withColor(key.key.label, keyNameColor), - ref => displayBuildRelative(currentBuild, multi, ref) + ref => displayRelative2(current, ref) + )) + + @deprecated("Use showBuildRelativeKey2 which doesn't take the unused multi param", "1.1.1") + def showBuildRelativeKey( + currentBuild: URI, + multi: Boolean, + keyNameColor: Option[String] = None, + ): Show[ScopedKey[_]] = + showBuildRelativeKey2(currentBuild, keyNameColor) + + def showBuildRelativeKey2( + currentBuild: URI, + keyNameColor: Option[String] = None, + ): Show[ScopedKey[_]] = + Show[ScopedKey[_]]( + key => + Scope.display( + key.scope, + withColor(key.key.label, keyNameColor), + ref => displayBuildRelative(currentBuild, ref) )) /** @@ -71,8 +85,11 @@ object Def extends Init[Scope] with TaskMacroExtra { def displayRelativeReference(current: ProjectRef, project: Reference): String = displayRelative(current, project, false) - @deprecated("Use displayRelativeReference", "1.1.0") + @deprecated("Use displayRelative2 which doesn't take the unused multi param", "1.1.1") def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String = + displayRelative2(current, project) + + def displayRelative2(current: ProjectRef, project: Reference): String = displayRelative(current, project, true) /** @@ -91,7 +108,11 @@ object Def extends Init[Scope] with TaskMacroExtra { } } + @deprecated("Use variant without multi", "1.1.1") def displayBuildRelative(currentBuild: URI, multi: Boolean, project: Reference): String = + displayBuildRelative(currentBuild, project) + + def displayBuildRelative(currentBuild: URI, project: Reference): String = project match { case BuildRef(`currentBuild`) => "ThisBuild /" case ProjectRef(`currentBuild`, x) => x + " /" @@ -173,16 +194,31 @@ object Def extends Init[Scope] with TaskMacroExtra { // The following conversions enable the types Initialize[T], Initialize[Task[T]], and Task[T] to // be used in task and setting macros as inputs with an ultimate result of type T - implicit def macroValueI[T](in: Initialize[T]): MacroValue[T] = ??? - implicit def macroValueIT[T](in: Initialize[Task[T]]): MacroValue[T] = ??? - implicit def macroValueIInT[T](in: Initialize[InputTask[T]]): InputEvaluated[T] = ??? - implicit def taskMacroValueIT[T](in: Initialize[Task[T]]): MacroTaskValue[T] = ??? - implicit def macroPrevious[T](in: TaskKey[T]): MacroPrevious[T] = ??? + implicit def macroValueI[T](@deprecated("unused", "") in: Initialize[T]): MacroValue[T] = ??? - // The following conversions enable the types Parser[T], Initialize[Parser[T]], and Initialize[State => Parser[T]] to - // be used in the inputTask macro as an input with an ultimate result of type T - implicit def parserInitToInput[T](p: Initialize[Parser[T]]): ParserInput[T] = ??? - implicit def parserInitStateToInput[T](p: Initialize[State => Parser[T]]): ParserInput[T] = ??? + implicit def macroValueIT[T](@deprecated("unused", "") in: Initialize[Task[T]]): MacroValue[T] = + ??? + + implicit def macroValueIInT[T]( + @deprecated("unused", "") in: Initialize[InputTask[T]] + ): InputEvaluated[T] = ??? + + implicit def taskMacroValueIT[T]( + @deprecated("unused", "") in: Initialize[Task[T]] + ): MacroTaskValue[T] = ??? + + implicit def macroPrevious[T](@deprecated("unused", "") in: TaskKey[T]): MacroPrevious[T] = ??? + + // The following conversions enable the types Parser[T], Initialize[Parser[T]], and + // Initialize[State => Parser[T]] to be used in the inputTask macro as an input with an ultimate + // result of type T + implicit def parserInitToInput[T]( + @deprecated("unused", "") p: Initialize[Parser[T]] + ): ParserInput[T] = ??? + + implicit def parserInitStateToInput[T]( + @deprecated("unused", "") p: Initialize[State => Parser[T]] + ): ParserInput[T] = ??? def settingKey[T](description: String): SettingKey[T] = macro std.KeyMacro.settingKeyImpl[T] def taskKey[T](description: String): TaskKey[T] = macro std.KeyMacro.taskKeyImpl[T] @@ -190,27 +226,40 @@ object Def extends Init[Scope] with TaskMacroExtra { private[sbt] def dummy[T: Manifest](name: String, description: String): (TaskKey[T], Task[T]) = (TaskKey[T](name, description, DTask), dummyTask(name)) + private[sbt] def dummyTask[T](name: String): Task[T] = { import std.TaskExtra.{ task => newTask, _ } val base: Task[T] = newTask( sys.error("Dummy task '" + name + "' did not get converted to a full task.")) named name base.copy(info = base.info.set(isDummyTask, true)) } + private[sbt] def isDummy(t: Task[_]): Boolean = t.info.attributes.get(isDummyTask) getOrElse false + private[sbt] val isDummyTask = AttributeKey[Boolean]( "is-dummy-task", "Internal: used to identify dummy tasks. sbt injects values for these tasks at the start of task execution.", Invisible) + private[sbt] val (stateKey, dummyState) = dummy[State]("state", "Current build state.") + private[sbt] val (streamsManagerKey, dummyStreamsManager) = Def.dummy[std.Streams[ScopedKey[_]]]( "streams-manager", "Streams manager, which provides streams for different contexts.") } -// these need to be mixed into the sbt package object because the target doesn't involve Initialize or anything in Def + +// these need to be mixed into the sbt package object +// because the target doesn't involve Initialize or anything in Def trait TaskMacroExtra { - implicit def macroValueT[T](in: Task[T]): std.MacroValue[T] = ??? - implicit def macroValueIn[T](in: InputTask[T]): std.InputEvaluated[T] = ??? - implicit def parserToInput[T](in: Parser[T]): std.ParserInput[T] = ??? - implicit def stateParserToInput[T](in: State => Parser[T]): std.ParserInput[T] = ??? + implicit def macroValueT[T](@deprecated("unused", "") in: Task[T]): std.MacroValue[T] = ??? + + implicit def macroValueIn[T](@deprecated("unused", "") in: InputTask[T]): std.InputEvaluated[T] = + ??? + + implicit def parserToInput[T](@deprecated("unused", "") in: Parser[T]): std.ParserInput[T] = ??? + + implicit def stateParserToInput[T]( + @deprecated("unused", "") in: State => Parser[T] + ): std.ParserInput[T] = ??? } diff --git a/main-settings/src/main/scala/sbt/InputTask.scala b/main-settings/src/main/scala/sbt/InputTask.scala index fbdb4a221..2a722de45 100644 --- a/main-settings/src/main/scala/sbt/InputTask.scala +++ b/main-settings/src/main/scala/sbt/InputTask.scala @@ -49,8 +49,13 @@ object InputTask { ) } - implicit def inputTaskParsed[T](in: InputTask[T]): std.ParserInputTask[T] = ??? - implicit def inputTaskInitParsed[T](in: Initialize[InputTask[T]]): std.ParserInputTask[T] = ??? + implicit def inputTaskParsed[T]( + @deprecated("unused", "") in: InputTask[T] + ): std.ParserInputTask[T] = ??? + + implicit def inputTaskInitParsed[T]( + @deprecated("unused", "") in: Initialize[InputTask[T]] + ): std.ParserInputTask[T] = ??? def make[T](p: State => Parser[Task[T]]): InputTask[T] = new InputTask[T](p) diff --git a/main-settings/src/main/scala/sbt/Scope.scala b/main-settings/src/main/scala/sbt/Scope.scala index 0bd3e27c6..23955959b 100644 --- a/main-settings/src/main/scala/sbt/Scope.scala +++ b/main-settings/src/main/scala/sbt/Scope.scala @@ -201,23 +201,6 @@ object Scope { if (s == "") "" else s + " " - // sbt 0.12 style - def display012StyleMasked(scope: Scope, - sep: String, - showProject: Reference => String, - mask: ScopeMask): String = { - import scope.{ project, config, task, extra } - val configPrefix = config.foldStrict(displayConfigKey012Style, "*:", ".:") - val taskPrefix = task.foldStrict(_.label + "::", "", ".::") - val extras = extra.foldStrict(_.entries.map(_.toString).toList, Nil, Nil) - val postfix = if (extras.isEmpty) "" else extras.mkString("(", ", ", ")") - mask.concatShow(projectPrefix012Style(project, showProject012Style), - configPrefix, - taskPrefix, - sep, - postfix) - } - def equal(a: Scope, b: Scope, mask: ScopeMask): Boolean = (!mask.project || a.project == b.project) && (!mask.config || a.config == b.config) && @@ -241,7 +224,7 @@ object Scope { (parts.take(1) ++ parts.drop(1).map(_.capitalize)).mkString } - // *Inherit functions should be immediate delegates and not include argument itself. Transitivity will be provided by this method + @deprecated("Use variant without extraInherit", "1.1.1") def delegates[Proj]( refs: Seq[(ProjectRef, Proj)], configurations: Proj => Seq[ConfigKey], @@ -251,18 +234,47 @@ object Scope { configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey], taskInherit: AttributeKey[_] => Seq[AttributeKey[_]], extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap] + ): Scope => Seq[Scope] = + delegates( + refs, + configurations, + resolve, + rootProject, + projectInherit, + configInherit, + taskInherit, + ) + + // *Inherit functions should be immediate delegates and not include argument itself. Transitivity will be provided by this method + def delegates[Proj]( + refs: Seq[(ProjectRef, Proj)], + configurations: Proj => Seq[ConfigKey], + resolve: Reference => ResolvedReference, + rootProject: URI => String, + projectInherit: ProjectRef => Seq[ProjectRef], + configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey], + taskInherit: AttributeKey[_] => Seq[AttributeKey[_]], ): Scope => Seq[Scope] = { val index = delegates(refs, configurations, projectInherit, configInherit) scope => - indexedDelegates(resolve, index, rootProject, taskInherit, extraInherit)(scope) + indexedDelegates(resolve, index, rootProject, taskInherit)(scope) } + @deprecated("Use variant without extraInherit", "1.1.1") def indexedDelegates( resolve: Reference => ResolvedReference, index: DelegateIndex, rootProject: URI => String, taskInherit: AttributeKey[_] => Seq[AttributeKey[_]], extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap] + )(rawScope: Scope): Seq[Scope] = + indexedDelegates(resolve, index, rootProject, taskInherit)(rawScope) + + def indexedDelegates( + resolve: Reference => ResolvedReference, + index: DelegateIndex, + rootProject: URI => String, + taskInherit: AttributeKey[_] => Seq[AttributeKey[_]], )(rawScope: Scope): Seq[Scope] = { val scope = Scope.replaceThis(GlobalScope)(rawScope) diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index bcedc4171..3c1d705b4 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -379,8 +379,10 @@ object Scoped { sealed abstract class RichInitTaskBase[S, R[_]] { protected def onTask[T](f: Task[S] => Task[T]): Initialize[R[T]] - def flatMap[T](f: S => Task[T]): Initialize[R[T]] = flatMapR(f compose successM) - def map[T](f: S => T): Initialize[R[T]] = mapR(f compose successM) + def flatMap[T](f: S => Task[T]): Initialize[R[T]] = + onTask(_.result flatMap (f compose successM)) + + def map[T](f: S => T): Initialize[R[T]] = onTask(_.result map (f compose successM)) def andFinally(fin: => Unit): Initialize[R[S]] = onTask(_ andFinally fin) def doFinally(t: Task[Unit]): Initialize[R[S]] = onTask(_ doFinally t) @@ -393,22 +395,23 @@ object Scoped { @deprecated( "Use the `result` method to create a task that returns the full Result of this task. Then, call `flatMap` on the new task.", "0.13.0") - def flatMapR[T](f: Result[S] => Task[T]): Initialize[R[T]] = onTask(_ flatMapR f) + def flatMapR[T](f: Result[S] => Task[T]): Initialize[R[T]] = onTask(_.result flatMap f) @deprecated( "Use the `result` method to create a task that returns the full Result of this task. Then, call `map` on the new task.", "0.13.0") - def mapR[T](f: Result[S] => T): Initialize[R[T]] = onTask(_ mapR f) + def mapR[T](f: Result[S] => T): Initialize[R[T]] = onTask(_.result map f) @deprecated( "Use the `failure` method to create a task that returns Incomplete when this task fails and then call `flatMap` on the new task.", "0.13.0") - def flatFailure[T](f: Incomplete => Task[T]): Initialize[R[T]] = flatMapR(f compose failM) + def flatFailure[T](f: Incomplete => Task[T]): Initialize[R[T]] = + onTask(_.result flatMap (f compose failM)) @deprecated( "Use the `failure` method to create a task that returns Incomplete when this task fails and then call `map` on the new task.", "0.13.0") - def mapFailure[T](f: Incomplete => T): Initialize[R[T]] = mapR(f compose failM) + def mapFailure[T](f: Incomplete => T): Initialize[R[T]] = onTask(_.result map (f compose failM)) } type AnyInitTask = Initialize[Task[T]] forSome { type T } diff --git a/main-settings/src/main/scala/sbt/std/InputWrapper.scala b/main-settings/src/main/scala/sbt/std/InputWrapper.scala index b6dcc7b46..fb6b6bf70 100644 --- a/main-settings/src/main/scala/sbt/std/InputWrapper.scala +++ b/main-settings/src/main/scala/sbt/std/InputWrapper.scala @@ -31,27 +31,27 @@ object InputWrapper { @compileTimeOnly( "`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.") - def wrapTask_\u2603\u2603[T](in: Any): T = implDetailError + def wrapTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError @compileTimeOnly( "`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") - def wrapInit_\u2603\u2603[T](in: Any): T = implDetailError + def wrapInit_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError @compileTimeOnly( "`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.") - def wrapInitTask_\u2603\u2603[T](in: Any): T = implDetailError + def wrapInitTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError @compileTimeOnly( "`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask.") - def wrapInputTask_\u2603\u2603[T](in: Any): T = implDetailError + def wrapInputTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError @compileTimeOnly( "`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask.") - def wrapInitInputTask_\u2603\u2603[T](in: Any): T = implDetailError + def wrapInitInputTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError @compileTimeOnly( "`previous` can only be called on a task within a task or input task definition macro, such as :=, +=, ++=, Def.task, or Def.inputTask.") - def wrapPrevious_\u2603\u2603[T](in: Any): T = implDetailError + def wrapPrevious_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError private[this] def implDetailError = sys.error("This method is an implementation detail and should not be referenced.") @@ -164,7 +164,7 @@ object InputWrapper { format: c.Expr[sjsonnew.JsonFormat[T]]): c.Expr[Option[T]] = { import c.universe._ c.macroApplication match { - case a @ Apply(Select(Apply(_, t :: Nil), tp), fmt) => + case a @ Apply(Select(Apply(_, t :: Nil), _), _) => if (t.tpe <:< c.weakTypeOf[TaskKey[T]]) { val tsTyped = c.Expr[TaskKey[T]](t) val newTree = c.universe.reify { Previous.runtime[T](tsTyped.splice)(format.splice) } @@ -224,12 +224,12 @@ object ParserInput { @compileTimeOnly( "`parsed` can only be used within an input task macro, such as := or Def.inputTask.") - def parser_\u2603\u2603[T](i: Any): T = + def parser_\u2603\u2603[T](@deprecated("unused", "") i: Any): T = sys.error("This method is an implementation detail and should not be referenced.") @compileTimeOnly( "`parsed` can only be used within an input task macro, such as := or Def.inputTask.") - def initParser_\u2603\u2603[T](i: Any): T = + def initParser_\u2603\u2603[T](@deprecated("unused", "") i: Any): T = sys.error("This method is an implementation detail and should not be referenced.") private[std] def wrap[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], diff --git a/main-settings/src/main/scala/sbt/std/KeyMacro.scala b/main-settings/src/main/scala/sbt/std/KeyMacro.scala index aa08c14e6..697963ab0 100644 --- a/main-settings/src/main/scala/sbt/std/KeyMacro.scala +++ b/main-settings/src/main/scala/sbt/std/KeyMacro.scala @@ -61,10 +61,10 @@ private[sbt] object KeyMacro { n.decodedName.toString.trim // trim is not strictly correct, but macros don't expose the API necessary @tailrec def enclosingVal(trees: List[c.Tree]): String = { trees match { - case vd @ ValDef(_, name, _, _) :: ts => processName(name) + case ValDef(_, name, _, _) :: _ => processName(name) case (_: ApplyTree | _: Select | _: TypeApply) :: xs => enclosingVal(xs) // lazy val x: X = has this form for some reason (only when the explicit type is present, though) - case Block(_, _) :: DefDef(mods, name, _, _, _, _) :: xs if mods.hasFlag(Flag.LAZY) => + case Block(_, _) :: DefDef(mods, name, _, _, _, _) :: _ if mods.hasFlag(Flag.LAZY) => processName(name) case _ => c.error(c.enclosingPosition, invalidEnclosingTree(methodName.decodedName.toString)) diff --git a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala index ec6b04288..8ae98e8de 100644 --- a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala +++ b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala @@ -73,7 +73,7 @@ abstract class BaseTaskLinterDSL extends LinterDSL { val (qualName, isSettingKey) = Option(qual.symbol) .map(sym => (sym.name.decodedName.toString, qual.tpe <:< typeOf[SettingKey[_]])) - .getOrElse((ap.pos.lineContent, false)) + .getOrElse((ap.pos.source.lineToString(ap.pos.line - 1), false)) if (!isSettingKey && !shouldIgnore && isTask(wrapperName, tpe.tpe, qual)) { if (insideIf && !isDynamicTask) { diff --git a/main-settings/src/main/scala/sbt/std/TaskMacro.scala b/main-settings/src/main/scala/sbt/std/TaskMacro.scala index efeff7877..85e19c3f6 100644 --- a/main-settings/src/main/scala/sbt/std/TaskMacro.scala +++ b/main-settings/src/main/scala/sbt/std/TaskMacro.scala @@ -130,37 +130,41 @@ object TaskMacro { // These macros are there just so we can fail old operators like `<<=` and provide useful migration information. def fakeSettingAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)( - app: c.Expr[Initialize[T]]): c.Expr[Setting[T]] = - ContextUtil.selectMacroImpl[Setting[T]](c) { (ts, pos) => - c.abort(pos, assignMigration) - } - def fakeSettingAppend1Position[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)( - v: c.Expr[Initialize[V]])(a: c.Expr[Append.Value[S, V]]): c.Expr[Setting[S]] = - ContextUtil.selectMacroImpl[Setting[S]](c) { (ts, pos) => - c.abort(pos, append1Migration) - } - def fakeSettingAppendNPosition[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)( - vs: c.Expr[Initialize[V]])(a: c.Expr[Append.Values[S, V]]): c.Expr[Setting[S]] = - ContextUtil.selectMacroImpl[Setting[S]](c) { (ts, pos) => - c.abort(pos, appendNMigration) - } - def fakeItaskAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)( - app: c.Expr[Initialize[Task[T]]]): c.Expr[Setting[Task[T]]] = - ContextUtil.selectMacroImpl[Setting[Task[T]]](c) { (ts, pos) => - c.abort(pos, assignMigration) - } - def fakeTaskAppend1Position[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)( - v: c.Expr[Initialize[Task[V]]])(a: c.Expr[Append.Value[S, V]]): c.Expr[Setting[Task[S]]] = - ContextUtil.selectMacroImpl[Setting[Task[S]]](c) { (ts, pos) => - c.abort(pos, append1Migration) - } - def fakeTaskAppendNPosition[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)( - vs: c.Expr[Initialize[Task[V]]])(a: c.Expr[Append.Values[S, V]]): c.Expr[Setting[Task[S]]] = - ContextUtil.selectMacroImpl[Setting[Task[S]]](c) { (ts, pos) => - c.abort(pos, appendNMigration) - } + @deprecated("unused", "") app: c.Expr[Initialize[T]] + ): c.Expr[Setting[T]] = + ContextUtil.selectMacroImpl[Setting[T]](c)((_, pos) => c.abort(pos, assignMigration)) - /* Implementations of <<= macro variations for tasks and settings. These just get the source position of the call site.*/ + def fakeSettingAppend1Position[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)( + @deprecated("unused", "") v: c.Expr[Initialize[V]])( + @deprecated("unused", "") a: c.Expr[Append.Value[S, V]] + ): c.Expr[Setting[S]] = + ContextUtil.selectMacroImpl[Setting[S]](c)((_, pos) => c.abort(pos, append1Migration)) + + def fakeSettingAppendNPosition[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)( + @deprecated("unused", "") vs: c.Expr[Initialize[V]])( + @deprecated("unused", "") a: c.Expr[Append.Values[S, V]] + ): c.Expr[Setting[S]] = + ContextUtil.selectMacroImpl[Setting[S]](c)((_, pos) => c.abort(pos, appendNMigration)) + + def fakeItaskAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)( + @deprecated("unused", "") app: c.Expr[Initialize[Task[T]]] + ): c.Expr[Setting[Task[T]]] = + ContextUtil.selectMacroImpl[Setting[Task[T]]](c)((_, pos) => c.abort(pos, assignMigration)) + + def fakeTaskAppend1Position[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)( + @deprecated("unused", "") v: c.Expr[Initialize[Task[V]]])( + @deprecated("unused", "") a: c.Expr[Append.Value[S, V]] + ): c.Expr[Setting[Task[S]]] = + ContextUtil.selectMacroImpl[Setting[Task[S]]](c)((_, pos) => c.abort(pos, append1Migration)) + + def fakeTaskAppendNPosition[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)( + @deprecated("unused", "") vs: c.Expr[Initialize[Task[V]]])( + @deprecated("unused", "") a: c.Expr[Append.Values[S, V]] + ): c.Expr[Setting[Task[S]]] = + ContextUtil.selectMacroImpl[Setting[Task[S]]](c)((_, pos) => c.abort(pos, appendNMigration)) + + // Implementations of <<= macro variations for tasks and settings. + // These just get the source position of the call site. def itaskAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)( app: c.Expr[Initialize[Task[T]]]): c.Expr[Setting[Task[T]]] = @@ -221,7 +225,7 @@ object TaskMacro { if typeArgs.nonEmpty && (typeArgs.head weak_<:< c.weakTypeOf[Task[_]]) && (tpe weak_<:< c.weakTypeOf[Initialize[_]]) => c.macroApplication match { - case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), _) => + case Apply(Apply(TypeApply(Select(preT, _), _), _), _) => val tree = Apply( TypeApply(Select(preT, TermName("+=").encodedName), TypeTree(typeArgs.head) :: Nil), Select(v.tree, TermName("taskValue").encodedName) :: Nil) @@ -287,10 +291,14 @@ object TaskMacro { newName: String): c.Tree = { import c.universe._ c.macroApplication match { - case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), _) => - Apply(Apply(TypeApply(Select(preT, TermName(newName).encodedName), targs), - init :: sourcePosition(c).tree :: Nil), - append :: Nil) + case Apply(Apply(TypeApply(Select(preT, _), targs), _), _) => + Apply( + Apply( + TypeApply(Select(preT, TermName(newName).encodedName), targs), + init :: sourcePosition(c).tree :: Nil + ), + append :: Nil + ) case x => ContextUtil.unexpectedTree(x) } } @@ -299,10 +307,14 @@ object TaskMacro { newName: String): c.Tree = { import c.universe._ c.macroApplication match { - case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), r) => - Apply(Apply(TypeApply(Select(preT, TermName(newName).encodedName), targs), - init :: sourcePosition(c).tree :: Nil), - r) + case Apply(Apply(TypeApply(Select(preT, _), targs), _), _) => + Apply( + Apply( + TypeApply(Select(preT, TermName(newName).encodedName), targs), + init :: sourcePosition(c).tree :: Nil + ), + remove :: Nil + ) case x => ContextUtil.unexpectedTree(x) } } diff --git a/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala b/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala index 68d8f9cdf..a7df2aba8 100644 --- a/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala +++ b/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala @@ -10,12 +10,11 @@ package sbt.std class TaskPosSpec { // Dynamic tasks can have task invocations inside if branches locally { - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") val bar = taskKey[String]("") - var condition = true - val baz = Def.taskDyn[String] { + val condition = true + Def.taskDyn[String] { if (condition) foo else bar } @@ -23,23 +22,21 @@ class TaskPosSpec { // Dynamic settings can have setting invocations inside if branches locally { - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = settingKey[String]("") val bar = settingKey[String]("") - var condition = true - val baz = Def.settingDyn[String] { + val condition = true + Def.settingDyn[String] { if (condition) foo else bar } } locally { - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") - var condition = true - val baz = Def.task[String] { + val condition = true + Def.task[String] { val fooAnon = () => foo.value: @sbtUnchecked if (condition) fooAnon() else fooAnon() @@ -47,11 +44,10 @@ class TaskPosSpec { } locally { - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") - var condition = true - val baz = Def.task[String] { + val condition = true + Def.task[String] { val fooAnon = () => (foo.value: @sbtUnchecked) + "" if (condition) fooAnon() else fooAnon() @@ -59,12 +55,11 @@ class TaskPosSpec { } locally { - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") val bar = taskKey[String]("") - var condition = true - val baz = Def.task[String] { + val condition = true + Def.task[String] { if (condition) foo.value: @sbtUnchecked else bar.value: @sbtUnchecked } @@ -72,11 +67,10 @@ class TaskPosSpec { locally { // This is fix 1 for appearance of tasks inside anons - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") - var condition = true - val baz = Def.task[String] { + val condition = true + Def.task[String] { val fooResult = foo.value val anon = () => fooResult + " " if (condition) anon() @@ -86,11 +80,10 @@ class TaskPosSpec { locally { // This is fix 2 for appearance of tasks inside anons - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") - var condition = true - val baz = Def.taskDyn[String] { + val condition = true + Def.taskDyn[String] { val anon1 = (value: String) => value + " " if (condition) { Def.task(anon1(foo.value)) @@ -100,31 +93,27 @@ class TaskPosSpec { locally { // missing .value error should not happen inside task dyn - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") - val baz = Def.taskDyn[String] { + Def.taskDyn[String] { foo } } locally { - // missing .value error should not happen inside task dyn - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") val avoidDCE = "" - val baz = Def.task[String] { - foo: @sbtUnchecked + Def.task[String] { + val _ = foo: @sbtUnchecked avoidDCE } } locally { - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") - val baz = Def.task[String] { + Def.task[String] { def inner(s: KeyedInitialize[_]) = println(s) inner(foo) "" @@ -133,11 +122,10 @@ class TaskPosSpec { locally { // In theory, this should be reported, but missing .value analysis is dumb at the cost of speed - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") def avoidDCE = { println(""); "" } - val baz = Def.task[String] { + Def.task[String] { val (_, _) = "" match { case _ => (foo, 1 + 2) } @@ -146,15 +134,14 @@ class TaskPosSpec { } locally { - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = taskKey[String]("") - def avoidDCE = { println(""); "" } - val baz = Def.task[String] { + def avoidDCE(x: TaskKey[String]) = x.toString + Def.task[String] { val hehe = foo // We do not detect `hehe` because guessing that the user did the wrong thing would require // us to run the unused name traverser defined in Typer (and hence proxy it from context util) - avoidDCE + avoidDCE(hehe) } } @@ -168,11 +155,10 @@ class TaskPosSpec { } locally { - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = settingKey[String]("") val condition = true - val baz = Def.task[String] { + Def.task[String] { // settings can be evaluated in a condition if (condition) foo.value else "..." @@ -180,10 +166,9 @@ class TaskPosSpec { } locally { - import sbt._ - import sbt.Def._ + import sbt._, Def._ val foo = settingKey[String]("") - val baz = Def.task[Seq[String]] { + Def.task[Seq[String]] { (1 to 10).map(_ => foo.value) } } diff --git a/main-settings/src/test/scala/sbt/std/TestUtil.scala b/main-settings/src/test/scala/sbt/std/TestUtil.scala index ed6f15530..43beb90fb 100644 --- a/main-settings/src/test/scala/sbt/std/TestUtil.scala +++ b/main-settings/src/test/scala/sbt/std/TestUtil.scala @@ -7,11 +7,9 @@ package sbt.std -import scala.reflect._ +import scala.tools.reflect.ToolBox object TestUtil { - import tools.reflect.ToolBox - def eval(code: String, compileOptions: String = ""): Any = { val tb = mkToolbox(compileOptions) tb.eval(tb.parse(code)) diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 680bba811..0282184be 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -284,15 +284,14 @@ object Project extends ProjectExtra { structure: BuildStructure, keyNameColor: Option[String] = None ): Show[ScopedKey[_]] = - Def.showRelativeKey(session.current, structure.allProjects.size > 1, keyNameColor) + Def.showRelativeKey2(session.current, keyNameColor) def showLoadingKey( loaded: LoadedBuild, keyNameColor: Option[String] = None ): Show[ScopedKey[_]] = - Def.showRelativeKey( + Def.showRelativeKey2( ProjectRef(loaded.root, loaded.units(loaded.root).rootProjects.head), - loaded.allProjectRefs.size > 1, keyNameColor ) diff --git a/main/src/main/scala/sbt/internal/Act.scala b/main/src/main/scala/sbt/internal/Act.scala index 91051f3ec..2ab067db0 100644 --- a/main/src/main/scala/sbt/internal/Act.scala +++ b/main/src/main/scala/sbt/internal/Act.scala @@ -8,7 +8,7 @@ package sbt package internal -import Def.{ showRelativeKey, ScopedKey } +import Def.{ showRelativeKey2, ScopedKey } import Keys.sessionSettings import sbt.internal.util.complete.{ DefaultParsers, Parser } import Aggregation.{ KeyValue, Values } @@ -56,7 +56,7 @@ object Act { keyMap: Map[String, AttributeKey[_]], data: Settings[Scope]): Parser[ParsedKey] = scopedKeyFull(index, current, defaultConfigs, keyMap) flatMap { choices => - select(choices, data)(showRelativeKey(current, index.buildURIs.size > 1)) + select(choices, data)(showRelativeKey2(current)) } def scopedKeyFull(index: KeyIndex, diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index c6264f671..c87d583ab 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -208,7 +208,6 @@ private[sbt] object Load { project => projectInherit(lb, project), (project, config) => configInherit(lb, project, config, rootProject), task => task.extend, - (project, extra) => Nil ) } diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index 25dd1b66a..fa106d23e 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -21,7 +21,7 @@ import sjsonnew.support.scalajson.unsafe._ object SettingQuery { import sbt.internal.util.{ AttributeKey, Settings } import sbt.internal.util.complete.{ DefaultParsers, Parser }, DefaultParsers._ - import sbt.Def.{ showBuildRelativeKey, ScopedKey } + import sbt.Def.{ showBuildRelativeKey2, ScopedKey } // Similar to Act.ParsedAxis / Act.projectRef / Act.resolveProject except you can't omit the project reference @@ -67,7 +67,7 @@ object SettingQuery { data: Settings[Scope] ): Parser[ParsedKey] = scopedKeyFull(index, currentBuild, defaultConfigs, keyMap) flatMap { choices => - Act.select(choices, data)(showBuildRelativeKey(currentBuild, index.buildURIs.size > 1)) + Act.select(choices, data)(showBuildRelativeKey2(currentBuild)) } def scopedKey( diff --git a/main/src/test/scala/sbt/internal/TestBuild.scala b/main/src/test/scala/sbt/internal/TestBuild.scala index 7b8a70db5..e7bb2ead0 100644 --- a/main/src/test/scala/sbt/internal/TestBuild.scala +++ b/main/src/test/scala/sbt/internal/TestBuild.scala @@ -142,7 +142,6 @@ abstract class TestBuild { inheritProject, inheritConfig, inheritTask, - (ref, mp) => Nil ) lazy val allFullScopes: Seq[Scope] = for { @@ -213,7 +212,7 @@ abstract class TestBuild { } def structure(env: Env, settings: Seq[Setting[_]], current: ProjectRef): Structure = { - implicit val display = Def.showRelativeKey(current, env.allProjects.size > 1) + implicit val display = Def.showRelativeKey2(current) if (settings.isEmpty) { try { sys.error("settings is empty") From 072366d48e6a896e9f6cdf11f19dd18e0093be0f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Dec 2017 17:48:03 +0000 Subject: [PATCH 014/120] Remove all warnings from testingProj --- testing/src/main/scala/sbt/TestFramework.scala | 4 ++-- testing/src/main/scala/sbt/TestReportListener.scala | 2 +- testing/src/main/scala/sbt/internal/testing/TestLogger.scala | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/testing/src/main/scala/sbt/TestFramework.scala b/testing/src/main/scala/sbt/TestFramework.scala index ffd7ef36d..95fa55788 100644 --- a/testing/src/main/scala/sbt/TestFramework.scala +++ b/testing/src/main/scala/sbt/TestFramework.scala @@ -52,8 +52,8 @@ final class TestFramework(val implClassNames: String*) extends Serializable { case oldFramework: OldFramework => new FrameworkWrapper(oldFramework) }) } catch { - case e: ClassNotFoundException => - log.debug("Framework implementation '" + head + "' not present."); + case _: ClassNotFoundException => + log.debug("Framework implementation '" + head + "' not present.") createFramework(loader, log, tail) } case Nil => diff --git a/testing/src/main/scala/sbt/TestReportListener.scala b/testing/src/main/scala/sbt/TestReportListener.scala index 090ddc7ef..12566c707 100644 --- a/testing/src/main/scala/sbt/TestReportListener.scala +++ b/testing/src/main/scala/sbt/TestReportListener.scala @@ -25,7 +25,7 @@ trait TestReportListener { def endGroup(name: String, result: TestResult): Unit /** Used by the test framework for logging test results */ - def contentLogger(test: TestDefinition): Option[ContentLogger] = None + def contentLogger(@deprecated("unused", "") test: TestDefinition): Option[ContentLogger] = None } diff --git a/testing/src/main/scala/sbt/internal/testing/TestLogger.scala b/testing/src/main/scala/sbt/internal/testing/TestLogger.scala index 1cd5ff4b4..6c49f793a 100644 --- a/testing/src/main/scala/sbt/internal/testing/TestLogger.scala +++ b/testing/src/main/scala/sbt/internal/testing/TestLogger.scala @@ -9,7 +9,7 @@ package sbt package internal.testing import testing.{ Logger => TLogger } -import sbt.internal.util.{ ManagedLogger, BufferedAppender } +import sbt.internal.util.{ BufferedAppender, ConsoleAppender, ManagedLogger } import sbt.util.{ Level, LogExchange, ShowLines } import sbt.protocol.testing._ import java.util.concurrent.atomic.AtomicInteger @@ -89,7 +89,7 @@ object TestLogger { def debug(s: String) = log(Level.Debug, TestStringEvent(s)) def trace(t: Throwable) = logger.trace(t) private def log(level: Level.Value, event: TestStringEvent) = logger.logEvent(level, event) - def ansiCodesSupported() = logger.ansiCodesSupported + def ansiCodesSupported() = ConsoleAppender.formatEnabledInEnv } private[sbt] def toTestItemEvent(event: TestEvent): TestItemEvent = From f50260218d046bcce7db02677c0fceda04470b01 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 5 Dec 2017 16:16:26 +0000 Subject: [PATCH 015/120] Remove all warnings from actionsProj --- build.sbt | 6 ++ main-actions/src/main/scala/sbt/Doc.scala | 100 +++++------------- .../src/main/scala/sbt/ForkTests.scala | 3 +- main-actions/src/main/scala/sbt/Package.scala | 16 ++- .../src/main/scala/sbt/RawCompileLike.scala | 11 +- main-actions/src/main/scala/sbt/Sync.scala | 26 +++-- .../src/main/scala/sbt/TestResultLogger.scala | 11 +- main-actions/src/main/scala/sbt/Tests.scala | 21 +++- main/src/main/scala/sbt/Defaults.scala | 2 +- 9 files changed, 91 insertions(+), 105 deletions(-) diff --git a/build.sbt b/build.sbt index 516a4e2d0..6ef429611 100644 --- a/build.sbt +++ b/build.sbt @@ -285,6 +285,12 @@ lazy val actionsProj = (project in file("main-actions")) name := "Actions", libraryDependencies += sjsonNewScalaJson.value, mimaSettings, + mimaBinaryIssueFilters ++= Seq( + // Removed unused private[sbt] nested class + exclude[MissingClassProblem]("sbt.Doc$Scaladoc"), + // Removed no longer used private[sbt] method + exclude[DirectMissingMethodProblem]("sbt.Doc.generate"), + ), ) .configure( addSbtIO, diff --git a/main-actions/src/main/scala/sbt/Doc.scala b/main-actions/src/main/scala/sbt/Doc.scala index db8a70db9..12df12c9e 100644 --- a/main-actions/src/main/scala/sbt/Doc.scala +++ b/main-actions/src/main/scala/sbt/Doc.scala @@ -10,10 +10,6 @@ package sbt import java.io.File import sbt.internal.inc.AnalyzingCompiler -import Predef.{ conforms => _, _ } -import sbt.io.syntax._ -import sbt.io.IO - import sbt.util.CacheStoreFactory import xsbti.Reporter import xsbti.compile.JavaTools @@ -23,10 +19,12 @@ import sbt.internal.util.ManagedLogger object Doc { import RawCompileLike._ + def scaladoc(label: String, cacheStoreFactory: CacheStoreFactory, compiler: AnalyzingCompiler): Gen = scaladoc(label, cacheStoreFactory, compiler, Seq()) + def scaladoc(label: String, cacheStoreFactory: CacheStoreFactory, compiler: AnalyzingCompiler, @@ -34,82 +32,32 @@ object Doc { cached(cacheStoreFactory, fileInputOptions, prepare(label + " Scala API documentation", compiler.doc)) - def javadoc(label: String, - cacheStoreFactory: CacheStoreFactory, - doc: JavaTools, - log: Logger, - reporter: Reporter): Gen = - javadoc(label, cacheStoreFactory, doc, log, reporter, Seq()) - def javadoc(label: String, - cacheStoreFactory: CacheStoreFactory, - doc: JavaTools, - log: Logger, - reporter: Reporter, - fileInputOptions: Seq[String]): Gen = - cached( - cacheStoreFactory, - fileInputOptions, - prepare( - label + " Java API documentation", - filterSources( - javaSourcesOnly, - (sources: Seq[File], - classpath: Seq[File], - outputDirectory: File, - options: Seq[String], - maxErrors: Int, - log: Logger) => { - // doc.doc - ??? - } - ) - ) - ) + @deprecated("Going away", "1.1.1") + def javadoc( + label: String, + cacheStoreFactory: CacheStoreFactory, + doc: JavaTools, + log: Logger, + reporter: Reporter, + ): Gen = ??? + + @deprecated("Going away", "1.1.1") + def javadoc( + label: String, + cacheStoreFactory: CacheStoreFactory, + doc: JavaTools, + log: Logger, + reporter: Reporter, + fileInputOptions: Seq[String], + ): Gen = ??? + + @deprecated("Going away", "1.1.1") val javaSourcesOnly: File => Boolean = _.getName.endsWith(".java") - - private[sbt] final class Scaladoc(maximumErrors: Int, compiler: AnalyzingCompiler) extends Doc { - def apply(label: String, - sources: Seq[File], - classpath: Seq[File], - outputDirectory: File, - options: Seq[String], - log: ManagedLogger): Unit = { - generate("Scala", - label, - compiler.doc, - sources, - classpath, - outputDirectory, - options, - maximumErrors, - log) - } - } } +@deprecated("Going away", "1.1.1") sealed trait Doc { + @deprecated("Going away", "1.1.1") type Gen = (Seq[File], Seq[File], File, Seq[String], Int, ManagedLogger) => Unit - - private[sbt] final def generate(variant: String, - label: String, - docf: Gen, - sources: Seq[File], - classpath: Seq[File], - outputDirectory: File, - options: Seq[String], - maxErrors: Int, - log: ManagedLogger): Unit = { - val logSnip = variant + " API documentation" - if (sources.isEmpty) - log.info("No sources available, skipping " + logSnip + "...") - else { - log.info( - "Generating " + logSnip + " for " + label + " sources to " + outputDirectory.absolutePath + "...") - IO.delete(outputDirectory) - IO.createDirectory(outputDirectory) - docf(sources, classpath, outputDirectory, options, maxErrors, log) - log.info(logSnip + " generation successful.") - } - } } diff --git a/main-actions/src/main/scala/sbt/ForkTests.scala b/main-actions/src/main/scala/sbt/ForkTests.scala index 5beec08d3..c6eb96a1e 100755 --- a/main-actions/src/main/scala/sbt/ForkTests.scala +++ b/main-actions/src/main/scala/sbt/ForkTests.scala @@ -17,6 +17,7 @@ import sbt.io.IO import sbt.util.Logger import sbt.ConcurrentRestrictions.Tag import sbt.protocol.testing._ +import sbt.internal.util.ConsoleAppender private[sbt] object ForkTests { def apply(runners: Map[TestFramework, Runner], @@ -78,7 +79,7 @@ private[sbt] object ForkTests { val is = new ObjectInputStream(socket.getInputStream) try { - val config = new ForkConfiguration(log.ansiCodesSupported, parallel) + val config = new ForkConfiguration(ConsoleAppender.formatEnabledInEnv, parallel) os.writeObject(config) val taskdefs = opts.tests.map( diff --git a/main-actions/src/main/scala/sbt/Package.scala b/main-actions/src/main/scala/sbt/Package.scala index 76333963e..3297d3b4a 100644 --- a/main-actions/src/main/scala/sbt/Package.scala +++ b/main-actions/src/main/scala/sbt/Package.scala @@ -100,10 +100,18 @@ object Package { org: String, orgName: String): PackageOption = { import Attributes.Name._ - val attribKeys = Seq(IMPLEMENTATION_TITLE, - IMPLEMENTATION_VERSION, - IMPLEMENTATION_VENDOR, - IMPLEMENTATION_VENDOR_ID) + + // The ones in Attributes.Name are deprecated saying: + // "Extension mechanism will be removed in a future release. Use class path instead." + val IMPLEMENTATION_VENDOR_ID = new Attributes.Name("Implementation-Vendor-Id") + val IMPLEMENTATION_URL = new Attributes.Name("Implementation-URL") + + val attribKeys = Seq( + IMPLEMENTATION_TITLE, + IMPLEMENTATION_VERSION, + IMPLEMENTATION_VENDOR, + IMPLEMENTATION_VENDOR_ID, + ) val attribVals = Seq(name, version, orgName, org) ManifestAttributes((attribKeys zip attribVals) ++ { homepage map (h => (IMPLEMENTATION_URL, h.toString)) diff --git a/main-actions/src/main/scala/sbt/RawCompileLike.scala b/main-actions/src/main/scala/sbt/RawCompileLike.scala index f06e7e3a8..f8b85e25c 100644 --- a/main-actions/src/main/scala/sbt/RawCompileLike.scala +++ b/main-actions/src/main/scala/sbt/RawCompileLike.scala @@ -7,6 +7,7 @@ package sbt +import scala.annotation.tailrec import java.io.File import sbt.internal.inc.{ RawCompiler, ScalaInstance } @@ -30,7 +31,7 @@ object RawCompileLike { type Gen = (Seq[File], Seq[File], File, Seq[String], Int, ManagedLogger) => Unit private def optionFiles(options: Seq[String], fileInputOpts: Seq[String]): List[File] = { - @annotation.tailrec + @tailrec def loop(opt: List[String], result: List[File]): List[File] = { opt.dropWhile(!fileInputOpts.contains(_)) match { case List(_, fileOpt, tail @ _*) => { @@ -46,6 +47,7 @@ object RawCompileLike { def cached(cacheStoreFactory: CacheStoreFactory, doCompile: Gen): Gen = cached(cacheStoreFactory, Seq(), doCompile) + def cached(cacheStoreFactory: CacheStoreFactory, fileInputOpts: Seq[String], doCompile: Gen): Gen = @@ -67,6 +69,7 @@ object RawCompileLike { } cachedComp(inputs)(exists(outputDirectory.allPaths.get.toSet)) } + def prepare(description: String, doCompile: Gen): Gen = (sources, classpath, outputDirectory, options, maxErrors, log) => { if (sources.isEmpty) @@ -79,20 +82,22 @@ object RawCompileLike { log.info(description.capitalize + " successful.") } } + def filterSources(f: File => Boolean, doCompile: Gen): Gen = (sources, classpath, outputDirectory, options, maxErrors, log) => doCompile(sources filter f, classpath, outputDirectory, options, maxErrors, log) def rawCompile(instance: ScalaInstance, cpOptions: ClasspathOptions): Gen = - (sources, classpath, outputDirectory, options, maxErrors, log) => { + (sources, classpath, outputDirectory, options, _, log) => { val compiler = new RawCompiler(instance, cpOptions, log) compiler(sources, classpath, outputDirectory, options) } + def compile(label: String, cacheStoreFactory: CacheStoreFactory, instance: ScalaInstance, cpOptions: ClasspathOptions): Gen = cached(cacheStoreFactory, prepare(label + " sources", rawCompile(instance, cpOptions))) - val nop: Gen = (sources, classpath, outputDirectory, options, maxErrors, log) => () + val nop: Gen = (_, _, _, _, _, _) => () } diff --git a/main-actions/src/main/scala/sbt/Sync.scala b/main-actions/src/main/scala/sbt/Sync.scala index ec2eb73e3..b5024f2ae 100644 --- a/main-actions/src/main/scala/sbt/Sync.scala +++ b/main-actions/src/main/scala/sbt/Sync.scala @@ -30,10 +30,18 @@ import sjsonnew.{ Builder, JsonFormat, Unbuilder, deserializationError } * It is safe to use for its intended purpose: copying resources to a class output directory. */ object Sync { - def apply(store: CacheStore, - inStyle: FileInfo.Style = FileInfo.lastModified, - outStyle: FileInfo.Style = FileInfo.exists) - : Traversable[(File, File)] => Relation[File, File] = + @deprecated("Use sync, which doesn't take the unused outStyle param", "1.1.1") + def apply( + store: CacheStore, + inStyle: FileInfo.Style = FileInfo.lastModified, + outStyle: FileInfo.Style = FileInfo.exists, + ): Traversable[(File, File)] => Relation[File, File] = + sync(store, inStyle) + + def sync( + store: CacheStore, + inStyle: FileInfo.Style = FileInfo.lastModified, + ): Traversable[(File, File)] => Relation[File, File] = mappings => { val relation = Relation.empty ++ mappings noDuplicateTargets(relation) @@ -70,13 +78,9 @@ object Sync { } def noDuplicateTargets(relation: Relation[File, File]): Unit = { - val dups = relation.reverseMap.filter { - case (_, srcs) => - srcs.size >= 2 && srcs.exists(!_.isDirectory) - } map { - case (target, srcs) => - "\n\t" + target + "\nfrom\n\t" + srcs.mkString("\n\t\t") - } + val dups = relation.reverseMap + .filter { case (_, srcs) => srcs.size >= 2 && srcs.exists(!_.isDirectory) } + .map { case (target, srcs) => "\n\t" + target + "\nfrom\n\t" + srcs.mkString("\n\t\t") } if (dups.nonEmpty) sys.error("Duplicate mappings:" + dups.mkString) } diff --git a/main-actions/src/main/scala/sbt/TestResultLogger.scala b/main-actions/src/main/scala/sbt/TestResultLogger.scala index 01cc9e03f..8248946b3 100644 --- a/main-actions/src/main/scala/sbt/TestResultLogger.scala +++ b/main-actions/src/main/scala/sbt/TestResultLogger.scala @@ -133,17 +133,20 @@ object TestResultLogger { failuresCount, ignoredCount, canceledCount, - pendingCount) = + pendingCount, + ) = results.events.foldLeft((0, 0, 0, 0, 0, 0, 0)) { - case ((skippedAcc, errorAcc, passedAcc, failureAcc, ignoredAcc, canceledAcc, pendingAcc), - (name @ _, testEvent)) => + case (acc, (_, testEvent)) => + val (skippedAcc, errorAcc, passedAcc, failureAcc, ignoredAcc, canceledAcc, pendingAcc) = + acc (skippedAcc + testEvent.skippedCount, errorAcc + testEvent.errorCount, passedAcc + testEvent.passedCount, failureAcc + testEvent.failureCount, ignoredAcc + testEvent.ignoredCount, canceledAcc + testEvent.canceledCount, - pendingAcc + testEvent.pendingCount) + pendingAcc + testEvent.pendingCount, + ) } val totalCount = failuresCount + errorsCount + skippedCount + passedCount val base = diff --git a/main-actions/src/main/scala/sbt/Tests.scala b/main-actions/src/main/scala/sbt/Tests.scala index bd69e4c30..6885a0dc5 100644 --- a/main-actions/src/main/scala/sbt/Tests.scala +++ b/main-actions/src/main/scala/sbt/Tests.scala @@ -34,6 +34,7 @@ import sbt.util.Logger import sbt.protocol.testing.TestResult sealed trait TestOption + object Tests { /** @@ -227,7 +228,7 @@ object Tests { if (config.parallel) makeParallel(loader, runnables, setupTasks, config.tags) //.toSeq.join else - makeSerial(loader, runnables, setupTasks, config.tags) + makeSerial(loader, runnables, setupTasks) val taggedMainTasks = mainTasks.tagw(config.tags: _*) taggedMainTasks map processResults flatMap { results => val cleanupTasks = fj(partApp(userCleanup) :+ frameworkCleanup(results.overall)) @@ -294,10 +295,20 @@ object Tests { } } - def makeSerial(loader: ClassLoader, - runnables: Seq[TestRunnable], - setupTasks: Task[Unit], - tags: Seq[(Tag, Int)]): Task[List[(String, SuiteResult)]] = { + @deprecated("Use the variant without tags", "1.1.1") + def makeSerial( + loader: ClassLoader, + runnables: Seq[TestRunnable], + setupTasks: Task[Unit], + tags: Seq[(Tag, Int)], + ): Task[List[(String, SuiteResult)]] = + makeSerial(loader, runnables, setupTasks) + + def makeSerial( + loader: ClassLoader, + runnables: Seq[TestRunnable], + setupTasks: Task[Unit], + ): Task[List[(String, SuiteResult)]] = { @tailrec def processRunnable(runnableList: List[TestRunnable], acc: List[(String, SuiteResult)]): List[(String, SuiteResult)] = diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 1f2dcfd3a..d141175ce 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1533,7 +1533,7 @@ object Defaults extends BuildCommon { val cacheStore = s.cacheStoreFactory make "copy-resources" val mappings = (resources.value --- dirs) pair (rebase(dirs, t) | flat(t)) s.log.debug("Copy resource mappings: " + mappings.mkString("\n\t", "\n\t", "")) - Sync(cacheStore)(mappings) + Sync.sync(cacheStore)(mappings) mappings } From a90832b5935340425088a84a4fed96f35c4f4dac Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 14 Dec 2017 13:41:40 +0000 Subject: [PATCH 016/120] Remove all warnings from mainProj --- build.sbt | 18 +- .../src/main/scala/sbt/Structure.scala | 2 + main/src/main/scala/sbt/Cross.scala | 8 +- main/src/main/scala/sbt/Defaults.scala | 123 +++++---- main/src/main/scala/sbt/EvaluateTask.scala | 40 ++- main/src/main/scala/sbt/Extracted.scala | 40 +-- main/src/main/scala/sbt/Main.scala | 37 ++- main/src/main/scala/sbt/PluginCross.scala | 5 +- main/src/main/scala/sbt/Plugins.scala | 16 +- main/src/main/scala/sbt/Project.scala | 11 +- main/src/main/scala/sbt/ScopeFilter.scala | 2 +- main/src/main/scala/sbt/SessionVar.scala | 2 +- main/src/main/scala/sbt/TemplateCommand.scala | 5 +- main/src/main/scala/sbt/internal/Act.scala | 17 +- .../main/scala/sbt/internal/Aggregation.scala | 36 +-- .../main/scala/sbt/internal/BuildDef.scala | 2 +- .../scala/sbt/internal/BuildStructure.scala | 4 +- .../scala/sbt/internal/CommandExchange.scala | 69 ++--- .../DefaultBackgroundJobService.scala | 7 +- .../sbt/internal/EvaluateConfigurations.scala | 252 +++++++++++------- .../scala/sbt/internal/GlobalPlugin.scala | 2 +- .../sbt/internal/LibraryManagement.scala | 12 +- main/src/main/scala/sbt/internal/Load.scala | 5 +- .../main/scala/sbt/internal/LogManager.scala | 6 +- .../scala/sbt/internal/PluginsDebug.scala | 11 +- .../scala/sbt/internal/RelayAppender.scala | 4 +- .../sbt/internal/SettingCompletions.scala | 124 ++++----- .../scala/sbt/internal/SettingGraph.scala | 2 +- .../main/scala/sbt/internal/TaskTimings.scala | 2 +- .../scala/sbt/internal/parser/SbtParser.scala | 8 +- .../sbt/internal/parser/SbtRefactorings.scala | 5 +- .../sbt/internal/server/Definition.scala | 4 +- main/src/test/scala/Delegates.scala | 17 +- main/src/test/scala/ParseKey.scala | 12 +- main/src/test/scala/PluginsTest.scala | 20 +- .../scala/sbt/internal/parser/ErrorSpec.scala | 3 +- .../internal/server/SettingQueryTest.scala | 2 +- sbt/src/sbt-test/actions/run-task/A.scala | 9 +- .../project/Common.scala | 4 +- 39 files changed, 502 insertions(+), 446 deletions(-) diff --git a/build.sbt b/build.sbt index 6ef429611..58d677122 100644 --- a/build.sbt +++ b/build.sbt @@ -421,17 +421,17 @@ lazy val mainProj = (project in file("main")) sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", mimaSettings, mimaBinaryIssueFilters ++= Vector( - // Changed the signature of NetworkChannel ctor. internal. - exclude[DirectMissingMethodProblem]("sbt.internal.server.NetworkChannel.*"), - // ctor for ConfigIndex. internal. - exclude[DirectMissingMethodProblem]("sbt.internal.ConfigIndex.*"), + // Changed signature or removed something in the internal pacakge + exclude[DirectMissingMethodProblem]("sbt.internal.*"), + // New and changed methods on KeyIndex. internal. exclude[ReversedMissingMethodProblem]("sbt.internal.KeyIndex.*"), - exclude[DirectMissingMethodProblem]("sbt.internal.KeyIndex.*"), - // Removed unused val. internal. - exclude[DirectMissingMethodProblem]("sbt.internal.RelayAppender.jsonFormat"), - // Removed unused def. internal. - exclude[DirectMissingMethodProblem]("sbt.internal.Load.isProjectThis"), + + // Changed signature or removed private[sbt] methods + exclude[DirectMissingMethodProblem]("sbt.Classpaths.unmanagedLibs0"), + exclude[DirectMissingMethodProblem]("sbt.Defaults.allTestGroupsTask"), + exclude[DirectMissingMethodProblem]("sbt.Plugins.topologicalSort"), + exclude[IncompatibleMethTypeProblem]("sbt.Defaults.allTestGroupsTask"), ) ) .configure( diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index 3c1d705b4..abe27d015 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -324,6 +324,8 @@ object Scoped { "0.13.2") def task: SettingKey[Task[S]] = scopedSetting(scope, key) + def toSettingKey: SettingKey[Task[S]] = scopedSetting(scope, key) + def get(settings: Settings[Scope]): Option[Task[S]] = settings.get(scope, key) def ? : Initialize[Task[Option[S]]] = Def.optional(scopedKey) { diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 745206a27..5d3de17c0 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -72,8 +72,7 @@ object Cross { } & spacedFirst(CrossCommand) } - private def crossRestoreSessionParser(state: State): Parser[String] = - token(CrossRestoreSessionCommand) + private def crossRestoreSessionParser: Parser[String] = token(CrossRestoreSessionCommand) private[sbt] def requireSession[T](p: State => Parser[T]): State => Parser[T] = s => if (s get sessionSettings isEmpty) failure("No project loaded") else p(s) @@ -189,9 +188,10 @@ object Cross { } def crossRestoreSession: Command = - Command.arb(crossRestoreSessionParser, crossRestoreSessionHelp)(crossRestoreSessionImpl) + Command.arb(_ => crossRestoreSessionParser, crossRestoreSessionHelp)((s, _) => + crossRestoreSessionImpl(s)) - private def crossRestoreSessionImpl(state: State, arg: String): State = { + private def crossRestoreSessionImpl(state: State): State = { restoreCapturedSession(state, Project.extract(state)) } diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index d141175ce..cafe05227 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -670,7 +670,6 @@ object Defaults extends BuildCommon { (testGrouping in test).value, (testExecution in test).value, (fullClasspath in test).value, - (javaHome in test).value, testForkedParallel.value, (javaOptions in test).value ) @@ -828,7 +827,6 @@ object Defaults extends BuildCommon { testGrouping.value, newConfig, fullClasspath.value, - javaHome.value, testForkedParallel.value, javaOptions.value ) @@ -855,20 +853,20 @@ object Defaults extends BuildCommon { } } - private[sbt] def allTestGroupsTask(s: TaskStreams, - frameworks: Map[TestFramework, Framework], - loader: ClassLoader, - groups: Seq[Tests.Group], - config: Tests.Execution, - cp: Classpath, - javaHome: Option[File]): Initialize[Task[Tests.Output]] = { + private[sbt] def allTestGroupsTask( + s: TaskStreams, + frameworks: Map[TestFramework, Framework], + loader: ClassLoader, + groups: Seq[Tests.Group], + config: Tests.Execution, + cp: Classpath, + ): Initialize[Task[Tests.Output]] = { allTestGroupsTask(s, frameworks, loader, groups, config, cp, - javaHome, forkedParallelExecution = false, javaOptions = Nil) } @@ -880,7 +878,6 @@ object Defaults extends BuildCommon { groups: Seq[Tests.Group], config: Tests.Execution, cp: Classpath, - javaHome: Option[File], forkedParallelExecution: Boolean): Initialize[Task[Tests.Output]] = { allTestGroupsTask(s, frameworks, @@ -888,7 +885,6 @@ object Defaults extends BuildCommon { groups, config, cp, - javaHome, forkedParallelExecution, javaOptions = Nil) } @@ -899,12 +895,11 @@ object Defaults extends BuildCommon { groups: Seq[Tests.Group], config: Tests.Execution, cp: Classpath, - javaHome: Option[File], forkedParallelExecution: Boolean, javaOptions: Seq[String]): Initialize[Task[Tests.Output]] = { val runners = createTestRunners(frameworks, loader, config) val groupTasks = groups map { - case Tests.Group(name, tests, runPolicy) => + case Tests.Group(_, tests, runPolicy) => runPolicy match { case Tests.SubProcess(opts) => s.log.debug(s"javaOptions: ${opts.runJVMOptions}") @@ -1606,7 +1601,11 @@ object Defaults extends BuildCommon { val sv = (sbtVersion in pluginCrossBuild).value val scalaV = (scalaVersion in pluginCrossBuild).value val binVersion = (scalaBinaryVersion in pluginCrossBuild).value - val cross = if (id.crossVersioned) CrossVersion.binary else Disabled() + val cross = id.crossVersionedValue match { + case CrossValue.Disabled => Disabled() + case CrossValue.Full => CrossVersion.full + case CrossValue.Binary => CrossVersion.binary + } val base = ModuleID(id.groupID, id.name, sv).withCrossVersion(cross) CrossVersion(scalaV, binVersion)(base).withCrossVersion(Disabled()) } @@ -1699,7 +1698,7 @@ object Classpaths { } def packaged(pkgTasks: Seq[TaskKey[File]]): Initialize[Task[Map[Artifact, File]]] = - enabledOnly(packagedArtifact.task, pkgTasks) apply (_.join.map(_.toMap)) + enabledOnly(packagedArtifact.toSettingKey, pkgTasks) apply (_.join.map(_.toMap)) def artifactDefs(pkgTasks: Seq[TaskKey[File]]): Initialize[Seq[Artifact]] = enabledOnly(artifact, pkgTasks) @@ -1709,8 +1708,10 @@ object Classpaths { case (a, true) => a }) - def forallIn[T](key: Scoped.ScopingSetting[SettingKey[T]], - pkgTasks: Seq[TaskKey[_]]): Initialize[Seq[T]] = + def forallIn[T]( + key: Scoped.ScopingSetting[SettingKey[T]], // should be just SettingKey[T] (mea culpa) + pkgTasks: Seq[TaskKey[_]], + ): Initialize[Seq[T]] = pkgTasks.map(pkg => key in pkg.scope in pkg).join private[this] def publishGlobalDefaults = @@ -1740,9 +1741,9 @@ object Classpaths { deliver := deliverTask(makeIvyXmlConfiguration).value, deliverLocal := deliverTask(makeIvyXmlLocalConfiguration).value, makeIvyXml := deliverTask(makeIvyXmlConfiguration).value, - publish := publishTask(publishConfiguration, deliver).value, - publishLocal := publishTask(publishLocalConfiguration, deliverLocal).value, - publishM2 := publishTask(publishM2Configuration, deliverLocal).value + publish := publishTask(publishConfiguration).value, + publishLocal := publishTask(publishLocalConfiguration).value, + publishM2 := publishTask(publishM2Configuration).value ) private[this] def baseGlobalDefaults = @@ -1816,7 +1817,7 @@ object Classpaths { appResolvers.value, useJCenter.value) match { case (Some(delegated), Seq(), _, _) => delegated - case (_, rs, Some(ars), uj) => ars ++ rs + case (_, rs, Some(ars), _) => ars ++ rs case (_, rs, _, uj) => Resolver.combineDefaultResolvers(rs.toVector, uj, mavenCentral = true) }), appResolvers := { @@ -2027,7 +2028,6 @@ object Classpaths { val docTypes = docArtifactTypes.value val out = is.withIvy(s.log)(_.getSettings.getDefaultIvyUserDir) val uwConfig = (unresolvedWarningConfiguration in update).value - val scalaModule = scalaModuleInfo.value withExcludes(out, mod.classifiers, lock(app)) { excludes => lm.updateClassifiers( GetClassifiersConfiguration( @@ -2058,7 +2058,6 @@ object Classpaths { // Override the default to handle mixing in the sbtPlugin + scala dependencies. allDependencies := { val base = projectDependencies.value ++ libraryDependencies.value - val dependency = sbtDependency.value val isPlugin = sbtPlugin.value val sbtdeps = (sbtDependency in pluginCrossBuild).value.withConfigurations(Some(Provided.name)) @@ -2177,9 +2176,6 @@ object Classpaths { val log = s.log val out = is.withIvy(log)(_.getSettings.getDefaultIvyUserDir) val uwConfig = (unresolvedWarningConfiguration in update).value - val depDir = dependencyCacheDirectory.value - val ivy = scalaModuleInfo.value - val st = state.value withExcludes(out, mod.classifiers, lock(app)) { excludes => // val noExplicitCheck = ivy.map(_.withCheckExplicit(false)) @@ -2196,7 +2192,7 @@ object Classpaths { uwConfig, log ) match { - case Left(uw) => ??? + case Left(_) => ??? case Right(ur) => ur } } @@ -2227,16 +2223,20 @@ object Classpaths { IvyActions.deliver(ivyModule.value, config.value, streams.value.log) } - def publishTask(config: TaskKey[PublishConfiguration], - deliverKey: TaskKey[_]): Initialize[Task[Unit]] = + @deprecated("Use variant without delivery key", "1.1.1") + def publishTask( + config: TaskKey[PublishConfiguration], + deliverKey: TaskKey[_], + ): Initialize[Task[Unit]] = + publishTask(config) + + def publishTask(config: TaskKey[PublishConfiguration]): Initialize[Task[Unit]] = Def.taskDyn { val s = streams.value val skp = (skip in publish).value val ref = thisProjectRef.value if (skp) Def.task { s.log.debug(s"Skipping publish* for ${ref.project}") } else - Def.task { - IvyActions.publish(ivyModule.value, config.value, s.log) - } + Def.task { IvyActions.publish(ivyModule.value, config.value, s.log) } } tag (Tags.Publish, Tags.Network) val moduleIdJsonKeyFormat: sjsonnew.JsonKeyFormat[ModuleID] = @@ -2403,7 +2403,7 @@ object Classpaths { s.init.evaluate(empty) map { _ -> s.pos } }: _*) } catch { - case NonFatal(e) => Map() + case NonFatal(_) => Map() } val outCacheStore = cacheStoreFactory make "output_dsp" @@ -2708,14 +2708,16 @@ object Classpaths { data: Settings[Scope], deps: BuildDependencies): Initialize[Task[Classpath]] = Def.value { - interDependencies(projectRef, - deps, - conf, - conf, - data, - TrackLevel.TrackAlways, - true, - unmanagedLibs0) + interDependencies( + projectRef, + deps, + conf, + conf, + data, + TrackLevel.TrackAlways, + true, + (dep, conf, data, _) => unmanagedLibs(dep, conf, data), + ) } private[sbt] def internalDependenciesImplTask(projectRef: ProjectRef, conf: Configuration, @@ -2820,20 +2822,19 @@ object Classpaths { case TrackLevel.TrackIfMissing => getClasspath(exportedProductJarsIfMissing, dep, conf, data) case TrackLevel.TrackAlways => getClasspath(exportedProductJars, dep, conf, data) } - private[sbt] def unmanagedLibs0(dep: ResolvedReference, - conf: String, - data: Settings[Scope], - track: TrackLevel): Task[Classpath] = - unmanagedLibs(dep, conf, data) + def unmanagedLibs(dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] = getClasspath(unmanagedJars, dep, conf, data) + def getClasspath(key: TaskKey[Classpath], dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] = (key in (dep, ConfigKey(conf))) get data getOrElse constant(Nil) + def defaultConfigurationTask(p: ResolvedReference, data: Settings[Scope]): Configuration = flatten(defaultConfiguration in p get data) getOrElse Configurations.Default + def flatten[T](o: Option[Option[T]]): Option[T] = o flatMap idFun val sbtIvySnapshots: URLRepository = Resolver.sbtIvyRepo("snapshots") @@ -2866,7 +2867,7 @@ object Classpaths { up.filter(configurationFilter(config.name) && artifactFilter(`type` = jarTypes)) .toSeq .map { - case (conf, module, art, file) => + case (_, module, art, file) => Attributed(file)( AttributeMap.empty .put(artifact.key, art) @@ -3126,13 +3127,16 @@ trait BuildExtra extends BuildCommon with DefExtra { file.value, managedScalaInstance.value) - def externalPom(file: Initialize[File] = inBase("pom.xml"), - iScala: Initialize[Option[ScalaModuleInfo]] = scalaModuleInfo) - : Setting[Task[ModuleSettings]] = - moduleSettings := PomConfiguration(ivyValidate.value, - scalaModuleInfo.value, - file.value, - managedScalaInstance.value) + def externalPom( + file: Initialize[File] = inBase("pom.xml"), + iScala: Initialize[Option[ScalaModuleInfo]] = scalaModuleInfo, + ): Setting[Task[ModuleSettings]] = + moduleSettings := PomConfiguration( + ivyValidate.value, + iScala.value, + file.value, + managedScalaInstance.value, + ) def runInputTask(config: Configuration, mainClass: String, @@ -3161,7 +3165,10 @@ trait BuildExtra extends BuildCommon with DefExtra { config: Configuration, mainClass: String, baseArguments: String*): Vector[Setting[_]] = { - // Use Def.inputTask with the `Def.spaceDelimited()` parser + // TODO: Re-write to avoid InputTask.apply which is deprecated + // I tried "Def.spaceDelimited().parsed" (after importing Def.parserToInput) + // but it broke actions/run-task + // Maybe it needs to be defined inside a Def.inputTask? def inputTask[T](f: TaskKey[Seq[String]] => Initialize[Task[T]]): Initialize[InputTask[T]] = InputTask.apply(Def.value((s: State) => Def.spaceDelimited()))(f) @@ -3216,7 +3223,7 @@ trait BuildExtra extends BuildCommon with DefExtra { trait DefExtra { private[this] val ts: TaskSequential = new TaskSequential {} - implicit def toTaskSequential(d: Def.type): TaskSequential = ts + implicit def toTaskSequential(@deprecated("unused", "") d: Def.type): TaskSequential = ts } trait BuildCommon { @@ -3224,7 +3231,7 @@ trait BuildCommon { /** * Allows a String to be used where a `NameFilter` is expected. * Asterisks (`*`) in the string are interpreted as wildcards. - * All other characters must match exactly. See [[sbt.GlobFilter]]. + * All other characters must match exactly. See [[sbt.io.GlobFilter]]. */ implicit def globFilter(expression: String): NameFilter = GlobFilter(expression) diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index 1d17f5cc3..fed8b4acb 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -8,7 +8,7 @@ package sbt import sbt.internal.{ Load, BuildStructure, TaskTimings, TaskName, GCUtil } -import sbt.internal.util.{ Attributed, ErrorHandling, HList, RMap, Signals, Types } +import sbt.internal.util.{ Attributed, ConsoleAppender, ErrorHandling, HList, RMap, Signals, Types } import sbt.util.{ Logger, Show } import sbt.librarymanagement.{ Resolver, UpdateReport } @@ -247,7 +247,11 @@ object EvaluateTask { (executionRoots in Global) ::= dummyRoots ) - def evalPluginDef(log: Logger)(pluginDef: BuildStructure, state: State): PluginData = { + @deprecated("Use variant which doesn't take a logger", "1.1.1") + def evalPluginDef(log: Logger)(pluginDef: BuildStructure, state: State): PluginData = + evalPluginDef(pluginDef, state) + + def evalPluginDef(pluginDef: BuildStructure, state: State): PluginData = { val root = ProjectRef(pluginDef.root, Load.getRootProject(pluginDef.units)(pluginDef.root)) val pluginKey = pluginData val config = extractedTaskConfig(Project.extract(state), pluginDef, state) @@ -256,7 +260,7 @@ object EvaluateTask { val (newS, result) = evaluated getOrElse sys.error( "Plugin data does not exist for plugin definition at " + pluginDef.root) Project.runUnloadHooks(newS) // discard states - processResult(result, log) + processResult2(result) } /** @@ -296,8 +300,8 @@ object EvaluateTask { def logIncomplete(result: Incomplete, state: State, streams: Streams): Unit = { val all = Incomplete linearize result - val keyed = for (Incomplete(Some(key: ScopedKey[_]), _, msg, _, ex) <- all) - yield (key, msg, ex) + val keyed = + all collect { case Incomplete(Some(key: ScopedKey[_]), _, msg, _, ex) => (key, msg, ex) } import ExceptionCategory._ for ((key, msg, Some(ex)) <- keyed) { @@ -312,7 +316,7 @@ object EvaluateTask { for ((key, msg, ex) <- keyed if (msg.isDefined || ex.isDefined)) { val msgString = (msg.toList ++ ex.toList.map(ErrorHandling.reducedToString)).mkString("\n\t") val log = getStreams(key, streams).log - val display = contextDisplay(state, log.ansiCodesSupported) + val display = contextDisplay(state, ConsoleAppender.formatEnabledInEnv) log.error("(" + display.show(key) + ") " + msgString) } } @@ -433,12 +437,21 @@ object EvaluateTask { case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node)) case i => i } + type AnyCyclic = Execute[({ type A[_] <: AnyRef })#A]#CyclicException[_] + def convertCyclicInc: Incomplete => Incomplete = { - case in @ Incomplete(_, _, _, _, Some(c: AnyCyclic)) => + case in @ Incomplete( + _, + _, + _, + _, + Some(c: Execute[({ type A[_] <: AnyRef })#A @unchecked]#CyclicException[_]) + ) => in.copy(directCause = Some(new RuntimeException(convertCyclic(c)))) case i => i } + def convertCyclic(c: AnyCyclic): String = (c.caller, c.target) match { case (caller: Task[_], target: Task[_]) => @@ -448,7 +461,7 @@ object EvaluateTask { } def liftAnonymous: Incomplete => Incomplete = { - case i @ Incomplete(node, tpe, None, causes, None) => + case i @ Incomplete(_, _, None, causes, None) => causes.find(inc => inc.node.isEmpty && (inc.message.isDefined || inc.directCause.isDefined)) match { case Some(lift) => i.copy(directCause = lift.directCause, message = lift.message) case None => i @@ -456,12 +469,19 @@ object EvaluateTask { case i => i } + @deprecated("Use processResult2 which doesn't take the unused log param", "1.1.1") def processResult[T](result: Result[T], log: Logger, show: Boolean = false): T = - onResult(result, log) { v => + processResult2(result, show) + + def processResult2[T](result: Result[T], show: Boolean = false): T = + onResult(result) { v => if (show) println("Result: " + v); v } - def onResult[T, S](result: Result[T], log: Logger)(f: T => S): S = + @deprecated("Use variant that doesn't take log", "1.1.1") + def onResult[T, S](result: Result[T], log: Logger)(f: T => S): S = onResult(result)(f) + + def onResult[T, S](result: Result[T])(f: T => S): S = result match { case Value(v) => f(v) case Inc(inc) => throw inc diff --git a/main/src/main/scala/sbt/Extracted.scala b/main/src/main/scala/sbt/Extracted.scala index 80243ada1..01139d0dd 100644 --- a/main/src/main/scala/sbt/Extracted.scala +++ b/main/src/main/scala/sbt/Extracted.scala @@ -54,12 +54,12 @@ final case class Extracted(structure: BuildStructure, * See `runAggregated` for that. */ def runTask[T](key: TaskKey[T], state: State): (State, T) = { - val rkey = resolve(key.scopedKey) + val rkey = resolve(key) val config = extractedTaskConfig(this, structure, state) val value: Option[(State, Result[T])] = EvaluateTask(structure, key.scopedKey, state, currentRef, config) val (newS, result) = getOrError(rkey.scope, rkey.key, value) - (newS, EvaluateTask.processResult(result, newS.log)) + (newS, EvaluateTask.processResult2(result)) } /** @@ -72,22 +72,22 @@ final case class Extracted(structure: BuildStructure, * This method requests execution of only the given task and does not aggregate execution. */ def runInputTask[T](key: InputKey[T], input: String, state: State): (State, T) = { - val scopedKey = ScopedKey( + val key2 = Scoped.scopedSetting( Scope.resolveScope(Load.projectScope(currentRef), currentRef.build, rootProject)(key.scope), key.key ) - val rkey = resolve(scopedKey) - val inputTask = get(Scoped.scopedSetting(rkey.scope, rkey.key)) + val rkey = resolve(key2) + val inputTask = get(rkey) val task = Parser.parse(input, inputTask.parser(state)) match { case Right(t) => t case Left(msg) => sys.error(s"Invalid programmatic input:\n$msg") } val config = extractedTaskConfig(this, structure, state) EvaluateTask.withStreams(structure, state) { str => - val nv = EvaluateTask.nodeView(state, str, rkey :: Nil) + val nv = EvaluateTask.nodeView(state, str, rkey.scopedKey :: Nil) val (newS, result) = EvaluateTask.runTask(task, state, str, structure.index.triggers, config)(nv) - (newS, EvaluateTask.processResult(result, newS.log)) + (newS, EvaluateTask.processResult2(result)) } } @@ -98,27 +98,29 @@ final case class Extracted(structure: BuildStructure, * Other axes are resolved to `Zero` if unspecified. */ def runAggregated[T](key: TaskKey[T], state: State): State = { - val rkey = resolve(key.scopedKey) + val rkey = resolve(key) val keys = Aggregation.aggregate(rkey, ScopeMask(), structure.extra) val tasks = Act.keyValues(structure)(keys) - Aggregation.runTasks(state, - structure, - tasks, - DummyTaskMap(Nil), - show = Aggregation.defaultShow(state, false))(showKey) + Aggregation.runTasks( + state, + tasks, + DummyTaskMap(Nil), + show = Aggregation.defaultShow(state, false), + )(showKey) } - private[this] def resolve[T](key: ScopedKey[T]): ScopedKey[T] = - Project.mapScope(Scope.resolveScope(GlobalScope, currentRef.build, rootProject))(key.scopedKey) + private[this] def resolve[K <: Scoped.ScopingSetting[K] with Scoped](key: K): K = + key in Scope.resolveScope(GlobalScope, currentRef.build, rootProject)(key.scope) private def getOrError[T](scope: Scope, key: AttributeKey[_], value: Option[T])( - implicit display: Show[ScopedKey[_]]): T = + implicit display: Show[ScopedKey[_]] + ): T = value getOrElse sys.error(display.show(ScopedKey(scope, key)) + " is undefined.") private def getOrError[T](scope: Scope, key: AttributeKey[T])( - implicit display: Show[ScopedKey[_]]): T = - structure.data.get(scope, key) getOrElse sys.error( - display.show(ScopedKey(scope, key)) + " is undefined.") + implicit display: Show[ScopedKey[_]] + ): T = + getOrError(scope, key, structure.data.get(scope, key))(display) def append(settings: Seq[Setting[_]], state: State): State = { val appendSettings = diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 09d668c4b..869c528ea 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -273,9 +273,9 @@ object BuiltinCommands { case _ => si.actualVersion } - private[this] def quiet[T](t: => T): Option[T] = try { Some(t) } catch { - case e: Exception => None - } + private[this] def quiet[T](t: => T): Option[T] = + try Some(t) + catch { case _: Exception => None } def settingsCommand: Command = showSettingLike(SettingsCommand, @@ -400,7 +400,7 @@ object BuiltinCommands { // For correct behavior, we also need to re-inject a settings logger, as we'll be re-evaluating settings val loggerInject = LogManager.settingsLogger(s) val withLogger = newSession.appendRaw(loggerInject :: Nil) - val show = Project.showContextKey(newSession, structure) + val show = Project.showContextKey2(newSession) val newStructure = Load.reapply(withLogger.mergeSettings, structure)(show) Project.setProject(newSession, newStructure, s) } @@ -424,19 +424,27 @@ object BuiltinCommands { )(cl) val setResult = if (all) SettingCompletions.setAll(extracted, settings) - else SettingCompletions.setThis(s, extracted, settings, arg) + else SettingCompletions.setThis(extracted, settings, arg) s.log.info(setResult.quietSummary) s.log.debug(setResult.verboseSummary) reapply(setResult.session, structure, s) } + @deprecated("Use variant that doesn't take a State", "1.1.1") def setThis( s: State, extracted: Extracted, settings: Seq[Def.Setting[_]], arg: String ): SetResult = - SettingCompletions.setThis(s, extracted, settings, arg) + setThis(extracted, settings, arg) + + def setThis( + extracted: Extracted, + settings: Seq[Def.Setting[_]], + arg: String + ): SetResult = + SettingCompletions.setThis(extracted, settings, arg) def inspect: Command = Command(InspectCommand, inspectBrief, inspectDetailed)(Inspect.parser) { case (s, (option, sk)) => @@ -448,10 +456,10 @@ object BuiltinCommands { Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) { case (s, (pattern, Some(sks))) => val (str, _, display) = extractLast(s) - Output.lastGrep(sks, str.streams(s), pattern, printLast(s))(display) + Output.lastGrep(sks, str.streams(s), pattern, printLast)(display) keepLastLog(s) case (s, (pattern, None)) => - for (logFile <- lastLogFile(s)) yield Output.lastGrep(logFile, pattern, printLast(s)) + for (logFile <- lastLogFile(s)) yield Output.lastGrep(logFile, pattern, printLast) keepLastLog(s) } @@ -493,7 +501,7 @@ object BuiltinCommands { lastOnly_keys <- keysParser kvs = Act.keyValues(structure)(lastOnly_keys._2) f <- if (lastOnly_keys._1) success(() => s) - else Aggregation.evaluatingParser(s, structure, show)(kvs) + else Aggregation.evaluatingParser(s, show)(kvs) } yield () => { def export0(s: State): State = lastImpl(s, kvs, Some(ExportStream)) @@ -516,7 +524,7 @@ object BuiltinCommands { def last: Command = Command(LastCommand, lastBrief, lastDetailed)(aggregatedKeyValueParser) { case (s, Some(sks)) => lastImpl(s, sks, None) case (s, None) => - for (logFile <- lastLogFile(s)) yield Output.last(logFile, printLast(s)) + for (logFile <- lastLogFile(s)) yield Output.last(logFile, printLast) keepLastLog(s) } @@ -525,7 +533,7 @@ object BuiltinCommands { private[this] def lastImpl(s: State, sks: AnyKeys, sid: Option[String]): State = { val (str, _, display) = extractLast(s) - Output.last(sks, str.streams(s), printLast(s), sid)(display) + Output.last(sks, str.streams(s), printLast, sid)(display) keepLastLog(s) } @@ -550,7 +558,10 @@ object BuiltinCommands { */ def isLastOnly(s: State): Boolean = s.history.previous.forall(_.commandLine == Shell) - def printLast(s: State): Seq[String] => Unit = _ foreach println + @deprecated("Use variant that doesn't take the state", "1.1.1") + def printLast(s: State): Seq[String] => Unit = printLast + + def printLast: Seq[String] => Unit = _ foreach println def autoImports(extracted: Extracted): EvalImports = new EvalImports(imports(extracted), "") @@ -620,7 +631,7 @@ object BuiltinCommands { val extraUpdated = Project.updateExtraBuilds(s, f) try doLoadProject(extraUpdated, LoadAction.Current) catch { - case e: Exception => + case _: Exception => s.log.error("Project loading failed: reverting to previous state.") Project.setExtraBuilds(s, original) } diff --git a/main/src/main/scala/sbt/PluginCross.scala b/main/src/main/scala/sbt/PluginCross.scala index 0e9e1462d..bf6039c74 100644 --- a/main/src/main/scala/sbt/PluginCross.scala +++ b/main/src/main/scala/sbt/PluginCross.scala @@ -16,7 +16,7 @@ import sbt.internal.Load import sbt.internal.CommandStrings._ import Cross.{ spacedFirst, requireSession } import sbt.librarymanagement.VersionNumber -import Project.{ inScope } +import Project.inScope /** * Module responsible for plugin cross building. @@ -24,8 +24,7 @@ import Project.{ inScope } private[sbt] object PluginCross { lazy val pluginSwitch: Command = { def switchParser(state: State): Parser[(String, String)] = { - val knownVersions = Nil - lazy val switchArgs = token(NotSpace.examples(knownVersions: _*)) ~ (token( + lazy val switchArgs = token(NotSpace.examples()) ~ (token( Space ~> matched(state.combinedParser)) ?? "") lazy val nextSpaced = spacedFirst(PluginSwitchCommand) token(PluginSwitchCommand ~ OptSpace) flatMap { _ => diff --git a/main/src/main/scala/sbt/Plugins.scala b/main/src/main/scala/sbt/Plugins.scala index 0b65c6aea..a0aa1ee2a 100644 --- a/main/src/main/scala/sbt/Plugins.scala +++ b/main/src/main/scala/sbt/Plugins.scala @@ -111,7 +111,7 @@ abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions { def extraProjects: Seq[Project] = Nil /** The [[Project]]s to add to the current build based on an existing project. */ - def derivedProjects(proj: ProjectDefinition[_]): Seq[Project] = Nil + def derivedProjects(@deprecated("unused", "") proj: ProjectDefinition[_]): Seq[Project] = Nil private[sbt] def unary_! : Exclude = Exclude(this) @@ -224,20 +224,19 @@ object Plugins extends PluginsFunctions { _.label }) } - val retval = topologicalSort(selectedPlugins, log) + val retval = topologicalSort(selectedPlugins) // log.debug(s" :: sorted deduced result: ${retval.toString}") retval } } } } - private[sbt] def topologicalSort(ns: List[AutoPlugin], log: Logger): List[AutoPlugin] = { - // log.debug(s"sorting: ns: ${ns.toString}") + + private[sbt] def topologicalSort(ns: List[AutoPlugin]): List[AutoPlugin] = { @tailrec def doSort(found0: List[AutoPlugin], notFound0: List[AutoPlugin], limit0: Int): List[AutoPlugin] = { - // log.debug(s" :: sorting:: found: ${found0.toString} not found ${notFound0.toString}") if (limit0 < 0) throw AutoPluginException(s"Failed to sort ${ns} topologically") else if (notFound0.isEmpty) found0 else { @@ -250,6 +249,7 @@ object Plugins extends PluginsFunctions { val (roots, nonRoots) = ns partition (_.isRoot) doSort(roots, nonRoots, ns.size * ns.size + 1) } + private[sbt] def translateMessage(e: LogicException) = e match { case ic: InitialContradictions => s"Contradiction in selected plugins. These plugins were both included and excluded: ${literalsString( @@ -260,6 +260,7 @@ object Plugins extends PluginsFunctions { case cn: CyclicNegation => s"Cycles in plugin requirements cannot involve excludes. The problematic cycle is: ${literalsString(cn.cycle)}" } + private[this] def literalsString(lits: Seq[Literal]): String = lits map { case Atom(l) => l; case Negated(Atom(l)) => l } mkString (", ") @@ -271,6 +272,7 @@ object Plugins extends PluginsFunctions { val message = s"Plugin$ns provided by multiple AutoPlugins:$nl${dupStrings.mkString(nl)}" throw AutoPluginException(message) } + private[this] def exclusionConflictError(requested: Plugins, selected: Seq[AutoPlugin], conflicting: Seq[AutoPlugin]): Unit = { @@ -360,14 +362,14 @@ ${listConflicts(conflicting)}""") // This would handle things like !!p or !(p && z) case Exclude(n) => hasInclude(n, p) case And(ns) => ns.forall(n => hasExclude(n, p)) - case b: Basic => false + case _: Basic => false case Empty => false } private[sbt] def hasInclude(n: Plugins, p: AutoPlugin): Boolean = n match { case `p` => true case Exclude(n) => hasExclude(n, p) case And(ns) => ns.forall(n => hasInclude(n, p)) - case b: Basic => false + case _: Basic => false case Empty => false } private[this] def flattenConvert(n: Plugins): Seq[Literal] = n match { diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 0282184be..0c5b70a58 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -276,13 +276,20 @@ object Project extends ProjectExtra { showContextKey(state, None) def showContextKey(state: State, keyNameColor: Option[String]): Show[ScopedKey[_]] = - if (isProjectLoaded(state)) showContextKey(session(state), structure(state), keyNameColor) + if (isProjectLoaded(state)) showContextKey2(session(state), keyNameColor) else Def.showFullKey + @deprecated("Use showContextKey2 which doesn't take the unused structure param", "1.1.1") def showContextKey( session: SessionSettings, structure: BuildStructure, keyNameColor: Option[String] = None + ): Show[ScopedKey[_]] = + showContextKey2(session, keyNameColor) + + def showContextKey2( + session: SessionSettings, + keyNameColor: Option[String] = None ): Show[ScopedKey[_]] = Def.showRelativeKey2(session.current, keyNameColor) @@ -402,7 +409,7 @@ object Project extends ProjectExtra { def extract(state: State): Extracted = extract(session(state), structure(state)) private[sbt] def extract(se: SessionSettings, st: BuildStructure): Extracted = - Extracted(st, se, se.current)(showContextKey(se, st)) + Extracted(st, se, se.current)(showContextKey2(se)) def getProjectForReference(ref: Reference, structure: BuildStructure): Option[ResolvedProject] = ref match { case pr: ProjectRef => getProject(pr, structure); case _ => None } diff --git a/main/src/main/scala/sbt/ScopeFilter.scala b/main/src/main/scala/sbt/ScopeFilter.scala index a303ca09a..3b9347532 100644 --- a/main/src/main/scala/sbt/ScopeFilter.scala +++ b/main/src/main/scala/sbt/ScopeFilter.scala @@ -104,7 +104,7 @@ object ScopeFilter { /** Selects all scopes that apply to a single project. Zero and build-level scopes are excluded. */ def inAnyProject: ProjectFilter = - selectAxis(const { case p: ProjectRef => true; case _ => false }) + selectAxis(const { case _: ProjectRef => true; case _ => false }) /** Accepts all values for the task axis except Zero. */ def inAnyTask: TaskFilter = selectAny[AttributeKey[_]] diff --git a/main/src/main/scala/sbt/SessionVar.scala b/main/src/main/scala/sbt/SessionVar.scala index adbda41a5..58ec38b15 100644 --- a/main/src/main/scala/sbt/SessionVar.scala +++ b/main/src/main/scala/sbt/SessionVar.scala @@ -63,7 +63,7 @@ object SessionVar { def read[T](key: ScopedKey[Task[T]], state: State)(implicit f: JsonFormat[T]): Option[T] = Project.structure(state).streams(state).use(key) { s => - try { Some(s.getInput(key, DefaultDataID).read[T]) } catch { case NonFatal(e) => None } + try { Some(s.getInput(key, DefaultDataID).read[T]) } catch { case NonFatal(_) => None } } def load[T](key: ScopedKey[Task[T]], state: State)(implicit f: JsonFormat[T]): Option[T] = diff --git a/main/src/main/scala/sbt/TemplateCommand.scala b/main/src/main/scala/sbt/TemplateCommand.scala index 2da6dcb33..e02b1a6a2 100644 --- a/main/src/main/scala/sbt/TemplateCommand.scala +++ b/main/src/main/scala/sbt/TemplateCommand.scala @@ -21,9 +21,10 @@ import BasicCommandStrings._, BasicKeys._ private[sbt] object TemplateCommandUtil { def templateCommand: Command = - Command(TemplateCommand, templateBrief, templateDetailed)(templateCommandParser)(runTemplate) + Command(TemplateCommand, templateBrief, templateDetailed)(_ => templateCommandParser)( + runTemplate) - private def templateCommandParser(state: State): Parser[Seq[String]] = + private def templateCommandParser: Parser[Seq[String]] = (token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF) map (_ => Nil)) private def runTemplate(state: State, inputArg: Seq[String]): State = { diff --git a/main/src/main/scala/sbt/internal/Act.scala b/main/src/main/scala/sbt/internal/Act.scala index 2ab067db0..9349f2b64 100644 --- a/main/src/main/scala/sbt/internal/Act.scala +++ b/main/src/main/scala/sbt/internal/Act.scala @@ -100,7 +100,7 @@ object Act { conf <- configs(confAmb, defaultConfigs, proj, index) } yield for { - taskAmb <- taskAxis(conf, index.tasks(proj, conf), keyMap) + taskAmb <- taskAxis(index.tasks(proj, conf), keyMap) task = resolveTask(taskAmb) key <- key(index, proj, conf, task, keyMap) extra <- extraAxis(keyMap, IMap.empty) @@ -161,6 +161,7 @@ object Act { def examples(p: Parser[String], exs: Set[String], label: String): Parser[String] = p !!! ("Expected " + label) examples exs + def examplesStrict(p: Parser[String], exs: Set[String], label: String): Parser[String] = filterStrings(examples(p, exs, label), exs, label) @@ -168,6 +169,7 @@ object Act { p.? map { opt => toAxis(opt, ifNone) } + def toAxis[T](opt: Option[T], ifNone: ScopeAxis[T]): ScopeAxis[T] = opt match { case Some(t) => Select(t); case None => ifNone } @@ -231,8 +233,8 @@ object Act { // This queries the key index so tab completion will list the build-level keys. val buildKeys: Set[String] = proj match { - case Some(ProjectRef(uri, id)) => index.keys(Some(BuildRef(uri)), conf, task) - case _ => Set() + case Some(ProjectRef(uri, _)) => index.keys(Some(BuildRef(uri)), conf, task) + case _ => Set() } val keys: Set[String] = index.keys(proj, conf, task) ++ buildKeys keyParser(keys) @@ -255,9 +257,10 @@ object Act { optionalAxis(extras, Zero) } - def taskAxis(d: Option[String], - tasks: Set[AttributeKey[_]], - allKnown: Map[String, AttributeKey[_]]): Parser[ParsedAxis[AttributeKey[_]]] = { + def taskAxis( + tasks: Set[AttributeKey[_]], + allKnown: Map[String, AttributeKey[_]], + ): Parser[ParsedAxis[AttributeKey[_]]] = { val taskSeq = tasks.toSeq def taskKeys(f: AttributeKey[_] => String): Seq[(String, AttributeKey[_])] = taskSeq.map(key => (f(key), key)) @@ -380,7 +383,7 @@ object Act { def evaluate(kvs: Seq[ScopedKey[_]]): Parser[() => State] = { val preparedPairs = anyKeyValues(structure, kvs) val showConfig = Aggregation.defaultShow(state, showTasks = action == ShowAction) - evaluatingParser(state, structure, showConfig)(preparedPairs) map { evaluate => () => + evaluatingParser(state, showConfig)(preparedPairs) map { evaluate => () => { val keyStrings = preparedPairs.map(pp => showKey.show(pp.key)).mkString(", ") state.log.debug("Evaluating tasks: " + keyStrings) diff --git a/main/src/main/scala/sbt/internal/Aggregation.scala b/main/src/main/scala/sbt/internal/Aggregation.scala index 0d2da79d8..ea523df53 100644 --- a/main/src/main/scala/sbt/internal/Aggregation.scala +++ b/main/src/main/scala/sbt/internal/Aggregation.scala @@ -61,11 +61,10 @@ object Aggregation { def applyTasks[T]( s: State, - structure: BuildStructure, ps: Values[Parser[Task[T]]], show: ShowConfig )(implicit display: Show[ScopedKey[_]]): Parser[() => State] = - Command.applyEffect(seqParser(ps))(ts => runTasks(s, structure, ts, DummyTaskMap(Nil), show)) + Command.applyEffect(seqParser(ps))(ts => runTasks(s, ts, DummyTaskMap(Nil), show)) private def showRun[T](complete: Complete[T], show: ShowConfig)( implicit display: Show[ScopedKey[_]] @@ -104,7 +103,6 @@ object Aggregation { } def runTasks[HL <: HList, T](s: State, - structure: BuildStructure, ts: Values[Task[T]], extra: DummyTaskMap, show: ShowConfig)(implicit display: Show[ScopedKey[_]]): State = { @@ -128,33 +126,26 @@ object Aggregation { key in currentRef get structure.data getOrElse true if (get(showSuccess)) { if (get(showTiming)) { - val msg = timingString(start, stop, "", structure.data, currentRef, log) + val msg = timingString(start, stop, structure.data, currentRef) if (success) log.success(msg) else log.error(msg) } else if (success) log.success("") } } + private def timingString( startTime: Long, endTime: Long, - s: String, data: Settings[Scope], currentRef: ProjectRef, - log: Logger ): String = { val format = timingFormat in currentRef get data getOrElse defaultFormat - timing(format, startTime, endTime, "", log) + timing(format, startTime, endTime) } - def timing( - format: java.text.DateFormat, - startTime: Long, - endTime: Long, - s: String, - log: Logger - ): String = { - val ss = if (s.isEmpty) "" else s + " " + + def timing(format: java.text.DateFormat, startTime: Long, endTime: Long): String = { val nowString = format.format(new java.util.Date(endTime)) - "Total " + ss + "time: " + (endTime - startTime + 500) / 1000 + " s, completed " + nowString + "Total time: " + (endTime - startTime + 500) / 1000 + " s, completed " + nowString } def defaultFormat: DateFormat = { @@ -164,20 +155,19 @@ object Aggregation { def applyDynamicTasks[I]( s: State, - structure: BuildStructure, inputs: Values[InputTask[I]], show: ShowConfig )(implicit display: Show[ScopedKey[_]]): Parser[() => State] = { val parsers = for (KeyValue(k, it) <- inputs) yield it.parser(s).map(v => KeyValue(k, v)) Command.applyEffect(seq(parsers)) { roots => - runTasks(s, structure, roots, DummyTaskMap(Nil), show) + runTasks(s, roots, DummyTaskMap(Nil), show) } } - def evaluatingParser(s: State, structure: BuildStructure, show: ShowConfig)( - keys: Seq[KeyValue[_]] - )(implicit display: Show[ScopedKey[_]]): Parser[() => State] = { + def evaluatingParser(s: State, show: ShowConfig)(keys: Seq[KeyValue[_]])( + implicit display: Show[ScopedKey[_]] + ): Parser[() => State] = { // to make the call sites clearer def separate[L](in: Seq[KeyValue[_]])( @@ -210,12 +200,12 @@ object Aggregation { val otherStrings = other.map(_.key).mkString("Task(s)/setting(s):\n\t", "\n\t", "\n") failure(s"Cannot mix input tasks with plain tasks/settings. $inputStrings $otherStrings") } else - applyDynamicTasks(s, structure, maps(inputTasks)(castToAny), show) + applyDynamicTasks(s, maps(inputTasks)(castToAny), show) } else { val base = if (tasks.isEmpty) success(() => s) else - applyTasks(s, structure, maps(tasks)(x => success(castToAny(x))), show) + applyTasks(s, maps(tasks)(x => success(castToAny(x))), show) base.map { res => () => val newState = res() if (show.settingValues && settings.nonEmpty) printSettings(settings, show.print) diff --git a/main/src/main/scala/sbt/internal/BuildDef.scala b/main/src/main/scala/sbt/internal/BuildDef.scala index 96c4641f6..dd1d11aec 100644 --- a/main/src/main/scala/sbt/internal/BuildDef.scala +++ b/main/src/main/scala/sbt/internal/BuildDef.scala @@ -16,7 +16,7 @@ import sbt.internal.util.Attributed import sbt.internal.inc.ReflectUtilities trait BuildDef { - def projectDefinitions(baseDirectory: File): Seq[Project] = projects + def projectDefinitions(@deprecated("unused", "") baseDirectory: File): Seq[Project] = projects def projects: Seq[Project] = ReflectUtilities.allVals[Project](this).values.toSeq // TODO: Should we grab the build core settings here or in a plugin? def settings: Seq[Setting[_]] = Defaults.buildCore diff --git a/main/src/main/scala/sbt/internal/BuildStructure.scala b/main/src/main/scala/sbt/internal/BuildStructure.scala index 9b02e9dfd..626123638 100644 --- a/main/src/main/scala/sbt/internal/BuildStructure.scala +++ b/main/src/main/scala/sbt/internal/BuildStructure.scala @@ -172,9 +172,7 @@ final class DetectedPlugins(val autoPlugins: Seq[DetectedAutoPlugin], private[this] lazy val (autoPluginAutoImports, topLevelAutoPluginAutoImports) = autoPlugins .flatMap { - case DetectedAutoPlugin(name, ap, hasAutoImport) => - if (hasAutoImport) Some(name) - else None + case DetectedAutoPlugin(name, _, hasAutoImport) => if (hasAutoImport) Some(name) else None } .partition(nonTopLevelPlugin) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 7bbb2c38b..3821fa807 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -36,9 +36,9 @@ import sbt.util.{ Level, Logger, LogExchange } * this exchange, which could serve command request from either of the channel. */ private[sbt] final class CommandExchange { - private val autoStartServer = sys.props.get("sbt.server.autostart") map { - _.toLowerCase == "true" - } getOrElse true + private val autoStartServer = + sys.props get "sbt.server.autostart" forall (_.toLowerCase == "true") + private val lock = new AnyRef {} private var server: Option[ServerInstance] = None private var consoleChannel: Option[ConsoleChannel] = None @@ -83,7 +83,6 @@ private[sbt] final class CommandExchange { else s } - private def newChannelName: String = s"channel-${nextChannelId.incrementAndGet()}" private def newNetworkName: String = s"network-${nextChannelId.incrementAndGet()}" /** @@ -192,42 +191,24 @@ private[sbt] final class CommandExchange { val params = toLogMessageParams(entry) channels collect { case c: ConsoleChannel => - if (broadcastStringMessage) { + if (broadcastStringMessage || (entry.channelName forall (_ == c.name))) c.publishEvent(event) - } else { - if (entry.channelName.isEmpty || entry.channelName == Some(c.name)) { - c.publishEvent(event) - } - } case c: NetworkChannel => try { // Note that language server's LogMessageParams does not hold the execid, // so this is weaker than the StringMessage. We might want to double-send // in case we have a better client that can utilize the knowledge. import sbt.internal.langserver.codec.JsonProtocol._ - if (broadcastStringMessage) { + if (broadcastStringMessage || (entry.channelName contains c.name)) c.langNotify("window/logMessage", params) - } else { - if (entry.channelName == Some(c.name)) { - c.langNotify("window/logMessage", params) - } - } - } catch { - case _: IOException => - toDel += c - } + } catch { case _: IOException => toDel += c } } case _ => - channels collect { - case c: ConsoleChannel => - c.publishEvent(event) + channels foreach { + case c: ConsoleChannel => c.publishEvent(event) case c: NetworkChannel => - try { - c.publishEvent(event) - } catch { - case _: IOException => - toDel += c - } + try c.publishEvent(event) + catch { case _: IOException => toDel += c } } } toDel.toList match { @@ -283,6 +264,11 @@ private[sbt] final class CommandExchange { // fanout publishEvent def publishEventMessage(event: EventMessage): Unit = { val toDel: ListBuffer[CommandChannel] = ListBuffer.empty + + def tryTo(x: => Unit, c: CommandChannel): Unit = + try x + catch { case _: IOException => toDel += c } + event match { // Special treatment for ConsolePromptEvent since it's hand coded without codec. case entry: ConsolePromptEvent => @@ -296,32 +282,17 @@ private[sbt] final class CommandExchange { case entry: ExecStatusEvent => channels collect { case c: ConsoleChannel => - if (entry.channelName.isEmpty || entry.channelName == Some(c.name)) { - c.publishEventMessage(event) - } + if (entry.channelName forall (_ == c.name)) c.publishEventMessage(event) case c: NetworkChannel => - try { - if (entry.channelName == Some(c.name)) { - c.publishEventMessage(event) - } - } catch { - case e: IOException => - toDel += c - } + if (entry.channelName contains c.name) tryTo(c.publishEventMessage(event), c) } case _ => channels collect { - case c: ConsoleChannel => - c.publishEventMessage(event) - case c: NetworkChannel => - try { - c.publishEventMessage(event) - } catch { - case _: IOException => - toDel += c - } + case c: ConsoleChannel => c.publishEventMessage(event) + case c: NetworkChannel => tryTo(c.publishEventMessage(event), c) } } + toDel.toList match { case Nil => // do nothing case xs => diff --git a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala index dc73c66fd..a4dc0641a 100644 --- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala +++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala @@ -36,12 +36,15 @@ private[sbt] abstract class BackgroundJob { } def shutdown(): Unit + // this should be true on construction and stay true until // the job is complete def isRunning(): Boolean + // called after stop or on spontaneous exit, closing the result // removes the listener def onStop(listener: () => Unit)(implicit ex: ExecutionContext): Closeable + // do we need this or is the spawning task good enough? // def tags: SomeType } @@ -57,8 +60,8 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe private val serviceTempDir = IO.createTemporaryDirectory // hooks for sending start/stop events - protected def onAddJob(job: JobHandle): Unit = {} - protected def onRemoveJob(job: JobHandle): Unit = {} + protected def onAddJob(@deprecated("unused", "") job: JobHandle): Unit = () + protected def onRemoveJob(@deprecated("unused", "") job: JobHandle): Unit = () // this mutable state could conceptually go on State except // that then every task that runs a background job would have diff --git a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala index 6b4e77f91..85b40293c 100644 --- a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala +++ b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala @@ -8,11 +8,18 @@ package sbt package internal -import sbt.internal.util.{ complete, AttributeEntry, AttributeKey, LineRange, MessageOnlyException, RangePosition, Settings } +import sbt.internal.util.{ + AttributeEntry, + AttributeKey, + LineRange, + MessageOnlyException, + RangePosition, + Settings +} import java.io.File import compiler.{ Eval, EvalImports } -import complete.DefaultParsers.validID +import sbt.internal.util.complete.DefaultParsers.validID import Def.{ ScopedKey, Setting } import Scope.GlobalScope import sbt.internal.parser.SbtParser @@ -37,7 +44,9 @@ private[sbt] object EvaluateConfigurations { /** * This represents the parsed expressions in a build sbt, as well as where they were defined. */ - private[this] final class ParsedFile(val imports: Seq[(String, Int)], val definitions: Seq[(String, LineRange)], val settings: Seq[(String, LineRange)]) + private[this] final class ParsedFile(val imports: Seq[(String, Int)], + val definitions: Seq[(String, LineRange)], + val settings: Seq[(String, LineRange)]) /** The keywords we look for when classifying a string as a definition. */ private[this] val DefinitionKeywords = Seq("lazy val ", "def ", "val ") @@ -48,18 +57,24 @@ private[sbt] object EvaluateConfigurations { * return a parsed, compiled + evaluated [[LoadedSbtFile]]. The result has * raw sbt-types that can be accessed and used. */ - def apply(eval: Eval, srcs: Seq[File], imports: Seq[String]): LazyClassLoaded[LoadedSbtFile] = - { - val loadFiles = srcs.sortBy(_.getName) map { src => evaluateSbtFile(eval, src, IO.readLines(src), imports, 0) } - loader => (LoadedSbtFile.empty /: loadFiles) { (loaded, load) => loaded merge load(loader) } + def apply(eval: Eval, srcs: Seq[File], imports: Seq[String]): LazyClassLoaded[LoadedSbtFile] = { + val loadFiles = srcs.sortBy(_.getName) map { src => + evaluateSbtFile(eval, src, IO.readLines(src), imports, 0) } + loader => + (LoadedSbtFile.empty /: loadFiles) { (loaded, load) => + loaded merge load(loader) + } + } /** * Reads a given .sbt file and evaluates it into a sequence of setting values. * * Note: This ignores any non-Setting[_] values in the file. */ - def evaluateConfiguration(eval: Eval, src: File, imports: Seq[String]): LazyClassLoaded[Seq[Setting[_]]] = + def evaluateConfiguration(eval: Eval, + src: File, + imports: Seq[String]): LazyClassLoaded[Seq[Setting[_]]] = evaluateConfiguration(eval, src, IO.readLines(src), imports, 0) /** @@ -68,13 +83,16 @@ private[sbt] object EvaluateConfigurations { * * @param builtinImports The set of import statements to add to those parsed in the .sbt file. */ - private[this] def parseConfiguration(file: File, lines: Seq[String], builtinImports: Seq[String], offset: Int): ParsedFile = - { - val (importStatements, settingsAndDefinitions) = splitExpressions(file, lines) - val allImports = builtinImports.map(s => (s, -1)) ++ addOffset(offset, importStatements) - val (definitions, settings) = splitSettingsDefinitions(addOffsetToRange(offset, settingsAndDefinitions)) - new ParsedFile(allImports, definitions, settings) - } + private[this] def parseConfiguration(file: File, + lines: Seq[String], + builtinImports: Seq[String], + offset: Int): ParsedFile = { + val (importStatements, settingsAndDefinitions) = splitExpressions(file, lines) + val allImports = builtinImports.map(s => (s, -1)) ++ addOffset(offset, importStatements) + val (definitions, settings) = splitSettingsDefinitions( + addOffsetToRange(offset, settingsAndDefinitions)) + new ParsedFile(allImports, definitions, settings) + } /** * Evaluates a parsed sbt configuration file. @@ -86,11 +104,15 @@ private[sbt] object EvaluateConfigurations { * * @return Just the Setting[_] instances defined in the .sbt file. */ - def evaluateConfiguration(eval: Eval, file: File, lines: Seq[String], imports: Seq[String], offset: Int): LazyClassLoaded[Seq[Setting[_]]] = - { - val l = evaluateSbtFile(eval, file, lines, imports, offset) - loader => l(loader).settings - } + def evaluateConfiguration(eval: Eval, + file: File, + lines: Seq[String], + imports: Seq[String], + offset: Int): LazyClassLoaded[Seq[Setting[_]]] = { + val l = evaluateSbtFile(eval, file, lines, imports, offset) + loader => + l(loader).settings + } /** * Evaluates a parsed sbt configuration file. @@ -102,27 +124,33 @@ private[sbt] object EvaluateConfigurations { * @return A function which can take an sbt classloader and return the raw types/configuration * which was compiled/parsed for the given file. */ - private[sbt] def evaluateSbtFile(eval: Eval, file: File, lines: Seq[String], imports: Seq[String], offset: Int): 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.getPath - val parsed = parseConfiguration(file, lines, imports, offset) - val (importDefs, definitions) = - if (parsed.definitions.isEmpty) (Nil, DefinedSbtValues.empty) else { - val definitions = evaluateDefinitions(eval, name, parsed.imports, parsed.definitions, Some(file)) - val imp = BuildUtil.importAllRoot(definitions.enclosingModule :: Nil) - (imp, DefinedSbtValues(definitions)) - } - val allImports = importDefs.map(s => (s, -1)) ++ parsed.imports - val dslEntries = parsed.settings map { - case (dslExpression, range) => - evaluateDslEntry(eval, name, allImports, dslExpression, range) + private[sbt] def evaluateSbtFile(eval: Eval, + file: File, + lines: Seq[String], + imports: Seq[String], + offset: Int): 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.getPath + val parsed = parseConfiguration(file, lines, imports, offset) + val (importDefs, definitions) = + if (parsed.definitions.isEmpty) (Nil, DefinedSbtValues.empty) + else { + val definitions = + evaluateDefinitions(eval, name, parsed.imports, parsed.definitions, Some(file)) + val imp = BuildUtil.importAllRoot(definitions.enclosingModule :: Nil) + (imp, DefinedSbtValues(definitions)) } - eval.unlinkDeferred() - // Tracks all the files we generated from evaluating the sbt file. - val allGeneratedFiles = (definitions.generated ++ dslEntries.flatMap(_.generated)) - loader => { + val allImports = importDefs.map(s => (s, -1)) ++ parsed.imports + val dslEntries = parsed.settings map { + case (dslExpression, range) => + evaluateDslEntry(eval, name, allImports, dslExpression, range) + } + eval.unlinkDeferred() + // Tracks all the files we generated from evaluating the sbt file. + val allGeneratedFiles = (definitions.generated ++ dslEntries.flatMap(_.generated)) + loader => + { val projects = definitions.values(loader).collect { case p: Project => resolveBase(file.getParentFile, p) @@ -140,9 +168,14 @@ private[sbt] object EvaluateConfigurations { case DslEntry.ProjectManipulation(f) => f } // TODO -get project manipulations. - new LoadedSbtFile(settings, projects, importDefs, manipulations, definitions, allGeneratedFiles) + new LoadedSbtFile(settings, + projects, + importDefs, + manipulations, + definitions, + allGeneratedFiles) } - } + } /** move a project to be relative to this file after we've evaluated it. */ private[this] def resolveBase(f: File, p: Project) = p.copy(base = IO.resolve(f, p.base)) @@ -173,11 +206,19 @@ private[sbt] object EvaluateConfigurations { * @return A method that given an sbt classloader, can return the actual [[sbt.internal.DslEntry]] defined by * the expression, and the sequence of .class files generated. */ - private[sbt] def evaluateDslEntry(eval: Eval, name: String, imports: Seq[(String, Int)], expression: String, range: LineRange): TrackedEvalResult[DslEntry] = { + private[sbt] def evaluateDslEntry(eval: Eval, + name: String, + imports: Seq[(String, Int)], + expression: String, + range: LineRange): TrackedEvalResult[DslEntry] = { // TODO - Should we try to namespace these between.sbt files? IF they hash to the same value, they may actually be // exactly the same setting, so perhaps we don't care? val result = try { - eval.eval(expression, imports = new EvalImports(imports, name), srcName = name, tpeName = Some(SettingsDefinitionName), line = range.start) + eval.eval(expression, + imports = new EvalImports(imports, name), + srcName = name, + tpeName = Some(SettingsDefinitionName), + line = range.start) } catch { case e: sbt.compiler.EvalException => throw new MessageOnlyException(e.getMessage) } @@ -206,7 +247,11 @@ private[sbt] object EvaluateConfigurations { */ // Build DSL now includes non-Setting[_] type settings. // Note: This method is used by the SET command, so we may want to evaluate that sucker a bit. - def evaluateSetting(eval: Eval, name: String, imports: Seq[(String, Int)], expression: String, range: LineRange): LazyClassLoaded[Seq[Setting[_]]] = + def evaluateSetting(eval: Eval, + name: String, + imports: Seq[(String, Int)], + expression: String, + range: LineRange): LazyClassLoaded[Seq[Setting[_]]] = evaluateDslEntry(eval, name, imports, expression, range).result andThen { case DslEntry.ProjectSettings(values) => values case _ => Nil @@ -216,44 +261,59 @@ private[sbt] object EvaluateConfigurations { * Splits a set of lines into (imports, expressions). That is, * anything on the right of the tuple is a scala expression (definition or setting). */ - private[sbt] def splitExpressions(file: File, lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)]) = - { - val split = SbtParser(file, lines) - // TODO - Look at pulling the parsed expression trees from the SbtParser and stitch them back into a different - // scala compiler rather than re-parsing. - (split.imports, split.settings) - } + private[sbt] def splitExpressions( + file: File, + lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)]) = { + val split = SbtParser(file, lines) + // TODO - Look at pulling the parsed expression trees from the SbtParser and stitch them back into a different + // scala compiler rather than re-parsing. + (split.imports, split.settings) + } - private[this] def splitSettingsDefinitions(lines: Seq[(String, LineRange)]): (Seq[(String, LineRange)], Seq[(String, LineRange)]) = - lines partition { case (line, range) => isDefinition(line) } + private[this] def splitSettingsDefinitions( + lines: Seq[(String, LineRange)]): (Seq[(String, LineRange)], Seq[(String, LineRange)]) = + lines partition { case (line, _) => isDefinition(line) } - private[this] def isDefinition(line: String): Boolean = - { - val trimmed = line.trim - DefinitionKeywords.exists(trimmed startsWith _) - } + private[this] def isDefinition(line: String): Boolean = { + val trimmed = line.trim + DefinitionKeywords.exists(trimmed startsWith _) + } private[this] def extractedValTypes: Seq[String] = - Seq(classOf[Project], classOf[InputKey[_]], classOf[TaskKey[_]], classOf[SettingKey[_]]).map(_.getName) + Seq(classOf[Project], classOf[InputKey[_]], classOf[TaskKey[_]], classOf[SettingKey[_]]) + .map(_.getName) - private[this] def evaluateDefinitions(eval: Eval, name: String, imports: Seq[(String, Int)], definitions: Seq[(String, LineRange)], file: Option[File]): compiler.EvalDefinitions = - { - val convertedRanges = definitions.map { case (s, r) => (s, r.start to r.end) } - eval.evalDefinitions(convertedRanges, new EvalImports(imports, name), name, file, extractedValTypes) - } + private[this] def evaluateDefinitions(eval: Eval, + name: String, + imports: Seq[(String, Int)], + definitions: Seq[(String, LineRange)], + file: Option[File]): compiler.EvalDefinitions = { + val convertedRanges = definitions.map { case (s, r) => (s, r.start to r.end) } + eval.evalDefinitions(convertedRanges, + new EvalImports(imports, name), + name, + file, + extractedValTypes) + } } object Index { - def taskToKeyMap(data: Settings[Scope]): Map[Task[_], ScopedKey[Task[_]]] = - { - // AttributeEntry + the checked type test 'value: Task[_]' ensures that the cast is correct. - // (scalac couldn't determine that 'key' is of type AttributeKey[Task[_]] on its own and a type match still required the cast) - val pairs = for (scope <- data.scopes; AttributeEntry(key, value: Task[_]) <- data.data(scope).entries) yield (value, ScopedKey(scope, key.asInstanceOf[AttributeKey[Task[_]]])) // unclear why this cast is needed even with a type test in the above filter - pairs.toMap[Task[_], ScopedKey[Task[_]]] - } + def taskToKeyMap(data: Settings[Scope]): Map[Task[_], ScopedKey[Task[_]]] = { + + val pairs = data.scopes flatMap (scope => + data.data(scope).entries collect { + case AttributeEntry(key, value: Task[_]) => + (value, ScopedKey(scope, key.asInstanceOf[AttributeKey[Task[_]]])) + }) + + pairs.toMap[Task[_], ScopedKey[Task[_]]] + } def allKeys(settings: Seq[Setting[_]]): Set[ScopedKey[_]] = - settings.flatMap(s => if (s.key.key.isLocal) Nil else s.key +: s.dependencies).filter(!_.key.isLocal).toSet + settings + .flatMap(s => if (s.key.key.isLocal) Nil else s.key +: s.dependencies) + .filter(!_.key.isLocal) + .toSet def attributeKeys(settings: Settings[Scope]): Set[AttributeKey[_]] = settings.data.values.flatMap(_.keys).toSet[AttributeKey[_]] @@ -261,30 +321,36 @@ object Index { def stringToKeyMap(settings: Set[AttributeKey[_]]): Map[String, AttributeKey[_]] = stringToKeyMap0(settings)(_.label) - private[this] def stringToKeyMap0(settings: Set[AttributeKey[_]])(label: AttributeKey[_] => String): Map[String, AttributeKey[_]] = - { - val multiMap = settings.groupBy(label) - val duplicates = multiMap collect { case (k, xs) if xs.size > 1 => (k, xs.map(_.manifest)) } collect { case (k, xs) if xs.size > 1 => (k, xs) } - if (duplicates.isEmpty) - multiMap.collect { case (k, v) if validID(k) => (k, v.head) } toMap - else - sys.error(duplicates map { case (k, tps) => "'" + k + "' (" + tps.mkString(", ") + ")" } mkString ("Some keys were defined with the same name but different types: ", ", ", "")) + private[this] def stringToKeyMap0(settings: Set[AttributeKey[_]])( + label: AttributeKey[_] => String): Map[String, AttributeKey[_]] = { + val multiMap = settings.groupBy(label) + val duplicates = multiMap collect { case (k, xs) if xs.size > 1 => (k, xs.map(_.manifest)) } collect { + case (k, xs) if xs.size > 1 => (k, xs) } + if (duplicates.isEmpty) + multiMap.collect { case (k, v) if validID(k) => (k, v.head) } toMap + else + sys.error( + duplicates map { case (k, tps) => "'" + k + "' (" + tps.mkString(", ") + ")" } mkString ("Some keys were defined with the same name but different types: ", ", ", "")) + } - private[this]type TriggerMap = collection.mutable.HashMap[Task[_], Seq[Task[_]]] + private[this] type TriggerMap = collection.mutable.HashMap[Task[_], Seq[Task[_]]] - def triggers(ss: Settings[Scope]): Triggers[Task] = - { - val runBefore = new TriggerMap - val triggeredBy = new TriggerMap - for ((_, amap) <- ss.data; AttributeEntry(_, value: Task[_]) <- amap.entries) { - val as = value.info.attributes - update(runBefore, value, as get Keys.runBefore) - update(triggeredBy, value, as get Keys.triggeredBy) + def triggers(ss: Settings[Scope]): Triggers[Task] = { + val runBefore = new TriggerMap + val triggeredBy = new TriggerMap + ss.data.values foreach ( + _.entries foreach { + case AttributeEntry(_, value: Task[_]) => + val as = value.info.attributes + update(runBefore, value, as get Keys.runBefore) + update(triggeredBy, value, as get Keys.triggeredBy) + case _ => () } - val onComplete = Keys.onComplete in GlobalScope get ss getOrElse { () => () } - new Triggers[Task](runBefore, triggeredBy, map => { onComplete(); map }) - } + ) + val onComplete = Keys.onComplete in GlobalScope get ss getOrElse (() => ()) + new Triggers[Task](runBefore, triggeredBy, map => { onComplete(); map }) + } private[this] def update(map: TriggerMap, base: Task[_], tasksOpt: Option[Seq[Task[_]]]): Unit = for (tasks <- tasksOpt; task <- tasks) diff --git a/main/src/main/scala/sbt/internal/GlobalPlugin.scala b/main/src/main/scala/sbt/internal/GlobalPlugin.scala index e71b8ee24..0424a325c 100644 --- a/main/src/main/scala/sbt/internal/GlobalPlugin.scala +++ b/main/src/main/scala/sbt/internal/GlobalPlugin.scala @@ -94,7 +94,7 @@ object GlobalPlugin { val nv = nodeView(state, str, roots) val config = EvaluateTask.extractedTaskConfig(Project.extract(state), structure, state) val (newS, result) = runTask(t, state, str, structure.index.triggers, config)(nv) - (newS, processResult(result, newS.log)) + (newS, processResult2(result)) } } val globalPluginSettings = Project.inScope(Scope.GlobalScope in LocalRootProject)( diff --git a/main/src/main/scala/sbt/internal/LibraryManagement.scala b/main/src/main/scala/sbt/internal/LibraryManagement.scala index a245344dc..adf3f8f5d 100644 --- a/main/src/main/scala/sbt/internal/LibraryManagement.scala +++ b/main/src/main/scala/sbt/internal/LibraryManagement.scala @@ -36,18 +36,12 @@ private[sbt] object LibraryManagement { ): UpdateReport = { /* Resolve the module settings from the inputs. */ - def resolve(inputs: UpdateInputs): UpdateReport = { + def resolve: UpdateReport = { import sbt.util.ShowLines._ log.info(s"Updating $label...") val reportOrUnresolved: Either[UnresolvedWarning, UpdateReport] = - //try { lm.update(module, updateConfig, uwConfig, log) - // } catch { - // case e: Throwable => - // e.printStackTrace - // throw e - // } val report = reportOrUnresolved match { case Right(report0) => report0 case Left(unresolvedWarning) => @@ -95,12 +89,12 @@ private[sbt] object LibraryManagement { import sbt.librarymanagement.LibraryManagementCodec._ val cachedResolve = Tracked.lastOutput[UpdateInputs, UpdateReport](cache) { case (_, Some(out)) if upToDate(inChanged, out) => markAsCached(out) - case _ => resolve(updateInputs) + case _ => resolve } import scala.util.control.Exception.catching catching(classOf[NullPointerException], classOf[OutOfMemoryError]) .withApply { t => - val resolvedAgain = resolve(updateInputs) + val resolvedAgain = resolve val culprit = t.getClass.getSimpleName log.warn(s"Update task caching failed due to $culprit.") log.warn("Report the following output to sbt:") diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index c87d583ab..da6c2c8c1 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -61,7 +61,7 @@ private[sbt] object Load { val globalBase = getGlobalBase(state) val base = baseDirectory.getCanonicalFile val rawConfig = defaultPreGlobal(state, base, globalBase, log) - val config0 = defaultWithGlobal(state, base, rawConfig, globalBase, log) + val config0 = defaultWithGlobal(state, base, rawConfig, globalBase) val config = if (isPlugin) enableSbtPlugin(config0) else config0.copy(extraBuilds = topLevelExtras) (base, config) @@ -109,7 +109,7 @@ private[sbt] object Load { javaHome = None, scalac ) - val evalPluginDef = EvaluateTask.evalPluginDef(log) _ + val evalPluginDef: (BuildStructure, State) => PluginData = EvaluateTask.evalPluginDef _ val delegates = defaultDelegates val pluginMgmt = PluginManagement(loader) val inject = InjectSettings(injectGlobal(state), Nil, const(Nil)) @@ -145,7 +145,6 @@ private[sbt] object Load { base: File, rawConfig: LoadBuildConfiguration, globalBase: File, - log: Logger ): LoadBuildConfiguration = { val globalPluginsDir = getGlobalPluginsDirectory(state, globalBase) val withGlobal = loadGlobal(state, base, globalPluginsDir, rawConfig) diff --git a/main/src/main/scala/sbt/internal/LogManager.scala b/main/src/main/scala/sbt/internal/LogManager.scala index 9abec04c6..3d700667f 100644 --- a/main/src/main/scala/sbt/internal/LogManager.scala +++ b/main/src/main/scala/sbt/internal/LogManager.scala @@ -15,6 +15,7 @@ import Keys.{ logLevel, logManager, persistLogLevel, persistTraceLevel, sLog, tr import scala.Console.{ BLUE, RESET } import sbt.internal.util.{ AttributeKey, + ConsoleAppender, ConsoleOut, Settings, SuppressedTraceContext, @@ -105,7 +106,7 @@ object LogManager { def backgroundLog(data: Settings[Scope], state: State, task: ScopedKey[_]): ManagedLogger = { val console = screen(task, state) - LogManager.backgroundLog(data, state, task, console, relay(()), extra(task).toList) + LogManager.backgroundLog(data, state, task, console, relay(())) } } @@ -191,7 +192,6 @@ object LogManager { console: Appender, /* TODO: backed: Appender,*/ relay: Appender, - extra: List[Appender] ): ManagedLogger = { val scope = task.scope val screenLevel = getOr(logLevel.key, data, scope, state, Level.Info) @@ -253,7 +253,7 @@ object LogManager { private[this] def slog: Logger = Option(ref.get) getOrElse sys.error("Settings logger used after project was loaded.") - override val ansiCodesSupported = slog.ansiCodesSupported + override val ansiCodesSupported = ConsoleAppender.formatEnabledInEnv override def trace(t: => Throwable) = slog.trace(t) override def success(message: => String) = slog.success(message) override def log(level: Level.Value, message: => String) = slog.log(level, message) diff --git a/main/src/main/scala/sbt/internal/PluginsDebug.scala b/main/src/main/scala/sbt/internal/PluginsDebug.scala index 676a4caff..f908a29fe 100644 --- a/main/src/main/scala/sbt/internal/PluginsDebug.scala +++ b/main/src/main/scala/sbt/internal/PluginsDebug.scala @@ -57,7 +57,7 @@ private[sbt] class PluginsDebug( if (possible.nonEmpty) { val explained = possible.map(explainPluginEnable) val possibleString = - if (explained.size > 1) + if (explained.lengthCompare(1) > 0) explained.zipWithIndex .map { case (s, i) => s"$i. $s" } .mkString(s"Multiple plugins are available that can provide $notFoundKey:\n", "\n", "") @@ -111,7 +111,7 @@ private[sbt] class PluginsDebug( } private[this] def multi(strs: Seq[String]): String = - strs.mkString(if (strs.size > 4) "\n\t" else ", ") + strs.mkString(if (strs.lengthCompare(4) > 0) "\n\t" else ", ") } private[sbt] object PluginsDebug { @@ -377,7 +377,7 @@ private[sbt] object PluginsDebug { def explainPluginEnable(ps: PluginEnable): String = ps match { case PluginRequirements(plugin, - context, + _, blockingExcludes, enablingPlugins, extraEnabledPlugins, @@ -393,9 +393,8 @@ private[sbt] object PluginsDebug { note(willRemove(plugin, toBeRemoved.toList)) :: Nil parts.filterNot(_.isEmpty).mkString("\n") - case PluginImpossible(plugin, context, contradictions) => - pluginImpossible(plugin, contradictions) - case PluginActivated(plugin, context) => s"Plugin ${plugin.label} already activated." + case PluginImpossible(plugin, _, contradictions) => pluginImpossible(plugin, contradictions) + case PluginActivated(plugin, _) => s"Plugin ${plugin.label} already activated." } /** diff --git a/main/src/main/scala/sbt/internal/RelayAppender.scala b/main/src/main/scala/sbt/internal/RelayAppender.scala index 3e0daf474..2898b5bdc 100644 --- a/main/src/main/scala/sbt/internal/RelayAppender.scala +++ b/main/src/main/scala/sbt/internal/RelayAppender.scala @@ -26,7 +26,7 @@ class RelayAppender(name: String) val level = ConsoleAppender.toLevel(event.getLevel) val message = event.getMessage message match { - case o: ObjectMessage => appendEvent(level, o.getParameter) + case o: ObjectMessage => appendEvent(o.getParameter) case p: ParameterizedMessage => appendLog(level, p.getFormattedMessage) case r: RingBufferLogEvent => appendLog(level, r.getFormattedMessage) case _ => appendLog(level, message.toString) @@ -35,7 +35,7 @@ class RelayAppender(name: String) def appendLog(level: Level.Value, message: => String): Unit = { exchange.publishEventMessage(LogEvent(level.toString, message)) } - def appendEvent(level: Level.Value, event: AnyRef): Unit = + def appendEvent(event: AnyRef): Unit = event match { case x: StringEvent => { import JsonProtocol._ diff --git a/main/src/main/scala/sbt/internal/SettingCompletions.scala b/main/src/main/scala/sbt/internal/SettingCompletions.scala index d9bbafbff..3a3f8388e 100644 --- a/main/src/main/scala/sbt/internal/SettingCompletions.scala +++ b/main/src/main/scala/sbt/internal/SettingCompletions.scala @@ -15,7 +15,7 @@ import sbt.librarymanagement.Configuration import Project._ import Def.{ ScopedKey, Setting } import Scope.Global -import Types.{ const, idFun } +import Types.idFun import complete._ import DefaultParsers._ @@ -64,11 +64,10 @@ private[sbt] object SettingCompletions { setResult(session, r, redefined) } - /** Implementation of the `set` command that will reload the current project with `settings` appended to the current settings. */ - def setThis(s: State, - extracted: Extracted, - settings: Seq[Def.Setting[_]], - arg: String): SetResult = { + /** Implementation of the `set` command that will reload the current project with `settings` + * appended to the current settings. + */ + def setThis(extracted: Extracted, settings: Seq[Def.Setting[_]], arg: String): SetResult = { import extracted._ val append = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings) @@ -82,16 +81,19 @@ private[sbt] object SettingCompletions { private[this] def setResult( session: SessionSettings, r: Relation[ScopedKey[_], ScopedKey[_]], - redefined: Seq[Setting[_]])(implicit show: Show[ScopedKey[_]]): SetResult = { + redefined: Seq[Setting[_]], + )(implicit show: Show[ScopedKey[_]]): SetResult = { val redefinedKeys = redefined.map(_.key).toSet val affectedKeys = redefinedKeys.flatMap(r.reverse) def summary(verbose: Boolean): String = setSummary(redefinedKeys, affectedKeys, verbose) new SetResult(session, summary(true), summary(false)) } - private[this] def setSummary(redefined: Set[ScopedKey[_]], - affected: Set[ScopedKey[_]], - verbose: Boolean)(implicit display: Show[ScopedKey[_]]): String = { + private[this] def setSummary( + redefined: Set[ScopedKey[_]], + affected: Set[ScopedKey[_]], + verbose: Boolean, + )(implicit display: Show[ScopedKey[_]]): String = { val QuietLimit = 3 def strings(in: Set[ScopedKey[_]]): Seq[String] = in.toSeq.map(sk => display.show(sk)).sorted def lines(in: Seq[String]): (String, Boolean) = @@ -129,17 +131,17 @@ private[sbt] object SettingCompletions { * when there are fewer choices or tab is pressed multiple times. * The last part of the completion will generate a template for the value or function literal that will initialize the setting or task. */ - def settingParser(settings: Settings[Scope], - rawKeyMap: Map[String, AttributeKey[_]], - context: ResolvedProject): Parser[String] = { - val keyMap - : Map[String, AttributeKey[_]] = rawKeyMap.map { case (k, v) => (keyScalaID(k), v) }.toMap - def inputScopedKey(pred: AttributeKey[_] => Boolean): Parser[ScopedKey[_]] = - scopedKeyParser(keyMap.filter { case (_, k) => pred(k) }, settings, context) + def settingParser( + settings: Settings[Scope], + rawKeyMap: Map[String, AttributeKey[_]], + context: ResolvedProject, + ): Parser[String] = { + val keyMap: Map[String, AttributeKey[_]] = + rawKeyMap.map { case (k, v) => (keyScalaID(k), v) }.toMap val full = for { defineKey <- scopedKeyParser(keyMap, settings, context) a <- assign(defineKey) - _ <- valueParser(defineKey, a, inputScopedKey(keyFilter(defineKey.key))) + _ <- valueParser(defineKey, a) } yield () // parser is currently only for completion and the parsed data structures are not used @@ -167,9 +169,7 @@ private[sbt] object SettingCompletions { * Parser for the initialization expression for the assignment method `assign` on the key `sk`. * `scopedKeyP` is used to parse and complete the input keys for an initialization that depends on other keys. */ - def valueParser(sk: ScopedKey[_], - assign: Assign.Value, - scopedKeyP: Parser[ScopedKey[_]]): Parser[Seq[ScopedKey[_]]] = { + def valueParser(sk: ScopedKey[_], assign: Assign.Value): Parser[Seq[ScopedKey[_]]] = { val fullTypeString = keyTypeString(sk.key) val typeString = if (assignNoAppend(assign)) fullTypeString else "..." if (assign == Assign.Update) { @@ -181,14 +181,6 @@ private[sbt] object SettingCompletions { } } - /** - * For a setting definition `definingKey <<= (..., in, ...) { ... }`, - * `keyFilter(definingKey)(in)` returns true when `in` is an allowed input for `definingKey` based on whether they are settings or not. - * For example, if `definingKey` is for a setting, `in` may only be a setting itself. - */ - def keyFilter(definingKey: AttributeKey[_]): AttributeKey[_] => Boolean = - if (isSetting(definingKey)) isSetting _ else isTaskOrSetting _ - /** * Parser for a Scope for a `key` given the current project `context` and evaluated `settings`. * The completions are restricted to be more useful. Currently, this parser will suggest @@ -202,17 +194,20 @@ private[sbt] object SettingCompletions { val definedScopes = data.toSeq flatMap { case (scope, attrs) => if (attrs contains key) scope :: Nil else Nil } - scope(key, allScopes, definedScopes, context) + scope(allScopes, definedScopes, context) } - private[this] def scope(key: AttributeKey[_], - allScopes: Seq[Scope], - definedScopes: Seq[Scope], - context: ResolvedProject): Parser[Scope] = { - def axisParser[T](axis: Scope => ScopeAxis[T], - name: T => String, - description: T => Option[String], - label: String): Parser[ScopeAxis[T]] = { + private[this] def scope( + allScopes: Seq[Scope], + definedScopes: Seq[Scope], + context: ResolvedProject, + ): Parser[Scope] = { + def axisParser[T]( + axis: Scope => ScopeAxis[T], + name: T => String, + description: T => Option[String], + label: String, + ): Parser[ScopeAxis[T]] = { def getChoice(s: Scope): Seq[(String, T)] = axis(s) match { case Select(t) => (name(t), t) :: Nil case _ => Nil @@ -220,19 +215,23 @@ private[sbt] object SettingCompletions { def getChoices(scopes: Seq[Scope]): Map[String, T] = scopes.flatMap(getChoice).toMap val definedChoices: Set[String] = definedScopes.flatMap(s => axis(s).toOption.map(name)).toSet - val fullChoices: Map[String, T] = getChoices(allScopes.toSeq) + val fullChoices: Map[String, T] = getChoices(allScopes) val completions = fixedCompletions { (seen, level) => completeScope(seen, level, definedChoices, fullChoices)(description).toSet } - Act.optionalAxis(inParser ~> token(Space) ~> token(scalaID(fullChoices, label), completions), - This) + Act.optionalAxis( + inParser ~> token(Space) ~> token(scalaID(fullChoices, label), completions), + This, + ) } val configurations: Map[String, Configuration] = context.configurations.map(c => (configScalaID(c.name), c)).toMap - val configParser = axisParser[ConfigKey](_.config, - c => configScalaID(c.name), - ck => configurations.get(ck.name).map(_.description), - "configuration") + val configParser = axisParser[ConfigKey]( + _.config, + c => configScalaID(c.name), + ck => configurations.get(ck.name).map(_.description), + "configuration", + ) val taskParser = axisParser[AttributeKey[_]](_.task, k => keyScalaID(k.label), _.description, "task") val nonGlobal = (configParser ~ taskParser) map { case (c, t) => Scope(This, c, t, Zero) } @@ -242,8 +241,8 @@ private[sbt] object SettingCompletions { /** Parser for the assignment method (such as `:=`) for defining `key`. */ def assign(key: ScopedKey[_]): Parser[Assign.Value] = { - val completions = fixedCompletions { (seen, level) => - completeAssign(seen, level, key).toSet + val completions = fixedCompletions { (seen, _) => + completeAssign(seen, key).toSet } val identifier = Act.filterStrings(Op, Assign.values.map(_.toString), "assignment method") map Assign.withName token(Space) ~> token(optionallyQuoted(identifier), completions) @@ -267,7 +266,7 @@ private[sbt] object SettingCompletions { * Completions for an assignment method for `key` given the tab completion `level` and existing partial string `seen`. * This will filter possible assignment methods based on the underlying type of `key`, so that only `<<=` is shown for input tasks, for example. */ - def completeAssign(seen: String, level: Int, key: ScopedKey[_]): Seq[Completion] = { + def completeAssign(seen: String, key: ScopedKey[_]): Seq[Completion] = { val allowed: Iterable[Assign.Value] = if (appendable(key.key)) Assign.values else assignNoAppend @@ -284,7 +283,7 @@ private[sbt] object SettingCompletions { prominentCutoff: Int, detailLimit: Int): Seq[Completion] = completeSelectDescribed(seen, level, keys, detailLimit)(_.description) { - case (k, v) => v.rank <= prominentCutoff + case (_, v) => v.rank <= prominentCutoff } def completeScope[T]( @@ -293,17 +292,17 @@ private[sbt] object SettingCompletions { definedChoices: Set[String], allChoices: Map[String, T])(description: T => Option[String]): Seq[Completion] = completeSelectDescribed(seen, level, allChoices, 10)(description) { - case (k, v) => definedChoices(k) + case (k, _) => definedChoices(k) } def completeSelectDescribed[T](seen: String, level: Int, all: Map[String, T], detailLimit: Int)( description: T => Option[String])(prominent: (String, T) => Boolean): Seq[Completion] = { - val applicable = all.toSeq.filter { case (k, v) => k startsWith seen } + val applicable = all.toSeq.filter { case (k, _) => k startsWith seen } val prominentOnly = applicable filter { case (k, v) => prominent(k, v) } - val showAll = (level >= 3) || (level == 2 && prominentOnly.size <= detailLimit) || prominentOnly.isEmpty + val showAll = (level >= 3) || (level == 2 && prominentOnly.lengthCompare(detailLimit) <= 0) || prominentOnly.isEmpty val showKeys = if (showAll) applicable else prominentOnly - val showDescriptions = (level >= 2) || (showKeys.size <= detailLimit) + val showDescriptions = (level >= 2) || showKeys.lengthCompare(detailLimit) <= 0 completeDescribed(seen, showDescriptions, showKeys)(s => description(s).toList.mkString) } def completeDescribed[T](seen: String, showDescriptions: Boolean, in: Seq[(String, T)])( @@ -315,14 +314,11 @@ private[sbt] object SettingCompletions { val withDescriptions = in map { case (id, key) => (id, description(key)) } val padded = CommandUtil.aligned("", " ", withDescriptions) (padded, in).zipped.map { - case (line, (id, key)) => + case (line, (id, _)) => Completion.tokenDisplay(append = appendString(id), display = line + "\n") } } else - in map { - case (id, key) => - Completion.tokenDisplay(display = id, append = appendString(id)) - } + in map { case (id, _) => Completion.tokenDisplay(display = id, append = appendString(id)) } } /** @@ -364,18 +360,6 @@ private[sbt] object SettingCompletions { keyType(key)(mfToString, mfToString, mfToString) } - /** True if the `key` represents an input task, false if it represents a task or setting. */ - def isInputTask(key: AttributeKey[_]): Boolean = - keyType(key)(const(false), const(false), const(true)) - - /** True if the `key` represents a setting, false if it represents a task or an input task.*/ - def isSetting(key: AttributeKey[_]): Boolean = - keyType(key)(const(true), const(false), const(false)) - - /** True if the `key` represents a setting or task, false if it is for an input task. */ - def isTaskOrSetting(key: AttributeKey[_]): Boolean = - keyType(key)(const(true), const(true), const(false)) - /** True if the `key` represents a setting or task that may be appended using an assignment method such as `+=`. */ def appendable(key: AttributeKey[_]): Boolean = { val underlying = keyUnderlyingType(key).runtimeClass diff --git a/main/src/main/scala/sbt/internal/SettingGraph.scala b/main/src/main/scala/sbt/internal/SettingGraph.scala index 33f1b9a5c..8b74b51a4 100644 --- a/main/src/main/scala/sbt/internal/SettingGraph.scala +++ b/main/src/main/scala/sbt/internal/SettingGraph.scala @@ -99,7 +99,7 @@ object Graph { val withBar = childLines.zipWithIndex flatMap { case ((line, withBar), pos) if pos < (cs.size - 1) => (line +: withBar) map { insertBar(_, 2 * (level + 1)) } - case ((line, withBar), pos) if withBar.lastOption.getOrElse(line).trim != "" => + case ((line, withBar), _) if withBar.lastOption.getOrElse(line).trim != "" => (line +: withBar) ++ Vector(twoSpaces * (level + 1)) case ((line, withBar), _) => line +: withBar } diff --git a/main/src/main/scala/sbt/internal/TaskTimings.scala b/main/src/main/scala/sbt/internal/TaskTimings.scala index 2c39ffc43..51ccfcaa1 100644 --- a/main/src/main/scala/sbt/internal/TaskTimings.scala +++ b/main/src/main/scala/sbt/internal/TaskTimings.scala @@ -81,7 +81,7 @@ private[sbt] final class TaskTimings(shutdown: Boolean) extends ExecuteProgress[ println(s"Total time: $total $unit") import collection.JavaConverters._ def sumTimes(in: Seq[(Task[_], Long)]) = in.map(_._2).sum - val timingsByName = timings.asScala.toSeq.groupBy { case (t, time) => mappedName(t) } mapValues (sumTimes) + val timingsByName = timings.asScala.toSeq.groupBy { case (t, _) => mappedName(t) } mapValues (sumTimes) val times = timingsByName.toSeq .sortBy(_._2) .reverse diff --git a/main/src/main/scala/sbt/internal/parser/SbtParser.scala b/main/src/main/scala/sbt/internal/parser/SbtParser.scala index 6e89cdf28..d8ab96653 100644 --- a/main/src/main/scala/sbt/internal/parser/SbtParser.scala +++ b/main/src/main/scala/sbt/internal/parser/SbtParser.scala @@ -277,7 +277,7 @@ private[sbt] case class SbtParser(file: File, lines: Seq[String]) extends Parsed modifiedContent: String, imports: Seq[Tree] ): Seq[(String, Int)] = { - val toLineRange = imports map convertImport(modifiedContent) + val toLineRange = imports map convertImport val groupedByLineNumber = toLineRange.groupBy { case (_, lineNumber) => lineNumber } val mergedImports = groupedByLineNumber.map { case (l, seq) => (l, extractLine(modifiedContent, seq)) @@ -286,12 +286,10 @@ private[sbt] case class SbtParser(file: File, lines: Seq[String]) extends Parsed } /** - * - * @param modifiedContent - modifiedContent * @param t - tree - * @return ((start,end),lineNumber) + * @return ((start, end), lineNumber) */ - private def convertImport(modifiedContent: String)(t: Tree): ((Int, Int), Int) = { + private def convertImport(t: Tree): ((Int, Int), Int) = { val lineNumber = t.pos.line - 1 ((t.pos.start, t.pos.end), lineNumber) } diff --git a/main/src/main/scala/sbt/internal/parser/SbtRefactorings.scala b/main/src/main/scala/sbt/internal/parser/SbtRefactorings.scala index 14e09e2bd..1e3893672 100644 --- a/main/src/main/scala/sbt/internal/parser/SbtRefactorings.scala +++ b/main/src/main/scala/sbt/internal/parser/SbtRefactorings.scala @@ -57,10 +57,7 @@ private[sbt] object SbtRefactorings { commands.flatMap { case (_, command) => val map = toTreeStringMap(command) - map.flatMap { - case (name, statement) => - treesToReplacements(split, name, command) - } + map.flatMap { case (name, _) => treesToReplacements(split, name, command) } } private def treesToReplacements(split: SbtParser, name: String, command: Seq[String]) = diff --git a/main/src/main/scala/sbt/internal/server/Definition.scala b/main/src/main/scala/sbt/internal/server/Definition.scala index 4459861db..cd81e9650 100644 --- a/main/src/main/scala/sbt/internal/server/Definition.scala +++ b/main/src/main/scala/sbt/internal/server/Definition.scala @@ -227,7 +227,7 @@ private[sbt] object Definition { updateCache(StandardMain.cache)(cacheFile, useBinary) } - private[sbt] def getAnalyses(log: Logger): Future[Seq[Analysis]] = { + private[sbt] def getAnalyses: Future[Seq[Analysis]] = { import scalacache.modes.scalaFuture._ import scala.concurrent.ExecutionContext.Implicits.global StandardMain.cache @@ -260,7 +260,7 @@ private[sbt] object Definition { val LspDefinitionLogHead = "lsp-definition" import sjsonnew.support.scalajson.unsafe.CompactPrinter log.debug(s"$LspDefinitionLogHead json request: ${CompactPrinter(jsonDefinition)}") - lazy val analyses = getAnalyses(log) + lazy val analyses = getAnalyses val definition = getDefinition(jsonDefinition) definition .flatMap { definition => diff --git a/main/src/test/scala/Delegates.scala b/main/src/test/scala/Delegates.scala index 492cdcdd9..af77a8e23 100644 --- a/main/src/test/scala/Delegates.scala +++ b/main/src/test/scala/Delegates.scala @@ -47,7 +47,7 @@ object Delegates extends Properties("delegates") { } } property("Initial scope present with all combinations of Global axes") = allAxes( - globalCombinations) + (s, ds, _) => globalCombinations(s, ds)) property("initial scope first") = forAll { (keys: Keys) => allDelegates(keys) { (scope, ds) => @@ -66,6 +66,7 @@ object Delegates extends Properties("delegates") { all(f(s, ds, _.project), f(s, ds, _.config), f(s, ds, _.task), f(s, ds, _.extra)) } } + def allDelegates(keys: Keys)(f: (Scope, Seq[Scope]) => Prop): Prop = all(keys.scopes map { scope => val delegates = keys.env.delegates(scope) @@ -73,16 +74,20 @@ object Delegates extends Properties("delegates") { ("Delegates:\n\t" + delegates.map(scope => Scope.display(scope, "_")).mkString("\n\t")) |: f(scope, delegates) }: _*) + def alwaysZero(s: Scope, ds: Seq[Scope], axis: Scope => ScopeAxis[_]): Prop = (axis(s) != Zero) || all(ds map { d => (axis(d) == Zero): Prop }: _*) - def globalCombinations(s: Scope, ds: Seq[Scope], axis: Scope => ScopeAxis[_]): Prop = { - val mods = List[Scope => Scope](_.copy(project = Zero), - _.copy(config = Zero), - _.copy(task = Zero), - _.copy(extra = Zero)) + + def globalCombinations(s: Scope, ds: Seq[Scope]): Prop = { + val mods = List[Scope => Scope]( + _.copy(project = Zero), + _.copy(config = Zero), + _.copy(task = Zero), + _.copy(extra = Zero), + ) val modAndIdent = mods.map(_ :: idFun[Scope] :: Nil) def loop(cur: Scope, acc: List[Scope], rem: List[Seq[Scope => Scope]]): Seq[Scope] = diff --git a/main/src/test/scala/ParseKey.scala b/main/src/test/scala/ParseKey.scala index 2d6991af7..1f17d9288 100644 --- a/main/src/test/scala/ParseKey.scala +++ b/main/src/test/scala/ParseKey.scala @@ -55,9 +55,9 @@ object ParseKey extends Properties("Key parser test") { ("Mask: " + mask) |: ("Current: " + structure.current) |: parse(structure, string) { - case Left(err) => false - case Right(sk) if hasZeroConfig => true - case Right(sk) => sk.scope.project == Select(structure.current) + case Left(_) => false + case Right(_) if hasZeroConfig => true + case Right(sk) => sk.scope.project == Select(structure.current) } } @@ -70,7 +70,7 @@ object ParseKey extends Properties("Key parser test") { ("Key: " + displayPedantic(key)) |: ("Mask: " + mask) |: parse(structure, string) { - case Left(err) => false + case Left(_) => false case Right(sk) => sk.scope.task == Zero } } @@ -88,7 +88,7 @@ object ParseKey extends Properties("Key parser test") { ("Expected configuration: " + resolvedConfig.map(_.name)) |: parse(structure, string) { case Right(sk) => (sk.scope.config == resolvedConfig) || (sk.scope == Scope.GlobalScope) - case Left(err) => false + case Left(_) => false } } @@ -117,7 +117,7 @@ object ParseKey extends Properties("Key parser test") { ("Expected: " + displayFull(expected)) |: ("Mask: " + mask) |: parse(structure, s) { - case Left(err) => false + case Left(_) => false case Right(sk) => (s"${sk}.key == ${expected}.key: ${sk.key == expected.key}") |: (s"${sk.scope} == ${expected.scope}: ${Scope.equal(sk.scope, expected.scope, mask)}") |: diff --git a/main/src/test/scala/PluginsTest.scala b/main/src/test/scala/PluginsTest.scala index 0062a7782..620e6941c 100644 --- a/main/src/test/scala/PluginsTest.scala +++ b/main/src/test/scala/PluginsTest.scala @@ -39,18 +39,18 @@ object PluginsTest extends Specification { } "throw an AutoPluginException on conflicting requirements" in { deducePlugin(S, log) must throwAn[AutoPluginException]( - message = """Contradiction in enabled plugins: - - requested: sbt.AI\$S - - enabled: sbt.AI\$S, sbt.AI\$Q, sbt.AI\$R, sbt.AI\$B, sbt.AI\$A - - conflict: sbt.AI\$R is enabled by sbt.AI\$Q; excluded by sbt.AI\$S""") + message = s"""Contradiction in enabled plugins: + - requested: sbt.AI\\$$S + - enabled: sbt.AI\\$$S, sbt.AI\\$$Q, sbt.AI\\$$R, sbt.AI\\$$B, sbt.AI\\$$A + - conflict: sbt.AI\\$$R is enabled by sbt.AI\\$$Q; excluded by sbt.AI\\$$S""") } "generates a detailed report on conflicting requirements" in { - deducePlugin(T && U, log) must throwAn[AutoPluginException](message = - """Contradiction in enabled plugins: - - requested: sbt.AI\$T && sbt.AI\$U - - enabled: sbt.AI\$U, sbt.AI\$T, sbt.AI\$A, sbt.AI\$Q, sbt.AI\$R, sbt.AI\$B - - conflict: sbt.AI\$Q is enabled by sbt.AI\$A && sbt.AI\$B; required by sbt.AI\$T, sbt.AI\$R; excluded by sbt.AI\$U - - conflict: sbt.AI\$R is enabled by sbt.AI\$Q; excluded by sbt.AI\$T""") + deducePlugin(T && U, log) must throwAn[AutoPluginException]( + message = s"""Contradiction in enabled plugins: + - requested: sbt.AI\\$$T && sbt.AI\\$$U + - enabled: sbt.AI\\$$U, sbt.AI\\$$T, sbt.AI\\$$A, sbt.AI\\$$Q, sbt.AI\\$$R, sbt.AI\\$$B + - conflict: sbt.AI\\$$Q is enabled by sbt.AI\\$$A && sbt.AI\\$$B; required by sbt.AI\\$$T, sbt.AI\\$$R; excluded by sbt.AI\\$$U + - conflict: sbt.AI\\$$R is enabled by sbt.AI\\$$Q; excluded by sbt.AI\\$$T""") } } } diff --git a/main/src/test/scala/sbt/internal/parser/ErrorSpec.scala b/main/src/test/scala/sbt/internal/parser/ErrorSpec.scala index 6d7cc7b42..845baccd7 100644 --- a/main/src/test/scala/sbt/internal/parser/ErrorSpec.scala +++ b/main/src/test/scala/sbt/internal/parser/ErrorSpec.scala @@ -77,8 +77,7 @@ class ErrorSpec extends AbstractSpec { case exception: MessageOnlyException => val error = exception.getMessage """(\d+)""".r.findFirstIn(error) match { - case Some(x) => - true + case Some(_) => true case None => println(s"Number not found in $error") false diff --git a/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala b/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala index 7acf7955f..a80819af1 100644 --- a/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala +++ b/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala @@ -122,7 +122,7 @@ object SettingQueryTest extends org.specs2.mutable.Specification { .put(globalBaseDirectory, globalDirFile) val config0 = defaultPreGlobal(state, baseFile, globalDirFile, state.log) - val config = defaultWithGlobal(state, baseFile, config0, globalDirFile, state.log) + val config = defaultWithGlobal(state, baseFile, config0, globalDirFile) val buildUnit: BuildUnit = { val loadedPlugins: LoadedPlugins = diff --git a/sbt/src/sbt-test/actions/run-task/A.scala b/sbt/src/sbt-test/actions/run-task/A.scala index f12139e7b..f003b3e20 100644 --- a/sbt/src/sbt-test/actions/run-task/A.scala +++ b/sbt/src/sbt-test/actions/run-task/A.scala @@ -1,7 +1,6 @@ object A { - def main(args: Array[String]) = - { - assert(args(0).toInt == args(1).toInt) - assert(java.lang.Boolean.getBoolean("sbt.check.forked")) - } + def main(args: Array[String]) = { + assert(args(0).toInt == args(1).toInt) + assert(java.lang.Boolean.getBoolean("sbt.check.forked")) + } } diff --git a/sbt/src/sbt-test/project/session-update-from-cmd/project/Common.scala b/sbt/src/sbt-test/project/session-update-from-cmd/project/Common.scala index f977fa81a..423d628f6 100644 --- a/sbt/src/sbt-test/project/session-update-from-cmd/project/Common.scala +++ b/sbt/src/sbt-test/project/session-update-from-cmd/project/Common.scala @@ -11,7 +11,7 @@ object Common { val UpdateK1 = Command.command("UpdateK1") { st: State => val ex = Project extract st import ex._ - val session2 = BuiltinCommands.setThis(st, ex, Seq(k1 := {}), """k1 := { + val session2 = BuiltinCommands.setThis(ex, Seq(k1 := {}), """k1 := { |// |// |}""".stripMargin).session @@ -24,7 +24,7 @@ object Common { val UpdateK3 = Command.command("UpdateK3") { st: State => val ex = Project extract st import ex._ - val session2 = BuiltinCommands.setThis(st, ex, Seq(k3 := {}), """k3 := { + val session2 = BuiltinCommands.setThis(ex, Seq(k3 := {}), """k3 := { |// |// |}""".stripMargin).session From 657ff56011c6a2cc498ad32cd3b2b8d932b36849 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 6 Dec 2017 16:44:56 +0000 Subject: [PATCH 017/120] Remove all warnings from scriptedSbtProj --- scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala index 29b4f2258..3e2798300 100644 --- a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala @@ -385,8 +385,11 @@ class ScriptedRunner { instances: Int ): Unit = { val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, bootProperties, launchOpts) + val sbtVersion = bootProperties.getName.dropWhile(!_.isDigit).dropRight(".jar".length) + val accept = isTestCompatible(resourceBaseDirectory, sbtVersion) _ // The scripted tests mapped to the inputs that the user wrote after `scripted`. - val scriptedTests = get(tests, resourceBaseDirectory, logger).map(st => (st.group, st.name)) + val scriptedTests = + get(tests, resourceBaseDirectory, accept, logger).map(st => (st.group, st.name)) val scriptedRunners = runner.batchScriptedRunner(scriptedTests, prescripted, instances, logger) val parallelRunners = scriptedRunners.toParArray val pool = new java.util.concurrent.ForkJoinPool(instances) From b8bb8fe18509462a3f98d5118315c7ff11e2285f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 6 Dec 2017 12:26:05 +0000 Subject: [PATCH 018/120] Remove all warnings from sbtProj --- sbt/src/main/scala/Import.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/sbt/src/main/scala/Import.scala b/sbt/src/main/scala/Import.scala index 3a0adc48a..170a39e4a 100644 --- a/sbt/src/main/scala/Import.scala +++ b/sbt/src/main/scala/Import.scala @@ -67,6 +67,7 @@ trait Import { type Cache[I, O] = sbt.util.Cache[I, O] val Cache = sbt.util.Cache val CacheImplicits = sbt.util.CacheImplicits + @deprecated("Use Tracked.inputChanged and Tracked.outputChanged instead", "1.0.1") type Changed[O] = sbt.util.Changed[O] type ChangeReport[T] = sbt.util.ChangeReport[T] val ChangeReport = sbt.util.ChangeReport From 43a9bd25f0e300e97e2d68f9d672d5e291a974c9 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 6 Dec 2017 14:53:16 +0000 Subject: [PATCH 019/120] Remove all warnings from scriptedPluginProj --- scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala index 3e38a26ac..4788e0186 100644 --- a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala +++ b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala @@ -13,7 +13,6 @@ import sbt.internal.util.complete.{ Parser, DefaultParsers } import sbt.internal.inc.classpath.ClasspathUtilities import sbt.internal.inc.ModuleUtilities import java.lang.reflect.Method -import sbt.librarymanagement.CrossVersion.partialVersion object ScriptedPlugin extends AutoPlugin { override def requires = plugins.JvmPlugin @@ -40,7 +39,7 @@ object ScriptedPlugin extends AutoPlugin { scriptedSbt := (sbtVersion in pluginCrossBuild).value, sbtLauncher := getJars(ScriptedLaunchConf).map(_.get.head).value, sbtTestDirectory := sourceDirectory.value / "sbt-test", - libraryDependencies ++= (partialVersion(scriptedSbt.value) match { + libraryDependencies ++= (CrossVersion.partialVersion(scriptedSbt.value) match { case Some((0, 13)) => Seq( "org.scala-sbt" % "scripted-sbt" % scriptedSbt.value % ScriptedConf, @@ -51,13 +50,15 @@ object ScriptedPlugin extends AutoPlugin { "org.scala-sbt" %% "scripted-sbt" % scriptedSbt.value % ScriptedConf, "org.scala-sbt" % "sbt-launch" % scriptedSbt.value % ScriptedLaunchConf ) + case Some((x, y)) => sys error s"Unknown sbt version ${scriptedSbt.value} ($x.$y)" + case None => sys error s"Unknown sbt version ${scriptedSbt.value}" }), scriptedBufferLog := true, scriptedClasspath := getJars(ScriptedConf).value, scriptedTests := scriptedTestsTask.value, scriptedRun := scriptedRunTask.value, scriptedDependencies := { - def use[A](x: A*): Unit = () // avoid unused warnings + def use[A](@deprecated("unused", "") x: A*): Unit = () // avoid unused warnings val analysis = (compile in Test).value val pub = (publishLocal).value use(analysis, pub) From 5f0852818bcda9d03a27afa7cd2bac0a6fdb2f70 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 15 Dec 2017 01:43:12 +0000 Subject: [PATCH 020/120] Add project id to watching message We redefine watchingMessage in project scope so we can use thisProjectRef to make the watching message more precise. Fixes #2038 --- main-command/src/main/scala/sbt/Watched.scala | 13 +++++++++---- main/src/main/scala/sbt/Defaults.scala | 1 + notes/1.2.0/add-project-id-to-watching-message.md | 12 ++++++++++++ notes/{1.0.2 => }/sample.md | 0 4 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 notes/1.2.0/add-project-id-to-watching-message.md rename notes/{1.0.2 => }/sample.md (100%) diff --git a/main-command/src/main/scala/sbt/Watched.scala b/main-command/src/main/scala/sbt/Watched.scala index fa1dee03f..1960a337b 100644 --- a/main-command/src/main/scala/sbt/Watched.scala +++ b/main-command/src/main/scala/sbt/Watched.scala @@ -44,8 +44,13 @@ trait Watched { } object Watched { - val defaultWatchingMessage - : WatchState => String = _.count + ". Waiting for source changes... (press enter to interrupt)" + val defaultWatchingMessage: WatchState => String = ws => + s"${ws.count}. Waiting for source changes... (press enter to interrupt)" + + def projectWatchingMessage(projectId: String): WatchState => String = + ws => + s"${ws.count}. Waiting for source changes in project $projectId... (press enter to interrupt)" + val defaultTriggeredMessage: WatchState => String = const("") val clearWhenTriggered: WatchState => String = const(clearScreen) def clearScreen: String = "\u001b[2J\u001b[0;0H" @@ -70,8 +75,8 @@ object Watched { * @param base The base directory from which to include files. * @return An instance of `Source`. */ - def apply(base: File): Source = - apply(base, AllPassFilter, NothingFilter) + def apply(base: File): Source = apply(base, AllPassFilter, NothingFilter) + } private[this] class AWatched extends Watched diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 1f2dcfd3a..912cd2a50 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -530,6 +530,7 @@ object Defaults extends BuildCommon { clean := (Def.task { IO.delete(cleanFiles.value) } tag (Tags.Clean)).value, consoleProject := consoleProjectTask.value, watchTransitiveSources := watchTransitiveSourcesTask.value, + watchingMessage := Watched.projectWatchingMessage(thisProjectRef.value.project), watch := watchSetting.value ) diff --git a/notes/1.2.0/add-project-id-to-watching-message.md b/notes/1.2.0/add-project-id-to-watching-message.md new file mode 100644 index 000000000..7c6a1bf57 --- /dev/null +++ b/notes/1.2.0/add-project-id-to-watching-message.md @@ -0,0 +1,12 @@ +[@dwijnand]: https://github.com/dwijnand + +[#2038]: https://github.com/sbt/sbt/issues/2038 +[#3813]: https://github.com/sbt/sbt/pull/3813 + +### Fixes with compatibility implications + +### Improvements + +- Add the current project's id to `~`'s watching message. [#2038][]/[#3813][] by [@dwijnand][] + +### Bug fixes diff --git a/notes/1.0.2/sample.md b/notes/sample.md similarity index 100% rename from notes/1.0.2/sample.md rename to notes/sample.md From 4668faff7c48e30753cedbb554017cc03a3d4477 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Thu, 23 Nov 2017 11:52:07 +1300 Subject: [PATCH 021/120] don't require publishTo specified if publishArtifact is `false` Even with `publishArtifact := false` the user is still forced to define a (dummy) resolver that's never used, e.g. `publishTo := { Some("publishMeNot" at "https://publish/me/not") }` Otherwise the following error is thrown: ``` publish [error] java.lang.RuntimeException: Repository for publishing is not specified. [error] at scala.sys.package$.error(package.scala:27) [error] at sbt.Classpaths$.$anonfun$getPublishTo$1(Defaults.scala:2436) [error] at scala.Option.getOrElse(Option.scala:121) [error] at sbt.Classpaths$.getPublishTo(Defaults.scala:2436) [error] at sbt.Classpaths$.$anonfun$ivyBaseSettings$48(Defaults.scala:1917) ``` --- main/src/main/scala/sbt/Defaults.scala | 6 ++++-- .../publishTo-not-required-if-not-publishing.md | 12 ++++++++++++ sbt/src/sbt-test/project/no-publish/build.sbt | 3 +++ sbt/src/sbt-test/project/no-publish/test | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 notes/1.2.0/publishTo-not-required-if-not-publishing.md create mode 100644 sbt/src/sbt-test/project/no-publish/build.sbt create mode 100644 sbt/src/sbt-test/project/no-publish/test diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 1f2dcfd3a..3bc4a3a20 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1935,8 +1935,10 @@ object Classpaths { if (isSnapshot.value) "integration" else "release", ivyConfigurations.value.map(c => ConfigRef(c.name)).toVector, packagedArtifacts.in(publish).value.toVector, - checksums.in(publish).value.toVector, - getPublishTo(publishTo.value).name, + checksums.in(publish).value.toVector, { //resolvername: not required if publishTo is false + val publishToOption = publishTo.value + if (publishArtifact.value) getPublishTo(publishToOption).name else "local" + }, ivyLoggingLevel.value, isSnapshot.value ) diff --git a/notes/1.2.0/publishTo-not-required-if-not-publishing.md b/notes/1.2.0/publishTo-not-required-if-not-publishing.md new file mode 100644 index 000000000..b2c4ca842 --- /dev/null +++ b/notes/1.2.0/publishTo-not-required-if-not-publishing.md @@ -0,0 +1,12 @@ + +[@mpollmeier]: https://github.com/mpollmeier + +[#3760]: https://github.com/sbt/sbt/pull/3760 + +### Fixes with compatibility implications + +### Improvements + +### Bug fixes + +- Remove requirement on `publishTo` if `publishArtifact` is false. [#3760][] by [@mpollmeier][] diff --git a/sbt/src/sbt-test/project/no-publish/build.sbt b/sbt/src/sbt-test/project/no-publish/build.sbt new file mode 100644 index 000000000..36e1cdfd8 --- /dev/null +++ b/sbt/src/sbt-test/project/no-publish/build.sbt @@ -0,0 +1,3 @@ +// this is supposed to do nothing, and more importantly: not fail +// https://github.com/sbt/sbt/pull/3760 +publishArtifact := false diff --git a/sbt/src/sbt-test/project/no-publish/test b/sbt/src/sbt-test/project/no-publish/test new file mode 100644 index 000000000..c78ab3f9f --- /dev/null +++ b/sbt/src/sbt-test/project/no-publish/test @@ -0,0 +1 @@ +> publish \ No newline at end of file From fa2c48ed84a13d5d3bb4e233a232bc22ddb330a9 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 15 Dec 2017 15:44:21 +0000 Subject: [PATCH 022/120] Drop 0.14.0 references Fixes #3411 --- main/src/main/scala/sbt/internal/PluginDiscovery.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/internal/PluginDiscovery.scala b/main/src/main/scala/sbt/internal/PluginDiscovery.scala index fb82ea5e1..af2097298 100644 --- a/main/src/main/scala/sbt/internal/PluginDiscovery.scala +++ b/main/src/main/scala/sbt/internal/PluginDiscovery.scala @@ -65,7 +65,7 @@ object PluginDiscovery { new DiscoveredNames(discover[AutoPlugin], discover[BuildDef]) } - // TODO: for 0.14.0, consider consolidating into a single file, which would make the classpath search 4x faster + // TODO: consider consolidating into a single file, which would make the classpath search 4x faster /** Writes discovered module `names` to zero or more files in `dir` as per [[writeDescriptor]] and returns the list of files written. */ def writeDescriptors(names: DiscoveredNames, dir: File): Seq[File] = { import Paths._ From 29c15e4e2e98fb29f276f7c1ce258a4f69115eb3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 19 Dec 2017 15:19:12 +0000 Subject: [PATCH 023/120] Disable project/unique-settings-computation I've seen this fail Travis CI too many times. Here's two examples: * https://travis-ci.org/sbt/sbt/jobs/318632919 * https://travis-ci.org/sbt/sbt/jobs/313847550 --- .../project/unique-settings-computation/{pending => disabled} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sbt/src/sbt-test/project/unique-settings-computation/{pending => disabled} (100%) diff --git a/sbt/src/sbt-test/project/unique-settings-computation/pending b/sbt/src/sbt-test/project/unique-settings-computation/disabled similarity index 100% rename from sbt/src/sbt-test/project/unique-settings-computation/pending rename to sbt/src/sbt-test/project/unique-settings-computation/disabled From 6e09c660fe54ce674e6df26f1b423f1b6ef84990 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 20 Dec 2017 11:50:02 +0000 Subject: [PATCH 024/120] Replace Throwable catching with DeserializationException catching --- .../protocol/codec/JsonRpcRequestMessageFormats.scala | 4 ++-- .../protocol/codec/JsonRpcResponseMessageFormats.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcRequestMessageFormats.scala b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcRequestMessageFormats.scala index dac0987e4..e0e047cc6 100644 --- a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcRequestMessageFormats.scala +++ b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcRequestMessageFormats.scala @@ -8,7 +8,7 @@ package sbt.internal.protocol.codec import sjsonnew.shaded.scalajson.ast.unsafe.JValue -import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +import sjsonnew.{ Builder, DeserializationException, JsonFormat, Unbuilder, deserializationError } trait JsonRpcRequestMessageFormats { self: sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => @@ -24,7 +24,7 @@ trait JsonRpcRequestMessageFormats { val id = try { unbuilder.readField[String]("id") } catch { - case _: Throwable => unbuilder.readField[Long]("id").toString + case _: DeserializationException => unbuilder.readField[Long]("id").toString } val method = unbuilder.readField[String]("method") val params = unbuilder.lookupField("params") map { diff --git a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala index d10164e67..56cec6321 100644 --- a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala +++ b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala @@ -7,7 +7,7 @@ package sbt.internal.protocol.codec -import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +import sjsonnew.{ Builder, DeserializationException, JsonFormat, Unbuilder, deserializationError } import sjsonnew.shaded.scalajson.ast.unsafe.JValue trait JsonRpcResponseMessageFormats { @@ -27,7 +27,7 @@ trait JsonRpcResponseMessageFormats { val id = try { unbuilder.readField[Option[String]]("id") } catch { - case _: Throwable => unbuilder.readField[Option[Long]]("id") map { _.toString } + case _: DeserializationException => unbuilder.readField[Option[Long]]("id") map { _.toString } } val result = unbuilder.lookupField("result") map { From 4752084f91909d67ced6e649400c553f4d1cd654 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 21 Dec 2017 16:11:17 +0000 Subject: [PATCH 025/120] Introduce projectToLocalProject to replace projectToRef Fixes #3757 --- main/src/main/scala/sbt/Project.scala | 4 +++- notes/1.2.0/introduce-projectToLocalProject.md | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 notes/1.2.0/introduce-projectToLocalProject.md diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index c83c7a217..e2d99dac7 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -754,7 +754,9 @@ object Project extends ProjectExtra { EvaluateTask(extracted.structure, taskKey, state, extracted.currentRef, config) } - implicit def projectToRef(p: Project): ProjectReference = LocalProject(p.id) + def projectToRef(p: Project): ProjectReference = LocalProject(p.id) + + implicit def projectToLocalProject(p: Project): LocalProject = LocalProject(p.id) final class RichTaskSessionVar[S](i: Def.Initialize[Task[S]]) { import SessionVar.{ persistAndSet, resolveContext, set, transform => tx } diff --git a/notes/1.2.0/introduce-projectToLocalProject.md b/notes/1.2.0/introduce-projectToLocalProject.md new file mode 100644 index 000000000..8f856d3e8 --- /dev/null +++ b/notes/1.2.0/introduce-projectToLocalProject.md @@ -0,0 +1,12 @@ +[@dwijnand]: https://github.com/dwijnand + +[#3757]: https://github.com/sbt/sbt/issues/3757 +[#3836]: https://github.com/sbt/sbt/pull/3836 + +### Fixes with compatibility implications + +### Improvements + +- Introduces `projectToLocalProject` to replace `projectToRef`. [#3757][]/[#3836][] by [@dwijnand][] + +### Bug fixes From 7ff88a3e51cd19af845094a8fa0c613fe52bc75f Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Tue, 26 Dec 2017 16:32:29 +0900 Subject: [PATCH 026/120] delete buildinfo.BuildInfo from sbt main sbt-buildinfo plugin have `buildInfoScopedSettings(Compile)` in default. I think it is unnecessary. or we should set "buildinfoPackage in Compile" and "buildinfoObject in Compile" https://github.com/sbt/sbt-buildinfo/blob/v0.7.0/src/main/scala/sbtbuildinfo/BuildInfoPlugin.scala#L11 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index f9fe5937b..18413ff99 100644 --- a/build.sbt +++ b/build.sbt @@ -448,7 +448,6 @@ lazy val mainProj = (project in file("main")) // with the sole purpose of providing certain identifiers without qualification (with a package object) lazy val sbtProj = (project in file("sbt")) .dependsOn(mainProj, scriptedSbtProj % "test->test") - .enablePlugins(BuildInfoPlugin) .settings( baseSettings, name := "sbt", @@ -459,6 +458,7 @@ lazy val sbtProj = (project in file("sbt")) mimaSettings, mimaBinaryIssueFilters ++= sbtIgnoredProblems, addBuildInfoToConfig(Test), + BuildInfoPlugin.buildInfoDefaultSettings, buildInfoObject in Test := "TestBuildInfo", buildInfoKeys in Test := Seq[BuildInfoKey](fullClasspath in Compile), connectInput in run in Test := true, From afd214d4b0cfcd6b14c89857eed49e0c337ecca6 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Wed, 10 Jan 2018 12:48:07 +0900 Subject: [PATCH 027/120] update mimaPreviousArtifacts. add sbt 1.1.0 --- build.sbt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index acee0ff52..1c8ffd801 100644 --- a/build.sbt +++ b/build.sbt @@ -76,9 +76,11 @@ def testedBaseSettings: Seq[Setting[_]] = baseSettings ++ testDependencies val mimaSettings = Def settings ( - mimaPreviousArtifacts := (0 to 4).map { v => - organization.value % moduleName.value % s"1.0.$v" cross (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled) - }.toSet + mimaPreviousArtifacts := { + ((0 to 4).map(v => s"1.0.$v") ++ (0 to 0).map(v => s"1.1.$v")).map{ v => + organization.value % moduleName.value % v cross (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled) + }.toSet + } ) lazy val sbtRoot: Project = (project in file(".")) @@ -336,6 +338,7 @@ lazy val commandProj = (project in file("main-command")) contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats, mimaSettings, mimaBinaryIssueFilters ++= Vector( + exclude[DirectMissingMethodProblem]("sbt.BasicCommands.rebootOptionParser"), // Changed the signature of Server method. nacho cheese. exclude[DirectMissingMethodProblem]("sbt.internal.server.Server.*"), // Added method to ServerInstance. This is also internal. @@ -397,6 +400,9 @@ lazy val mainSettingsProj = (project in file("main-settings")) name := "Main Settings", resourceGenerators in Compile += generateToolboxClasspath.taskValue, mimaSettings, + mimaBinaryIssueFilters ++= Seq( + exclude[DirectMissingMethodProblem]("sbt.Scope.display012StyleMasked"), + ), ) .configure( addSbtIO, @@ -467,6 +473,9 @@ lazy val sbtProj = (project in file("sbt")) lazy val sbtIgnoredProblems = { Vector( + exclude[MissingClassProblem]("buildinfo.BuildInfo"), + exclude[MissingClassProblem]("buildinfo.BuildInfo$"), + // Added more items to Import trait. exclude[ReversedMissingMethodProblem]("sbt.Import.sbt$Import$_setter_$WatchSource_="), exclude[ReversedMissingMethodProblem]("sbt.Import.WatchSource"), From cb2042c2833477d7295266a7ff53b568b54a0740 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 10 Jan 2018 16:08:13 +0000 Subject: [PATCH 028/120] Upgrade to sbt 1.1.0 --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 394cb75cf..8b697bbb9 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.0.4 +sbt.version=1.1.0 From cdba3e6beab32d67617b0999dfcc7b495f19d878 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 11 Jan 2018 14:13:21 +0000 Subject: [PATCH 029/120] Add, configure & enforce sbt-whitesource --- .travis.yml | 8 +++++--- build.sbt | 9 +++++++++ project/plugins.sbt | 9 +++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4d33b9c23..a8a539237 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,10 @@ matrix: fast_finish: true env: + global: + - secure: d3bu2KNwsVHwfhbGgO+gmRfDKBJhfICdCJFGWKf2w3Gv86AJZX9nuTYRxz0KtdvEHO5Xw8WTBZLPb2thSJqhw9OCm4J8TBAVqCP0ruUj4+aqBUFy4bVexQ6WKE6nWHs4JPzPk8c6uC1LG3hMuzlC8RGETXtL/n81Ef1u7NjyXjs= matrix: - - SBT_CMD=";mimaReportBinaryIssues ;scalafmt::test ;test:scalafmt::test ;sbt:scalafmt::test ;headerCheck ;test:headerCheck ;test:compile ;mainSettingsProj/test ;safeUnitTests ;otherUnitTests" + - SBT_CMD=";mimaReportBinaryIssues ;scalafmt::test ;test:scalafmt::test ;sbt:scalafmt::test ;headerCheck ;test:headerCheck ;whitesourceCheckPolicies ;test:compile ;mainSettingsProj/test ;safeUnitTests ;otherUnitTests" - SBT_CMD="scripted actions/*" - SBT_CMD="scripted apiinfo/* compiler-project/* ivy-deps-management/*" - SBT_CMD="scripted dependency-management/*1of4" @@ -46,5 +48,5 @@ script: - sbt -J-XX:ReservedCodeCacheSize=128m -J-Xmx800M -J-Xms800M -J-server "$SBT_CMD" before_cache: - - find $HOME/.ivy2 -name "ivydata-*.properties" -print -delete - - find $HOME/.sbt -name "*.lock" -print -delete + - find $HOME/.ivy2 -name "ivydata-*.properties" -delete + - find $HOME/.sbt -name "*.lock" -delete diff --git a/build.sbt b/build.sbt index 1c8ffd801..2f4c132f8 100644 --- a/build.sbt +++ b/build.sbt @@ -707,3 +707,12 @@ def customCommands: Seq[Setting[_]] = Seq( state } ) + +inThisBuild(Seq( + whitesourceProduct := "Lightbend Reactive Platform", + whitesourceAggregateProjectName := "sbt-master", + whitesourceAggregateProjectToken := "e7a1e55518c0489a98e9c7430c8b2ccd53d9f97c12ed46148b592ebe4c8bf128", + whitesourceIgnoredScopes ++= Seq("plugin", "scalafmt", "sxr"), + whitesourceFailOnError := sys.env.contains("WHITESOURCE_PASSWORD"), // fail if pwd is present + whitesourceForceCheckAllDependencies := true, +)) diff --git a/project/plugins.sbt b/project/plugins.sbt index 7ea70f8ea..dfc4564bf 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,7 +1,8 @@ scalaVersion := "2.12.4" scalacOptions ++= Seq("-feature", "-language:postfixOps") -addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.5") -addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.2") -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2") -addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0") +addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.5") +addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.2") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2") +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0") +addSbtPlugin("com.lightbend" % "sbt-whitesource" % "0.1.9") From 113656aed18bd159bd62a5083f6d61c4f799ac98 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 16 Jan 2018 10:02:35 +0000 Subject: [PATCH 030/120] Remove compile warnings --- main-actions/src/main/scala/sbt/Package.scala | 4 +- main-actions/src/main/scala/sbt/Sync.scala | 10 +- .../sbt/internal/client/NetworkClient.scala | 1 + .../scala/sbt/internal/server/Server.scala | 2 +- .../main/scala/sbt/std/TaskLinterDSL.scala | 1 + main/src/main/scala/sbt/Defaults.scala | 4 +- main/src/main/scala/sbt/MainLoop.scala | 1 + main/src/main/scala/sbt/Resolvers.scala | 2 +- main/src/main/scala/sbt/TemplateCommand.scala | 4 +- .../scala/sbt/internal/CommandExchange.scala | 4 +- .../scala/sbt/internal/ConsoleProject.scala | 2 +- .../main/scala/sbt/internal/TaskTimings.scala | 2 +- .../sbt/internal/server/Definition.scala | 250 +++++++++--------- .../server/LanguageServerProtocol.scala | 3 + .../sbt/internal/server/NetworkChannel.scala | 1 + .../sbt/internal/server/DefinitionTest.scala | 25 +- .../codec/JsonRpcResponseMessageFormats.scala | 3 +- run/src/main/scala/sbt/Run.scala | 2 +- run/src/main/scala/sbt/TrapExit.scala | 4 +- .../src/main/scala/sbt/test/SbtHandler.scala | 1 + .../main/scala/sbt/CompletionService.scala | 4 +- .../scala/sbt/ConcurrentRestrictions.scala | 4 +- .../scala/sbt/JUnitXmlTestsListener.scala | 2 +- .../main/scala/sbt/TestStatusReporter.scala | 2 +- 24 files changed, 176 insertions(+), 162 deletions(-) diff --git a/main-actions/src/main/scala/sbt/Package.scala b/main-actions/src/main/scala/sbt/Package.scala index 3297d3b4a..080667e6a 100644 --- a/main-actions/src/main/scala/sbt/Package.scala +++ b/main-actions/src/main/scala/sbt/Package.scala @@ -85,8 +85,10 @@ object Package { } def setVersion(main: Attributes): Unit = { val version = Attributes.Name.MANIFEST_VERSION - if (main.getValue(version) eq null) + if (main.getValue(version) eq null) { main.put(version, "1.0") + () + } } def addSpecManifestAttributes(name: String, version: String, orgName: String): PackageOption = { import Attributes.Name._ diff --git a/main-actions/src/main/scala/sbt/Sync.scala b/main-actions/src/main/scala/sbt/Sync.scala index b5024f2ae..deec0ac34 100644 --- a/main-actions/src/main/scala/sbt/Sync.scala +++ b/main-actions/src/main/scala/sbt/Sync.scala @@ -71,11 +71,11 @@ object Sync { def copy(source: File, target: File): Unit = if (source.isFile) IO.copyFile(source, target, true) - else if (!target.exists) // we don't want to update the last modified time of an existing directory - { - IO.createDirectory(target) - IO.copyLastModified(source, target) - } + else if (!target.exists) { // we don't want to update the last modified time of an existing directory + IO.createDirectory(target) + IO.copyLastModified(source, target) + () + } def noDuplicateTargets(relation: Relation[File, File]): Unit = { val dups = relation.reverseMap diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index ff5c5661b..d3cd2dc0e 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -126,6 +126,7 @@ object NetworkClient { def run(arguments: List[String]): Unit = try { new NetworkClient(arguments) + () } catch { case NonFatal(e) => println(e.getMessage) } diff --git a/main-command/src/main/scala/sbt/internal/server/Server.scala b/main-command/src/main/scala/sbt/internal/server/Server.scala index 521c8d399..8104a1736 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -94,7 +94,7 @@ private[sbt] object Server { def tryClient(f: => Socket): Unit = { if (portfile.exists) { Try { f } match { - case Failure(e) => () + case Failure(_) => () case Success(socket) => socket.close() throw new AlreadyRunningException() diff --git a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala index 8ae98e8de..a328cee70 100644 --- a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala +++ b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala @@ -48,6 +48,7 @@ abstract class BaseTaskLinterDSL extends LinterDSL { case _ => exprAtUseSite } uncheckedWrappers.add(removedSbtWrapper) + () } case _ => } diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 0b82ce8ea..5c030829f 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -500,7 +500,7 @@ object Defaults extends BuildCommon { }, compileIncSetup := compileIncSetupTask.value, console := consoleTask.value, - collectAnalyses := Definition.collectAnalysesTask.value, + collectAnalyses := Definition.collectAnalysesTask.map(_ => ()).value, consoleQuick := consoleQuickTask.value, discoveredMainClasses := (compile map discoverMainClasses storeAs discoveredMainClasses xtriggeredBy compile).value, discoveredSbtPlugins := discoverSbtPluginNames.value, @@ -1373,7 +1373,7 @@ object Defaults extends BuildCommon { private[this] def exported(w: PrintWriter, command: String): Seq[String] => Unit = args => w.println((command +: args).mkString(" ")) - private[this] def exported(s: TaskStreams, command: String): Seq[String] => Unit = args => { + private[this] def exported(s: TaskStreams, command: String): Seq[String] => Unit = { val w = s.text(ExportStream) try exported(w, command) finally w.close() // workaround for #937 diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index 83b38b4c8..110a78fa4 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -34,6 +34,7 @@ object MainLoop { runLoggedLoop(state, state.globalLogging.backing) } finally { Runtime.getRuntime.removeShutdownHook(shutdownHook) + () } } diff --git a/main/src/main/scala/sbt/Resolvers.scala b/main/src/main/scala/sbt/Resolvers.scala index c574c55aa..36afc0217 100644 --- a/main/src/main/scala/sbt/Resolvers.scala +++ b/main/src/main/scala/sbt/Resolvers.scala @@ -40,7 +40,7 @@ object Resolvers { val to = uniqueSubdirectoryFor(info.uri, in = info.staging) Some { () => - creates(to) { IO.unzipURL(url, to) } + creates(to) { IO.unzipURL(url, to); () } } } diff --git a/main/src/main/scala/sbt/TemplateCommand.scala b/main/src/main/scala/sbt/TemplateCommand.scala index e02b1a6a2..51f67bb17 100644 --- a/main/src/main/scala/sbt/TemplateCommand.scala +++ b/main/src/main/scala/sbt/TemplateCommand.scala @@ -75,8 +75,10 @@ private[sbt] object TemplateCommandUtil { private def runTemplate(info: TemplateResolverInfo, arguments: List[String], - loader: ClassLoader): Unit = + loader: ClassLoader): Unit = { call(info.implementationClass, "run", loader)(classOf[Array[String]])(arguments.toArray) + () + } private def infoLoader( info: TemplateResolverInfo, diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 33fbfa986..2ce2819fa 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -156,9 +156,9 @@ private[sbt] final class CommandExchange { Try(Await.ready(x.ready, Duration(d))) x.ready.value match { case Some(Success(_)) => - // rememeber to shutdown only when the server comes up + // remember to shutdown only when the server comes up server = Some(x) - case Some(Failure(e: AlreadyRunningException)) => + case Some(Failure(_: AlreadyRunningException)) => s.log.warn( "sbt server could not start because there's another instance of sbt running on this build.") s.log.warn("Running multiple instances is unsupported") diff --git a/main/src/main/scala/sbt/internal/ConsoleProject.scala b/main/src/main/scala/sbt/internal/ConsoleProject.scala index bc3756460..866d4bd5e 100644 --- a/main/src/main/scala/sbt/internal/ConsoleProject.scala +++ b/main/src/main/scala/sbt/internal/ConsoleProject.scala @@ -48,7 +48,7 @@ object ConsoleProject { options, initCommands, cleanupCommands - )(Some(unit.loader), bindings) + )(Some(unit.loader), bindings).get } /** Conveniences for consoleProject that shouldn't normally be used for builds. */ diff --git a/main/src/main/scala/sbt/internal/TaskTimings.scala b/main/src/main/scala/sbt/internal/TaskTimings.scala index 51ccfcaa1..3586eb4b1 100644 --- a/main/src/main/scala/sbt/internal/TaskTimings.scala +++ b/main/src/main/scala/sbt/internal/TaskTimings.scala @@ -61,7 +61,7 @@ private[sbt] final class TaskTimings(shutdown: Boolean) extends ExecuteProgress[ } } def ready(state: Unit, task: Task[_]) = () - def workStarting(task: Task[_]) = timings.put(task, System.nanoTime) + def workStarting(task: Task[_]) = { timings.put(task, System.nanoTime); () } def workFinished[T](task: Task[T], result: Either[Task[T], Result[T]]) = { timings.put(task, System.nanoTime - timings.get(task)) result.left.foreach { t => diff --git a/main/src/main/scala/sbt/internal/server/Definition.scala b/main/src/main/scala/sbt/internal/server/Definition.scala index 01881b614..bc6a0a220 100644 --- a/main/src/main/scala/sbt/internal/server/Definition.scala +++ b/main/src/main/scala/sbt/internal/server/Definition.scala @@ -9,38 +9,47 @@ package sbt package internal package server -import sbt.io.IO -import sbt.internal.inc.MixedAnalyzingCompiler -import sbt.internal.langserver.ErrorCodes -import sbt.util.Logger +import java.io.File +import java.net.URI +import java.nio.file._ + import scala.annotation.tailrec +import scala.collection.JavaConverters._ import scala.concurrent.{ ExecutionContext, Future } -import scala.concurrent.duration.Duration.Inf -import scala.util.matching.Regex.MatchIterator -import java.nio.file.{ Files, Paths } -import sbt.StandardMain +import scala.concurrent.duration.Duration +import scala.reflect.NameTransformer +import scala.tools.reflect.{ ToolBox, ToolBoxError } +import scala.util.matching.Regex + +import sjsonnew.JsonFormat +import sjsonnew.shaded.scalajson.ast.unsafe.JValue +import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter } + +import scalacache._ + +import sbt.io.IO +import sbt.internal.inc.{ Analysis, MixedAnalyzingCompiler } +import sbt.internal.inc.JavaInterfaceUtil._ +import sbt.internal.protocol.JsonRpcResponseError +import sbt.internal.protocol.codec.JsonRPCProtocol +import sbt.internal.langserver +import sbt.internal.langserver.{ ErrorCodes, Location, Position, Range, TextDocumentPositionParams } +import sbt.util.Logger +import sbt.Keys._ private[sbt] object Definition { - import java.net.URI - import Keys._ - import sbt.internal.inc.Analysis - import sbt.internal.inc.JavaInterfaceUtil._ - val AnalysesKey = "lsp.definition.analyses.key" - - import sjsonnew.JsonFormat def send[A: JsonFormat](source: CommandSource, execId: String)(params: A): Unit = { for { channel <- StandardMain.exchange.channels.collectFirst { case c if c.name == source.channelName => c } - } yield { + } { channel.publishEvent(params, Option(execId)) } } object textProcessor { private val isIdentifier = { - import scala.tools.reflect.{ ToolBox, ToolBoxError } lazy val tb = scala.reflect.runtime.universe .runtimeMirror(this.getClass.getClassLoader) @@ -58,23 +67,14 @@ private[sbt] object Definition { private def findInBackticks(line: String, point: Int): Option[String] = { val (even, odd) = line.zipWithIndex - .collect { - case (char, backtickIndex) if char == '`' => - backtickIndex - } + .collect { case (char, backtickIndex) if char == '`' => backtickIndex } .zipWithIndex - .partition { bs => - val (_, index) = bs - index % 2 == 0 - } + .partition { case (_, index) => index % 2 == 0 } + even - .collect { - case (backtickIndex, _) => backtickIndex - } + .collect { case (backtickIndex, _) => backtickIndex } .zip { - odd.collect { - case (backtickIndex, _) => backtickIndex + 1 - } + odd.collect { case (backtickIndex, _) => backtickIndex + 1 } } .collectFirst { case (from, to) if from <= point && point < to => line.slice(from, to) @@ -83,43 +83,43 @@ private[sbt] object Definition { def identifier(line: String, point: Int): Option[String] = findInBackticks(line, point).orElse { val whiteSpaceReg = "(\\s|\\.)+".r + val (zero, end) = fold(Seq.empty)(whiteSpaceReg.findAllIn(line)) .collect { case (white, ind) => (ind, ind + white.length) } - .fold((0, line.length)) { (z, e) => - val (from, to) = e - val (left, right) = z - (if (to > left && to <= point) to else left, - if (from < right && from >= point) from else right) + .fold((0, line.length)) { + case ((left, right), (from, to)) => + val zero = if (to > left && to <= point) to else left + val end = if (from < right && from >= point) from else right + (zero, end) } + val ranges = for { from <- zero to point to <- point to end } yield (from -> to) + ranges - .sortBy { - case (from, to) => -(to - from) - } - .foldLeft(Seq.empty[String]) { (z, e) => - val (from, to) = e - val fragment = line.slice(from, to).trim - z match { - case Nil if fragment.nonEmpty && isIdentifier(fragment) => fragment +: z - case h +: _ if h.length < fragment.length && isIdentifier(fragment) => - Seq(fragment) - case h +: _ if h.length == fragment.length && isIdentifier(fragment) => - fragment +: z - case z => z - } + .sortBy { case (from, to) => -(to - from) } + .foldLeft(List.empty[String]) { + case (z, (from, to)) => + val fragment = line.slice(from, to).trim + if (isIdentifier(fragment)) + z match { + case Nil if fragment.nonEmpty => fragment :: z + case h :: _ if h.length < fragment.length => fragment :: Nil + case h :: _ if h.length == fragment.length => fragment :: z + case _ => z + } else z } .headOption } private def asClassObjectIdentifier(sym: String) = Seq(s".$sym", s".$sym$$", s"$$$sym", s"$$$sym$$") + def potentialClsOrTraitOrObj(sym: String): PartialFunction[String, String] = { - import scala.reflect.NameTransformer val encodedSym = NameTransformer.encode(sym.toSeq match { case '`' +: body :+ '`' => body.mkString case noBackticked => noBackticked.mkString @@ -135,17 +135,17 @@ private[sbt] object Definition { } @tailrec - private def fold(z: Seq[(String, Int)])(it: MatchIterator): Seq[(String, Int)] = { + private def fold(z: Seq[(String, Int)])(it: Regex.MatchIterator): Seq[(String, Int)] = { if (!it.hasNext) z else fold(z :+ (it.next() -> it.start))(it) } def classTraitObjectInLine(sym: String)(line: String): Seq[(String, Int)] = { - import scala.util.matching.Regex.quote - val potentials = - Seq(s"object\\s+${quote(sym)}".r, - s"trait\\s+${quote(sym)} *\\[?".r, - s"class\\s+${quote(sym)} *\\[?".r) + val potentials = Seq( + s"object\\s+${Regex quote sym}".r, + s"trait\\s+${Regex quote sym} *\\[?".r, + s"class\\s+${Regex quote sym} *\\[?".r, + ) potentials .flatMap { reg => fold(Seq.empty)(reg.findAllIn(line)) @@ -156,10 +156,7 @@ private[sbt] object Definition { } } - import java.io.File def markPosition(file: File, sym: String): Seq[(File, Long, Long, Long)] = { - import java.nio.file._ - import scala.collection.JavaConverters._ val findInLine = classTraitObjectInLine(sym)(_) Files .lines(file.toPath) @@ -179,43 +176,49 @@ private[sbt] object Definition { } } - import sbt.internal.langserver.TextDocumentPositionParams - import sjsonnew.shaded.scalajson.ast.unsafe.JValue private def getDefinition(jsonDefinition: JValue): Option[TextDocumentPositionParams] = { - import sbt.internal.langserver.codec.JsonProtocol._ - import sjsonnew.support.scalajson.unsafe.Converter + import langserver.codec.JsonProtocol._ Converter.fromJson[TextDocumentPositionParams](jsonDefinition).toOption } - import java.io.File + object AnalysesAccess { + private[this] val AnalysesKey = "lsp.definition.analyses.key" + + private[server] type Analyses = Set[((String, Boolean), Option[Analysis])] + + private[server] def getFrom[F[_]]( + cache: Cache[Any] + )(implicit mode: Mode[F], flags: Flags): F[Option[Analyses]] = + mode.M.map(cache.get(AnalysesKey))(_ map (_.asInstanceOf[Analyses])) + + private[server] def putIn[F[_]]( + cache: Cache[Any], + value: Analyses, + ttl: Option[Duration], + )(implicit mode: Mode[F], flags: Flags): F[Any] = + cache.put(AnalysesKey)(value, ttl) + } + private def storeAnalysis(cacheFile: File, useBinary: Boolean): Option[Analysis] = MixedAnalyzingCompiler .staticCachedStore(cacheFile, !useBinary) .get .toOption - .collect { - case contents => - contents.getAnalysis - } - .collect { - case a: Analysis => a - } + .map { _.getAnalysis } + .collect { case a: Analysis => a } - import scalacache._ private[sbt] def updateCache[F[_]](cache: Cache[Any])(cacheFile: String, useBinary: Boolean)( implicit mode: Mode[F], flags: Flags): F[Any] = { - mode.M.flatMap(cache.get(AnalysesKey)) { + mode.M.flatMap(AnalysesAccess.getFrom(cache)) { case None => - cache.put(AnalysesKey)(Set(cacheFile -> useBinary -> None), Option(Inf)) + AnalysesAccess.putIn(cache, Set(cacheFile -> useBinary -> None), Option(Duration.Inf)) case Some(set) => - cache.put(AnalysesKey)( - set.asInstanceOf[Set[((String, Boolean), Option[Analysis])]].filterNot { - case ((file, _), _) => file == cacheFile - } + (cacheFile -> useBinary -> None), - Option(Inf)) - case _ => mode.M.pure(()) + val newSet = set + .filterNot { case ((file, _), _) => file == cacheFile } + .+(cacheFile -> useBinary -> None) + AnalysesAccess.putIn(cache, newSet, Option(Duration.Inf)) } } @@ -231,14 +234,13 @@ private[sbt] object Definition { private[sbt] def getAnalyses: Future[Seq[Analysis]] = { import scalacache.modes.scalaFuture._ import scala.concurrent.ExecutionContext.Implicits.global - StandardMain.cache - .get(AnalysesKey) - .collect { - case Some(a) => a.asInstanceOf[Set[((String, Boolean), Option[Analysis])]] - } + AnalysesAccess + .getFrom(StandardMain.cache) + .collect { case Some(a) => a } .map { caches => - val (working, uninitialized) = caches.partition { cacheAnalysis => - cacheAnalysis._2 != None + val (working, uninitialized) = caches.partition { + case (_, Some(_)) => true + case (_, None) => false } val addToCache = uninitialized.collect { case (title @ (file, useBinary), _) if Files.exists(Paths.get(file)) => @@ -246,7 +248,7 @@ private[sbt] object Definition { } val validCaches = working ++ addToCache if (addToCache.nonEmpty) - StandardMain.cache.put(AnalysesKey)(validCaches, Option(Inf)) + AnalysesAccess.putIn(StandardMain.cache, validCaches, Option(Duration.Inf)) validCaches.toSeq.collect { case (_, Some(analysis)) => analysis @@ -254,19 +256,19 @@ private[sbt] object Definition { } } - def lspDefinition(jsonDefinition: JValue, - requestId: String, - commandSource: CommandSource, - log: Logger)(implicit ec: ExecutionContext): Future[Unit] = Future { + def lspDefinition( + jsonDefinition: JValue, + requestId: String, + commandSource: CommandSource, + log: Logger, + )(implicit ec: ExecutionContext): Future[Unit] = Future { val LspDefinitionLogHead = "lsp-definition" - import sjsonnew.support.scalajson.unsafe.CompactPrinter - log.debug(s"$LspDefinitionLogHead json request: ${CompactPrinter(jsonDefinition)}") + val jsonDefinitionString = CompactPrinter(jsonDefinition) + log.debug(s"$LspDefinitionLogHead json request: $jsonDefinitionString") lazy val analyses = getAnalyses - val definition = getDefinition(jsonDefinition) - definition + getDefinition(jsonDefinition) .flatMap { definition => val uri = URI.create(definition.textDocument.uri) - import java.nio.file._ Files .lines(Paths.get(uri)) .skip(definition.position.line) @@ -274,11 +276,10 @@ private[sbt] object Definition { .toOption .flatMap { line => log.debug(s"$LspDefinitionLogHead found line: $line") - textProcessor - .identifier(line, definition.position.character.toInt) + textProcessor.identifier(line, definition.position.character.toInt) } - } - .map { sym => + } match { + case Some(sym) => log.debug(s"symbol $sym") analyses .map { analyses => @@ -291,40 +292,39 @@ private[sbt] object Definition { log.debug(s"$LspDefinitionLogHead potentials: $classes") classes .flatMap { className => - analysis.relations.definesClass(className) ++ analysis.relations - .libraryDefinesClass(className) + analysis.relations.definesClass(className) ++ + analysis.relations.libraryDefinesClass(className) } .flatMap { classFile => textProcessor.markPosition(classFile, sym).collect { case (file, line, from, to) => - import sbt.internal.langserver.{ Location, Position, Range } - Location(IO.toURI(file).toString, - Range(Position(line, from), Position(line, to))) + Location( + IO.toURI(file).toString, + Range(Position(line, from), Position(line, to)), + ) } } }.seq - log.debug(s"$LspDefinitionLogHead locations ${locations}") - import sbt.internal.langserver.codec.JsonProtocol._ + log.debug(s"$LspDefinitionLogHead locations $locations") + import langserver.codec.JsonProtocol._ send(commandSource, requestId)(locations.toArray) } .recover { - case anyException @ _ => - log.warn( - s"Problem with processing analyses $anyException for ${CompactPrinter(jsonDefinition)}") - import sbt.internal.protocol.JsonRpcResponseError - import sbt.internal.protocol.codec.JsonRPCProtocol._ - send(commandSource, requestId)( - JsonRpcResponseError(ErrorCodes.InternalError, - "Problem with processing analyses.", - None)) + case t => + log.warn(s"Problem with processing analyses $t for $jsonDefinitionString") + val rsp = JsonRpcResponseError( + ErrorCodes.InternalError, + "Problem with processing analyses.", + None, + ) + import JsonRPCProtocol._ + send(commandSource, requestId)(rsp) } - } - .orElse { - log.info(s"Symbol not found in definition request ${CompactPrinter(jsonDefinition)}") - import sbt.internal.langserver.Location - import sbt.internal.langserver.codec.JsonProtocol._ + () + case None => + log.info(s"Symbol not found in definition request $jsonDefinitionString") + import langserver.codec.JsonProtocol._ send(commandSource, requestId)(Array.empty[Location]) - None - } + } } } diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index ce1131d0f..637bd97d0 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -39,6 +39,7 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { notification.method match { case "textDocument/didSave" => append(Exec(";compile; collectAnalyses", None, Some(CommandSource(name)))) + () case u => log.debug(s"Unhandled notification received: $u") } } @@ -69,9 +70,11 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { case "textDocument/definition" => import scala.concurrent.ExecutionContext.Implicits.global Definition.lspDefinition(json, request.id, CommandSource(name), log) + () case "sbt/exec" => val param = Converter.fromJson[SbtExecParams](json).get append(Exec(param.commandLine, Some(request.id), Some(CommandSource(name)))) + () case "sbt/setting" => { import sbt.protocol.codec.JsonProtocol._ val param = Converter.fromJson[Q](json).get diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 7c27063de..3ad7f2fc5 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -331,6 +331,7 @@ final class NetworkChannel(val name: String, if (initialized) { append( Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name)))) + () } else { log.warn(s"ignoring command $cmd before initialization") } diff --git a/main/src/test/scala/sbt/internal/server/DefinitionTest.scala b/main/src/test/scala/sbt/internal/server/DefinitionTest.scala index 534d77083..5cbff9163 100644 --- a/main/src/test/scala/sbt/internal/server/DefinitionTest.scala +++ b/main/src/test/scala/sbt/internal/server/DefinitionTest.scala @@ -9,8 +9,6 @@ package sbt package internal package server -import sbt.internal.inc.Analysis - class DefinitionTest extends org.specs2.mutable.Specification { import Definition.textProcessor @@ -126,9 +124,12 @@ class DefinitionTest extends org.specs2.mutable.Specification { textProcessor.classTraitObjectInLine("B")("trait A ") must be empty } } + "definition" should { + import scalacache.caffeine._ import scalacache.modes.sync._ + "cache data in cache" in { val cache = CaffeineCache[Any] val cacheFile = "Test.scala" @@ -136,12 +137,11 @@ class DefinitionTest extends org.specs2.mutable.Specification { Definition.updateCache(cache)(cacheFile, useBinary) - val actual = cache.get(Definition.AnalysesKey) + val actual = Definition.AnalysesAccess.getFrom(cache) - actual.collect { - case s => s.asInstanceOf[Set[((String, Boolean), Option[Analysis])]] - }.get should contain("Test.scala" -> true -> None) + actual.get should contain("Test.scala" -> true -> None) } + "replace cache data in cache" in { val cache = CaffeineCache[Any] val cacheFile = "Test.scala" @@ -151,12 +151,11 @@ class DefinitionTest extends org.specs2.mutable.Specification { Definition.updateCache(cache)(cacheFile, falseUseBinary) Definition.updateCache(cache)(cacheFile, useBinary) - val actual = cache.get(Definition.AnalysesKey) + val actual = Definition.AnalysesAccess.getFrom(cache) - actual.collect { - case s => s.asInstanceOf[Set[((String, Boolean), Option[Analysis])]] - }.get should contain("Test.scala" -> true -> None) + actual.get should contain("Test.scala" -> true -> None) } + "cache more data in cache" in { val cache = CaffeineCache[Any] val cacheFile = "Test.scala" @@ -167,11 +166,9 @@ class DefinitionTest extends org.specs2.mutable.Specification { Definition.updateCache(cache)(otherCacheFile, otherUseBinary) Definition.updateCache(cache)(cacheFile, useBinary) - val actual = cache.get(Definition.AnalysesKey) + val actual = Definition.AnalysesAccess.getFrom(cache) - actual.collect { - case s => s.asInstanceOf[Set[((String, Boolean), Option[Analysis])]] - }.get should contain("Test.scala" -> true -> None, "OtherTest.scala" -> false -> None) + actual.get should contain("Test.scala" -> true -> None, "OtherTest.scala" -> false -> None) } } } diff --git a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala index 56cec6321..8367ea906 100644 --- a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala +++ b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala @@ -27,7 +27,8 @@ trait JsonRpcResponseMessageFormats { val id = try { unbuilder.readField[Option[String]]("id") } catch { - case _: DeserializationException => unbuilder.readField[Option[Long]]("id") map { _.toString } + case _: DeserializationException => + unbuilder.readField[Option[Long]]("id") map { _.toString } } val result = unbuilder.lookupField("result") map { diff --git a/run/src/main/scala/sbt/Run.scala b/run/src/main/scala/sbt/Run.scala index ff5426b6d..15469f86e 100644 --- a/run/src/main/scala/sbt/Run.scala +++ b/run/src/main/scala/sbt/Run.scala @@ -90,7 +90,7 @@ class Run(instance: ScalaInstance, trapExit: Boolean, nativeTmp: File) extends S val currentThread = Thread.currentThread val oldLoader = Thread.currentThread.getContextClassLoader currentThread.setContextClassLoader(loader) - try { main.invoke(null, options.toArray[String]) } finally { + try { main.invoke(null, options.toArray[String]); () } finally { currentThread.setContextClassLoader(oldLoader) } } diff --git a/run/src/main/scala/sbt/TrapExit.scala b/run/src/main/scala/sbt/TrapExit.scala index d1a16ef52..54cf83760 100644 --- a/run/src/main/scala/sbt/TrapExit.scala +++ b/run/src/main/scala/sbt/TrapExit.scala @@ -152,7 +152,7 @@ private final class TrapExit(delegateManager: SecurityManager) extends SecurityM def runManaged(f: Supplier[Unit], xlog: xsbti.Logger): Int = { val _ = running.incrementAndGet() try runManaged0(f, xlog) - finally running.decrementAndGet() + finally { running.decrementAndGet(); () } } private[this] def runManaged0(f: Supplier[Unit], xlog: xsbti.Logger): Int = { val log: Logger = xlog @@ -264,6 +264,7 @@ private final class TrapExit(delegateManager: SecurityManager) extends SecurityM val old = groups.putIfAbsent(groupID, new WeakReference(g)) if (old.isEmpty) { // wasn't registered threadToApp.put(groupID, this) + () } } @@ -299,6 +300,7 @@ private final class TrapExit(delegateManager: SecurityManager) extends SecurityM threadToApp.remove(id) threads.remove(id) groups.remove(id) + () } /** Final cleanup for this application after it has terminated. */ diff --git a/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala b/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala index 04b85e572..12598df78 100644 --- a/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala +++ b/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala @@ -65,6 +65,7 @@ final class SbtHandler(directory: File, try { send("exit", server) process.exitValue() + () } catch { case _: IOException => process.destroy() } diff --git a/tasks/src/main/scala/sbt/CompletionService.scala b/tasks/src/main/scala/sbt/CompletionService.scala index f6fc3a957..f88f6885a 100644 --- a/tasks/src/main/scala/sbt/CompletionService.scala +++ b/tasks/src/main/scala/sbt/CompletionService.scala @@ -23,13 +23,13 @@ import java.util.concurrent.{ object CompletionService { def apply[A, T](poolSize: Int): (CompletionService[A, T], () => Unit) = { val pool = Executors.newFixedThreadPool(poolSize) - (apply[A, T](pool), () => pool.shutdownNow()) + (apply[A, T](pool), () => { pool.shutdownNow(); () }) } def apply[A, T](x: Executor): CompletionService[A, T] = apply(new ExecutorCompletionService[T](x)) def apply[A, T](completion: JCompletionService[T]): CompletionService[A, T] = new CompletionService[A, T] { - def submit(node: A, work: () => T) = CompletionService.submit(work, completion) + def submit(node: A, work: () => T) = { CompletionService.submit(work, completion); () } def take() = completion.take().get() } def submit[T](work: () => T, completion: JCompletionService[T]): () => T = { diff --git a/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala b/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala index 40a15d4fd..a827c139a 100644 --- a/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala +++ b/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala @@ -128,7 +128,7 @@ object ConcurrentRestrictions { def completionService[A, R](tags: ConcurrentRestrictions[A], warn: String => Unit): (CompletionService[A, R], () => Unit) = { val pool = Executors.newCachedThreadPool() - (completionService[A, R](pool, tags, warn), () => pool.shutdownNow()) + (completionService[A, R](pool, tags, warn), () => { pool.shutdownNow(); () }) } /** @@ -167,6 +167,7 @@ object ConcurrentRestrictions { if (running == 0) errorAddingToIdle() pending.add(new Enqueue(node, work)) } + () } private[this] def submitValid(node: A, work: () => R) = { running += 1 @@ -192,6 +193,7 @@ object ConcurrentRestrictions { if (!tried.isEmpty) { if (running == 0) errorAddingToIdle() pending.addAll(tried) + () } } else { val next = pending.remove() diff --git a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala index 3e62dcea2..ad547c511 100644 --- a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala +++ b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala @@ -127,7 +127,7 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { val testSuite = new DynamicVariable(null: TestSuite) /**Creates the output Dir*/ - override def doInit() = { targetDir.mkdirs() } + override def doInit() = { targetDir.mkdirs(); () } /** * Starts a new, initially empty Suite with the given name. diff --git a/testing/src/main/scala/sbt/TestStatusReporter.scala b/testing/src/main/scala/sbt/TestStatusReporter.scala index 732e3257c..ab248741f 100644 --- a/testing/src/main/scala/sbt/TestStatusReporter.scala +++ b/testing/src/main/scala/sbt/TestStatusReporter.scala @@ -18,7 +18,7 @@ private[sbt] class TestStatusReporter(f: File) extends TestsListener { private lazy val succeeded = TestStatus.read(f) def doInit = () - def startGroup(name: String): Unit = { succeeded remove name } + def startGroup(name: String): Unit = { succeeded remove name; () } def testEvent(event: TestEvent): Unit = () def endGroup(name: String, t: Throwable): Unit = () def endGroup(name: String, result: TestResult): Unit = { From c00739ebcdec7a6f469940e885285b5944ab05aa Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 16 Jan 2018 14:38:09 +0000 Subject: [PATCH 031/120] Fix sbtOn's prompt & echo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is what it looks like now! 🎉 > sbtOn /s/t-sbtOn [...] [info] Running (fork) sbt.RunFromSourceMain /s/t-sbtOn Listening for transport dt_socket at address: 5005 [warn] sbt version mismatch, current: 1.0.3, in build.properties: "1.1.0", use 'reboot' to use the new value. [info] Loading settings from idea.sbt,global-plugins.sbt ... [info] Loading global plugins from /Users/dnw/.dotfiles/.sbt/1.0/plugins [info] Updating ProjectRef(uri("file:/Users/dnw/.sbt/1.0/plugins/"), "global-plugins")... [info] Done updating. [info] Loading settings from plugins.sbt ... [info] Loading project definition from /s/t-sbtOn/project [info] Updating ProjectRef(uri("file:/s/t-sbtOn/project/"), "t-sbton-build")... [info] Done updating. [info] Loading settings from build.sbt ... [info] Set current project to t (in build file:/s/t-sbtOn/) [info] sbt server started at local:///Users/dnw/.sbt/1.0/server/2c27eaf4c750902a3a41/sock > show baseDirectory [info] /s/t-sbtOn > exit [info] shutting down server [success] Total time: 34 s, completed 16-Jan-2018 14:37:32 > Exception in thread "Thread-17" java.io.IOException: Stream closed at java.lang.ProcessBuilder$NullOutputStream.write(ProcessBuilder.java:433) at java.io.OutputStream.write(OutputStream.java:116) at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) at java.io.FilterOutputStream.close(FilterOutputStream.java:158) at scala.sys.process.BasicIO$.$anonfun$input$1(BasicIO.scala:200) at scala.sys.process.BasicIO$.$anonfun$input$1$adapted(BasicIO.scala:198) at scala.sys.process.ProcessBuilderImpl$Simple.$anonfun$run$2(ProcessBuilderImpl.scala:75) at scala.sys.process.ProcessImpl$Spawn$$anon$1.run(ProcessImpl.scala:23) > show {.}/baseDirectory [...] [info] ThisBuild / baseDirectory [info] /d/sbt --- build.sbt | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sbt b/build.sbt index 2f4c132f8..b9d11c0d6 100644 --- a/build.sbt +++ b/build.sbt @@ -468,6 +468,7 @@ lazy val sbtProj = (project in file("sbt")) buildInfoObject in Test := "TestBuildInfo", buildInfoKeys in Test := Seq[BuildInfoKey](fullClasspath in Compile), connectInput in run in Test := true, + outputStrategy in run in Test := Some(StdoutOutput), ) .configure(addSbtCompilerBridge) From b2290658ba366203d9b2aa6e0845690e00f6d1fd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 16 Jan 2018 17:04:53 +0000 Subject: [PATCH 032/120] Tweak RunFromSourceMain so compile/run/test work Tested pos/neg compilation of a simple hello world file, running said file & a simple uTest test suite. Uses things already downloaded in ~/.ivy2/cache, & shares ~/.sbt/boot for the compiler-bridge component. --- .../test/scala/sbt/RunFromSourceMain.scala | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/sbt/src/test/scala/sbt/RunFromSourceMain.scala b/sbt/src/test/scala/sbt/RunFromSourceMain.scala index be79cc54a..bd2f142f7 100644 --- a/sbt/src/test/scala/sbt/RunFromSourceMain.scala +++ b/sbt/src/test/scala/sbt/RunFromSourceMain.scala @@ -12,7 +12,7 @@ import scala.annotation.tailrec import xsbti._ object RunFromSourceMain { - private val sbtVersion = "1.0.3" // "dev" + private val sbtVersion = "1.1.0" // "dev" private val scalaVersion = "2.12.4" def main(args: Array[String]): Unit = args match { @@ -51,6 +51,8 @@ object RunFromSourceMain { def baseDirectory = baseDir def arguments = args.toArray def provider = new AppProvider { appProvider => + def bootDirectory: File = file(sys.props("user.home")) / ".sbt" / "boot" + def scalaHome: File = bootDirectory / s"scala-$scalaVersion" def scalaProvider = new ScalaProvider { scalaProvider => def scalaOrg = "org.scala-lang" def launcher = new Launcher { @@ -60,15 +62,15 @@ object RunFromSourceMain { def app(id: xsbti.ApplicationID, version: String) = appProvider def topLoader = new java.net.URLClassLoader(Array(), null) def globalLock = noGlobalLock - def bootDirectory = file(sys.props("user.home")) / ".sbt" / "boot" - def ivyRepositories = Array() - def appRepositories = Array() - def isOverrideRepositories = false + def bootDirectory = appProvider.bootDirectory def ivyHome = file(sys.props("user.home")) / ".ivy2" + def ivyRepositories = Array(new PredefinedRepository { def id() = Predefined.Local }) + def appRepositories = Array(new PredefinedRepository { def id() = Predefined.Local }) + def isOverrideRepositories = false def checksums = Array("sha1", "md5") } def version = scalaVersion - def libDir: File = launcher.bootDirectory / s"scala-$version" / "lib" + def libDir: File = scalaHome / "lib" def jar(name: String): File = libDir / s"$name.jar" def libraryJar = jar("scala-library") def compilerJar = jar("scala-compiler") @@ -86,6 +88,7 @@ object RunFromSourceMain { CrossValue.Disabled, Nil ) + def appHome: File = scalaHome / id.groupID / id.name / id.version def mainClasspath = buildinfo.TestBuildInfo.fullClasspath.iterator @@ -98,11 +101,11 @@ object RunFromSourceMain { def newMain = new xMain def components = new ComponentProvider { - def componentLocation(id: String) = ??? - def component(componentID: String) = ??? - def defineComponent(componentID: String, components: Array[File]) = ??? - def addToComponent(componentID: String, components: Array[File]) = ??? - def lockFile = ??? + def componentLocation(id: String) = appHome / id + def component(id: String) = IO.listFiles(componentLocation(id), _.isFile) + def defineComponent(id: String, components: Array[File]) = () + def addToComponent(id: String, components: Array[File]) = false + def lockFile = null } } } From 12d2ecfa6294053de525597bf249f83c6e60eaa5 Mon Sep 17 00:00:00 2001 From: jvican Date: Fri, 20 Oct 2017 23:37:55 +0200 Subject: [PATCH 033/120] Enable parallel execution of scripted in the plugin The change to enable batched and parallel execution for scripted was done only for the scripted-sbt project. This pull request enables it for scripted-plugin, so that all sbt plugins in 1.x. can benefit from it. By default, it configures a number of parallel instances of 1 and batch execution is disabled. Users can change the number of parallel sbt hosts running scripted tests via the `scriptedParallelInstances` setting. In some plugins scripted tests', batch execution can cause issues because the first time `>` commands are executed they assume sbt starts up. This error can be fixed by doing `reload` before running the `>` command. Note that the current scripted plugin does not allow parallel execution in non-batched mode. --- .../src/main/scala/sbt/ScriptedPlugin.scala | 49 ++++++++++++------- .../main/scala/sbt/test/ScriptedTests.scala | 19 ++++++- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala index 4788e0186..a7bfdc907 100644 --- a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala +++ b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala @@ -26,6 +26,11 @@ object ScriptedPlugin extends AutoPlugin { val scriptedBufferLog = SettingKey[Boolean]("scripted-buffer-log") val scriptedClasspath = TaskKey[PathFinder]("scripted-classpath") val scriptedTests = TaskKey[AnyRef]("scripted-tests") + val scriptedBatchExecution = + settingKey[Boolean]("Enables or disables batch execution for scripted.") + val scriptedParallelInstances = + settingKey[Int]( + "Configures the number of scripted instances for parallel testing, only used in batch mode.") val scriptedRun = TaskKey[Method]("scripted-run") val scriptedLaunchOpts = SettingKey[Seq[String]]( "scripted-launch-opts", @@ -56,6 +61,8 @@ object ScriptedPlugin extends AutoPlugin { scriptedBufferLog := true, scriptedClasspath := getJars(ScriptedConf).value, scriptedTests := scriptedTestsTask.value, + scriptedParallelInstances := 1, + scriptedBatchExecution := false, scriptedRun := scriptedRunTask.value, scriptedDependencies := { def use[A](@deprecated("unused", "") x: A*): Unit = () // avoid unused warnings @@ -73,15 +80,18 @@ object ScriptedPlugin extends AutoPlugin { ModuleUtilities.getObject("sbt.test.ScriptedTests", loader) } - def scriptedRunTask: Initialize[Task[Method]] = Def.task( - scriptedTests.value.getClass.getMethod("run", - classOf[File], - classOf[Boolean], - classOf[Array[String]], - classOf[File], - classOf[Array[String]], - classOf[java.util.List[File]]) - ) + private[this] val fCls = classOf[File] + private[this] val bCls = classOf[Boolean] + private[this] val asCls = classOf[Array[String]] + private[this] val lfCls = classOf[java.util.List[File]] + private[this] val iCls = classOf[Integer] + + def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn { + val clazz = scriptedTests.value.getClass + if (scriptedBatchExecution.value) + Def.task(clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls)) + else Def.task(clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls)) + } import DefaultParsers._ case class ScriptedTestPage(page: Int, total: Int) @@ -134,15 +144,18 @@ object ScriptedPlugin extends AutoPlugin { val args = scriptedParser(sbtTestDirectory.value).parsed scriptedDependencies.value try { - scriptedRun.value.invoke( - scriptedTests.value, - sbtTestDirectory.value, - scriptedBufferLog.value: java.lang.Boolean, - args.toArray, - sbtLauncher.value, - scriptedLaunchOpts.value.toArray, - new java.util.ArrayList() - ) + val method = scriptedRun.value + val scriptedInstance = scriptedTests.value + val dir = sbtTestDirectory.value + val log: java.lang.Boolean = scriptedBufferLog.value + val launcher = sbtLauncher.value + val opts = scriptedLaunchOpts.value.toArray + val empty = new java.util.ArrayList() + val instances: Integer = scriptedParallelInstances.value + + if (scriptedBatchExecution.value) + method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty, instances) + else method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty) } catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause } } diff --git a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala index 3e2798300..7af336a26 100644 --- a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala @@ -324,8 +324,8 @@ class ScriptedRunner { prescripted.add(f); () }) //new FullLogger(Logger.xlog2Log(log))) } - // This is called by sbt-scripted 0.13.x (the sbt host) when cross-compiling to sbt 0.13.x and 1.0.x - // See https://github.com/sbt/sbt/issues/3245 + + // This is called by sbt-scripted 0.13.x and 1.x (see https://github.com/sbt/sbt/issues/3245) def run(resourceBaseDirectory: File, bufferLog: Boolean, tests: Array[String], @@ -374,6 +374,21 @@ class ScriptedRunner { 1) } + // This is used by sbt-scripted sbt 1.x + def runInParallel( + baseDir: File, + bufferLog: Boolean, + tests: Array[String], + bootProps: File, + launchOpts: Array[String], + prescripted: java.util.List[File], + instances: Integer + ): Unit = { + val logger = ConsoleLogger() + val addTestFile = (f: File) => { prescripted.add(f); () } + runInParallel(baseDir, bufferLog, tests, logger, bootProps, launchOpts, addTestFile, instances) + } + def runInParallel( resourceBaseDirectory: File, bufferLog: Boolean, From 2aed011bc990d53c17aafb0f4b9ac04532b2b54a Mon Sep 17 00:00:00 2001 From: Allan Renucci Date: Wed, 17 Jan 2018 14:54:21 +0100 Subject: [PATCH 034/120] Cleanup --- .../src/main/scala/sbt/ScriptedPlugin.scala | 26 +++++++++++-------- .../main/scala/sbt/test/ScriptedTests.scala | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala index a7bfdc907..a7bf73543 100644 --- a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala +++ b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala @@ -80,17 +80,21 @@ object ScriptedPlugin extends AutoPlugin { ModuleUtilities.getObject("sbt.test.ScriptedTests", loader) } - private[this] val fCls = classOf[File] - private[this] val bCls = classOf[Boolean] - private[this] val asCls = classOf[Array[String]] - private[this] val lfCls = classOf[java.util.List[File]] - private[this] val iCls = classOf[Integer] - def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn { + val fCls = classOf[File] + val bCls = classOf[Boolean] + val asCls = classOf[Array[String]] + val lfCls = classOf[java.util.List[File]] + val iCls = classOf[Int] + val clazz = scriptedTests.value.getClass - if (scriptedBatchExecution.value) - Def.task(clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls)) - else Def.task(clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls)) + val method = + if (scriptedBatchExecution.value) + clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls) + else + clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls) + + Def.task(method) } import DefaultParsers._ @@ -150,8 +154,8 @@ object ScriptedPlugin extends AutoPlugin { val log: java.lang.Boolean = scriptedBufferLog.value val launcher = sbtLauncher.value val opts = scriptedLaunchOpts.value.toArray - val empty = new java.util.ArrayList() - val instances: Integer = scriptedParallelInstances.value + val empty = new java.util.ArrayList[File]() + val instances: java.lang.Integer = scriptedParallelInstances.value if (scriptedBatchExecution.value) method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty, instances) diff --git a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala index 7af336a26..f516d0beb 100644 --- a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala @@ -382,7 +382,7 @@ class ScriptedRunner { bootProps: File, launchOpts: Array[String], prescripted: java.util.List[File], - instances: Integer + instances: Int ): Unit = { val logger = ConsoleLogger() val addTestFile = (f: File) => { prescripted.add(f); () } From 655c2ac5d159b5e510b2a0f2cc41868befb1d531 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 13 Jan 2018 00:35:11 -0500 Subject: [PATCH 035/120] Make ScriptedPlugin not a triggered plugin Fixes #3514 --- scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala index a7bf73543..9ba17d09c 100644 --- a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala +++ b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala @@ -16,7 +16,7 @@ import java.lang.reflect.Method object ScriptedPlugin extends AutoPlugin { override def requires = plugins.JvmPlugin - override def trigger = allRequirements + object autoImport { val ScriptedConf = Configurations.config("scripted-sbt") hide val ScriptedLaunchConf = Configurations.config("scripted-sbt-launch") hide From 08eaba9107bdeb47a4273fef882702d1257de3ac Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 13 Jan 2018 14:28:06 -0500 Subject: [PATCH 036/120] Add SbtPlugin Fixes #3538 This brings in `sbt.ScriptedPlugin` as `sbt.plugins.ScriptedPlugin` into sbt mothership. In addition, `sbt.plugins.SbtPlugin` is added that enables the scripted plugin and `sbtPlugin := true`. This allows plugin authors to bring in scripted plugin by writing: ```scala lazy val root = (project in file(".")) .enablePlugins(SbtPlugin) ``` --- build.sbt | 11 +++++++++-- .../scala/sbt/internal/PluginDiscovery.scala | 2 ++ .../main/scala/sbt/plugins/SbtPlugin.scala | 19 +++++++++++++++++++ .../scala/sbt/plugins}/ScriptedPlugin.scala | 14 +++++++++++--- .../project/scripted-plugin/build.sbt | 2 ++ 5 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 main/src/main/scala/sbt/plugins/SbtPlugin.scala rename {scripted/plugin/src/main/scala/sbt => main/src/main/scala/sbt/plugins}/ScriptedPlugin.scala (96%) create mode 100644 sbt/src/sbt-test/project/scripted-plugin/build.sbt diff --git a/build.sbt b/build.sbt index b9d11c0d6..4473012ac 100644 --- a/build.sbt +++ b/build.sbt @@ -272,11 +272,18 @@ lazy val scriptedSbtProj = (project in scriptedPath / "sbt") .configure(addSbtIO, addSbtUtilLogging, addSbtCompilerInterface, addSbtUtilScripted, addSbtLmCore) lazy val scriptedPluginProj = (project in scriptedPath / "plugin") - .dependsOn(sbtProj) + .dependsOn(mainProj) .settings( baseSettings, name := "Scripted Plugin", mimaSettings, + mimaBinaryIssueFilters ++= Seq( + // scripted plugin has moved into sbt mothership as sbt.plugins.ScriptedPlugin. + // sbt.ScriptedPlugin is still here for bincomat. + exclude[DirectMissingMethodProblem]("sbt.ScriptedPlugin#autoImport*"), + exclude[IncompatibleResultTypeProblem]("sbt.ScriptedPlugin.requires"), + exclude[DirectMissingMethodProblem]("sbt.ScriptedPlugin.scriptedParser"), + ), ) .configure(addSbtCompilerClasspath) @@ -417,7 +424,7 @@ lazy val mainSettingsProj = (project in file("main-settings")) // The main integration project for sbt. It brings all of the projects together, configures them, and provides for overriding conventions. lazy val mainProj = (project in file("main")) .enablePlugins(ContrabandPlugin) - .dependsOn(logicProj, actionsProj, mainSettingsProj, runProj, commandProj, collectionProj) + .dependsOn(logicProj, actionsProj, mainSettingsProj, runProj, commandProj, collectionProj, scriptedSbtProj) .settings( testedBaseSettings, name := "Main", diff --git a/main/src/main/scala/sbt/internal/PluginDiscovery.scala b/main/src/main/scala/sbt/internal/PluginDiscovery.scala index af2097298..50e60663b 100644 --- a/main/src/main/scala/sbt/internal/PluginDiscovery.scala +++ b/main/src/main/scala/sbt/internal/PluginDiscovery.scala @@ -47,6 +47,8 @@ object PluginDiscovery { "sbt.plugins.IvyPlugin" -> sbt.plugins.IvyPlugin, "sbt.plugins.JvmPlugin" -> sbt.plugins.JvmPlugin, "sbt.plugins.CorePlugin" -> sbt.plugins.CorePlugin, + "sbt.plugins.ScriptedPlugin" -> sbt.plugins.ScriptedPlugin, + "sbt.plugins.SbtPlugin" -> sbt.plugins.SbtPlugin, "sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin, "sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin ) diff --git a/main/src/main/scala/sbt/plugins/SbtPlugin.scala b/main/src/main/scala/sbt/plugins/SbtPlugin.scala new file mode 100644 index 000000000..a8a52413c --- /dev/null +++ b/main/src/main/scala/sbt/plugins/SbtPlugin.scala @@ -0,0 +1,19 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt +package plugins + +import Keys._ + +object SbtPlugin extends AutoPlugin { + override def requires = ScriptedPlugin + + override lazy val projectSettings = Seq( + sbtPlugin := true + ) +} diff --git a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala b/main/src/main/scala/sbt/plugins/ScriptedPlugin.scala similarity index 96% rename from scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala rename to main/src/main/scala/sbt/plugins/ScriptedPlugin.scala index 9ba17d09c..cf5355ec7 100644 --- a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/plugins/ScriptedPlugin.scala @@ -6,13 +6,21 @@ */ package sbt +package plugins +import java.io.File import Def.Initialize import Keys._ import sbt.internal.util.complete.{ Parser, DefaultParsers } import sbt.internal.inc.classpath.ClasspathUtilities import sbt.internal.inc.ModuleUtilities import java.lang.reflect.Method +import sbt.librarymanagement._ +import sbt.librarymanagement.syntax._ +import sbt.io._ +import sbt.io.syntax._ +import Project._ +import Def._ object ScriptedPlugin extends AutoPlugin { override def requires = plugins.JvmPlugin @@ -38,6 +46,7 @@ object ScriptedPlugin extends AutoPlugin { val scriptedDependencies = TaskKey[Unit]("scripted-dependencies") val scripted = InputKey[Unit]("scripted") } + import autoImport._ override lazy val projectSettings = Seq( ivyConfigurations ++= Seq(ScriptedConf, ScriptedLaunchConf), @@ -66,7 +75,7 @@ object ScriptedPlugin extends AutoPlugin { scriptedRun := scriptedRunTask.value, scriptedDependencies := { def use[A](@deprecated("unused", "") x: A*): Unit = () // avoid unused warnings - val analysis = (compile in Test).value + val analysis = (Keys.compile in Test).value val pub = (publishLocal).value use(analysis, pub) }, @@ -143,7 +152,6 @@ object ScriptedPlugin extends AutoPlugin { //(token(Space) ~> matched(testID)).* (token(Space) ~> (PagedIds | testIdAsGroup)).* map (_.flatten) } - def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask { val args = scriptedParser(sbtTestDirectory.value).parsed scriptedDependencies.value @@ -164,6 +172,6 @@ object ScriptedPlugin extends AutoPlugin { } private[this] def getJars(config: Configuration): Initialize[Task[PathFinder]] = Def.task { - PathFinder(Classpaths.managedJars(config, classpathTypes.value, update.value).map(_.data)) + PathFinder(Classpaths.managedJars(config, classpathTypes.value, Keys.update.value).map(_.data)) } } diff --git a/sbt/src/sbt-test/project/scripted-plugin/build.sbt b/sbt/src/sbt-test/project/scripted-plugin/build.sbt new file mode 100644 index 000000000..6c91c26b3 --- /dev/null +++ b/sbt/src/sbt-test/project/scripted-plugin/build.sbt @@ -0,0 +1,2 @@ +lazy val root = (project in file(".")) + .enablePlugins(SbtPlugin) From b05802f63b67bc9df04ab48046973b3771980a16 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 13 Jan 2018 02:47:53 -0500 Subject: [PATCH 037/120] move some scripted defaults settings to global Fixes #3656 --- .../main/scala/sbt/plugins/ScriptedPlugin.scala | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/main/src/main/scala/sbt/plugins/ScriptedPlugin.scala b/main/src/main/scala/sbt/plugins/ScriptedPlugin.scala index cf5355ec7..97f332986 100644 --- a/main/src/main/scala/sbt/plugins/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/plugins/ScriptedPlugin.scala @@ -48,6 +48,12 @@ object ScriptedPlugin extends AutoPlugin { } import autoImport._ + + override lazy val globalSettings = Seq( + scriptedBufferLog := true, + scriptedLaunchOpts := Seq(), + ) + override lazy val projectSettings = Seq( ivyConfigurations ++= Seq(ScriptedConf, ScriptedLaunchConf), scriptedSbt := (sbtVersion in pluginCrossBuild).value, @@ -67,7 +73,6 @@ object ScriptedPlugin extends AutoPlugin { case Some((x, y)) => sys error s"Unknown sbt version ${scriptedSbt.value} ($x.$y)" case None => sys error s"Unknown sbt version ${scriptedSbt.value}" }), - scriptedBufferLog := true, scriptedClasspath := getJars(ScriptedConf).value, scriptedTests := scriptedTestsTask.value, scriptedParallelInstances := 1, @@ -79,17 +84,16 @@ object ScriptedPlugin extends AutoPlugin { val pub = (publishLocal).value use(analysis, pub) }, - scriptedLaunchOpts := Seq(), scripted := scriptedTask.evaluated ) - def scriptedTestsTask: Initialize[Task[AnyRef]] = + private[sbt] def scriptedTestsTask: Initialize[Task[AnyRef]] = Def.task { val loader = ClasspathUtilities.toLoader(scriptedClasspath.value, scalaInstance.value.loader) ModuleUtilities.getObject("sbt.test.ScriptedTests", loader) } - def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn { + private[sbt] def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn { val fCls = classOf[File] val bCls = classOf[Boolean] val asCls = classOf[Array[String]] @@ -107,7 +111,7 @@ object ScriptedPlugin extends AutoPlugin { } import DefaultParsers._ - case class ScriptedTestPage(page: Int, total: Int) + private[sbt] case class ScriptedTestPage(page: Int, total: Int) private[sbt] def scriptedParser(scriptedBase: File): Parser[Seq[String]] = { @@ -152,7 +156,8 @@ object ScriptedPlugin extends AutoPlugin { //(token(Space) ~> matched(testID)).* (token(Space) ~> (PagedIds | testIdAsGroup)).* map (_.flatten) } - def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask { + + private[sbt] def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask { val args = scriptedParser(sbtTestDirectory.value).parsed scriptedDependencies.value try { From c20029ce160cab04e8fea1adba4fd0e321131204 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 13 Jan 2018 17:08:48 -0500 Subject: [PATCH 038/120] Work around package name confusion This works around the name conflict between sbt.test package and sbt.Keys.test. 1. sbt.test package is renamed to sbt.scriptedtest. This allows 1.0 plugins and builds to use `test` to mean `Keys.test`. 2. To keep binary compatibility for sbt 0.13 scripted, I am adding `sbt.test.ScriptedRunner` and `sbt.test.ScriptedTests` in `scripted-plugin` artifact. 3. Another affected user is Giter8 plugin that uses ScriptedPlugin. Since the intereactions are limited to `sbt.ScriptedPlugin.*`, we should be fine here. - https://github.com/foundweekends/giter8/blob/v0.11.0-M2/plugin/src/main/scala-sbt-1.0/giter8/SBTCompat.scala --- build.sbt | 11 ++++--- .../sbt/{plugins => }/ScriptedPlugin.scala | 8 +++-- .../scala/sbt/internal/PluginDiscovery.scala | 2 +- project/Scripted.scala | 2 +- .../src/main/resources/sbt/sbt.autoplugins | 1 - .../src/main/scala/sbt/ScriptedPlugin.scala | 10 ++++++ .../main/scala/sbt/test/ScriptedTests.scala | 32 +++++++++++++++++++ .../BatchScriptRunner.scala | 4 +-- .../{test => scriptedtest}/SbtHandler.scala | 2 +- .../ScriptedTests.scala | 10 +++--- 10 files changed, 64 insertions(+), 18 deletions(-) rename main/src/main/scala/sbt/{plugins => }/ScriptedPlugin.scala (96%) delete mode 100644 scripted/plugin/src/main/resources/sbt/sbt.autoplugins create mode 100644 scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala create mode 100644 scripted/plugin/src/main/scala/sbt/test/ScriptedTests.scala rename scripted/sbt/src/main/scala/sbt/{test => scriptedtest}/BatchScriptRunner.scala (96%) rename scripted/sbt/src/main/scala/sbt/{test => scriptedtest}/SbtHandler.scala (99%) rename scripted/sbt/src/main/scala/sbt/{test => scriptedtest}/ScriptedTests.scala (98%) diff --git a/build.sbt b/build.sbt index 4473012ac..092830ad7 100644 --- a/build.sbt +++ b/build.sbt @@ -268,6 +268,10 @@ lazy val scriptedSbtProj = (project in scriptedPath / "sbt") name := "Scripted sbt", libraryDependencies ++= Seq(launcherInterface % "provided"), mimaSettings, + mimaBinaryIssueFilters ++= Seq( + // sbt.test package is renamed to sbt.scriptedtest. + exclude[MissingClassProblem]("sbt.test.*"), + ), ) .configure(addSbtIO, addSbtUtilLogging, addSbtCompilerInterface, addSbtUtilScripted, addSbtLmCore) @@ -278,11 +282,8 @@ lazy val scriptedPluginProj = (project in scriptedPath / "plugin") name := "Scripted Plugin", mimaSettings, mimaBinaryIssueFilters ++= Seq( - // scripted plugin has moved into sbt mothership as sbt.plugins.ScriptedPlugin. - // sbt.ScriptedPlugin is still here for bincomat. - exclude[DirectMissingMethodProblem]("sbt.ScriptedPlugin#autoImport*"), - exclude[IncompatibleResultTypeProblem]("sbt.ScriptedPlugin.requires"), - exclude[DirectMissingMethodProblem]("sbt.ScriptedPlugin.scriptedParser"), + // scripted plugin has moved into sbt mothership. + exclude[MissingClassProblem]("sbt.ScriptedPlugin*") ), ) .configure(addSbtCompilerClasspath) diff --git a/main/src/main/scala/sbt/plugins/ScriptedPlugin.scala b/main/src/main/scala/sbt/ScriptedPlugin.scala similarity index 96% rename from main/src/main/scala/sbt/plugins/ScriptedPlugin.scala rename to main/src/main/scala/sbt/ScriptedPlugin.scala index 97f332986..538a851ef 100644 --- a/main/src/main/scala/sbt/plugins/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/ScriptedPlugin.scala @@ -6,7 +6,6 @@ */ package sbt -package plugins import java.io.File import Def.Initialize @@ -90,7 +89,12 @@ object ScriptedPlugin extends AutoPlugin { private[sbt] def scriptedTestsTask: Initialize[Task[AnyRef]] = Def.task { val loader = ClasspathUtilities.toLoader(scriptedClasspath.value, scalaInstance.value.loader) - ModuleUtilities.getObject("sbt.test.ScriptedTests", loader) + try { + ModuleUtilities.getObject("sbt.scriptedtest.ScriptedTests", loader) + } catch { + case _: ClassNotFoundException => + ModuleUtilities.getObject("sbt.test.ScriptedTests", loader) + } } private[sbt] def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn { diff --git a/main/src/main/scala/sbt/internal/PluginDiscovery.scala b/main/src/main/scala/sbt/internal/PluginDiscovery.scala index 50e60663b..cc99453e3 100644 --- a/main/src/main/scala/sbt/internal/PluginDiscovery.scala +++ b/main/src/main/scala/sbt/internal/PluginDiscovery.scala @@ -47,7 +47,7 @@ object PluginDiscovery { "sbt.plugins.IvyPlugin" -> sbt.plugins.IvyPlugin, "sbt.plugins.JvmPlugin" -> sbt.plugins.JvmPlugin, "sbt.plugins.CorePlugin" -> sbt.plugins.CorePlugin, - "sbt.plugins.ScriptedPlugin" -> sbt.plugins.ScriptedPlugin, + "sbt.ScriptedPlugin" -> sbt.ScriptedPlugin, "sbt.plugins.SbtPlugin" -> sbt.plugins.SbtPlugin, "sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin, "sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin diff --git a/project/Scripted.scala b/project/Scripted.scala index 788f1d60e..0723d4d84 100644 --- a/project/Scripted.scala +++ b/project/Scripted.scala @@ -115,7 +115,7 @@ object Scripted { sys.props(org.apache.logging.log4j.util.LoaderUtil.IGNORE_TCCL_PROPERTY) = "true" val noJLine = new classpath.FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil) val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, noJLine) - val bridgeClass = Class.forName("sbt.test.ScriptedRunner", true, loader) + val bridgeClass = Class.forName("sbt.scriptedtest.ScriptedRunner", true, loader) val bridge = bridgeClass.getDeclaredConstructor().newInstance().asInstanceOf[SbtScriptedRunner] try { // Using java.util.List to encode File => Unit. diff --git a/scripted/plugin/src/main/resources/sbt/sbt.autoplugins b/scripted/plugin/src/main/resources/sbt/sbt.autoplugins deleted file mode 100644 index 0077b7635..000000000 --- a/scripted/plugin/src/main/resources/sbt/sbt.autoplugins +++ /dev/null @@ -1 +0,0 @@ -sbt.ScriptedPlugin \ No newline at end of file diff --git a/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala new file mode 100644 index 000000000..93c2d93f4 --- /dev/null +++ b/scripted/plugin/src/main/scala/sbt/ScriptedPlugin.scala @@ -0,0 +1,10 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt + +// ScriptedPlugin has moved to main. diff --git a/scripted/plugin/src/main/scala/sbt/test/ScriptedTests.scala b/scripted/plugin/src/main/scala/sbt/test/ScriptedTests.scala new file mode 100644 index 000000000..727c4bd6c --- /dev/null +++ b/scripted/plugin/src/main/scala/sbt/test/ScriptedTests.scala @@ -0,0 +1,32 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt.test + +import java.io.File + +/** + * This is a bincompat place holder sbt.test package that we are now trying to hide + * because of the name conflict with Keys.test. + */ +@deprecated("Use sbt.scriptedtest.ScriptedRunner.", "1.2.0") +private[sbt] class ScriptedRunner extends sbt.scriptedtest.ScriptedRunner + +/** + * This is a bincompat place holder for sbt.test package that we are now trying to hide + * because of the name conflict with Keys.test. + */ +@deprecated("Use sbt.scriptedtest.ScriptedTests.", "1.2.0") +private[sbt] object ScriptedTests extends ScriptedRunner { + + /** Represents the function that runs the scripted tests, both in single or batch mode. */ + type TestRunner = () => Seq[Option[String]] + + val emptyCallback: File => Unit = _ => () + def main(args: Array[String]): Unit = + sbt.scriptedtest.ScriptedTests.main(args) +} diff --git a/scripted/sbt/src/main/scala/sbt/test/BatchScriptRunner.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala similarity index 96% rename from scripted/sbt/src/main/scala/sbt/test/BatchScriptRunner.scala rename to scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala index ccf9d5148..fe9d35712 100644 --- a/scripted/sbt/src/main/scala/sbt/test/BatchScriptRunner.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala @@ -6,10 +6,10 @@ */ package sbt -package test +package scriptedtest import sbt.internal.scripted._ -import sbt.test.BatchScriptRunner.States +import sbt.scriptedtest.BatchScriptRunner.States /** Defines an alternative script runner that allows batch execution. */ private[sbt] class BatchScriptRunner extends ScriptRunner { diff --git a/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala similarity index 99% rename from scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala rename to scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala index 12598df78..3ddecbab8 100644 --- a/scripted/sbt/src/main/scala/sbt/test/SbtHandler.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala @@ -6,7 +6,7 @@ */ package sbt -package test +package scriptedtest import java.io.{ File, IOException } import xsbt.IPC diff --git a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala similarity index 98% rename from scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala rename to scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index f516d0beb..4d552994c 100644 --- a/scripted/sbt/src/main/scala/sbt/test/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -6,7 +6,7 @@ */ package sbt -package test +package scriptedtest import java.io.File import java.util.Properties @@ -466,13 +466,13 @@ class ScriptedRunner { final case class ScriptedTest(group: String, name: String) { override def toString = group + "/" + name } -private[test] object ListTests { +private[sbt] object ListTests { def list(directory: File, filter: java.io.FileFilter) = wrapNull(directory.listFiles(filter)) } import ListTests._ -private[test] final class ListTests(baseDirectory: File, - accept: ScriptedTest => Boolean, - log: Logger) { +private[sbt] final class ListTests(baseDirectory: File, + accept: ScriptedTest => Boolean, + log: Logger) { def filter = DirectoryFilter -- HiddenFileFilter def listTests: Seq[ScriptedTest] = { list(baseDirectory, filter) flatMap { group => From 0d83b2fc3f38ccc3e4071a132aa27d8fbecf0418 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 17 Jan 2018 14:58:46 -0500 Subject: [PATCH 039/120] notes --- notes/1.2.0/scripted-change.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 notes/1.2.0/scripted-change.md diff --git a/notes/1.2.0/scripted-change.md b/notes/1.2.0/scripted-change.md new file mode 100644 index 000000000..63715ecab --- /dev/null +++ b/notes/1.2.0/scripted-change.md @@ -0,0 +1,27 @@ + +### Fixes with compatibility implications + +- In sbt 1.2, `ScriptedPlugin` is no longer triggered automatically. This allows easier use of the plugin in a multi-project build. We recommend migration to `SbtPlugin`. [#3514][3514]/[#3875][3875] by [@eed3si9n][@eed3si9n] +- `scriptedBufferLog` and `scriptedLaunchOpts` settings are changed so they are scoped globally. + +### Features + +- Adds `SbtPlugin`. See below. + +### Bug fixes + + +### SbtPlugin + +`SbtPlugin` is a new plugin that represents sbt plugin projects. + + lazy val fooPlugin = (project in file("plugin")) + .enablePlugins(SbtPlugin) + +This sets `sbtPlugin` setting to `true`, and brings in the new non-triggered `ScriptedPlugin`. + +[#3875][3875] by [@eed3si9n][@eed3si9n] + + [@eed3si9n]: https://github.com/eed3si9n + [3514]: https://github.com/sbt/sbt/issues/3514 + [3875]: https://github.com/sbt/sbt/pull/3875 From 58ee24b427dea3b53429d4c19f3cc377a2e04915 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 22 Jan 2018 13:45:58 -0500 Subject: [PATCH 040/120] Migrate to the new way of enabling scripted --- sbt/src/sbt-test/project/scripted-skip-incompatible/build.sbt | 2 ++ .../project/scripted-skip-incompatible/project/plugins.sbt | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 sbt/src/sbt-test/project/scripted-skip-incompatible/build.sbt delete mode 100644 sbt/src/sbt-test/project/scripted-skip-incompatible/project/plugins.sbt diff --git a/sbt/src/sbt-test/project/scripted-skip-incompatible/build.sbt b/sbt/src/sbt-test/project/scripted-skip-incompatible/build.sbt new file mode 100644 index 000000000..6c91c26b3 --- /dev/null +++ b/sbt/src/sbt-test/project/scripted-skip-incompatible/build.sbt @@ -0,0 +1,2 @@ +lazy val root = (project in file(".")) + .enablePlugins(SbtPlugin) diff --git a/sbt/src/sbt-test/project/scripted-skip-incompatible/project/plugins.sbt b/sbt/src/sbt-test/project/scripted-skip-incompatible/project/plugins.sbt deleted file mode 100644 index 529e7d656..000000000 --- a/sbt/src/sbt-test/project/scripted-skip-incompatible/project/plugins.sbt +++ /dev/null @@ -1,3 +0,0 @@ -libraryDependencies += { - "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value -} From 92ed849eaa0c57f5e6bcc2a54a2a080ac44441be Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 22 Dec 2017 10:29:11 +0000 Subject: [PATCH 041/120] Remove import-excluding Predef.conforms As of Scala 2.11 Predef.conforms isn't implicit anymore (and it's deprecated), so import-excluding doesn't avoid the implicit (the implicit version is now Predef.$conforms). Also in Scala 2.13 Predef.conforms has been removed. --- main-actions/src/main/scala/sbt/Package.scala | 1 - main-actions/src/main/scala/sbt/RawCompileLike.scala | 1 - 2 files changed, 2 deletions(-) diff --git a/main-actions/src/main/scala/sbt/Package.scala b/main-actions/src/main/scala/sbt/Package.scala index 080667e6a..82ab1072f 100644 --- a/main-actions/src/main/scala/sbt/Package.scala +++ b/main-actions/src/main/scala/sbt/Package.scala @@ -7,7 +7,6 @@ package sbt -import scala.Predef.{ conforms => _, _ } import java.io.File import java.util.jar.{ Attributes, Manifest } import scala.collection.JavaConverters._ diff --git a/main-actions/src/main/scala/sbt/RawCompileLike.scala b/main-actions/src/main/scala/sbt/RawCompileLike.scala index f8b85e25c..56fc1b551 100644 --- a/main-actions/src/main/scala/sbt/RawCompileLike.scala +++ b/main-actions/src/main/scala/sbt/RawCompileLike.scala @@ -11,7 +11,6 @@ import scala.annotation.tailrec import java.io.File import sbt.internal.inc.{ RawCompiler, ScalaInstance } -import Predef.{ conforms => _, _ } import sbt.io.syntax._ import sbt.io.IO From 5daf10d6c765580c7c859f5409730b908840825d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 22 Dec 2017 10:54:56 +0000 Subject: [PATCH 042/120] Tweak the description of KList --- .../src/main/scala/sbt/internal/util/KList.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala index 51babcae3..7b7aaf404 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala @@ -10,7 +10,7 @@ package sbt.internal.util import Types._ import Classes.Applicative -/** Heterogeneous list with each element having type M[T] for some type T.*/ +/** A higher-kinded heterogeneous list of elements that share the same type constructor `M[_]`. */ sealed trait KList[+M[_]] { type Transform[N[_]] <: KList[N] From 3e11b3f0009fb391ec716a078a89eadd5e84b21e Mon Sep 17 00:00:00 2001 From: Colin Dean Date: Wed, 24 Jan 2018 23:13:00 -0500 Subject: [PATCH 043/120] Fixes link to documentation for deprecated 0.10/0.12 DSL syntax Fixes sbt/website#558 --- main-settings/src/main/scala/sbt/Structure.scala | 2 +- main-settings/src/main/scala/sbt/std/TaskMacro.scala | 6 +++--- notes/1.2.0/fix-sbt012x-link.md | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 notes/1.2.0/fix-sbt012x-link.md diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index abe27d015..72cc84b63 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -570,7 +570,7 @@ object Scoped { /** The sbt 0.10 style DSL was deprecated in 0.13.13, favouring the use of the '.value' macro. * - * See http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html for how to migrate. + * See http://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html#Migrating+from+sbt+0.12+style for how to migrate. */ trait TupleSyntax { import Scoped._ diff --git a/main-settings/src/main/scala/sbt/std/TaskMacro.scala b/main-settings/src/main/scala/sbt/std/TaskMacro.scala index 85e19c3f6..562a685f5 100644 --- a/main-settings/src/main/scala/sbt/std/TaskMacro.scala +++ b/main-settings/src/main/scala/sbt/std/TaskMacro.scala @@ -89,12 +89,12 @@ object TaskMacro { final val InputTaskCreateDynName = "createDyn" final val InputTaskCreateFreeName = "createFree" final val append1Migration = - "`<+=` operator is removed. Try `lhs += { x.value }`\n or see http://www.scala-sbt.org/1.0/docs/Migrating-from-sbt-012x.html." + "`<+=` 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." final val appendNMigration = - "`<++=` operator is removed. Try `lhs ++= { x.value }`\n or see http://www.scala-sbt.org/1.0/docs/Migrating-from-sbt-012x.html." + "`<++=` 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." final val assignMigration = """`<<=` operator is removed. Use `key := { x.value }` or `key ~= (old => { newValue })`. - |See http://www.scala-sbt.org/1.0/docs/Migrating-from-sbt-012x.html""".stripMargin + |See http://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html#Migrating+from+sbt+0.12+style""".stripMargin import LinterDSL.{ Empty => EmptyLinter } diff --git a/notes/1.2.0/fix-sbt012x-link.md b/notes/1.2.0/fix-sbt012x-link.md new file mode 100644 index 000000000..3e368f8cb --- /dev/null +++ b/notes/1.2.0/fix-sbt012x-link.md @@ -0,0 +1,4 @@ +### Bug fixes + +* Fixes link to SBT upgrade migration page + From 286758e2ba42f924ff7e1790287435f00ae3c650 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 23 Jan 2018 16:52:51 +0000 Subject: [PATCH 044/120] Minor cleanups --- .../scala/sbt/internal/util/LineReader.scala | 2 +- .../scala/sbt/internal/CommandChannel.scala | 3 +- main/src/main/scala/sbt/BuildSyntax.scala | 2 +- main/src/main/scala/sbt/MainLoop.scala | 24 ++- .../scala/sbt/internal/CommandExchange.scala | 139 ++++++++---------- 5 files changed, 72 insertions(+), 98 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index b5e9d53d2..406ee5e97 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -132,7 +132,7 @@ private[sbt] object JLine { def createReader(): ConsoleReader = createReader(None, JLine.makeInputStream(true)) def createReader(historyPath: Option[File], in: InputStream): ConsoleReader = - usingTerminal { t => + usingTerminal { _ => val cr = new ConsoleReader(in, System.out) cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650 cr.setBellEnabled(false) diff --git a/main-command/src/main/scala/sbt/internal/CommandChannel.scala b/main-command/src/main/scala/sbt/internal/CommandChannel.scala index 4fbb9a299..6b1b8e391 100644 --- a/main-command/src/main/scala/sbt/internal/CommandChannel.scala +++ b/main-command/src/main/scala/sbt/internal/CommandChannel.scala @@ -19,8 +19,7 @@ import sjsonnew.JsonFormat */ abstract class CommandChannel { private val commandQueue: ConcurrentLinkedQueue[Exec] = new ConcurrentLinkedQueue() - def append(exec: Exec): Boolean = - commandQueue.add(exec) + def append(exec: Exec): Boolean = commandQueue.add(exec) def poll: Option[Exec] = Option(commandQueue.poll) def publishEvent[A: JsonFormat](event: A, execId: Option[String]): Unit diff --git a/main/src/main/scala/sbt/BuildSyntax.scala b/main/src/main/scala/sbt/BuildSyntax.scala index a2ef2c2cd..527481e87 100644 --- a/main/src/main/scala/sbt/BuildSyntax.scala +++ b/main/src/main/scala/sbt/BuildSyntax.scala @@ -11,7 +11,7 @@ import sbt.internal.DslEntry import sbt.librarymanagement.Configuration private[sbt] trait BuildSyntax { - import language.experimental.macros + import scala.language.experimental.macros def settingKey[T](description: String): SettingKey[T] = macro std.KeyMacro.settingKeyImpl[T] def taskKey[T](description: String): TaskKey[T] = macro std.KeyMacro.taskKeyImpl[T] def inputKey[T](description: String): InputKey[T] = macro std.KeyMacro.inputKeyImpl[T] diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index 110a78fa4..cbcbcb2cf 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -7,14 +7,17 @@ package sbt +import java.io.PrintWriter import java.util.Properties + +import jline.TerminalFactory + import scala.annotation.tailrec import scala.util.control.NonFatal -import jline.TerminalFactory import sbt.io.{ IO, Using } import sbt.internal.util.{ ErrorHandling, GlobalLogBacking } -import sbt.internal.util.complete.DefaultParsers +import sbt.internal.util.complete.Parser import sbt.util.Logger import sbt.protocol._ @@ -25,9 +28,7 @@ object MainLoop { // We've disabled jline shutdown hooks to prevent classloader leaks, and have been careful to always restore // the jline terminal in finally blocks, but hitting ctrl+c prevents finally blocks from being executed, in that // case the only way to restore the terminal is in a shutdown hook. - val shutdownHook = new Thread(new Runnable { - def run(): Unit = TerminalFactory.get().restore() - }) + val shutdownHook = new Thread(() => TerminalFactory.get().restore()) try { Runtime.getRuntime.addShutdownHook(shutdownHook) @@ -100,7 +101,7 @@ object MainLoop { /** Runs the next sequence of commands with global logging in place. */ def runWithNewLog(state: State, logBacking: GlobalLogBacking): RunNext = Using.fileWriter(append = true)(logBacking.file) { writer => - val out = new java.io.PrintWriter(writer) + val out = new PrintWriter(writer) val full = state.globalLogging.full val newLogging = state.globalLogging.newAppender(full, out, logBacking) // transferLevels(state, newLogging) @@ -124,7 +125,7 @@ object MainLoop { final class KeepGlobalLog(val state: State) extends RunNext final class Return(val result: xsbti.MainResult) extends RunNext - /** Runs the next sequence of commands that doesn't require global logging changes.*/ + /** Runs the next sequence of commands that doesn't require global logging changes. */ @tailrec def run(state: State): RunNext = state.next match { case State.Continue => run(next(state)) @@ -143,14 +144,11 @@ object MainLoop { /** This is the main function State transfer function of the sbt command processing. */ def processCommand(exec: Exec, state: State): State = { - import DefaultParsers._ val channelName = exec.source map (_.channelName) - StandardMain.exchange publishEventMessage ExecStatusEvent("Processing", - channelName, - exec.execId, - Vector()) + StandardMain.exchange publishEventMessage + ExecStatusEvent("Processing", channelName, exec.execId, Vector()) val parser = Command combine state.definedCommands - val newState = parse(exec.commandLine, parser(state)) match { + val newState = Parser.parse(exec.commandLine, parser(state)) match { case Right(s) => s() // apply command. command side effects happen here case Left(errMsg) => state.log error errMsg diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 2ce2819fa..2ece2e12a 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -46,46 +46,41 @@ private[sbt] final class CommandExchange { private val autoStartServer = sys.props get "sbt.server.autostart" forall (_.toLowerCase == "true") - private val lock = new AnyRef {} private var server: Option[ServerInstance] = None private val firstInstance: AtomicBoolean = new AtomicBoolean(true) private var consoleChannel: Option[ConsoleChannel] = None private val commandQueue: ConcurrentLinkedQueue[Exec] = new ConcurrentLinkedQueue() private val channelBuffer: ListBuffer[CommandChannel] = new ListBuffer() + private val channelBufferLock = new AnyRef {} private val nextChannelId: AtomicInteger = new AtomicInteger(0) private lazy val jsonFormat = new sjsonnew.BasicJsonProtocol with JValueFormats {} def channels: List[CommandChannel] = channelBuffer.toList - def subscribe(c: CommandChannel): Unit = - lock.synchronized { - channelBuffer.append(c) - } + def subscribe(c: CommandChannel): Unit = channelBufferLock.synchronized(channelBuffer.append(c)) // periodically move all messages from all the channels @tailrec def blockUntilNextExec: Exec = { @tailrec def slurpMessages(): Unit = - (((None: Option[Exec]) /: channels) { _ orElse _.poll }) match { + channels.foldLeft(Option.empty[Exec]) { _ orElse _.poll } match { + case None => () case Some(x) => commandQueue.add(x) slurpMessages - case _ => () } slurpMessages() Option(commandQueue.poll) match { case Some(x) => x - case _ => + case None => Thread.sleep(50) blockUntilNextExec } } def run(s: State): State = { - consoleChannel match { - case Some(_) => // do nothing - case _ => - val x = new ConsoleChannel("console0") - consoleChannel = Some(x) - subscribe(x) + if (consoleChannel.isEmpty) { + val console0 = new ConsoleChannel("console0") + consoleChannel = Some(console0) + subscribe(console0) } if (autoStartServer) runServer(s) else s @@ -97,25 +92,12 @@ private[sbt] final class CommandExchange { * Check if a server instance is running already, and start one if it isn't. */ private[sbt] def runServer(s: State): State = { - lazy val port = (s get serverPort) match { - case Some(x) => x - case None => 5001 - } - lazy val host = (s get serverHost) match { - case Some(x) => x - case None => "127.0.0.1" - } - lazy val auth: Set[ServerAuthentication] = (s get serverAuthentication) match { - case Some(xs) => xs - case None => Set(ServerAuthentication.Token) - } - lazy val connectionType = (s get serverConnectionType) match { - case Some(x) => x - case None => ConnectionType.Tcp - } - lazy val level: Level.Value = (s get serverLogLevel) - .orElse(s get logLevel) - .getOrElse(Level.Warn) + lazy val port = s.get(serverPort).getOrElse(5001) + lazy val host = s.get(serverHost).getOrElse("127.0.0.1") + lazy val auth: Set[ServerAuthentication] = + s.get(serverAuthentication).getOrElse(Set(ServerAuthentication.Token)) + lazy val connectionType = s.get(serverConnectionType).getOrElse(ConnectionType.Tcp) + lazy val level = s.get(serverLogLevel).orElse(s.get(logLevel)).getOrElse(Level.Warn) def onIncomingSocket(socket: Socket, instance: ServerInstance): Unit = { val name = newNetworkName @@ -131,55 +113,50 @@ private[sbt] final class CommandExchange { new NetworkChannel(name, socket, Project structure s, auth, instance, logger) subscribe(channel) } - server match { - case Some(_) => // do nothing - case None if !firstInstance.get => // there's another server - case _ => - val portfile = (new File(".")).getAbsoluteFile / "project" / "target" / "active.json" - val h = Hash.halfHashString(IO.toURI(portfile).toString) - val tokenfile = BuildPaths.getGlobalBase(s) / "server" / h / "token.json" - val socketfile = BuildPaths.getGlobalBase(s) / "server" / h / "sock" - val pipeName = "sbt-server-" + h - val connection = - ServerConnection(connectionType, - host, - port, - auth, - portfile, - tokenfile, - socketfile, - pipeName) - val x = Server.start(connection, onIncomingSocket, s.log) - - // don't throw exception when it times out - val d = "10s" - Try(Await.ready(x.ready, Duration(d))) - x.ready.value match { - case Some(Success(_)) => - // remember to shutdown only when the server comes up - server = Some(x) - case Some(Failure(_: AlreadyRunningException)) => - s.log.warn( - "sbt server could not start because there's another instance of sbt running on this build.") - s.log.warn("Running multiple instances is unsupported") - server = None - firstInstance.set(false) - case Some(Failure(e)) => - s.log.error(e.toString) - server = None - case None => - s.log.warn(s"sbt server could not start in $d") - server = None - firstInstance.set(false) - } + if (server.isEmpty && firstInstance.get) { + val portfile = (new File(".")).getAbsoluteFile / "project" / "target" / "active.json" + val h = Hash.halfHashString(IO.toURI(portfile).toString) + val tokenfile = BuildPaths.getGlobalBase(s) / "server" / h / "token.json" + val socketfile = BuildPaths.getGlobalBase(s) / "server" / h / "sock" + val pipeName = "sbt-server-" + h + val connection = ServerConnection( + connectionType, + host, + port, + auth, + portfile, + tokenfile, + socketfile, + pipeName, + ) + val serverInstance = Server.start(connection, onIncomingSocket, s.log) + // don't throw exception when it times out + val d = "10s" + Try(Await.ready(serverInstance.ready, Duration(d))) + serverInstance.ready.value match { + case Some(Success(())) => + // remember to shutdown only when the server comes up + server = Some(serverInstance) + case Some(Failure(_: AlreadyRunningException)) => + s.log.warn( + "sbt server could not start because there's another instance of sbt running on this build.") + s.log.warn("Running multiple instances is unsupported") + server = None + firstInstance.set(false) + case Some(Failure(e)) => + s.log.error(e.toString) + server = None + case None => + s.log.warn(s"sbt server could not start in $d") + server = None + firstInstance.set(false) + } } s } def shutdown(): Unit = { - channels foreach { c => - c.shutdown() - } + channels foreach (_.shutdown()) // interrupt and kill the thread server.foreach(_.shutdown()) server = None @@ -202,7 +179,7 @@ private[sbt] final class CommandExchange { toDel.toList match { case Nil => // do nothing case xs => - lock.synchronized { + channelBufferLock.synchronized { channelBuffer --= xs () } @@ -241,7 +218,7 @@ private[sbt] final class CommandExchange { toDel.toList match { case Nil => // do nothing case xs => - lock.synchronized { + channelBufferLock.synchronized { channelBuffer --= xs () } @@ -283,7 +260,7 @@ private[sbt] final class CommandExchange { toDel.toList match { case Nil => // do nothing case xs => - lock.synchronized { + channelBufferLock.synchronized { channelBuffer --= xs () } @@ -325,7 +302,7 @@ private[sbt] final class CommandExchange { toDel.toList match { case Nil => // do nothing case xs => - lock.synchronized { + channelBufferLock.synchronized { channelBuffer --= xs () } From 2f48425511d5b4729857b6931fdc40101b89a0d1 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 17 Jan 2018 15:17:56 +0000 Subject: [PATCH 045/120] Remove old zinc addSbtAlternateResolver stuff --- build.sbt | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/build.sbt b/build.sbt index b9d11c0d6..2c860b440 100644 --- a/build.sbt +++ b/build.sbt @@ -537,11 +537,6 @@ def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask { val result = scriptedSource(dir => (s: State) => Scripted.scriptedParser(dir)).parsed // publishLocalBinAll.value // TODO: Restore scripted needing only binary jars. publishAll.value - // These two projects need to be visible in a repo even if the default - // local repository is hidden, so we publish them to an alternate location and add - // that alternate repo to the running scripted test (in Scripted.scriptedpreScripted). - // (altLocalPublish in interfaceProj).value - // (altLocalPublish in compileInterfaceProj).value Scripted.doScripted( (sbtLaunchJar in bundledLauncherProj).value, (fullClasspath in scriptedSbtProj in Test).value, @@ -599,7 +594,6 @@ def otherRootSettings = scripted := scriptedTask.evaluated, scriptedUnpublished := scriptedUnpublishedTask.evaluated, scriptedSource := (sourceDirectory in sbtProj).value / "sbt-test", - // scriptedPrescripted := { addSbtAlternateResolver _ }, scriptedLaunchOpts := List("-Xmx1500M", "-Xms512M", "-server"), publishAll := { val _ = (publishLocal).all(ScopeFilter(inAnyProject)).value }, publishLocalBinAll := { val _ = (publishLocalBin).all(ScopeFilter(inAnyProject)).value }, @@ -619,23 +613,6 @@ def otherRootSettings = scriptedSource := (sourceDirectory in sbtProj).value / "repo-override-test" )) -// def addSbtAlternateResolver(scriptedRoot: File) = { -// val resolver = scriptedRoot / "project" / "AddResolverPlugin.scala" -// if (!resolver.exists) { -// IO.write(resolver, s"""import sbt._ -// |import Keys._ -// | -// |object AddResolverPlugin extends AutoPlugin { -// | override def requires = sbt.plugins.JvmPlugin -// | override def trigger = allRequirements -// | -// | override lazy val projectSettings = Seq(resolvers += alternativeLocalResolver) -// | lazy val alternativeLocalResolver = Resolver.file("$altLocalRepoName", file("$altLocalRepoPath"))(Resolver.ivyStylePatterns) -// |} -// |""".stripMargin) -// } -// } - lazy val docProjects: ScopeFilter = ScopeFilter( inAnyProject -- inProjects(sbtRoot, sbtProj, scriptedSbtProj, scriptedPluginProj), inConfigurations(Compile) From bee69ebb92906637b6ca9723004bc0ad044816c7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 17 Jan 2018 15:19:21 +0000 Subject: [PATCH 046/120] Get rid of old MavenResolverPluginTest --- project/Scripted.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/project/Scripted.scala b/project/Scripted.scala index 788f1d60e..bd64b0cfb 100644 --- a/project/Scripted.scala +++ b/project/Scripted.scala @@ -40,8 +40,7 @@ object Scripted { // This is to workaround https://github.com/sbt/io/issues/110 sys.props.put("jna.nosys", "true") - lazy val MavenResolverPluginTest = config("mavenResolverPluginTest") extend Compile - lazy val RepoOverrideTest = config("repoOverrideTest") extend Compile + val RepoOverrideTest = config("repoOverrideTest") extend Compile import sbt.complete._ import DefaultParsers._ From 50f3dd2e20901ca4ee1d2e50ca02362a54884944 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 30 Jan 2018 00:43:30 -0500 Subject: [PATCH 047/120] Use ipcsocket --- build.sbt | 5 +- .../internal/NGUnixDomainServerSocket.java | 178 ---------------- .../java/sbt/internal/NGUnixDomainSocket.java | 192 ------------------ .../internal/NGUnixDomainSocketLibrary.java | 142 ------------- .../sbt/internal/NGWin32NamedPipeLibrary.java | 90 -------- .../NGWin32NamedPipeServerSocket.java | 173 ---------------- .../sbt/internal/NGWin32NamedPipeSocket.java | 172 ---------------- .../ReferenceCountedFileDescriptor.java | 82 -------- .../scala/sbt/internal/server/Server.scala | 7 +- project/Dependencies.scala | 1 + 10 files changed, 9 insertions(+), 1033 deletions(-) delete mode 100644 main-command/src/main/java/sbt/internal/NGUnixDomainServerSocket.java delete mode 100644 main-command/src/main/java/sbt/internal/NGUnixDomainSocket.java delete mode 100644 main-command/src/main/java/sbt/internal/NGUnixDomainSocketLibrary.java delete mode 100644 main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java delete mode 100644 main-command/src/main/java/sbt/internal/NGWin32NamedPipeServerSocket.java delete mode 100644 main-command/src/main/java/sbt/internal/NGWin32NamedPipeSocket.java delete mode 100644 main-command/src/main/java/sbt/internal/ReferenceCountedFileDescriptor.java diff --git a/build.sbt b/build.sbt index d832e23f4..3eb98a312 100644 --- a/build.sbt +++ b/build.sbt @@ -323,7 +323,7 @@ lazy val protocolProj = (project in file("protocol")) scalacOptions -= "-Ywarn-unused", scalacOptions += "-Xlint:-unused", name := "Protocol", - libraryDependencies ++= Seq(sjsonNewScalaJson.value), + libraryDependencies ++= Seq(sjsonNewScalaJson.value, ipcSocket), managedSourceDirectories in Compile += baseDirectory.value / "src" / "main" / "contraband-scala", sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", @@ -355,6 +355,9 @@ lazy val commandProj = (project in file("main-command")) exclude[ReversedMissingMethodProblem]("sbt.internal.CommandChannel.*"), // Added an overload to reboot. The overload is private[sbt]. exclude[ReversedMissingMethodProblem]("sbt.StateOps.reboot"), + // Replace nailgun socket stuff + exclude[MissingClassProblem]("sbt.internal.NG*"), + exclude[MissingClassProblem]("sbt.internal.ReferenceCountedFileDescriptor"), ), unmanagedSources in (Compile, headerCreate) := { val old = (unmanagedSources in (Compile, headerCreate)).value diff --git a/main-command/src/main/java/sbt/internal/NGUnixDomainServerSocket.java b/main-command/src/main/java/sbt/internal/NGUnixDomainServerSocket.java deleted file mode 100644 index 89d3bcf43..000000000 --- a/main-command/src/main/java/sbt/internal/NGUnixDomainServerSocket.java +++ /dev/null @@ -1,178 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGUnixDomainServerSocket.java - -/* - - Copyright 2004-2015, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketAddress; -import java.util.concurrent.atomic.AtomicInteger; - -import com.sun.jna.LastErrorException; -import com.sun.jna.ptr.IntByReference; - -/** - * Implements a {@link ServerSocket} which binds to a local Unix domain socket - * and returns instances of {@link NGUnixDomainSocket} from - * {@link #accept()}. - */ -public class NGUnixDomainServerSocket extends ServerSocket { - private static final int DEFAULT_BACKLOG = 50; - - // We use an AtomicInteger to prevent a race in this situation which - // could happen if fd were just an int: - // - // Thread 1 -> NGUnixDomainServerSocket.accept() - // -> lock this - // -> check isBound and isClosed - // -> unlock this - // -> descheduled while still in method - // Thread 2 -> NGUnixDomainServerSocket.close() - // -> lock this - // -> check isClosed - // -> NGUnixDomainSocketLibrary.close(fd) - // -> now fd is invalid - // -> unlock this - // Thread 1 -> re-scheduled while still in method - // -> NGUnixDomainSocketLibrary.accept(fd, which is invalid and maybe re-used) - // - // By using an AtomicInteger, we'll set this to -1 after it's closed, which - // will cause the accept() call above to cleanly fail instead of possibly - // being called on an unrelated fd (which may or may not fail). - private final AtomicInteger fd; - - private final int backlog; - private boolean isBound; - private boolean isClosed; - - public static class NGUnixDomainServerSocketAddress extends SocketAddress { - private final String path; - - public NGUnixDomainServerSocketAddress(String path) { - this.path = path; - } - - public String getPath() { - return path; - } - } - - /** - * Constructs an unbound Unix domain server socket. - */ - public NGUnixDomainServerSocket() throws IOException { - this(DEFAULT_BACKLOG, null); - } - - /** - * Constructs an unbound Unix domain server socket with the specified listen backlog. - */ - public NGUnixDomainServerSocket(int backlog) throws IOException { - this(backlog, null); - } - - /** - * Constructs and binds a Unix domain server socket to the specified path. - */ - public NGUnixDomainServerSocket(String path) throws IOException { - this(DEFAULT_BACKLOG, path); - } - - /** - * Constructs and binds a Unix domain server socket to the specified path - * with the specified listen backlog. - */ - public NGUnixDomainServerSocket(int backlog, String path) throws IOException { - try { - fd = new AtomicInteger( - NGUnixDomainSocketLibrary.socket( - NGUnixDomainSocketLibrary.PF_LOCAL, - NGUnixDomainSocketLibrary.SOCK_STREAM, - 0)); - this.backlog = backlog; - if (path != null) { - bind(new NGUnixDomainServerSocketAddress(path)); - } - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - public synchronized void bind(SocketAddress endpoint) throws IOException { - if (!(endpoint instanceof NGUnixDomainServerSocketAddress)) { - throw new IllegalArgumentException( - "endpoint must be an instance of NGUnixDomainServerSocketAddress"); - } - if (isBound) { - throw new IllegalStateException("Socket is already bound"); - } - if (isClosed) { - throw new IllegalStateException("Socket is already closed"); - } - NGUnixDomainServerSocketAddress unEndpoint = (NGUnixDomainServerSocketAddress) endpoint; - NGUnixDomainSocketLibrary.SockaddrUn address = - new NGUnixDomainSocketLibrary.SockaddrUn(unEndpoint.getPath()); - try { - int socketFd = fd.get(); - NGUnixDomainSocketLibrary.bind(socketFd, address, address.size()); - NGUnixDomainSocketLibrary.listen(socketFd, backlog); - isBound = true; - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - public Socket accept() throws IOException { - // We explicitly do not make this method synchronized, since the - // call to NGUnixDomainSocketLibrary.accept() will block - // indefinitely, causing another thread's call to close() to deadlock. - synchronized (this) { - if (!isBound) { - throw new IllegalStateException("Socket is not bound"); - } - if (isClosed) { - throw new IllegalStateException("Socket is already closed"); - } - } - try { - NGUnixDomainSocketLibrary.SockaddrUn sockaddrUn = - new NGUnixDomainSocketLibrary.SockaddrUn(); - IntByReference addressLen = new IntByReference(); - addressLen.setValue(sockaddrUn.size()); - int clientFd = NGUnixDomainSocketLibrary.accept(fd.get(), sockaddrUn, addressLen); - return new NGUnixDomainSocket(clientFd); - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - public synchronized void close() throws IOException { - if (isClosed) { - throw new IllegalStateException("Socket is already closed"); - } - try { - // Ensure any pending call to accept() fails. - NGUnixDomainSocketLibrary.close(fd.getAndSet(-1)); - isClosed = true; - } catch (LastErrorException e) { - throw new IOException(e); - } - } -} diff --git a/main-command/src/main/java/sbt/internal/NGUnixDomainSocket.java b/main-command/src/main/java/sbt/internal/NGUnixDomainSocket.java deleted file mode 100644 index b70bef611..000000000 --- a/main-command/src/main/java/sbt/internal/NGUnixDomainSocket.java +++ /dev/null @@ -1,192 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGUnixDomainSocket.java - -/* - - Copyright 2004-2015, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.LastErrorException; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import java.nio.ByteBuffer; - -import java.net.Socket; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Implements a {@link Socket} backed by a native Unix domain socket. - * - * Instances of this class always return {@code null} for - * {@link Socket#getInetAddress()}, {@link Socket#getLocalAddress()}, - * {@link Socket#getLocalSocketAddress()}, {@link Socket#getRemoteSocketAddress()}. - */ -public class NGUnixDomainSocket extends Socket { - private final ReferenceCountedFileDescriptor fd; - private final InputStream is; - private final OutputStream os; - - public NGUnixDomainSocket(String path) throws IOException { - try { - AtomicInteger fd = new AtomicInteger( - NGUnixDomainSocketLibrary.socket( - NGUnixDomainSocketLibrary.PF_LOCAL, - NGUnixDomainSocketLibrary.SOCK_STREAM, - 0)); - NGUnixDomainSocketLibrary.SockaddrUn address = - new NGUnixDomainSocketLibrary.SockaddrUn(path); - int socketFd = fd.get(); - NGUnixDomainSocketLibrary.connect(socketFd, address, address.size()); - this.fd = new ReferenceCountedFileDescriptor(socketFd); - this.is = new NGUnixDomainSocketInputStream(); - this.os = new NGUnixDomainSocketOutputStream(); - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - /** - * Creates a Unix domain socket backed by a native file descriptor. - */ - public NGUnixDomainSocket(int fd) { - this.fd = new ReferenceCountedFileDescriptor(fd); - this.is = new NGUnixDomainSocketInputStream(); - this.os = new NGUnixDomainSocketOutputStream(); - } - - public InputStream getInputStream() { - return is; - } - - public OutputStream getOutputStream() { - return os; - } - - public void shutdownInput() throws IOException { - doShutdown(NGUnixDomainSocketLibrary.SHUT_RD); - } - - public void shutdownOutput() throws IOException { - doShutdown(NGUnixDomainSocketLibrary.SHUT_WR); - } - - private void doShutdown(int how) throws IOException { - try { - int socketFd = fd.acquire(); - if (socketFd != -1) { - NGUnixDomainSocketLibrary.shutdown(socketFd, how); - } - } catch (LastErrorException e) { - throw new IOException(e); - } finally { - fd.release(); - } - } - - public void close() throws IOException { - super.close(); - try { - // This might not close the FD right away. In case we are about - // to read or write on another thread, it will delay the close - // until the read or write completes, to prevent the FD from - // being re-used for a different purpose and the other thread - // reading from a different FD. - fd.close(); - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - private class NGUnixDomainSocketInputStream extends InputStream { - public int read() throws IOException { - ByteBuffer buf = ByteBuffer.allocate(1); - int result; - if (doRead(buf) == 0) { - result = -1; - } else { - // Make sure to & with 0xFF to avoid sign extension - result = 0xFF & buf.get(); - } - return result; - } - - public int read(byte[] b, int off, int len) throws IOException { - if (len == 0) { - return 0; - } - ByteBuffer buf = ByteBuffer.wrap(b, off, len); - int result = doRead(buf); - if (result == 0) { - result = -1; - } - return result; - } - - private int doRead(ByteBuffer buf) throws IOException { - try { - int fdToRead = fd.acquire(); - if (fdToRead == -1) { - return -1; - } - return NGUnixDomainSocketLibrary.read(fdToRead, buf, buf.remaining()); - } catch (LastErrorException e) { - throw new IOException(e); - } finally { - fd.release(); - } - } - } - - private class NGUnixDomainSocketOutputStream extends OutputStream { - - public void write(int b) throws IOException { - ByteBuffer buf = ByteBuffer.allocate(1); - buf.put(0, (byte) (0xFF & b)); - doWrite(buf); - } - - public void write(byte[] b, int off, int len) throws IOException { - if (len == 0) { - return; - } - ByteBuffer buf = ByteBuffer.wrap(b, off, len); - doWrite(buf); - } - - private void doWrite(ByteBuffer buf) throws IOException { - try { - int fdToWrite = fd.acquire(); - if (fdToWrite == -1) { - return; - } - int ret = NGUnixDomainSocketLibrary.write(fdToWrite, buf, buf.remaining()); - if (ret != buf.remaining()) { - // This shouldn't happen with standard blocking Unix domain sockets. - throw new IOException("Could not write " + buf.remaining() + " bytes as requested " + - "(wrote " + ret + " bytes instead)"); - } - } catch (LastErrorException e) { - throw new IOException(e); - } finally { - fd.release(); - } - } - } -} diff --git a/main-command/src/main/java/sbt/internal/NGUnixDomainSocketLibrary.java b/main-command/src/main/java/sbt/internal/NGUnixDomainSocketLibrary.java deleted file mode 100644 index 4d781b6b6..000000000 --- a/main-command/src/main/java/sbt/internal/NGUnixDomainSocketLibrary.java +++ /dev/null @@ -1,142 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGUnixDomainSocketLibrary.java - -/* - - Copyright 2004-2015, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.LastErrorException; -import com.sun.jna.Native; -import com.sun.jna.Platform; -import com.sun.jna.Structure; -import com.sun.jna.Union; -import com.sun.jna.ptr.IntByReference; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.List; - -/** - * Utility class to bridge native Unix domain socket calls to Java using JNA. - */ -public class NGUnixDomainSocketLibrary { - public static final int PF_LOCAL = 1; - public static final int AF_LOCAL = 1; - public static final int SOCK_STREAM = 1; - - public static final int SHUT_RD = 0; - public static final int SHUT_WR = 1; - - // Utility class, do not instantiate. - private NGUnixDomainSocketLibrary() { } - - // BSD platforms write a length byte at the start of struct sockaddr_un. - private static final boolean HAS_SUN_LEN = - Platform.isMac() || Platform.isFreeBSD() || Platform.isNetBSD() || - Platform.isOpenBSD() || Platform.iskFreeBSD(); - - /** - * Bridges {@code struct sockaddr_un} to and from native code. - */ - public static class SockaddrUn extends Structure implements Structure.ByReference { - /** - * On BSD platforms, the {@code sun_len} and {@code sun_family} values in - * {@code struct sockaddr_un}. - */ - public static class SunLenAndFamily extends Structure { - public byte sunLen; - public byte sunFamily; - - protected List getFieldOrder() { - return Arrays.asList(new String[] { "sunLen", "sunFamily" }); - } - } - - /** - * On BSD platforms, {@code sunLenAndFamily} will be present. - * On other platforms, only {@code sunFamily} will be present. - */ - public static class SunFamily extends Union { - public SunLenAndFamily sunLenAndFamily; - public short sunFamily; - } - - public SunFamily sunFamily = new SunFamily(); - public byte[] sunPath = new byte[104]; - - /** - * Constructs an empty {@code struct sockaddr_un}. - */ - public SockaddrUn() { - if (HAS_SUN_LEN) { - sunFamily.sunLenAndFamily = new SunLenAndFamily(); - sunFamily.setType(SunLenAndFamily.class); - } else { - sunFamily.setType(Short.TYPE); - } - allocateMemory(); - } - - /** - * Constructs a {@code struct sockaddr_un} with a path whose bytes are encoded - * using the default encoding of the platform. - */ - public SockaddrUn(String path) throws IOException { - byte[] pathBytes = path.getBytes(); - if (pathBytes.length > sunPath.length - 1) { - throw new IOException("Cannot fit name [" + path + "] in maximum unix domain socket length"); - } - System.arraycopy(pathBytes, 0, sunPath, 0, pathBytes.length); - sunPath[pathBytes.length] = (byte) 0; - if (HAS_SUN_LEN) { - int len = fieldOffset("sunPath") + pathBytes.length; - sunFamily.sunLenAndFamily = new SunLenAndFamily(); - sunFamily.sunLenAndFamily.sunLen = (byte) len; - sunFamily.sunLenAndFamily.sunFamily = AF_LOCAL; - sunFamily.setType(SunLenAndFamily.class); - } else { - sunFamily.sunFamily = AF_LOCAL; - sunFamily.setType(Short.TYPE); - } - allocateMemory(); - } - - protected List getFieldOrder() { - return Arrays.asList(new String[] { "sunFamily", "sunPath" }); - } - } - - static { - Native.register(Platform.C_LIBRARY_NAME); - } - - public static native int socket(int domain, int type, int protocol) throws LastErrorException; - public static native int bind(int fd, SockaddrUn address, int addressLen) - throws LastErrorException; - public static native int listen(int fd, int backlog) throws LastErrorException; - public static native int accept(int fd, SockaddrUn address, IntByReference addressLen) - throws LastErrorException; - public static native int connect(int fd, SockaddrUn address, int addressLen) - throws LastErrorException; - public static native int read(int fd, ByteBuffer buffer, int count) - throws LastErrorException; - public static native int write(int fd, ByteBuffer buffer, int count) - throws LastErrorException; - public static native int close(int fd) throws LastErrorException; - public static native int shutdown(int fd, int how) throws LastErrorException; -} diff --git a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java b/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java deleted file mode 100644 index dd4d8f15a..000000000 --- a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java +++ /dev/null @@ -1,90 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGWin32NamedPipeLibrary.java - -/* - - Copyright 2004-2017, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import java.nio.ByteBuffer; - -import com.sun.jna.*; -import com.sun.jna.platform.win32.WinNT; -import com.sun.jna.platform.win32.WinNT.*; -import com.sun.jna.platform.win32.WinBase.*; -import com.sun.jna.ptr.IntByReference; - -import com.sun.jna.win32.W32APIOptions; - -public interface NGWin32NamedPipeLibrary extends Library, WinNT { - int PIPE_ACCESS_DUPLEX = 3; - int PIPE_UNLIMITED_INSTANCES = 255; - int FILE_FLAG_FIRST_PIPE_INSTANCE = 524288; - - NGWin32NamedPipeLibrary INSTANCE = - (NGWin32NamedPipeLibrary) Native.loadLibrary( - "kernel32", - NGWin32NamedPipeLibrary.class, - W32APIOptions.UNICODE_OPTIONS); - - HANDLE CreateNamedPipe( - String lpName, - int dwOpenMode, - int dwPipeMode, - int nMaxInstances, - int nOutBufferSize, - int nInBufferSize, - int nDefaultTimeOut, - SECURITY_ATTRIBUTES lpSecurityAttributes); - boolean ConnectNamedPipe( - HANDLE hNamedPipe, - Pointer lpOverlapped); - boolean DisconnectNamedPipe( - HANDLE hObject); - boolean ReadFile( - HANDLE hFile, - Memory lpBuffer, - int nNumberOfBytesToRead, - IntByReference lpNumberOfBytesRead, - Pointer lpOverlapped); - boolean WriteFile( - HANDLE hFile, - ByteBuffer lpBuffer, - int nNumberOfBytesToWrite, - IntByReference lpNumberOfBytesWritten, - Pointer lpOverlapped); - boolean CloseHandle( - HANDLE hObject); - boolean GetOverlappedResult( - HANDLE hFile, - Pointer lpOverlapped, - IntByReference lpNumberOfBytesTransferred, - boolean wait); - boolean CancelIoEx( - HANDLE hObject, - Pointer lpOverlapped); - HANDLE CreateEvent( - SECURITY_ATTRIBUTES lpEventAttributes, - boolean bManualReset, - boolean bInitialState, - String lpName); - int WaitForSingleObject( - HANDLE hHandle, - int dwMilliseconds - ); - - int GetLastError(); -} diff --git a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeServerSocket.java b/main-command/src/main/java/sbt/internal/NGWin32NamedPipeServerSocket.java deleted file mode 100644 index 137d9b5dc..000000000 --- a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeServerSocket.java +++ /dev/null @@ -1,173 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGWin32NamedPipeServerSocket.java - -/* - - Copyright 2004-2017, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.platform.win32.WinBase; -import com.sun.jna.platform.win32.WinError; -import com.sun.jna.platform.win32.WinNT; -import com.sun.jna.platform.win32.WinNT.HANDLE; -import com.sun.jna.ptr.IntByReference; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.LinkedBlockingQueue; - -public class NGWin32NamedPipeServerSocket extends ServerSocket { - private static final NGWin32NamedPipeLibrary API = NGWin32NamedPipeLibrary.INSTANCE; - private static final String WIN32_PIPE_PREFIX = "\\\\.\\pipe\\"; - private static final int BUFFER_SIZE = 65535; - private final LinkedBlockingQueue openHandles; - private final LinkedBlockingQueue connectedHandles; - private final NGWin32NamedPipeSocket.CloseCallback closeCallback; - private final String path; - private final int maxInstances; - private final HANDLE lockHandle; - - public NGWin32NamedPipeServerSocket(String path) throws IOException { - this(NGWin32NamedPipeLibrary.PIPE_UNLIMITED_INSTANCES, path); - } - - public NGWin32NamedPipeServerSocket(int maxInstances, String path) throws IOException { - this.openHandles = new LinkedBlockingQueue<>(); - this.connectedHandles = new LinkedBlockingQueue<>(); - this.closeCallback = handle -> { - if (connectedHandles.remove(handle)) { - closeConnectedPipe(handle, false); - } - if (openHandles.remove(handle)) { - closeOpenPipe(handle); - } - }; - this.maxInstances = maxInstances; - if (!path.startsWith(WIN32_PIPE_PREFIX)) { - this.path = WIN32_PIPE_PREFIX + path; - } else { - this.path = path; - } - String lockPath = this.path + "_lock"; - lockHandle = API.CreateNamedPipe( - lockPath, - NGWin32NamedPipeLibrary.FILE_FLAG_FIRST_PIPE_INSTANCE | NGWin32NamedPipeLibrary.PIPE_ACCESS_DUPLEX, - 0, - 1, - BUFFER_SIZE, - BUFFER_SIZE, - 0, - null); - if (lockHandle == NGWin32NamedPipeLibrary.INVALID_HANDLE_VALUE) { - throw new IOException(String.format("Could not create lock for %s, error %d", lockPath, API.GetLastError())); - } else { - if (!API.DisconnectNamedPipe(lockHandle)) { - throw new IOException(String.format("Could not disconnect lock %d", API.GetLastError())); - } - } - - } - - public void bind(SocketAddress endpoint) throws IOException { - throw new IOException("Win32 named pipes do not support bind(), pass path to constructor"); - } - - public Socket accept() throws IOException { - HANDLE handle = API.CreateNamedPipe( - path, - NGWin32NamedPipeLibrary.PIPE_ACCESS_DUPLEX | WinNT.FILE_FLAG_OVERLAPPED, - 0, - maxInstances, - BUFFER_SIZE, - BUFFER_SIZE, - 0, - null); - if (handle == NGWin32NamedPipeLibrary.INVALID_HANDLE_VALUE) { - throw new IOException(String.format("Could not create named pipe, error %d", API.GetLastError())); - } - openHandles.add(handle); - - HANDLE connWaitable = API.CreateEvent(null, true, false, null); - WinBase.OVERLAPPED olap = new WinBase.OVERLAPPED(); - olap.hEvent = connWaitable; - olap.write(); - - boolean immediate = API.ConnectNamedPipe(handle, olap.getPointer()); - if (immediate) { - openHandles.remove(handle); - connectedHandles.add(handle); - return new NGWin32NamedPipeSocket(handle, closeCallback); - } - - int connectError = API.GetLastError(); - if (connectError == WinError.ERROR_PIPE_CONNECTED) { - openHandles.remove(handle); - connectedHandles.add(handle); - return new NGWin32NamedPipeSocket(handle, closeCallback); - } else if (connectError == WinError.ERROR_NO_DATA) { - // Client has connected and disconnected between CreateNamedPipe() and ConnectNamedPipe() - // connection is broken, but it is returned it avoid loop here. - // Actual error will happen for NGSession when it will try to read/write from/to pipe - return new NGWin32NamedPipeSocket(handle, closeCallback); - } else if (connectError == WinError.ERROR_IO_PENDING) { - if (!API.GetOverlappedResult(handle, olap.getPointer(), new IntByReference(), true)) { - openHandles.remove(handle); - closeOpenPipe(handle); - throw new IOException("GetOverlappedResult() failed for connect operation: " + API.GetLastError()); - } - openHandles.remove(handle); - connectedHandles.add(handle); - return new NGWin32NamedPipeSocket(handle, closeCallback); - } else { - throw new IOException("ConnectNamedPipe() failed with: " + connectError); - } - } - - public void close() throws IOException { - try { - List handlesToClose = new ArrayList<>(); - openHandles.drainTo(handlesToClose); - for (HANDLE handle : handlesToClose) { - closeOpenPipe(handle); - } - - List handlesToDisconnect = new ArrayList<>(); - connectedHandles.drainTo(handlesToDisconnect); - for (HANDLE handle : handlesToDisconnect) { - closeConnectedPipe(handle, true); - } - } finally { - API.CloseHandle(lockHandle); - } - } - - private void closeOpenPipe(HANDLE handle) throws IOException { - API.CancelIoEx(handle, null); - API.CloseHandle(handle); - } - - private void closeConnectedPipe(HANDLE handle, boolean shutdown) throws IOException { - if (!shutdown) { - API.WaitForSingleObject(handle, 10000); - } - API.DisconnectNamedPipe(handle); - API.CloseHandle(handle); - } -} diff --git a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeSocket.java b/main-command/src/main/java/sbt/internal/NGWin32NamedPipeSocket.java deleted file mode 100644 index b22bb6bbf..000000000 --- a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeSocket.java +++ /dev/null @@ -1,172 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGWin32NamedPipeSocket.java -// Made change in `read` to read just the amount of bytes available. - -/* - - Copyright 2004-2017, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.Memory; -import com.sun.jna.platform.win32.WinBase; -import com.sun.jna.platform.win32.WinError; -import com.sun.jna.platform.win32.WinNT.HANDLE; -import com.sun.jna.ptr.IntByReference; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.nio.ByteBuffer; - -public class NGWin32NamedPipeSocket extends Socket { - private static final NGWin32NamedPipeLibrary API = NGWin32NamedPipeLibrary.INSTANCE; - private final HANDLE handle; - private final CloseCallback closeCallback; - private final InputStream is; - private final OutputStream os; - private final HANDLE readerWaitable; - private final HANDLE writerWaitable; - - interface CloseCallback { - void onNamedPipeSocketClose(HANDLE handle) throws IOException; - } - - public NGWin32NamedPipeSocket( - HANDLE handle, - NGWin32NamedPipeSocket.CloseCallback closeCallback) throws IOException { - this.handle = handle; - this.closeCallback = closeCallback; - this.readerWaitable = API.CreateEvent(null, true, false, null); - if (readerWaitable == null) { - throw new IOException("CreateEvent() failed "); - } - writerWaitable = API.CreateEvent(null, true, false, null); - if (writerWaitable == null) { - throw new IOException("CreateEvent() failed "); - } - this.is = new NGWin32NamedPipeSocketInputStream(handle); - this.os = new NGWin32NamedPipeSocketOutputStream(handle); - } - - @Override - public InputStream getInputStream() { - return is; - } - - @Override - public OutputStream getOutputStream() { - return os; - } - - @Override - public void close() throws IOException { - closeCallback.onNamedPipeSocketClose(handle); - } - - @Override - public void shutdownInput() throws IOException { - } - - @Override - public void shutdownOutput() throws IOException { - } - - private class NGWin32NamedPipeSocketInputStream extends InputStream { - private final HANDLE handle; - - NGWin32NamedPipeSocketInputStream(HANDLE handle) { - this.handle = handle; - } - - @Override - public int read() throws IOException { - int result; - byte[] b = new byte[1]; - if (read(b) == 0) { - result = -1; - } else { - result = 0xFF & b[0]; - } - return result; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - Memory readBuffer = new Memory(len); - - WinBase.OVERLAPPED olap = new WinBase.OVERLAPPED(); - olap.hEvent = readerWaitable; - olap.write(); - - boolean immediate = API.ReadFile(handle, readBuffer, len, null, olap.getPointer()); - if (!immediate) { - int lastError = API.GetLastError(); - if (lastError != WinError.ERROR_IO_PENDING) { - throw new IOException("ReadFile() failed: " + lastError); - } - } - - IntByReference read = new IntByReference(); - if (!API.GetOverlappedResult(handle, olap.getPointer(), read, true)) { - int lastError = API.GetLastError(); - throw new IOException("GetOverlappedResult() failed for read operation: " + lastError); - } - int actualLen = read.getValue(); - byte[] byteArray = readBuffer.getByteArray(0, actualLen); - System.arraycopy(byteArray, 0, b, off, actualLen); - return actualLen; - } - } - - private class NGWin32NamedPipeSocketOutputStream extends OutputStream { - private final HANDLE handle; - - NGWin32NamedPipeSocketOutputStream(HANDLE handle) { - this.handle = handle; - } - - @Override - public void write(int b) throws IOException { - write(new byte[]{(byte) (0xFF & b)}); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - ByteBuffer data = ByteBuffer.wrap(b, off, len); - - WinBase.OVERLAPPED olap = new WinBase.OVERLAPPED(); - olap.hEvent = writerWaitable; - olap.write(); - - boolean immediate = API.WriteFile(handle, data, len, null, olap.getPointer()); - if (!immediate) { - int lastError = API.GetLastError(); - if (lastError != WinError.ERROR_IO_PENDING) { - throw new IOException("WriteFile() failed: " + lastError); - } - } - IntByReference written = new IntByReference(); - if (!API.GetOverlappedResult(handle, olap.getPointer(), written, true)) { - int lastError = API.GetLastError(); - throw new IOException("GetOverlappedResult() failed for write operation: " + lastError); - } - if (written.getValue() != len) { - throw new IOException("WriteFile() wrote less bytes than requested"); - } - } - } -} diff --git a/main-command/src/main/java/sbt/internal/ReferenceCountedFileDescriptor.java b/main-command/src/main/java/sbt/internal/ReferenceCountedFileDescriptor.java deleted file mode 100644 index 7fb5d9d53..000000000 --- a/main-command/src/main/java/sbt/internal/ReferenceCountedFileDescriptor.java +++ /dev/null @@ -1,82 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/ReferenceCountedFileDescriptor.java - -/* - - Copyright 2004-2015, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.LastErrorException; - -import java.io.IOException; - -/** - * Encapsulates a file descriptor plus a reference count to ensure close requests - * only close the file descriptor once the last reference to the file descriptor - * is released. - * - * If not explicitly closed, the file descriptor will be closed when - * this object is finalized. - */ -public class ReferenceCountedFileDescriptor { - private int fd; - private int fdRefCount; - private boolean closePending; - - public ReferenceCountedFileDescriptor(int fd) { - this.fd = fd; - this.fdRefCount = 0; - this.closePending = false; - } - - protected void finalize() throws IOException { - close(); - } - - public synchronized int acquire() { - fdRefCount++; - return fd; - } - - public synchronized void release() throws IOException { - fdRefCount--; - if (fdRefCount == 0 && closePending && fd != -1) { - doClose(); - } - } - - public synchronized void close() throws IOException { - if (fd == -1 || closePending) { - return; - } - - if (fdRefCount == 0) { - doClose(); - } else { - // Another thread has the FD. We'll close it when they release the reference. - closePending = true; - } - } - - private void doClose() throws IOException { - try { - NGUnixDomainSocketLibrary.close(fd); - fd = -1; - } catch (LastErrorException e) { - throw new IOException(e); - } - } -} diff --git a/main-command/src/main/scala/sbt/internal/server/Server.scala b/main-command/src/main/scala/sbt/internal/server/Server.scala index 8104a1736..7e31910e6 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -25,6 +25,7 @@ import sjsonnew.support.scalajson.unsafe.{ Converter, CompactPrinter } import sbt.internal.protocol.codec._ import sbt.internal.util.ErrorHandling import sbt.internal.util.Util.isWindows +import org.scalasbt.ipcsocket._ private[sbt] sealed trait ServerInstance { def shutdown(): Unit @@ -57,11 +58,11 @@ private[sbt] object Server { connection.connectionType match { case ConnectionType.Local if isWindows => // Named pipe already has an exclusive lock. - addServerError(new NGWin32NamedPipeServerSocket(pipeName)) + addServerError(new Win32NamedPipeServerSocket(pipeName)) case ConnectionType.Local => - tryClient(new NGUnixDomainSocket(socketfile.getAbsolutePath)) + tryClient(new UnixDomainSocket(socketfile.getAbsolutePath)) prepareSocketfile() - addServerError(new NGUnixDomainServerSocket(socketfile.getAbsolutePath)) + addServerError(new UnixDomainServerSocket(socketfile.getAbsolutePath)) case ConnectionType.Tcp => tryClient(new Socket(InetAddress.getByName(host), port)) addServerError(new ServerSocket(port, 50, InetAddress.getByName(host))) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f8d470ccd..5ad8a4393 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -33,6 +33,7 @@ object Dependencies { val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.2" val rawLauncher = "org.scala-sbt" % "launcher" % "1.0.2" val testInterface = "org.scala-sbt" % "test-interface" % "1.0" + val ipcSocket = "org.scala-sbt.ipcsocket" % "ipcsocket" % "1.0.0" private val compilerInterface = "org.scala-sbt" % "compiler-interface" % zincVersion private val compilerClasspath = "org.scala-sbt" %% "zinc-classpath" % zincVersion From 4b1de14f87090512b913ba7470cb5485b98fb4f3 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 30 Jan 2018 00:42:02 -0500 Subject: [PATCH 048/120] Use State to pick the port file --- main/src/main/scala/sbt/internal/CommandExchange.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 2ece2e12a..094491945 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -114,7 +114,7 @@ private[sbt] final class CommandExchange { subscribe(channel) } if (server.isEmpty && firstInstance.get) { - val portfile = (new File(".")).getAbsoluteFile / "project" / "target" / "active.json" + val portfile = s.baseDir / "project" / "target" / "active.json" val h = Hash.halfHashString(IO.toURI(portfile).toString) val tokenfile = BuildPaths.getGlobalBase(s) / "server" / h / "token.json" val socketfile = BuildPaths.getGlobalBase(s) / "server" / h / "sock" From 1f9a8bf310cbb6b3a60c5e8f33efa4540986a909 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 24 Jan 2018 03:56:42 -0500 Subject: [PATCH 049/120] start an instance of sbt in the background --- .appveyor.yml | 2 +- .travis.yml | 2 +- build.sbt | 4 +- .../scala/sbt/protocol/ClientSocket.scala | 45 +++++ .../sbt-test/server/handshake/Client.scala | 106 ----------- sbt/src/sbt-test/server/handshake/build.sbt | 15 -- sbt/src/sbt-test/server/handshake/test | 6 - sbt/src/server-test/handshake/build.sbt | 6 + .../test/scala/sbt/RunFromSourceMain.scala | 2 +- sbt/src/test/scala/sbt/ServerSpec.scala | 179 ++++++++++++++++++ 10 files changed, 236 insertions(+), 131 deletions(-) create mode 100644 protocol/src/main/scala/sbt/protocol/ClientSocket.scala delete mode 100644 sbt/src/sbt-test/server/handshake/Client.scala delete mode 100644 sbt/src/sbt-test/server/handshake/build.sbt delete mode 100644 sbt/src/sbt-test/server/handshake/test create mode 100644 sbt/src/server-test/handshake/build.sbt create mode 100644 sbt/src/test/scala/sbt/ServerSpec.scala diff --git a/.appveyor.yml b/.appveyor.yml index a0d3292f1..7bc9e8b57 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -20,4 +20,4 @@ install: - SET PATH=C:\sbt\sbt\bin;%PATH% - SET SBT_OPTS=-XX:MaxPermSize=2g -Xmx4g -Dfile.encoding=UTF8 test_script: - - sbt "scripted actions/* server/*" + - sbt "scripted actions/*" "testOnly sbt.ServerSpec" diff --git a/.travis.yml b/.travis.yml index a8a539237..c6e2d000e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ env: - SBT_CMD="scripted dependency-management/*4of4" - SBT_CMD="scripted java/* package/* reporter/* run/* project-load/*" - SBT_CMD="scripted project/*1of2" - - SBT_CMD="scripted project/*2of2 server/*" + - SBT_CMD="scripted project/*2of2" - SBT_CMD="scripted source-dependencies/*1of3" - SBT_CMD="scripted source-dependencies/*2of3" - SBT_CMD="scripted source-dependencies/*3of3" diff --git a/build.sbt b/build.sbt index 3eb98a312..3cf04650b 100644 --- a/build.sbt +++ b/build.sbt @@ -318,6 +318,7 @@ lazy val actionsProj = (project in file("main-actions")) lazy val protocolProj = (project in file("protocol")) .enablePlugins(ContrabandPlugin, JsonCodecPlugin) + .dependsOn(collectionProj) .settings( testedBaseSettings, scalacOptions -= "-Ywarn-unused", @@ -466,7 +467,7 @@ lazy val mainProj = (project in file("main")) lazy val sbtProj = (project in file("sbt")) .dependsOn(mainProj, scriptedSbtProj % "test->test") .settings( - baseSettings, + testedBaseSettings, name := "sbt", normalizedName := "sbt", crossScalaVersions := Seq(baseScalaVersion), @@ -480,6 +481,7 @@ lazy val sbtProj = (project in file("sbt")) buildInfoKeys in Test := Seq[BuildInfoKey](fullClasspath in Compile), connectInput in run in Test := true, outputStrategy in run in Test := Some(StdoutOutput), + fork in Test := true, ) .configure(addSbtCompilerBridge) diff --git a/protocol/src/main/scala/sbt/protocol/ClientSocket.scala b/protocol/src/main/scala/sbt/protocol/ClientSocket.scala new file mode 100644 index 000000000..df155d439 --- /dev/null +++ b/protocol/src/main/scala/sbt/protocol/ClientSocket.scala @@ -0,0 +1,45 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt +package protocol + +import java.io.File +import java.net.{ Socket, URI, InetAddress } +import sjsonnew.BasicJsonProtocol +import sjsonnew.support.scalajson.unsafe.{ Parser, Converter } +import sjsonnew.shaded.scalajson.ast.unsafe.JValue +import sbt.internal.protocol.{ PortFile, TokenFile } +import sbt.internal.protocol.codec.{ PortFileFormats, TokenFileFormats } +import sbt.internal.util.Util.isWindows +import org.scalasbt.ipcsocket._ + +object ClientSocket { + private lazy val fileFormats = new BasicJsonProtocol with PortFileFormats with TokenFileFormats {} + + def socket(portfile: File): (Socket, Option[String]) = { + import fileFormats._ + val json: JValue = Parser.parseFromFile(portfile).get + val p = Converter.fromJson[PortFile](json).get + val uri = new URI(p.uri) + // println(uri) + val token = p.tokenfilePath map { tp => + val tokeFile = new File(tp) + val json: JValue = Parser.parseFromFile(tokeFile).get + val t = Converter.fromJson[TokenFile](json).get + t.token + } + val sk = uri.getScheme match { + case "local" if isWindows => + new Win32NamedPipeSocket("""\\.\pipe\""" + uri.getSchemeSpecificPart) + case "local" => new UnixDomainSocket(uri.getSchemeSpecificPart) + case "tcp" => new Socket(InetAddress.getByName(uri.getHost), uri.getPort) + case _ => sys.error(s"Unsupported uri: $uri") + } + (sk, token) + } +} diff --git a/sbt/src/sbt-test/server/handshake/Client.scala b/sbt/src/sbt-test/server/handshake/Client.scala deleted file mode 100644 index 2f41f4c18..000000000 --- a/sbt/src/sbt-test/server/handshake/Client.scala +++ /dev/null @@ -1,106 +0,0 @@ -package example - -import java.net.{ URI, Socket, InetAddress, SocketException } -import sbt.io._ -import sbt.io.syntax._ -import java.io.File -import sjsonnew.support.scalajson.unsafe.{ Parser, Converter, CompactPrinter } -import sjsonnew.shaded.scalajson.ast.unsafe.{ JValue, JObject, JString } - -object Client extends App { - val host = "127.0.0.1" - val delimiter: Byte = '\n'.toByte - - lazy val connection = getConnection - lazy val out = connection.getOutputStream - lazy val in = connection.getInputStream - - val t = getToken - val msg0 = s"""{ "type": "InitCommand", "token": "$t" }""" - - writeLine(s"Content-Length: ${ msg0.size + 2 }") - writeLine("Content-Type: application/sbt-x1") - writeLine("") - writeLine(msg0) - out.flush - - writeLine("Content-Length: 49") - writeLine("Content-Type: application/sbt-x1") - writeLine("") - // 12345678901234567890123456789012345678901234567890 - writeLine("""{ "type": "ExecCommand", "commandLine": "exit" }""") - writeLine("") - out.flush - - val baseDirectory = new File(args(0)) - IO.write(baseDirectory / "ok.txt", "ok") - - def getToken: String = { - val tokenfile = new File(getTokenFileUri) - val json: JValue = Parser.parseFromFile(tokenfile).get - json match { - case JObject(fields) => - (fields find { _.field == "token" } map { _.value }) match { - case Some(JString(value)) => value - case _ => - sys.error("json doesn't token field that is JString") - } - case _ => sys.error("json doesn't have token field") - } - } - - def getTokenFileUri: URI = { - val portfile = baseDirectory / "project" / "target" / "active.json" - val json: JValue = Parser.parseFromFile(portfile).get - json match { - case JObject(fields) => - (fields find { _.field == "tokenfileUri" } map { _.value }) match { - case Some(JString(value)) => new URI(value) - case _ => - sys.error("json doesn't tokenfile field that is JString") - } - case _ => sys.error("json doesn't have tokenfile field") - } - } - - def getPort: Int = { - val portfile = baseDirectory / "project" / "target" / "active.json" - val json: JValue = Parser.parseFromFile(portfile).get - json match { - case JObject(fields) => - (fields find { _.field == "uri" } map { _.value }) match { - case Some(JString(value)) => - val u = new URI(value) - u.getPort - case _ => - sys.error("json doesn't uri field that is JString") - } - case _ => sys.error("json doesn't have uri field") - } - } - - def getConnection: Socket = - try { - new Socket(InetAddress.getByName(host), getPort) - } catch { - case _ => - Thread.sleep(1000) - getConnection - } - - def writeLine(s: String): Unit = { - if (s != "") { - out.write(s.getBytes("UTF-8")) - } - writeEndLine - } - - def writeEndLine(): Unit = { - val retByte: Byte = '\r'.toByte - val delimiter: Byte = '\n'.toByte - - out.write(retByte.toInt) - out.write(delimiter.toInt) - out.flush - } -} diff --git a/sbt/src/sbt-test/server/handshake/build.sbt b/sbt/src/sbt-test/server/handshake/build.sbt deleted file mode 100644 index 851648f3c..000000000 --- a/sbt/src/sbt-test/server/handshake/build.sbt +++ /dev/null @@ -1,15 +0,0 @@ -lazy val runClient = taskKey[Unit]("") - -lazy val root = (project in file(".")) - .settings( - serverConnectionType in Global := ConnectionType.Tcp, - scalaVersion := "2.12.3", - serverPort in Global := 5123, - libraryDependencies += "org.scala-sbt" %% "io" % "1.0.1", - libraryDependencies += "com.eed3si9n" %% "sjson-new-scalajson" % "0.8.0", - runClient := (Def.taskDyn { - val b = baseDirectory.value - (bgRun in Compile).toTask(s""" $b""") - }).value - ) - \ No newline at end of file diff --git a/sbt/src/sbt-test/server/handshake/test b/sbt/src/sbt-test/server/handshake/test deleted file mode 100644 index 703942376..000000000 --- a/sbt/src/sbt-test/server/handshake/test +++ /dev/null @@ -1,6 +0,0 @@ -> show serverPort -> runClient - --> shell - -$ exists ok.txt diff --git a/sbt/src/server-test/handshake/build.sbt b/sbt/src/server-test/handshake/build.sbt new file mode 100644 index 000000000..192730eef --- /dev/null +++ b/sbt/src/server-test/handshake/build.sbt @@ -0,0 +1,6 @@ +lazy val root = (project in file(".")) + .settings( + Global / serverLog / logLevel := Level.Debug, + name := "handshake", + scalaVersion := "2.12.3", + ) diff --git a/sbt/src/test/scala/sbt/RunFromSourceMain.scala b/sbt/src/test/scala/sbt/RunFromSourceMain.scala index bd2f142f7..b4fc8bce4 100644 --- a/sbt/src/test/scala/sbt/RunFromSourceMain.scala +++ b/sbt/src/test/scala/sbt/RunFromSourceMain.scala @@ -22,7 +22,7 @@ object RunFromSourceMain { // this arrangement is because Scala does not always properly optimize away // the tail recursion in a catch statement - @tailrec private def run(baseDir: File, args: Seq[String]): Unit = + @tailrec private[sbt] def run(baseDir: File, args: Seq[String]): Unit = runImpl(baseDir, args) match { case Some((baseDir, args)) => run(baseDir, args) case None => () diff --git a/sbt/src/test/scala/sbt/ServerSpec.scala b/sbt/src/test/scala/sbt/ServerSpec.scala new file mode 100644 index 000000000..648d88203 --- /dev/null +++ b/sbt/src/test/scala/sbt/ServerSpec.scala @@ -0,0 +1,179 @@ +/* + * 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 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": "root/name" } }""", + out) + Thread.sleep(100) + val l2 = contentLength(in) + println(l2) + readLine(in) + readLine(in) + val x2 = readContentLength(in, l2) + println(x2) + assert(1 == 1) + } + } +} + +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 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 = 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. + if (chunk0.size > 0 && chunk0.indexOf(RetByte) == chunk0.size - 1) + Some(new String(chunk0.dropRight(1).toArray, "utf-8")) + else Some(new String(chunk0.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 { + Thread.sleep(1000) + waitForPortfile(n - 1) + } + } + waitForPortfile(10) + 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() + } + } +} From 02218582170aeb8953a89ecea963a6863860ae2b Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 31 Jan 2018 00:04:23 -0500 Subject: [PATCH 050/120] improve Windows build --- .appveyor.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7bc9e8b57..ccc70ab7c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,9 +4,8 @@ init: - git config --global core.autocrlf input install: - - cinst jdk8 -params 'installdir=C:\\jdk8' - - SET JAVA_HOME=C:\jdk8 - - SET PATH=C:\jdk8\bin;%PATH% + - SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0 + - SET PATH=%JAVA_HOME%\bin;%PATH% - ps: | Add-Type -AssemblyName System.IO.Compression.FileSystem @@ -21,3 +20,7 @@ install: - SET SBT_OPTS=-XX:MaxPermSize=2g -Xmx4g -Dfile.encoding=UTF8 test_script: - sbt "scripted actions/*" "testOnly sbt.ServerSpec" + +cache: + - '%USERPROFILE%\.ivy2\cache' + - '%USERPROFILE%\.sbt' From a9bdcc4f0fdd4b85c72c15c9e8925c16e14acba4 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 31 Jan 2018 10:20:33 +0000 Subject: [PATCH 051/120] Fix Codacy issue in ServerSpec --- sbt/src/test/scala/sbt/ServerSpec.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sbt/src/test/scala/sbt/ServerSpec.scala b/sbt/src/test/scala/sbt/ServerSpec.scala index 648d88203..9adeabcc8 100644 --- a/sbt/src/test/scala/sbt/ServerSpec.scala +++ b/sbt/src/test/scala/sbt/ServerSpec.scala @@ -108,9 +108,8 @@ object ServerSpec { val chunk0 = buffer.take(delimPos) buffer = buffer.drop(delimPos + 1) // remove \r at the end of line. - if (chunk0.size > 0 && chunk0.indexOf(RetByte) == chunk0.size - 1) - Some(new String(chunk0.dropRight(1).toArray, "utf-8")) - else Some(new String(chunk0.toArray, "utf-8")) + val chunk1 = if (chunk0.isEmpty || chunk0.last != RetByte) chunk0 else chunk0.dropRight(1) + Some(new String(chunk1.toArray, "utf-8")) } else None // no EOL yet, so skip this turn. } From abb6f579a73d7268ae2d8025c8f2b0b8690c36f7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Feb 2018 17:29:26 +0000 Subject: [PATCH 052/120] Un-stub RunFromSource's ComponentProvider impl --- .../test/scala/sbt/RunFromSourceMain.scala | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/sbt/src/test/scala/sbt/RunFromSourceMain.scala b/sbt/src/test/scala/sbt/RunFromSourceMain.scala index b4fc8bce4..4ccc110ba 100644 --- a/sbt/src/test/scala/sbt/RunFromSourceMain.scala +++ b/sbt/src/test/scala/sbt/RunFromSourceMain.scala @@ -103,9 +103,31 @@ object RunFromSourceMain { def components = new ComponentProvider { def componentLocation(id: String) = appHome / id def component(id: String) = IO.listFiles(componentLocation(id), _.isFile) - def defineComponent(id: String, components: Array[File]) = () - def addToComponent(id: String, components: Array[File]) = false - def lockFile = null + + def defineComponent(id: String, files: Array[File]) = { + val location = componentLocation(id) + if (location.exists) + sys error s"Cannot redefine component. ID: $id, files: ${files mkString ","}" + else { + copy(files.toList, location) + () + } + } + + def addToComponent(id: String, files: Array[File]) = + copy(files.toList, componentLocation(id)) + + def lockFile = appHome / "sbt.components.lock" + + private def copy(files: List[File], toDirectory: File): Boolean = + files exists (copy(_, toDirectory)) + + private def copy(file: File, toDirectory: File): Boolean = { + val to = toDirectory / file.getName + val missing = !to.exists + IO.copyFile(file, to) + missing + } } } } From 4f4328748c33f141f19a74f5f92ac54a7cc704b1 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Feb 2018 14:53:11 +0000 Subject: [PATCH 053/120] Try RunFromSourceBasedRemoteSbtCreator --- build.sbt | 16 ++ .../sbt/scriptedtest/RemoteSbtCreator.scala | 67 ++++++++ .../scala/sbt/scriptedtest/SbtHandler.scala | 31 ++-- .../sbt/scriptedtest/ScriptedTests.scala | 150 ++++++++++++++++-- 4 files changed, 232 insertions(+), 32 deletions(-) create mode 100644 scripted/sbt/src/main/scala/sbt/scriptedtest/RemoteSbtCreator.scala diff --git a/build.sbt b/build.sbt index 3cf04650b..b6ed67172 100644 --- a/build.sbt +++ b/build.sbt @@ -261,12 +261,27 @@ lazy val runProj = (project in file("run")) ) .configure(addSbtIO, addSbtUtilLogging, addSbtCompilerClasspath) +val sbtProjDepsCompileScopeFilter = + ScopeFilter(inDependencies(LocalProject("sbtProj"), includeRoot = false), inConfigurations(Compile)) + lazy val scriptedSbtProj = (project in scriptedPath / "sbt") .dependsOn(commandProj) .settings( baseSettings, name := "Scripted sbt", libraryDependencies ++= Seq(launcherInterface % "provided"), + resourceGenerators in Compile += Def task { + val mainClassDir = (classDirectory in Compile in LocalProject("sbtProj")).value + val testClassDir = (classDirectory in Test in LocalProject("sbtProj")).value + val classDirs = (classDirectory all sbtProjDepsCompileScopeFilter).value + val extDepsCp = (externalDependencyClasspath in Compile in LocalProject("sbtProj")).value + + val cpStrings = (mainClassDir +: testClassDir +: classDirs) ++ extDepsCp.files map (_.toString) + + val file = (resourceManaged in Compile).value / "RunFromSource.classpath" + IO.writeLines(file, cpStrings) + List(file) + }, mimaSettings, mimaBinaryIssueFilters ++= Seq( // sbt.test package is renamed to sbt.scriptedtest. @@ -550,6 +565,7 @@ def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask { val result = scriptedSource(dir => (s: State) => Scripted.scriptedParser(dir)).parsed // publishLocalBinAll.value // TODO: Restore scripted needing only binary jars. publishAll.value + (sbtProj / Test / compile).value // make sure sbt.RunFromSourceMain is compiled Scripted.doScripted( (sbtLaunchJar in bundledLauncherProj).value, (fullClasspath in scriptedSbtProj in Test).value, diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/RemoteSbtCreator.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/RemoteSbtCreator.scala new file mode 100644 index 000000000..5dbf2ef51 --- /dev/null +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/RemoteSbtCreator.scala @@ -0,0 +1,67 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt +package scriptedtest + +import java.io.File + +import scala.sys.process.{ BasicIO, Process } + +import sbt.io.IO +import sbt.util.Logger + +import xsbt.IPC + +private[sbt] sealed trait RemoteSbtCreatorKind +private[sbt] object RemoteSbtCreatorKind { + case object LauncherBased extends RemoteSbtCreatorKind + case object RunFromSourceBased extends RemoteSbtCreatorKind +} + +abstract class RemoteSbtCreator private[sbt] { + def newRemote(server: IPC.Server): Process +} + +final class LauncherBasedRemoteSbtCreator( + directory: File, + launcher: File, + log: Logger, + launchOpts: Seq[String] = Nil, +) extends RemoteSbtCreator { + def newRemote(server: IPC.Server) = { + val launcherJar = launcher.getAbsolutePath + val globalBase = "-Dsbt.global.base=" + (new File(directory, "global")).getAbsolutePath + val args = List("<" + server.port) + val cmd = "java" :: launchOpts.toList ::: globalBase :: "-jar" :: launcherJar :: args ::: Nil + val io = BasicIO(false, log).withInput(_.close()) + val p = Process(cmd, directory) run (io) + val thread = new Thread() { override def run() = { p.exitValue(); server.close() } } + thread.start() + p + } +} + +final class RunFromSourceBasedRemoteSbtCreator( + directory: File, + log: Logger, + launchOpts: Seq[String] = Nil, +) extends RemoteSbtCreator { + def newRemote(server: IPC.Server) = { + val globalBase = "-Dsbt.global.base=" + (new File(directory, "global")).getAbsolutePath + val cp = IO readLinesURL (getClass getResource "/RunFromSource.classpath") + val cpString = cp mkString File.pathSeparator + val mainClassName = "sbt.RunFromSourceMain" + val args = List(mainClassName, directory.toString, "<" + server.port) + val cmd = "java" :: launchOpts.toList ::: globalBase :: "-cp" :: cpString :: args ::: Nil + val io = BasicIO(false, log).withInput(_.close()) + val p = Process(cmd, directory) run (io) + val thread = new Thread() { override def run() = { p.exitValue(); server.close() } } + thread.start() + p + } +} diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala index 3ddecbab8..9ba5be673 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala @@ -8,23 +8,19 @@ package sbt package scriptedtest -import java.io.{ File, IOException } -import xsbt.IPC +import java.io.IOException +import java.net.SocketException + +import scala.sys.process.Process import sbt.internal.scripted.{ StatementHandler, TestFailed } -import sbt.util.Logger -import sbt.util.Logger._ - -import scala.sys.process.{ BasicIO, Process } +import xsbt.IPC final case class SbtInstance(process: Process, server: IPC.Server) -final class SbtHandler(directory: File, - launcher: File, - log: Logger, - launchOpts: Seq[String] = Seq()) - extends StatementHandler { +final class SbtHandler(remoteSbtCreator: RemoteSbtCreator) extends StatementHandler { + type State = Option[SbtInstance] def initialState = None @@ -78,16 +74,9 @@ final class SbtHandler(directory: File, if (!resultMessage.toBoolean) throw new TestFailed(errorMessage) } def newRemote(server: IPC.Server): Process = { - val launcherJar = launcher.getAbsolutePath - val globalBase = "-Dsbt.global.base=" + (new File(directory, "global")).getAbsolutePath - val args = "java" :: (launchOpts.toList ++ (globalBase :: "-jar" :: launcherJar :: ("<" + server.port) :: Nil)) - val io = BasicIO(false, log).withInput(_.close()) - val p = Process(args, directory) run (io) - val thread = new Thread() { override def run() = { p.exitValue(); server.close() } } - thread.start() - try { receive("Remote sbt initialization failed", server) } catch { - case _: java.net.SocketException => throw new TestFailed("Remote sbt initialization failed") - } + val p = remoteSbtCreator.newRemote(server) + try receive("Remote sbt initialization failed", server) + catch { case _: SocketException => throw new TestFailed("Remote sbt initialization failed") } p } import java.util.regex.Pattern.{ quote => q } diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index 4d552994c..88c28d01c 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -57,7 +57,8 @@ final class ScriptedTests(resourceBaseDirectory: File, val result = testResources.readWriteResourceDirectory(g, n) { testDirectory => val buffer = new BufferedLogger(new FullLogger(log)) val singleTestRunner = () => { - val handlers = createScriptedHandlers(testDirectory, buffer) + val handlers = + createScriptedHandlers(testDirectory, buffer, RemoteSbtCreatorKind.LauncherBased) val runner = new BatchScriptRunner val states = new mutable.HashMap[StatementHandler, Any]() commonRunTest(label, testDirectory, prescripted, handlers, runner, states, buffer) @@ -71,10 +72,17 @@ final class ScriptedTests(resourceBaseDirectory: File, private def createScriptedHandlers( testDir: File, - buffered: Logger + buffered: Logger, + remoteSbtCreatorKind: RemoteSbtCreatorKind, ): Map[Char, StatementHandler] = { val fileHandler = new FileCommands(testDir) - val sbtHandler = new SbtHandler(testDir, launcher, buffered, launchOpts) + val remoteSbtCreator = remoteSbtCreatorKind match { + case RemoteSbtCreatorKind.LauncherBased => + new LauncherBasedRemoteSbtCreator(testDir, launcher, buffered, launchOpts) + case RemoteSbtCreatorKind.RunFromSourceBased => + new RunFromSourceBasedRemoteSbtCreator(testDir, buffered, launchOpts) + } + val sbtHandler = new SbtHandler(remoteSbtCreator) Map('$' -> fileHandler, '>' -> sbtHandler, '#' -> CommentHandler) } @@ -94,6 +102,8 @@ final class ScriptedTests(resourceBaseDirectory: File, } yield (groupDir, testDir) } + type TestInfo = ((String, String), File) + val labelsAndDirs = groupAndNameDirs.map { case (groupDir, nameDir) => val groupName = groupDir.getName @@ -104,15 +114,132 @@ final class ScriptedTests(resourceBaseDirectory: File, if (labelsAndDirs.isEmpty) List() else { - val batchSeed = labelsAndDirs.size / sbtInstances - val batchSize = if (batchSeed == 0) labelsAndDirs.size else batchSeed - labelsAndDirs - .grouped(batchSize) - .map(batch => () => IO.withTemporaryDirectory(runBatchedTests(batch, _, prescripted, log))) - .toList + val totalSize = labelsAndDirs.size + val batchSize = totalSize / sbtInstances + + val (launcherBasedTests, runFromSourceBasedTests) = labelsAndDirs.partition { + case (testName, _) => + determineRemoteSbtCreatorKind(testName) match { + case RemoteSbtCreatorKind.LauncherBased => true + case RemoteSbtCreatorKind.RunFromSourceBased => false + } + } + + def logTests(size: Int, how: String) = + log.info( + f"Running $size / $totalSize (${size * 100D / totalSize}%3.2f%%) scripted tests with $how") + logTests(runFromSourceBasedTests.size, "RunFromSourceMain") + logTests(launcherBasedTests.size, "sbt/launcher") + + def createTestRunners( + tests: Seq[TestInfo], + remoteSbtCreatorKind: RemoteSbtCreatorKind, + ): Seq[TestRunner] = { + tests + .grouped(batchSize) + .map { batch => () => + IO.withTemporaryDirectory { + runBatchedTests(batch, _, prescripted, remoteSbtCreatorKind, log) + } + } + .toList + } + + createTestRunners(runFromSourceBasedTests, RemoteSbtCreatorKind.RunFromSourceBased) ++ + createTestRunners(launcherBasedTests, RemoteSbtCreatorKind.LauncherBased) } } + private def determineRemoteSbtCreatorKind(testName: (String, String)): RemoteSbtCreatorKind = { + import RemoteSbtCreatorKind._ + val (group, name) = testName + s"$group/$name" match { + case "actions/add-alias" => LauncherBased // sbt/Package$ + case "actions/cross-multiproject" => LauncherBased // tbd + case "actions/external-doc" => LauncherBased // sbt/Package$ + case "actions/input-task" => LauncherBased // sbt/Package$ + case "actions/input-task-dyn" => LauncherBased // sbt/Package$ + case "compiler-project/run-test" => LauncherBased // sbt/Package$ + case "compiler-project/src-dep-plugin" => LauncherBased // sbt/Package$ + case "dependency-management/artifact" => LauncherBased // tbd + case "dependency-management/cache-classifiers" => LauncherBased // tbd + case "dependency-management/cache-local" => LauncherBased // tbd + case "dependency-management/cache-resolver" => LauncherBased // sbt/Package$ + case "dependency-management/cache-update" => LauncherBased // tbd + case "dependency-management/cached-resolution-circular" => LauncherBased // tbd + case "dependency-management/cached-resolution-classifier" => LauncherBased // tbd + case "dependency-management/cached-resolution-configurations" => LauncherBased // tbd + case "dependency-management/cached-resolution-conflicts" => LauncherBased // tbd + case "dependency-management/cached-resolution-exclude" => LauncherBased // tbd + case "dependency-management/cached-resolution-force" => LauncherBased // tbd + case "dependency-management/cached-resolution-interproj" => LauncherBased // tbd + case "dependency-management/cached-resolution-overrides" => LauncherBased // tbd + case "dependency-management/chainresolver" => LauncherBased // tbd + case "dependency-management/circular-dependency" => LauncherBased // tbd + case "dependency-management/classifier" => LauncherBased // tbd + case "dependency-management/default-resolvers" => LauncherBased // tbd + case "dependency-management/deliver-artifacts" => LauncherBased // tbd + case "dependency-management/exclude-transitive" => LauncherBased // tbd + case "dependency-management/extra" => LauncherBased // tbd + case "dependency-management/force" => LauncherBased // tbd + case "dependency-management/info" => LauncherBased // tbd + case "dependency-management/inline-dependencies-a" => LauncherBased // tbd + case "dependency-management/ivy-settings-c" => LauncherBased // sbt/Package$ + case "dependency-management/latest-local-plugin" => LauncherBased // sbt/Package$ + case "dependency-management/metadata-only-resolver" => LauncherBased // tbd + case "dependency-management/no-file-fails-publish" => LauncherBased // tbd + case "dependency-management/override" => LauncherBased // tbd + case "dependency-management/parent-publish" => LauncherBased // sbt/Package$ + case "dependency-management/pom-parent-pom" => LauncherBased // tbd + case "dependency-management/publish-to-maven-local-file" => LauncherBased // sbt/Package$ + case "dependency-management/snapshot-resolution" => LauncherBased // tbd + case "dependency-management/test-artifact" => LauncherBased // sbt/Package$ + case "dependency-management/transitive-version-range" => LauncherBased // tbd + case "dependency-management/update-sbt-classifiers" => LauncherBased // tbd + case "dependency-management/url" => LauncherBased // tbd + case "java/argfile" => LauncherBased // sbt/Package$ + case "java/basic" => LauncherBased // sbt/Package$ + case "java/varargs-main" => LauncherBased // sbt/Package$ + case "package/lazy-name" => LauncherBased // sbt/Package$ + case "package/manifest" => LauncherBased // sbt/Package$ + case "package/resources" => LauncherBased // sbt/Package$ + case "project/Class.forName" => LauncherBased // sbt/Package$ + case "project/binary-plugin" => LauncherBased // sbt/Package$ + case "project/default-settings" => LauncherBased // sbt/Package$ + case "project/extra" => LauncherBased // tbd + case "project/flatten" => LauncherBased // sbt/Package$ + case "project/generated-root-no-publish" => LauncherBased // tbd + case "project/lib" => LauncherBased // sbt/Package$ + case "project/scripted-plugin" => LauncherBased // tbd + case "project/scripted-skip-incompatible" => LauncherBased // sbt/Package$ + case "project/session-update-from-cmd" => LauncherBased // tbd + case "project/transitive-plugins" => LauncherBased // tbd + case "run/awt" => LauncherBased // sbt/Package$ + case "run/classpath" => LauncherBased // sbt/Package$ + case "run/daemon" => LauncherBased // sbt/Package$ + case "run/daemon-exit" => LauncherBased // sbt/Package$ + case "run/error" => LauncherBased // sbt/Package$ + case "run/fork" => LauncherBased // sbt/Package$ + case "run/fork-loader" => LauncherBased // sbt/Package$ + case "run/non-local-main" => LauncherBased // sbt/Package$ + case "run/spawn" => LauncherBased // sbt/Package$ + case "run/spawn-exit" => LauncherBased // sbt/Package$ + case "source-dependencies/binary" => LauncherBased // sbt/Package$ + case "source-dependencies/export-jars" => LauncherBased // sbt/Package$ + case "source-dependencies/implicit-search" => LauncherBased // sbt/Package$ + case "source-dependencies/java-basic" => LauncherBased // sbt/Package$ + case "source-dependencies/less-inter-inv" => LauncherBased // sbt/Package$ + case "source-dependencies/less-inter-inv-java" => LauncherBased // sbt/Package$ + case "source-dependencies/linearization" => LauncherBased // sbt/Package$ + case "source-dependencies/named" => LauncherBased // sbt/Package$ + case "source-dependencies/specialized" => LauncherBased // sbt/Package$ + case _ => RunFromSourceBased + } + // sbt/Package$ means: + // java.lang.NoClassDefFoundError: sbt/Package$ (wrong name: sbt/package$) + // Typically from Compile / packageBin / packageOptions + } + /** Defines an auto plugin that is injected to sbt between every scripted session. * * It sets the name of the local root project for those tests run in batch mode. @@ -168,12 +295,13 @@ final class ScriptedTests(resourceBaseDirectory: File, groupedTests: Seq[((String, String), File)], tempTestDir: File, preHook: File => Unit, - log: Logger + remoteSbtCreatorKind: RemoteSbtCreatorKind, + log: Logger, ): Seq[Option[String]] = { val runner = new BatchScriptRunner val buffer = new BufferedLogger(new FullLogger(log)) - val handlers = createScriptedHandlers(tempTestDir, buffer) + val handlers = createScriptedHandlers(tempTestDir, buffer, remoteSbtCreatorKind) val states = new BatchScriptRunner.States val seqHandlers = handlers.values.toList runner.initStates(states, seqHandlers) From 3d0619fa3797edfcab248ec7e06cc6d59bfd0cd3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 6 Feb 2018 15:18:50 +0000 Subject: [PATCH 054/120] Add MavenCentral to RunFromSourceMain's repos --- sbt/src/test/scala/sbt/RunFromSourceMain.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sbt/src/test/scala/sbt/RunFromSourceMain.scala b/sbt/src/test/scala/sbt/RunFromSourceMain.scala index 4ccc110ba..b369fd5a7 100644 --- a/sbt/src/test/scala/sbt/RunFromSourceMain.scala +++ b/sbt/src/test/scala/sbt/RunFromSourceMain.scala @@ -64,8 +64,10 @@ object RunFromSourceMain { def globalLock = noGlobalLock def bootDirectory = appProvider.bootDirectory def ivyHome = file(sys.props("user.home")) / ".ivy2" - def ivyRepositories = Array(new PredefinedRepository { def id() = Predefined.Local }) - def appRepositories = Array(new PredefinedRepository { def id() = Predefined.Local }) + final case class PredefRepo(id: Predefined) extends PredefinedRepository + import Predefined._ + def ivyRepositories = Array(PredefRepo(Local), PredefRepo(MavenCentral)) + def appRepositories = Array(PredefRepo(Local), PredefRepo(MavenCentral)) def isOverrideRepositories = false def checksums = Array("sha1", "md5") } From 11dbbd0cfa6df6e4e536a1e833ed8aa3a86eee4a Mon Sep 17 00:00:00 2001 From: Deokhwan Kim Date: Wed, 7 Feb 2018 00:27:58 -0500 Subject: [PATCH 055/120] Update documentation for skip 'skip' is also supported for 'publish' by sbt/sbt#3380. --- main/src/main/scala/sbt/Keys.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index bfcb3139e..78cc96dfd 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -445,7 +445,7 @@ object Keys { val sbtDependency = settingKey[ModuleID]("Provides a definition for declaring the current version of sbt.").withRank(BMinusSetting) val sbtVersion = settingKey[String]("Provides the version of sbt. This setting should not be modified.").withRank(AMinusSetting) val sbtBinaryVersion = settingKey[String]("Defines the binary compatibility version substring.").withRank(BPlusSetting) - val skip = taskKey[Boolean]("For tasks that support it (currently only 'compile' and 'update'), setting skip to true will force the task to not to do its work. This exact semantics may vary by task.").withRank(BSetting) + val skip = taskKey[Boolean]("For tasks that support it (currently only 'compile', 'update', and 'publish'), setting skip to true will force the task to not to do its work. This exact semantics may vary by task.").withRank(BSetting) val templateResolverInfos = settingKey[Seq[TemplateResolverInfo]]("Template resolvers used for 'new'.").withRank(BSetting) val interactionService = taskKey[InteractionService]("Service used to ask for user input through the current user interface(s).").withRank(CTask) val insideCI = SettingKey[Boolean]("insideCI", "Determines if the SBT is running in a Continuous Integration environment", AMinusSetting) From f56d5b2d90095271ba963ad14c18001b79cb9b97 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Tue, 13 Feb 2018 10:00:50 +0900 Subject: [PATCH 056/120] update README. s/1.0.x/1.x/ --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dab1a3eee..1d1a8dc08 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,10 @@ sbt is a build tool for Scala, Java, and more. For general documentation, see http://www.scala-sbt.org/. -sbt 1.0.x +sbt 1.x --------- -This is the 1.0.x series of sbt. The source code of sbt is split across +This is the 1.x series of sbt. The source code of sbt is split across several Github repositories, including this one. - [sbt/io][sbt/io] hosts `sbt.io` module. From 20367938b180e5f76b7eb8dfeff6edc27d3d9d52 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 14 Feb 2018 14:38:25 +0000 Subject: [PATCH 057/120] version := "1.2.0-SNAPSHOT" --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 431491cb9..893c2c9f1 100644 --- a/build.sbt +++ b/build.sbt @@ -9,7 +9,7 @@ def buildLevelSettings: Seq[Setting[_]] = inThisBuild( Seq( organization := "org.scala-sbt", - version := "1.1.2-SNAPSHOT", + version := "1.2.0-SNAPSHOT", description := "sbt is an interactive build tool", bintrayOrganization := Some("sbt"), bintrayRepository := { From be43c43783f81bcebe131c12ce2aa690458e05c7 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Sat, 17 Feb 2018 13:41:00 -0500 Subject: [PATCH 058/120] Fix typo in the 1.1.1 notes --- notes/1.1.1.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/1.1.1.markdown b/notes/1.1.1.markdown index 58cba0a73..2115809d3 100644 --- a/notes/1.1.1.markdown +++ b/notes/1.1.1.markdown @@ -17,7 +17,7 @@ ### autoStartServer setting sbt 1.1.1 adds a new global `Boolean` setting called `autoStartServer`, which is set to `true` by default. -When set to `true`, sbt shell will automatically start sbt server. Otherwise, it will not start the server until `startSever` command is issued. This could be used to opt out of server for security reasons. +When set to `true`, sbt shell will automatically start sbt server. Otherwise, it will not start the server until `startServer` command is issued. This could be used to opt out of server for security reasons. [#3922][3922] by [@swaldman][@swaldman] From 9370a2adf0404d4588a27d61dd88e4ca7be9ffd4 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Wed, 7 Feb 2018 16:10:48 +0100 Subject: [PATCH 059/120] Speedup Parsers.validID It turned up in profiling sessions. Previously, it used parser combinators which are somewhat slow especially when the JVM is still cold. The grammar for ID is simple enough to afford this handwritten parser. --- .../sbt/internal/util/complete/Parsers.scala | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala index b04b61127..c148095ec 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala @@ -12,15 +12,17 @@ import Parser._ import java.io.File import java.net.URI import java.lang.Character.{ - getType, - MATH_SYMBOL, - OTHER_SYMBOL, + CURRENCY_SYMBOL, DASH_PUNCTUATION, - OTHER_PUNCTUATION, + MATH_SYMBOL, MODIFIER_SYMBOL, - CURRENCY_SYMBOL + OTHER_PUNCTUATION, + OTHER_SYMBOL, + getType } +import scala.annotation.tailrec + /** Provides standard implementations of commonly useful [[Parser]]s. */ trait Parsers { @@ -313,6 +315,16 @@ object DefaultParsers extends Parsers with ParserMain { apply(p)(s).resultEmpty.isValid /** Returns `true` if `s` parses successfully according to [[ID]].*/ - def validID(s: String): Boolean = matches(ID, s) + def validID(s: String): Boolean = { + // Handwritten version of `matches(ID, s)` because validID turned up in profiling. + def isIdChar(c: Char): Boolean = Character.isLetterOrDigit(c) || (c == '_') + @tailrec def isRestIdChar(cur: Int, s: String, length: Int): Boolean = + if (cur < length) + isIdChar(s.charAt(cur)) && isRestIdChar(cur + 1, s, length) + else + true + + !s.isEmpty && Character.isLetter(s.charAt(0)) && isRestIdChar(1, s, s.length) + } } From d66d0e34a96282f0d3ff68a3e05775c1720e335e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 19 Feb 2018 13:59:51 +0000 Subject: [PATCH 060/120] Add prop-based test for the validID re-impl --- .../src/test/scala/DefaultParsersSpec.scala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 internal/util-complete/src/test/scala/DefaultParsersSpec.scala diff --git a/internal/util-complete/src/test/scala/DefaultParsersSpec.scala b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala new file mode 100644 index 000000000..ce3a6b2e9 --- /dev/null +++ b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala @@ -0,0 +1,17 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt.internal.util +package complete + +import org.scalacheck._, Prop._ + +object DefaultParsersSpec extends Properties("DefaultParsers") { + import DefaultParsers._ + + property("validID == matches(ID, s)") = forAll((s: String) => validID(s) == matches(ID, s)) +} From fc73203d0b0823038403975b7d3334665afa8350 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 19 Feb 2018 18:42:24 +0000 Subject: [PATCH 061/120] Fix validID & expand tests Make sure that we generate valid ID (according to matches(ID, s)) so that we properly test the new validID implementation. And that's what led to the bug fix. :) --- .../sbt/internal/util/complete/Parsers.scala | 2 +- .../src/test/scala/DefaultParsersSpec.scala | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala index c148095ec..1120ed173 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala @@ -317,7 +317,7 @@ object DefaultParsers extends Parsers with ParserMain { /** Returns `true` if `s` parses successfully according to [[ID]].*/ def validID(s: String): Boolean = { // Handwritten version of `matches(ID, s)` because validID turned up in profiling. - def isIdChar(c: Char): Boolean = Character.isLetterOrDigit(c) || (c == '_') + def isIdChar(c: Char): Boolean = Character.isLetterOrDigit(c) || (c == '-') || (c == '_') @tailrec def isRestIdChar(cur: Int, s: String, length: Int): Boolean = if (cur < length) isIdChar(s.charAt(cur)) && isRestIdChar(cur + 1, s, length) diff --git a/internal/util-complete/src/test/scala/DefaultParsersSpec.scala b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala index ce3a6b2e9..253a4216b 100644 --- a/internal/util-complete/src/test/scala/DefaultParsersSpec.scala +++ b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala @@ -8,10 +8,21 @@ package sbt.internal.util package complete -import org.scalacheck._, Prop._ +import org.scalacheck._, Gen._, Prop._ object DefaultParsersSpec extends Properties("DefaultParsers") { - import DefaultParsers._ + import DefaultParsers.{ ID, isIDChar, matches, validID } - property("validID == matches(ID, s)") = forAll((s: String) => validID(s) == matches(ID, s)) + property("∀ s ∈ String: validID(s) == matches(ID, s)") = forAll( + (s: String) => validID(s) == matches(ID, s)) + + property("∀ s ∈ genID: matches(ID, s)") = forAll(genID)(s => matches(ID, s)) + property("∀ s ∈ genID: validID(s)") = forAll(genID)(s => validID(s)) + + private val chars: Seq[Char] = Char.MinValue to Char.MaxValue + private val genID: Gen[String] = + for { + c <- oneOf(chars filter (_.isLetter)) + cs <- listOf(oneOf(chars filter isIDChar)) + } yield (c :: cs).mkString } From 36438d2ac31d0e89348dafd21f9e924553a6cbe7 Mon Sep 17 00:00:00 2001 From: exoego Date: Thu, 15 Feb 2018 22:33:11 +0900 Subject: [PATCH 062/120] Add eviction warnings options to global --- main/src/main/scala/sbt/Defaults.scala | 3 ++- notes/1.2.0/global-eviction-warnin-options.md | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 notes/1.2.0/global-eviction-warnin-options.md diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index aba681b1a..2ab1b4259 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1753,6 +1753,7 @@ object Classpaths { Defaults.globalDefaults( Seq( conflictWarning :== ConflictWarning.default("global"), + evictionWarningOptions := EvictionWarningOptions.default, compatibilityWarningOptions :== CompatibilityWarningOptions.default, homepage :== None, startYear :== None, @@ -1988,7 +1989,6 @@ object Classpaths { val suffix = if (crossPaths.value) s"_$binVersion" else "" s"update_cache$suffix" }, - evictionWarningOptions in update := EvictionWarningOptions.default, dependencyPositions := dependencyPositionsTask.value, unresolvedWarningConfiguration in update := UnresolvedWarningConfiguration( dependencyPositions.value), @@ -1999,6 +1999,7 @@ object Classpaths { ConflictWarning(conflictWarning.value, report, log) report }, + evictionWarningOptions in update := (evictionWarningOptions in GlobalScope).value, evictionWarningOptions in evicted := EvictionWarningOptions.full, evicted := { import ShowLines._ diff --git a/notes/1.2.0/global-eviction-warnin-options.md b/notes/1.2.0/global-eviction-warnin-options.md new file mode 100644 index 000000000..981bc2477 --- /dev/null +++ b/notes/1.2.0/global-eviction-warnin-options.md @@ -0,0 +1,3 @@ +### Improvements + +- Add the eviction warning options to global, so that one can change the options for all sub projects at a time. From 0bea7e9e182329f888d7c9b2f5ce8c3fc034defe Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 5 Mar 2018 18:06:46 +1000 Subject: [PATCH 063/120] Fix race condition in non-forked, parallel tests. Non forked tests that are run in parallel groups can call into a single instance of TestStatusReporter concurrently. This seems to be limited to the startGroup/endGroup/testEvent methods called in: https://github.com/sbt/sbt/blob/a41727fb17bb923036f90ca2ca62897def1a893e/testing/src/main/scala/sbt/TestFramework.scala#L100-L124 Which itself is called within: https://github.com/sbt/sbt/blob/a41727fb17bb923036f90ca2ca62897def1a893e/testing/src/main/scala/sbt/TestFramework.scala#L203-L229 Creating the `runnables` that are run in parallel (in builds so configured): https://github.com/sbt/sbt/blob/a6eb1260c8162bfbfcbfb8656f152854a93d33ae/main-actions/src/main/scala/sbt/Tests.scala#L222-L230 We believe this to be the cause of the hang witnessed in the a suite of Scalacheck-framework tests in the Scala build: https://github.com/scala/scala-jenkins-infra/issues/249 This commit uses a concurrent map to support concurrent status updates. --- .../main/scala/sbt/internal/server/Server.scala | 3 ++- .../src/main/scala/sbt/TestStatusReporter.scala | 17 +++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/main-command/src/main/scala/sbt/internal/server/Server.scala b/main-command/src/main/scala/sbt/internal/server/Server.scala index 05b86c48a..c2bccb886 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -63,7 +63,8 @@ private[sbt] object Server { val maxSocketLength = new UnixDomainSocketLibrary.SockaddrUn().sunPath.length - 1 val path = socketfile.getAbsolutePath if (path.length > maxSocketLength) - sys.error("socket file absolute path too long; " + + sys.error( + "socket file absolute path too long; " + "either switch to another connection type " + "or define a short \"SBT_GLOBAL_SERVER_DIR\" value. " + s"Current path: ${path}") diff --git a/testing/src/main/scala/sbt/TestStatusReporter.scala b/testing/src/main/scala/sbt/TestStatusReporter.scala index 732e3257c..a99d084fe 100644 --- a/testing/src/main/scala/sbt/TestStatusReporter.scala +++ b/testing/src/main/scala/sbt/TestStatusReporter.scala @@ -8,14 +8,16 @@ package sbt import java.io.File -import sbt.io.IO -import scala.collection.mutable.Map +import sbt.io.IO import sbt.protocol.testing.TestResult +import java.util.concurrent.ConcurrentHashMap + +import scala.collection.concurrent // Assumes exclusive ownership of the file. private[sbt] class TestStatusReporter(f: File) extends TestsListener { - private lazy val succeeded = TestStatus.read(f) + private lazy val succeeded: concurrent.Map[String, Long] = TestStatus.read(f) def doInit = () def startGroup(name: String): Unit = { succeeded remove name } @@ -32,13 +34,16 @@ private[sbt] class TestStatusReporter(f: File) extends TestsListener { private[sbt] object TestStatus { import java.util.Properties - def read(f: File): Map[String, Long] = { + def read(f: File): concurrent.Map[String, Long] = { import scala.collection.JavaConverters._ val properties = new Properties IO.load(properties, f) - properties.asScala map { case (k, v) => (k, v.toLong) } + val result = new ConcurrentHashMap[String, Long]() + properties.asScala.iterator.foreach { case (k, v) => result.put(k, v.toLong) } + result.asScala } - def write(map: Map[String, Long], label: String, f: File): Unit = { + + def write(map: collection.Map[String, Long], label: String, f: File): Unit = { val properties = new Properties for ((test, lastSuccessTime) <- map) properties.setProperty(test, lastSuccessTime.toString) From 3921c45563461ada21d7a8c84e14885715b294f0 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 6 Mar 2018 10:37:42 +0000 Subject: [PATCH 064/120] Document RichInitX classes --- main-settings/src/main/scala/sbt/Structure.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index 72cc84b63..36955839d 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -338,6 +338,11 @@ object Scoped { (this.? zipWith i)((x, y) => (x, y) map { case (a, b) => a getOrElse b }) } + /** Enriches `Initialize[Task[S]]` types. + * + * @param i the original `Initialize[Task[S]]` value to enrich + * @tparam S the type of the underlying value + */ final class RichInitializeTask[S](i: Initialize[Task[S]]) extends RichInitTaskBase[S, Task] { protected def onTask[T](f: Task[S] => Task[T]): Initialize[Task[T]] = i apply f @@ -367,8 +372,14 @@ object Scoped { } } + /** Enriches `Initialize[InputTask[S]]` types. + * + * @param i the original `Initialize[InputTask[S]]` value to enrich + * @tparam S the type of the underlying value + */ final class RichInitializeInputTask[S](i: Initialize[InputTask[S]]) extends RichInitTaskBase[S, InputTask] { + protected def onTask[T](f: Task[S] => Task[T]): Initialize[InputTask[T]] = i(_ mapTask f) def dependsOn(tasks: AnyInitTask*): Initialize[InputTask[S]] = { @@ -378,6 +389,11 @@ object Scoped { } } + /** Enriches `Initialize[R[S]]` types. Abstracts over the specific task-like type constructor. + * + * @tparam S the type of the underlying vault + * @tparam R the task-like type constructor (either Task or InputTask) + */ sealed abstract class RichInitTaskBase[S, R[_]] { protected def onTask[T](f: Task[S] => Task[T]): Initialize[R[T]] From 77abb9ee292a785095285faf6c3eea6c3402e1e7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 27 Jan 2018 09:40:42 +0000 Subject: [PATCH 065/120] Cleanup InputConvert --- .../src/main/scala/sbt/std/InputConvert.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/main-settings/src/main/scala/sbt/std/InputConvert.scala b/main-settings/src/main/scala/sbt/std/InputConvert.scala index 3437af145..f7d581946 100644 --- a/main-settings/src/main/scala/sbt/std/InputConvert.scala +++ b/main-settings/src/main/scala/sbt/std/InputConvert.scala @@ -8,11 +8,11 @@ package sbt package std -import reflect.macros._ +import scala.reflect.macros._ -import Def.Initialize import sbt.internal.util.complete.Parser import sbt.internal.util.appmacro.{ Convert, Converted } +import Def.Initialize object InputInitConvert extends Convert { def apply[T: c.WeakTypeTag](c: blackbox.Context)(nme: String, in: c.Tree): Converted[c.type] = @@ -46,14 +46,13 @@ object TaskConvert extends Convert { /** Converts an input `Tree` of type `Initialize[T]`, `Initialize[Task[T]]`, or `Task[T]` into a `Tree` of type `Initialize[Task[T]]`.*/ object FullConvert extends Convert { - import InputWrapper._ def apply[T: c.WeakTypeTag](c: blackbox.Context)(nme: String, in: c.Tree): Converted[c.type] = nme match { - case WrapInitTaskName => Converted.Success[c.type](in) - case WrapPreviousName => Converted.Success[c.type](in) - case WrapInitName => wrapInit[T](c)(in) - case WrapTaskName => wrapTask[T](c)(in) - case _ => Converted.NotApplicable[c.type] + case InputWrapper.WrapInitTaskName => Converted.Success[c.type](in) + case InputWrapper.WrapPreviousName => Converted.Success[c.type](in) + case InputWrapper.WrapInitName => wrapInit[T](c)(in) + case InputWrapper.WrapTaskName => wrapTask[T](c)(in) + case _ => Converted.NotApplicable[c.type] } private def wrapInit[T: c.WeakTypeTag](c: blackbox.Context)(tree: c.Tree): Converted[c.type] = { From a73aa97b2cce19f3cd68aca0ee7f4e0bdd43d881 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 27 Jan 2018 09:40:47 +0000 Subject: [PATCH 066/120] Cleanup Extracted --- main/src/main/scala/sbt/Extracted.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main/src/main/scala/sbt/Extracted.scala b/main/src/main/scala/sbt/Extracted.scala index 1cf02b8b7..c35b6021d 100644 --- a/main/src/main/scala/sbt/Extracted.scala +++ b/main/src/main/scala/sbt/Extracted.scala @@ -8,7 +8,6 @@ package sbt import sbt.internal.{ Load, BuildStructure, Act, Aggregation, SessionSettings } -import Project._ import Scope.GlobalScope import Def.{ ScopedKey, Setting } import sbt.internal.util.complete.Parser @@ -43,7 +42,7 @@ final case class Extracted(structure: BuildStructure, structure.data.get(inCurrent(key.scope), key.key) private[this] def inCurrent[T](scope: Scope): Scope = - if (scope.project == This) scope.copy(project = Select(currentRef)) else scope + if (scope.project == This) scope in currentRef else scope /** * Runs the task specified by `key` and returns the transformed State and the resulting value of the task. From d26085155d0ce7acb82981e1fd975277eb04fcd6 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 29 Jan 2018 11:31:27 +0000 Subject: [PATCH 067/120] Cleanup InputWrapper imports --- main-settings/src/main/scala/sbt/std/InputWrapper.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/main-settings/src/main/scala/sbt/std/InputWrapper.scala b/main-settings/src/main/scala/sbt/std/InputWrapper.scala index fb6b6bf70..be34721ae 100644 --- a/main-settings/src/main/scala/sbt/std/InputWrapper.scala +++ b/main-settings/src/main/scala/sbt/std/InputWrapper.scala @@ -8,9 +8,10 @@ package sbt package std -import language.experimental.macros -import reflect.macros._ -import reflect.internal.annotations.compileTimeOnly +import scala.language.experimental.macros + +import scala.annotation.compileTimeOnly +import scala.reflect.macros._ import Def.Initialize import sbt.internal.util.appmacro.ContextUtil From b9e3d6aa2d49499fb9c0d11dc82d8a8b036a66c4 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 17 Jan 2018 15:18:37 +0000 Subject: [PATCH 068/120] Define local Scripted plugin keys in Global scope --- build.sbt | 1 - project/Scripted.scala | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/build.sbt b/build.sbt index d1e5d425a..910f9dbec 100644 --- a/build.sbt +++ b/build.sbt @@ -624,7 +624,6 @@ def otherRootSettings = aggregate in bintrayRelease := false ) ++ inConfig(Scripted.RepoOverrideTest)( Seq( - scriptedPrescripted := (_ => ()), scriptedLaunchOpts := List( "-Xmx1500M", "-Xms512M", diff --git a/project/Scripted.scala b/project/Scripted.scala index f584dc3e4..3d2d53528 100644 --- a/project/Scripted.scala +++ b/project/Scripted.scala @@ -13,11 +13,10 @@ object ScriptedPlugin extends sbt.AutoPlugin { } import autoImport._ - import Scripted._ - override def projectSettings = Seq( + + override def globalSettings = super.globalSettings ++ Seq( scriptedBufferLog := true, - scriptedPrescripted := { _ => - } + scriptedPrescripted := { _ => }, ) } From 41249f707a808e17a772a49697939e6d337ea183 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 17 Jan 2018 15:19:02 +0000 Subject: [PATCH 069/120] Define local Scripted plugin keys with macros --- project/Scripted.scala | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/project/Scripted.scala b/project/Scripted.scala index 3d2d53528..6991003a7 100644 --- a/project/Scripted.scala +++ b/project/Scripted.scala @@ -21,18 +21,15 @@ object ScriptedPlugin extends sbt.AutoPlugin { } trait ScriptedKeys { - lazy val publishAll = TaskKey[Unit]("publish-all") - lazy val publishLocalBinAll = taskKey[Unit]("") - lazy val scripted = InputKey[Unit]("scripted") - lazy val scriptedUnpublished = InputKey[Unit]( - "scripted-unpublished", - "Execute scripted without publishing SBT first. Saves you some time when only your test has changed.") - lazy val scriptedSource = SettingKey[File]("scripted-source") - lazy val scriptedPrescripted = TaskKey[File => Unit]("scripted-prescripted") - lazy val scriptedBufferLog = SettingKey[Boolean]("scripted-buffer-log") - lazy val scriptedLaunchOpts = SettingKey[Seq[String]]( - "scripted-launch-opts", - "options to pass to jvm launching scripted tasks") + val publishAll = taskKey[Unit]("") + val publishLocalBinAll = taskKey[Unit]("") + val scripted = inputKey[Unit]("") + val scriptedUnpublished = inputKey[Unit]("Execute scripted without publishing sbt first. " + + "Saves you some time when only your test has changed") + val scriptedSource = settingKey[File]("") + val scriptedPrescripted = taskKey[File => Unit]("") + val scriptedBufferLog = settingKey[Boolean]("") + val scriptedLaunchOpts = settingKey[Seq[String]]("options to pass to jvm launching scripted tasks") } object Scripted { From 8187a51d315f67084034e4d27a25e98fdc9c6a0e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 24 Jan 2018 13:42:18 +0000 Subject: [PATCH 070/120] Cleanup local Scripted plugin --- build.sbt | 6 +-- project/Scripted.scala | 98 ++++++++++++++++++++++++------------------ 2 files changed, 58 insertions(+), 46 deletions(-) diff --git a/build.sbt b/build.sbt index 910f9dbec..fbfc0ce51 100644 --- a/build.sbt +++ b/build.sbt @@ -557,7 +557,6 @@ lazy val vscodePlugin = (project in file("vscode-sbt-scala")) ) def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask { - val result = scriptedSource(dir => (s: State) => Scripted.scriptedParser(dir)).parsed // publishLocalBinAll.value // TODO: Restore scripted needing only binary jars. publishAll.value (sbtProj / Test / compile).value // make sure sbt.RunFromSourceMain is compiled @@ -567,21 +566,20 @@ def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask { (scalaInstance in scriptedSbtProj).value, scriptedSource.value, scriptedBufferLog.value, - result, + Def.setting(Scripted.scriptedParser(scriptedSource.value)).parsed, scriptedPrescripted.value, scriptedLaunchOpts.value ) } def scriptedUnpublishedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask { - val result = scriptedSource(dir => (s: State) => Scripted.scriptedParser(dir)).parsed Scripted.doScripted( (sbtLaunchJar in bundledLauncherProj).value, (fullClasspath in scriptedSbtProj in Test).value, (scalaInstance in scriptedSbtProj).value, scriptedSource.value, scriptedBufferLog.value, - result, + Def.setting(Scripted.scriptedParser(scriptedSource.value)).parsed, scriptedPrescripted.value, scriptedLaunchOpts.value ) diff --git a/project/Scripted.scala b/project/Scripted.scala index 6991003a7..7a46149fc 100644 --- a/project/Scripted.scala +++ b/project/Scripted.scala @@ -1,13 +1,12 @@ +import java.lang.reflect.InvocationTargetException + import sbt._ -import Keys._ -import Def.Initialize import sbt.internal.inc.ScalaInstance -import sbt.internal.inc.classpath +import sbt.internal.inc.classpath.{ ClasspathUtilities, FilteredLoader } -import scala.language.reflectiveCalls - -object ScriptedPlugin extends sbt.AutoPlugin { +object ScriptedPlugin extends AutoPlugin { override def requires = plugins.JvmPlugin + object autoImport extends ScriptedKeys { def scriptedPath = file("scripted") } @@ -39,27 +38,31 @@ object Scripted { val RepoOverrideTest = config("repoOverrideTest") extend Compile import sbt.complete._ - import DefaultParsers._ + // Paging, 1-index based. - case class ScriptedTestPage(page: Int, total: Int) + final case class ScriptedTestPage(page: Int, total: Int) + // FIXME: Duplicated with ScriptedPlugin.scriptedParser, this can be // avoided once we upgrade build.properties to 0.13.14 def scriptedParser(scriptedBase: File): Parser[Seq[String]] = { + import DefaultParsers._ + val scriptedFiles: NameFilter = ("test": NameFilter) | "pending" val pairs = (scriptedBase * AllPassFilter * AllPassFilter * scriptedFiles).get map { (f: File) => val p = f.getParentFile (p.getParentFile.getName, p.getName) } - val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet); + val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet) val id = charClass(c => !c.isWhitespace && c != '/').+.string - val groupP = token(id.examples(pairMap.keySet.toSet)) <~ token('/') + val groupP = token(id.examples(pairMap.keySet)) <~ token('/') // A parser for page definitions val pageP: Parser[ScriptedTestPage] = ("*" ~ NatBasic ~ "of" ~ NatBasic) map { case _ ~ page ~ _ ~ total => ScriptedTestPage(page, total) } + // Grabs the filenames from a given test group in the current page definition. def pagedFilenames(group: String, page: ScriptedTestPage): Seq[String] = { val files = pairMap(group).toSeq.sortBy(_.toLowerCase) @@ -69,9 +72,11 @@ object Scripted { if (page.page == page.total) dropped else dropped.take(pageSize) } + def nameP(group: String) = { token("*".id | id.examples(pairMap.getOrElse(group, Set.empty[String]))) } + val PagedIds: Parser[Seq[String]] = for { group <- groupP @@ -79,55 +84,64 @@ object Scripted { files = pagedFilenames(group, page) // TODO - Fail the parser if we don't have enough files for the given page size //if !files.isEmpty - } yield files map (f => group + '/' + f) + } yield files map (f => s"$group/$f") val testID = (for (group <- groupP; name <- nameP(group)) yield (group, name)) val testIdAsGroup = matched(testID) map (test => Seq(test)) + //(token(Space) ~> matched(testID)).* (token(Space) ~> (PagedIds | testIdAsGroup)).* map (_.flatten) } - // Interface to cross class loader - type SbtScriptedRunner = { - def runInParallel(resourceBaseDirectory: File, - bufferLog: Boolean, - tests: Array[String], - bootProperties: File, - launchOpts: Array[String], - prescripted: java.util.List[File]): Unit - } - - def doScripted(launcher: File, - scriptedSbtClasspath: Seq[Attributed[File]], - scriptedSbtInstance: ScalaInstance, - sourcePath: File, - bufferLog: Boolean, - args: Seq[String], - prescripted: File => Unit, - launchOpts: Seq[String]): Unit = { + def doScripted( + launcher: File, + scriptedSbtClasspath: Seq[Attributed[File]], + scriptedSbtInstance: ScalaInstance, + sourcePath: File, + bufferLog: Boolean, + args: Seq[String], + prescripted: File => Unit, + launchOpts: Seq[String], + ): Unit = { System.err.println(s"About to run tests: ${args.mkString("\n * ", "\n * ", "\n")}") + // Force Log4J to not use a thread context classloader otherwise it throws a CCE sys.props(org.apache.logging.log4j.util.LoaderUtil.IGNORE_TCCL_PROPERTY) = "true" - val noJLine = new classpath.FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil) - val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, noJLine) + + val noJLine = new FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil) + val loader = ClasspathUtilities.toLoader(scriptedSbtClasspath.files, noJLine) val bridgeClass = Class.forName("sbt.scriptedtest.ScriptedRunner", true, loader) + + // Interface to cross class loader + type SbtScriptedRunner = { + def runInParallel( + resourceBaseDirectory: File, + bufferLog: Boolean, + tests: Array[String], + bootProperties: File, + launchOpts: Array[String], + prescripted: java.util.List[File], + ): Unit + } + val bridge = bridgeClass.getDeclaredConstructor().newInstance().asInstanceOf[SbtScriptedRunner] + try { // Using java.util.List to encode File => Unit. val callback = new java.util.AbstractList[File] { - override def add(x: File): Boolean = { - prescripted(x) - false - } + override def add(x: File): Boolean = { prescripted(x); false } def get(x: Int): sbt.File = ??? def size(): Int = 0 } - bridge.runInParallel(sourcePath, - bufferLog, - args.toArray, - launcher, - launchOpts.toArray, - callback) - } catch { case ite: java.lang.reflect.InvocationTargetException => throw ite.getCause } + import scala.language.reflectiveCalls + bridge.runInParallel( + sourcePath, + bufferLog, + args.toArray, + launcher, + launchOpts.toArray, + callback, + ) + } catch { case ite: InvocationTargetException => throw ite.getCause } } } From 6e83d408da275f0cbbc952d0f5f32c433b9d57b3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 17 Jan 2018 15:20:22 +0000 Subject: [PATCH 071/120] Define ScriptedPlugin keys with macros --- main/src/main/scala/sbt/ScriptedPlugin.scala | 29 ++++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/main/src/main/scala/sbt/ScriptedPlugin.scala b/main/src/main/scala/sbt/ScriptedPlugin.scala index 538a851ef..29414556c 100644 --- a/main/src/main/scala/sbt/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/ScriptedPlugin.scala @@ -27,23 +27,22 @@ object ScriptedPlugin extends AutoPlugin { object autoImport { val ScriptedConf = Configurations.config("scripted-sbt") hide val ScriptedLaunchConf = Configurations.config("scripted-sbt-launch") hide - val scriptedSbt = SettingKey[String]("scripted-sbt") - val sbtLauncher = TaskKey[File]("sbt-launcher") - val sbtTestDirectory = SettingKey[File]("sbt-test-directory") - val scriptedBufferLog = SettingKey[Boolean]("scripted-buffer-log") - val scriptedClasspath = TaskKey[PathFinder]("scripted-classpath") - val scriptedTests = TaskKey[AnyRef]("scripted-tests") + + val scriptedSbt = settingKey[String]("") + val sbtLauncher = taskKey[File]("") + val sbtTestDirectory = settingKey[File]("") + val scriptedBufferLog = settingKey[Boolean]("") + val scriptedClasspath = taskKey[PathFinder]("") + val scriptedTests = taskKey[AnyRef]("") val scriptedBatchExecution = settingKey[Boolean]("Enables or disables batch execution for scripted.") - val scriptedParallelInstances = - settingKey[Int]( - "Configures the number of scripted instances for parallel testing, only used in batch mode.") - val scriptedRun = TaskKey[Method]("scripted-run") - val scriptedLaunchOpts = SettingKey[Seq[String]]( - "scripted-launch-opts", - "options to pass to jvm launching scripted tasks") - val scriptedDependencies = TaskKey[Unit]("scripted-dependencies") - val scripted = InputKey[Unit]("scripted") + val scriptedParallelInstances = settingKey[Int]( + "Configures the number of scripted instances for parallel testing, only used in batch mode.") + val scriptedRun = taskKey[Method]("") + val scriptedLaunchOpts = + settingKey[Seq[String]]("options to pass to jvm launching scripted tasks") + val scriptedDependencies = taskKey[Unit]("") + val scripted = inputKey[Unit]("") } import autoImport._ From 9006abe9be99d5ce070e91aa5f840900caa5d542 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 17 Jan 2018 15:21:50 +0000 Subject: [PATCH 072/120] Cleanup ScriptedPlugin --- main/src/main/scala/sbt/ScriptedPlugin.scala | 41 +++++++++++--------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/main/src/main/scala/sbt/ScriptedPlugin.scala b/main/src/main/scala/sbt/ScriptedPlugin.scala index 29414556c..7a3128c89 100644 --- a/main/src/main/scala/sbt/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/ScriptedPlugin.scala @@ -8,22 +8,24 @@ package sbt import java.io.File -import Def.Initialize -import Keys._ -import sbt.internal.util.complete.{ Parser, DefaultParsers } -import sbt.internal.inc.classpath.ClasspathUtilities -import sbt.internal.inc.ModuleUtilities import java.lang.reflect.Method -import sbt.librarymanagement._ -import sbt.librarymanagement.syntax._ + import sbt.io._ import sbt.io.syntax._ -import Project._ + +import sbt.internal.util.complete.{ Parser, DefaultParsers } + +import sbt.librarymanagement._ +import sbt.librarymanagement.syntax._ + +import sbt.internal.inc.classpath.ClasspathUtilities +import sbt.internal.inc.ModuleUtilities + import Def._ +import Keys._ +import Project._ object ScriptedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - object autoImport { val ScriptedConf = Configurations.config("scripted-sbt") hide val ScriptedLaunchConf = Configurations.config("scripted-sbt-launch") hide @@ -44,7 +46,6 @@ object ScriptedPlugin extends AutoPlugin { val scriptedDependencies = taskKey[Unit]("") val scripted = inputKey[Unit]("") } - import autoImport._ override lazy val globalSettings = Seq( @@ -113,10 +114,10 @@ object ScriptedPlugin extends AutoPlugin { Def.task(method) } - import DefaultParsers._ - private[sbt] case class ScriptedTestPage(page: Int, total: Int) + private[sbt] final case class ScriptedTestPage(page: Int, total: Int) private[sbt] def scriptedParser(scriptedBase: File): Parser[Seq[String]] = { + import DefaultParsers._ val scriptedFiles: NameFilter = ("test": NameFilter) | "pending" val pairs = (scriptedBase * AllPassFilter * AllPassFilter * scriptedFiles).get map { @@ -124,15 +125,16 @@ object ScriptedPlugin extends AutoPlugin { val p = f.getParentFile (p.getParentFile.getName, p.getName) } - val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet); + val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet) val id = charClass(c => !c.isWhitespace && c != '/').+.string - val groupP = token(id.examples(pairMap.keySet.toSet)) <~ token('/') + val groupP = token(id.examples(pairMap.keySet)) <~ token('/') // A parser for page definitions val pageP: Parser[ScriptedTestPage] = ("*" ~ NatBasic ~ "of" ~ NatBasic) map { case _ ~ page ~ _ ~ total => ScriptedTestPage(page, total) } + // Grabs the filenames from a given test group in the current page definition. def pagedFilenames(group: String, page: ScriptedTestPage): Seq[String] = { val files = pairMap(group).toSeq.sortBy(_.toLowerCase) @@ -142,9 +144,11 @@ object ScriptedPlugin extends AutoPlugin { if (page.page == page.total) dropped else dropped.take(pageSize) } + def nameP(group: String) = { token("*".id | id.examples(pairMap.getOrElse(group, Set.empty[String]))) } + val PagedIds: Parser[Seq[String]] = for { group <- groupP @@ -152,10 +156,11 @@ object ScriptedPlugin extends AutoPlugin { files = pagedFilenames(group, page) // TODO - Fail the parser if we don't have enough files for the given page size //if !files.isEmpty - } yield files map (f => group + '/' + f) + } yield files map (f => s"$group/$f") val testID = (for (group <- groupP; name <- nameP(group)) yield (group, name)) val testIdAsGroup = matched(testID) map (test => Seq(test)) + //(token(Space) ~> matched(testID)).* (token(Space) ~> (PagedIds | testIdAsGroup)).* map (_.flatten) } @@ -167,11 +172,11 @@ object ScriptedPlugin extends AutoPlugin { val method = scriptedRun.value val scriptedInstance = scriptedTests.value val dir = sbtTestDirectory.value - val log: java.lang.Boolean = scriptedBufferLog.value + val log = Boolean box scriptedBufferLog.value val launcher = sbtLauncher.value val opts = scriptedLaunchOpts.value.toArray val empty = new java.util.ArrayList[File]() - val instances: java.lang.Integer = scriptedParallelInstances.value + val instances = Int box scriptedParallelInstances.value if (scriptedBatchExecution.value) method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty, instances) From 21eb1f0f1219368b38bc0a65c0446103bb6959d2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 24 Jan 2018 12:58:52 +0000 Subject: [PATCH 073/120] Cleanup Attributes --- .../src/main/scala/sbt/internal/util/Attributes.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala index 0dfbea73e..3e4832f1e 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala @@ -31,7 +31,8 @@ sealed trait AttributeKey[T] { def description: Option[String] /** - * In environments that support delegation, looking up this key when it has no associated value will delegate to the values associated with these keys. + * In environments that support delegation, looking up this key when it has no associated value + * will delegate to the values associated with these keys. * The delegation proceeds in order the keys are returned here. */ def extend: Seq[AttributeKey[_]] From 6038ec51c3fd3451443a0d9632223c288ac2444c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 24 Jan 2018 12:59:22 +0000 Subject: [PATCH 074/120] Cleanup & simplify actions/run-task --- sbt/src/sbt-test/actions/run-task/build.sbt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/sbt/src/sbt-test/actions/run-task/build.sbt b/sbt/src/sbt-test/actions/run-task/build.sbt index 0166f62ed..69fb3e613 100644 --- a/sbt/src/sbt-test/actions/run-task/build.sbt +++ b/sbt/src/sbt-test/actions/run-task/build.sbt @@ -1,13 +1,7 @@ -lazy val root = (project in file(".")). - settings( - myRun, - fork in demo := true, - javaOptions in demo := "-Dsbt.check.forked=true" :: Nil, - myIn - ) +val demo = taskKey[Unit]("Demo run task") +fullRunTask(demo, Compile, "A", "1", "1") +fork in demo := true +javaOptions in demo := "-Dsbt.check.forked=true" :: Nil -lazy val demoIn = InputKey[Unit]("demoIn", "Demo run input task", demo) -lazy val demo = taskKey[Unit]("Demo run task") - -def myRun = fullRunTask(demo, Compile, "A", "1", "1") -def myIn = fullRunInputTask(demoIn, Compile, "A", "1") +val demoIn = InputKey[Unit]("demoIn", "Demo run input task", demo) +fullRunInputTask(demoIn, Compile, "A", "1") From 705eeddf5cb19d32ebc1bc7dd91ee5ff89951f0b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 24 Jan 2018 13:10:39 +0000 Subject: [PATCH 075/120] Cleanup & simplify actions/add-alias --- sbt/src/sbt-test/actions/add-alias/A.scala | 6 ++++-- sbt/src/sbt-test/actions/add-alias/build.sbt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sbt/src/sbt-test/actions/add-alias/A.scala b/sbt/src/sbt-test/actions/add-alias/A.scala index dbae0c48a..5b2ba478c 100644 --- a/sbt/src/sbt-test/actions/add-alias/A.scala +++ b/sbt/src/sbt-test/actions/add-alias/A.scala @@ -1,3 +1,5 @@ -object A extends App { - if(args(0).toBoolean) () else sys.error("Fail") +object A { + def main(args: Array[String]): Unit = { + if (args(0).toBoolean) () else sys.error("Fail") + } } diff --git a/sbt/src/sbt-test/actions/add-alias/build.sbt b/sbt/src/sbt-test/actions/add-alias/build.sbt index 451b72246..cb5461c6b 100644 --- a/sbt/src/sbt-test/actions/add-alias/build.sbt +++ b/sbt/src/sbt-test/actions/add-alias/build.sbt @@ -1,2 +1,2 @@ -addCommandAlias("demo-success", "run true") ++ +addCommandAlias("demo-success", "run true") addCommandAlias("demo-failure", "run false") From 78f4f56d1c7602921019dcf568d388a3409d12b6 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 24 Jan 2018 13:36:26 +0000 Subject: [PATCH 076/120] Fix 2 "discarded non-Unit value" warnings in ScriptedPlugin --- main/src/main/scala/sbt/ScriptedPlugin.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/main/src/main/scala/sbt/ScriptedPlugin.scala b/main/src/main/scala/sbt/ScriptedPlugin.scala index 7a3128c89..6e2544426 100644 --- a/main/src/main/scala/sbt/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/ScriptedPlugin.scala @@ -181,6 +181,7 @@ object ScriptedPlugin extends AutoPlugin { if (scriptedBatchExecution.value) method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty, instances) else method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty) + () } catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause } } From 6f52437e95ca512b40d72b5dfc0ddc3b03f2a778 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Feb 2018 15:21:34 +0000 Subject: [PATCH 077/120] Fix a "discarded non-Unit value" warning in FileExamplesTest --- .../src/test/scala/sbt/complete/FileExamplesTest.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala index 173b276fc..f7b79573c 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -9,6 +9,7 @@ package sbt.internal.util package complete import java.io.File +import org.scalatest.Assertion import sbt.io.IO class FileExamplesTest extends UnitSpec { @@ -57,7 +58,8 @@ class FileExamplesTest extends UnitSpec { } def withDirectoryStructure[A](withCompletionPrefix: String = "")( - thunk: DirectoryStructure => A): Unit = { + thunk: DirectoryStructure => Assertion + ): Assertion = { IO.withTemporaryDirectory { tempDir => val ds = new DirectoryStructure(withCompletionPrefix) ds.createSampleDirStructure(tempDir) From 82c39ec96f58ea190c2fbaba103539d6bef64f7d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Feb 2018 15:21:47 +0000 Subject: [PATCH 078/120] Fix a "discarded non-Unit value" warning in TaskRunnerFork --- tasks-standard/src/test/scala/TaskRunnerFork.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks-standard/src/test/scala/TaskRunnerFork.scala b/tasks-standard/src/test/scala/TaskRunnerFork.scala index 105f4a2de..639963454 100644 --- a/tasks-standard/src/test/scala/TaskRunnerFork.scala +++ b/tasks-standard/src/test/scala/TaskRunnerFork.scala @@ -33,6 +33,7 @@ object TaskRunnerForkTest extends Properties("TaskRunner Fork") { def runDoubleJoin(a: Int, b: Int, workers: Int): Unit = { def inner = List.range(0, b).map(j => task(j).named(j.toString)).join tryRun(List.range(0, a).map(_ => inner).join, false, workers) + () } property("fork and reduce") = forAll(TaskListGen, MaxWorkersGen) { (m: List[Int], workers: Int) => m.nonEmpty ==> { From dbbba67d36813adae76cd6e9ed066406a61a2c55 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 24 Jan 2018 13:36:55 +0000 Subject: [PATCH 079/120] Fix a Scaladoc error in BuildStructure --- main/src/main/scala/sbt/internal/BuildStructure.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/internal/BuildStructure.scala b/main/src/main/scala/sbt/internal/BuildStructure.scala index 626123638..65b5eea6a 100644 --- a/main/src/main/scala/sbt/internal/BuildStructure.scala +++ b/main/src/main/scala/sbt/internal/BuildStructure.scala @@ -154,7 +154,8 @@ case class DetectedAutoPlugin(name: String, value: AutoPlugin, hasAutoImport: Bo * Auto-discovered modules for the build definition project. These include modules defined in build definition sources * as well as modules in binary dependencies. * - * @param builds The [[Build]]s detected in the build definition. This does not include the default [[Build]] that sbt creates if none is defined. + * @param builds The [[BuildDef]]s detected in the build definition. + * This does not include the default [[BuildDef]] that sbt creates if none is defined. */ final class DetectedPlugins(val autoPlugins: Seq[DetectedAutoPlugin], val builds: DetectedModules[BuildDef]) { From 9d808a389c557355c8e8d0ad4bf4bc2be53248d2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 24 Jan 2018 13:53:11 +0000 Subject: [PATCH 080/120] Update PublishBinPlugin to current sbt --- project/PublishBinPlugin.scala | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/project/PublishBinPlugin.scala b/project/PublishBinPlugin.scala index a54d9958e..c013e78d6 100644 --- a/project/PublishBinPlugin.scala +++ b/project/PublishBinPlugin.scala @@ -5,14 +5,12 @@ import sbt.librarymanagement.ConfigRef /** This local plugin provides ways of publishing just the binary jar. */ object PublishBinPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin override def trigger = allRequirements object autoImport { val publishLocalBin = taskKey[Unit]("") val publishLocalBinConfig = taskKey[PublishConfiguration]("") } - import autoImport._ override def globalSettings = Seq(publishLocalBin := (())) @@ -20,22 +18,17 @@ object PublishBinPlugin extends AutoPlugin { override def projectSettings = Def settings ( publishLocalBin := Classpaths.publishTask(publishLocalBinConfig, deliverLocal).value, publishLocalBinConfig := { - val _ = deliverLocal.value Classpaths.publishConfig( - publishMavenStyle.value, - deliverPattern(crossTarget.value), + false, // publishMavenStyle.value, + Classpaths.deliverPattern(crossTarget.value), if (isSnapshot.value) "integration" else "release", ivyConfigurations.value.map(c => ConfigRef(c.name)).toVector, (packagedArtifacts in publishLocalBin).value.toVector, (checksums in publishLocalBin).value.toVector, - resolverName = "local", logging = ivyLoggingLevel.value, overwrite = isSnapshot.value ) }, packagedArtifacts in publishLocalBin := Classpaths.packaged(Seq(packageBin in Compile)).value ) - - def deliverPattern(outputPath: File): String = - (outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath } From b8abf4285f2ca160d62f1c69c488ff3e0a741e9a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 24 Jan 2018 14:46:41 +0000 Subject: [PATCH 081/120] Make BatchScriptRunner.States hold StatementHandler#State --- .../scala/sbt/scriptedtest/BatchScriptRunner.scala | 13 +++++++------ .../main/scala/sbt/scriptedtest/ScriptedTests.scala | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala index fe9d35712..2d23a0604 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala @@ -8,11 +8,17 @@ package sbt package scriptedtest +import scala.collection.mutable + import sbt.internal.scripted._ -import sbt.scriptedtest.BatchScriptRunner.States + +private[sbt] object BatchScriptRunner { + type States = mutable.HashMap[StatementHandler, StatementHandler#State] +} /** Defines an alternative script runner that allows batch execution. */ private[sbt] class BatchScriptRunner extends ScriptRunner { + import BatchScriptRunner.States /** Defines a method to run batched execution. * @@ -58,8 +64,3 @@ private[sbt] class BatchScriptRunner extends ScriptRunner { } } } - -private[sbt] object BatchScriptRunner { - import scala.collection.mutable - type States = mutable.HashMap[StatementHandler, Any] -} diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index 88c28d01c..e9c26281b 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -60,7 +60,7 @@ final class ScriptedTests(resourceBaseDirectory: File, val handlers = createScriptedHandlers(testDirectory, buffer, RemoteSbtCreatorKind.LauncherBased) val runner = new BatchScriptRunner - val states = new mutable.HashMap[StatementHandler, Any]() + val states = new mutable.HashMap[StatementHandler, StatementHandler#State]() commonRunTest(label, testDirectory, prescripted, handlers, runner, states, buffer) } runOrHandleDisabled(label, testDirectory, singleTestRunner, buffer) From cb2e4e67fd7fb63cfe343c81aaaaad4eff3fec7d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 30 Jan 2018 15:53:05 +0000 Subject: [PATCH 082/120] Cleanup BatchScriptRunner --- .../src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala index 2d23a0604..805059518 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala @@ -43,9 +43,8 @@ private[sbt] class BatchScriptRunner extends ScriptRunner { def processStatement(handler: StatementHandler, statement: Statement, states: States): Unit = { val state = states(handler).asInstanceOf[handler.State] val nextState = - try { Right(handler(statement.command, statement.arguments, state)) } catch { - case e: Exception => Left(e) - } + try Right(handler(statement.command, statement.arguments, state)) + catch { case e: Exception => Left(e) } nextState match { case Left(err) => if (statement.successExpected) { From 4debec00536c565f9bade4d6051d48275cf90362 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 24 Jan 2018 14:11:11 +0000 Subject: [PATCH 083/120] Cleanup ScriptedTests --- .../sbt/scriptedtest/ScriptedTests.scala | 241 +++++++++--------- 1 file changed, 124 insertions(+), 117 deletions(-) diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index e9c26281b..69a856d89 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -9,27 +9,31 @@ package sbt package scriptedtest import java.io.File +import java.net.SocketException import java.util.Properties +import java.util.concurrent.ForkJoinPool -import scala.util.control.NonFatal -import sbt.internal.scripted._ -import sbt.io.{ DirectoryFilter, HiddenFileFilter, IO } -import sbt.io.IO.wrapNull -import sbt.io.FileFilter._ -import sbt.internal.io.Resources -import sbt.internal.util.{ BufferedLogger, ConsoleLogger, FullLogger } -import sbt.util.{ AbstractLogger, Logger } - +import scala.collection.GenSeq import scala.collection.mutable import scala.collection.parallel.ForkJoinTaskSupport -import scala.collection.parallel.mutable.ParSeq +import scala.util.control.NonFatal + +import sbt.internal.scripted._ +import sbt.internal.io.Resources +import sbt.internal.util.{ BufferedLogger, ConsoleLogger, FullLogger } +import sbt.io.syntax._ +import sbt.io.{ DirectoryFilter, HiddenFileFilter, IO } +import sbt.io.FileFilter._ +import sbt.util.{ AbstractLogger, Logger } + +final class ScriptedTests( + resourceBaseDirectory: File, + bufferLog: Boolean, + launcher: File, + launchOpts: Seq[String], +) { + import ScriptedTests.{ TestRunner, emptyCallback } -final class ScriptedTests(resourceBaseDirectory: File, - bufferLog: Boolean, - launcher: File, - launchOpts: Seq[String]) { - import sbt.io.syntax._ - import ScriptedTests._ private val testResources = new Resources(resourceBaseDirectory) val ScriptFilename = "test" @@ -37,14 +41,17 @@ final class ScriptedTests(resourceBaseDirectory: File, def scriptedTest(group: String, name: String, log: xsbti.Logger): Seq[TestRunner] = scriptedTest(group, name, Logger.xlog2Log(log)) + def scriptedTest(group: String, name: String, log: Logger): Seq[TestRunner] = singleScriptedTest(group, name, emptyCallback, log) /** Returns a sequence of test runners that have to be applied in the call site. */ - def singleScriptedTest(group: String, - name: String, - prescripted: File => Unit, - log: Logger): Seq[TestRunner] = { + def singleScriptedTest( + group: String, + name: String, + prescripted: File => Unit, + log: Logger, + ): Seq[TestRunner] = { // Test group and names may be file filters (like '*') for (groupDir <- (resourceBaseDirectory * group).get; nme <- (groupDir * name).get) yield { @@ -97,8 +104,8 @@ final class ScriptedTests(resourceBaseDirectory: File, val groupAndNameDirs = { for { (group, name) <- testGroupAndNames - groupDir <- resourceBaseDirectory.*(group).get - testDir <- groupDir.*(name).get + groupDir <- (resourceBaseDirectory * group).get + testDir <- (groupDir * name).get } yield (groupDir, testDir) } @@ -318,8 +325,8 @@ final class ScriptedTests(resourceBaseDirectory: File, // Reload and initialize (to reload contents of .sbtrc files) val pluginImplementation = createAutoPlugin(name) IO.write(tempTestDir / "project" / "InstrumentScripted.scala", pluginImplementation) - val sbtHandlerError = "Missing sbt handler. Scripted is misconfigured." - val sbtHandler = handlers.getOrElse('>', sbtHandlerError).asInstanceOf[SbtHandler] + def sbtHandlerError = sys error "Missing sbt handler. Scripted is misconfigured." + val sbtHandler = handlers.getOrElse('>', sbtHandlerError) val commandsToRun = ";reload;setUpScripted" val statement = Statement(commandsToRun, Nil, successExpected = true, line = -1) @@ -368,7 +375,7 @@ final class ScriptedTests(resourceBaseDirectory: File, label: String, testDirectory: File, preScriptedHook: File => Unit, - createHandlers: Map[Char, StatementHandler], + handlers: Map[Char, StatementHandler], runner: BatchScriptRunner, states: BatchScriptRunner.States, log: BufferedLogger @@ -382,15 +389,15 @@ final class ScriptedTests(resourceBaseDirectory: File, } val pendingMark = if (pending) PendingLabel else "" + def testFailed(t: Throwable): Option[String] = { if (pending) log.clear() else log.stop() log.error(s"x $label $pendingMark") if (!NonFatal(t)) throw t // We make sure fatal errors are rethrown if (t.isInstanceOf[TestException]) { t.getCause match { - case null | _: java.net.SocketException => - log.error(" Cause of test exception: " + t.getMessage) - case _ => t.printStackTrace() + case null | _: SocketException => log.error(s" Cause of test exception: ${t.getMessage}") + case _ => t.printStackTrace() } } if (pending) None else Some(label) @@ -399,7 +406,6 @@ final class ScriptedTests(resourceBaseDirectory: File, import scala.util.control.Exception.catching catching(classOf[TestException]).withApply(testFailed).andFinally(log.clear).apply { preScriptedHook(testDirectory) - val handlers = createHandlers val parser = new TestScriptParser(handlers) val handlersAndStatements = parser.parse(file) runner.apply(handlersAndStatements, states) @@ -421,6 +427,7 @@ object ScriptedTests extends ScriptedRunner { type TestRunner = () => Seq[Option[String]] val emptyCallback: File => Unit = _ => () + def main(args: Array[String]): Unit = { val directory = new File(args(0)) val buffer = args(1).toBoolean @@ -432,48 +439,52 @@ object ScriptedTests extends ScriptedRunner { val logger = ConsoleLogger() run(directory, buffer, tests, logger, bootProperties, Array(), emptyCallback) } + } +/** Runner for `scripted`. Not be confused with ScriptRunner. */ class ScriptedRunner { // This is called by project/Scripted.scala // Using java.util.List[File] to encode File => Unit - def run(resourceBaseDirectory: File, - bufferLog: Boolean, - tests: Array[String], - bootProperties: File, - launchOpts: Array[String], - prescripted: java.util.List[File]): Unit = { - - // Force Log4J to not use a thread context classloader otherwise it throws a CCE - sys.props(org.apache.logging.log4j.util.LoaderUtil.IGNORE_TCCL_PROPERTY) = "true" - - run(resourceBaseDirectory, bufferLog, tests, ConsoleLogger(), bootProperties, launchOpts, { - f: File => - prescripted.add(f); () - }) //new FullLogger(Logger.xlog2Log(log))) + def run( + resourceBaseDirectory: File, + bufferLog: Boolean, + tests: Array[String], + bootProperties: File, + launchOpts: Array[String], + prescripted: java.util.List[File], + ): Unit = { + val logger = ConsoleLogger() + val addTestFile = (f: File) => { prescripted.add(f); () } + run(resourceBaseDirectory, bufferLog, tests, logger, bootProperties, launchOpts, addTestFile) + //new FullLogger(Logger.xlog2Log(log))) } // This is called by sbt-scripted 0.13.x and 1.x (see https://github.com/sbt/sbt/issues/3245) - def run(resourceBaseDirectory: File, - bufferLog: Boolean, - tests: Array[String], - bootProperties: File, - launchOpts: Array[String]): Unit = - run(resourceBaseDirectory, - bufferLog, - tests, - ConsoleLogger(), - bootProperties, - launchOpts, - ScriptedTests.emptyCallback) + def run( + resourceBaseDirectory: File, + bufferLog: Boolean, + tests: Array[String], + bootProperties: File, + launchOpts: Array[String], + ): Unit = { + val logger = ConsoleLogger() + val prescripted = ScriptedTests.emptyCallback + run(resourceBaseDirectory, bufferLog, tests, logger, bootProperties, launchOpts, prescripted) + } + + def run( + resourceBaseDirectory: File, + bufferLog: Boolean, + tests: Array[String], + logger: AbstractLogger, + bootProperties: File, + launchOpts: Array[String], + prescripted: File => Unit, + ): Unit = { + // Force Log4J to not use a thread context classloader otherwise it throws a CCE + sys.props(org.apache.logging.log4j.util.LoaderUtil.IGNORE_TCCL_PROPERTY) = "true" - def run(resourceBaseDirectory: File, - bufferLog: Boolean, - tests: Array[String], - logger: AbstractLogger, - bootProperties: File, - launchOpts: Array[String], - prescripted: File => Unit): Unit = { val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, bootProperties, launchOpts) val sbtVersion = bootProperties.getName.dropWhile(!_.isDigit).dropRight(".jar".length) val accept = isTestCompatible(resourceBaseDirectory, sbtVersion) _ @@ -484,22 +495,15 @@ class ScriptedRunner { runAll(allTests) } - def runInParallel(resourceBaseDirectory: File, - bufferLog: Boolean, - tests: Array[String], - bootProperties: File, - launchOpts: Array[String], - prescripted: java.util.List[File]): Unit = { - val logger = ConsoleLogger() - val addTestFile = (f: File) => { prescripted.add(f); () } - runInParallel(resourceBaseDirectory, - bufferLog, - tests, - logger, - bootProperties, - launchOpts, - addTestFile, - 1) + def runInParallel( + baseDir: File, + bufferLog: Boolean, + tests: Array[String], + bootProps: File, + launchOpts: Array[String], + prescripted: java.util.List[File], + ): Unit = { + runInParallel(baseDir, bufferLog, tests, bootProps, launchOpts, prescripted, 1) } // This is used by sbt-scripted sbt 1.x @@ -518,54 +522,54 @@ class ScriptedRunner { } def runInParallel( - resourceBaseDirectory: File, + baseDir: File, bufferLog: Boolean, tests: Array[String], logger: AbstractLogger, - bootProperties: File, + bootProps: File, launchOpts: Array[String], prescripted: File => Unit, instances: Int ): Unit = { - val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, bootProperties, launchOpts) - val sbtVersion = bootProperties.getName.dropWhile(!_.isDigit).dropRight(".jar".length) - val accept = isTestCompatible(resourceBaseDirectory, sbtVersion) _ + val runner = new ScriptedTests(baseDir, bufferLog, bootProps, launchOpts) + val sbtVersion = bootProps.getName.dropWhile(!_.isDigit).dropRight(".jar".length) + val accept = isTestCompatible(baseDir, sbtVersion) _ // The scripted tests mapped to the inputs that the user wrote after `scripted`. val scriptedTests = - get(tests, resourceBaseDirectory, accept, logger).map(st => (st.group, st.name)) + get(tests, baseDir, accept, logger).map(st => (st.group, st.name)) val scriptedRunners = runner.batchScriptedRunner(scriptedTests, prescripted, instances, logger) val parallelRunners = scriptedRunners.toParArray - val pool = new java.util.concurrent.ForkJoinPool(instances) - parallelRunners.tasksupport = new ForkJoinTaskSupport(pool) - runAllInParallel(parallelRunners) + parallelRunners.tasksupport = new ForkJoinTaskSupport(new ForkJoinPool(instances)) + runAll(parallelRunners) } - private def reportErrors(errors: Seq[String]): Unit = + private def reportErrors(errors: GenSeq[String]): Unit = if (errors.nonEmpty) sys.error(errors.mkString("Failed tests:\n\t", "\n\t", "\n")) else () - def runAll(toRun: Seq[ScriptedTests.TestRunner]): Unit = - reportErrors(toRun.flatMap(test => test.apply().flatten.toSeq)) - - // We cannot reuse `runAll` because parallel collections != collections - def runAllInParallel(tests: ParSeq[ScriptedTests.TestRunner]): Unit = { - reportErrors(tests.flatMap(test => test.apply().flatten.toSeq).toList) - } + def runAll(toRun: GenSeq[ScriptedTests.TestRunner]): Unit = + reportErrors(toRun.flatMap(test => test.apply().flatten)) @deprecated("No longer used", "1.1.0") def get(tests: Seq[String], baseDirectory: File, log: Logger): Seq[ScriptedTest] = get(tests, baseDirectory, _ => true, log) - def get(tests: Seq[String], - baseDirectory: File, - accept: ScriptedTest => Boolean, - log: Logger): Seq[ScriptedTest] = + + def get( + tests: Seq[String], + baseDirectory: File, + accept: ScriptedTest => Boolean, + log: Logger, + ): Seq[ScriptedTest] = if (tests.isEmpty) listTests(baseDirectory, accept, log) else parseTests(tests) @deprecated("No longer used", "1.1.0") def listTests(baseDirectory: File, log: Logger): Seq[ScriptedTest] = listTests(baseDirectory, _ => true, log) - def listTests(baseDirectory: File, - accept: ScriptedTest => Boolean, - log: Logger): Seq[ScriptedTest] = + + def listTests( + baseDirectory: File, + accept: ScriptedTest => Boolean, + log: Logger, + ): Seq[ScriptedTest] = (new ListTests(baseDirectory, accept, log)).listTests def parseTests(in: Seq[String]): Seq[ScriptedTest] = @@ -575,7 +579,8 @@ class ScriptedRunner { } private def isTestCompatible(resourceBaseDirectory: File, sbtVersion: String)( - test: ScriptedTest): Boolean = { + test: ScriptedTest + ): Boolean = { import sbt.internal.librarymanagement.cross.CrossVersionUtil.binarySbtVersion val buildProperties = new Properties() val testDir = new File(new File(resourceBaseDirectory, test.group), test.name) @@ -592,36 +597,38 @@ class ScriptedRunner { } final case class ScriptedTest(group: String, name: String) { - override def toString = group + "/" + name + override def toString = s"$group/$name" } -private[sbt] object ListTests { - def list(directory: File, filter: java.io.FileFilter) = wrapNull(directory.listFiles(filter)) -} -import ListTests._ -private[sbt] final class ListTests(baseDirectory: File, - accept: ScriptedTest => Boolean, - log: Logger) { + +private[sbt] final class ListTests( + baseDirectory: File, + accept: ScriptedTest => Boolean, + log: Logger, +) { + def filter = DirectoryFilter -- HiddenFileFilter + def listTests: Seq[ScriptedTest] = { - list(baseDirectory, filter) flatMap { group => + IO.listFiles(baseDirectory, filter) flatMap { group => val groupName = group.getName listTests(group).map(ScriptedTest(groupName, _)) } } + private[this] def listTests(group: File): Set[String] = { val groupName = group.getName - val allTests = list(group, filter) + val allTests = IO.listFiles(group, filter) if (allTests.isEmpty) { - log.warn("No tests in test group " + groupName) + log.warn(s"No tests in test group $groupName") Set.empty } else { val (included, skipped) = allTests.toList.partition(test => accept(ScriptedTest(groupName, test.getName))) if (included.isEmpty) - log.warn("Test group " + groupName + " skipped.") + log.warn(s"Test group $groupName skipped.") else if (skipped.nonEmpty) { - log.warn("Tests skipped in group " + group.getName + ":") - skipped.foreach(testName => log.warn(" " + testName.getName)) + log.warn(s"Tests skipped in group $groupName:") + skipped.foreach(testName => log.warn(s" ${testName.getName}")) } Set(included.map(_.getName): _*) } From 89e501443c40802f6825aa20860fa3404f34e762 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 25 Jan 2018 12:09:21 +0000 Subject: [PATCH 084/120] Cleanup SbtHandler --- .../scala/sbt/scriptedtest/SbtHandler.scala | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala index 9ba5be673..bf7174731 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/SbtHandler.scala @@ -22,41 +22,40 @@ final case class SbtInstance(process: Process, server: IPC.Server) final class SbtHandler(remoteSbtCreator: RemoteSbtCreator) extends StatementHandler { type State = Option[SbtInstance] + def initialState = None def apply(command: String, arguments: List[String], i: Option[SbtInstance]): Option[SbtInstance] = - onSbtInstance(i) { (process, server) => + onSbtInstance(i) { (_, server) => send((command :: arguments.map(escape)).mkString(" "), server) - receive(command + " failed", server) + receive(s"$command failed", server) } def onSbtInstance(i: Option[SbtInstance])(f: (Process, IPC.Server) => Unit): Option[SbtInstance] = i match { - case Some(SbtInstance(_, server)) if server.isClosed => - finish(i) - onNewSbtInstance(f) - case Some(SbtInstance(process, server)) => - f(process, server) - i - case None => - onNewSbtInstance(f) + case Some(SbtInstance(_, server)) if server.isClosed => finish(i); onNewSbtInstance(f) + case Some(SbtInstance(process, server)) => f(process, server); i + case None => onNewSbtInstance(f) } private[this] def onNewSbtInstance(f: (Process, IPC.Server) => Unit): Option[SbtInstance] = { val server = IPC.unmanagedServer - val p = try newRemote(server) - catch { case e: Throwable => server.close(); throw e } - val ai = Some(SbtInstance(p, server)) + val p = + try newRemote(server) + catch { case e: Throwable => server.close(); throw e } + val i = Some(SbtInstance(p, server)) try f(p, server) catch { case e: Throwable => // TODO: closing is necessary only because StatementHandler uses exceptions for signaling errors - finish(ai); throw e + finish(i) + throw e } - ai + i } - def finish(state: Option[SbtInstance]) = state match { + def finish(state: State) = state match { + case None => case Some(SbtInstance(process, server)) => try { send("exit", server) @@ -65,24 +64,28 @@ final class SbtHandler(remoteSbtCreator: RemoteSbtCreator) extends StatementHand } catch { case _: IOException => process.destroy() } - case None => } - def send(message: String, server: IPC.Server) = server.connection { _.send(message) } + + def send(message: String, server: IPC.Server) = server.connection(_.send(message)) + def receive(errorMessage: String, server: IPC.Server) = server.connection { ipc => val resultMessage = ipc.receive if (!resultMessage.toBoolean) throw new TestFailed(errorMessage) } + def newRemote(server: IPC.Server): Process = { val p = remoteSbtCreator.newRemote(server) try receive("Remote sbt initialization failed", server) catch { case _: SocketException => throw new TestFailed("Remote sbt initialization failed") } p } - import java.util.regex.Pattern.{ quote => q } + // if the argument contains spaces, enclose it in quotes, quoting backslashes and quotes - def escape(argument: String) = + def escape(argument: String) = { + import java.util.regex.Pattern.{ quote => q } if (argument.contains(" ")) "\"" + argument.replaceAll(q("""\"""), """\\""").replaceAll(q("\""), "\\\"") + "\"" else argument + } } From 2effe0845fb871be4877a6bfd57ee1347c1747f3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 25 Jan 2018 16:26:31 +0000 Subject: [PATCH 085/120] Cleanup IPC --- main-command/src/main/scala/xsbt/IPC.scala | 35 ++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/main-command/src/main/scala/xsbt/IPC.scala b/main-command/src/main/scala/xsbt/IPC.scala index 2b9750438..c964bca7b 100644 --- a/main-command/src/main/scala/xsbt/IPC.scala +++ b/main-command/src/main/scala/xsbt/IPC.scala @@ -10,47 +10,57 @@ package xsbt import java.io.{ BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter } import java.net.{ InetAddress, ServerSocket, Socket } +import scala.annotation.tailrec import scala.util.control.NonFatal object IPC { private val portMin = 1025 private val portMax = 65536 - private val loopback = InetAddress.getByName(null) // loopback + private val loopback = InetAddress.getByName(null) - def client[T](port: Int)(f: IPC => T): T = - ipc(new Socket(loopback, port))(f) + def client[T](port: Int)(f: IPC => T): T = ipc(new Socket(loopback, port))(f) def pullServer[T](f: Server => T): T = { val server = makeServer - try { f(new Server(server)) } finally { server.close() } + try f(new Server(server)) + finally server.close() } + def unmanagedServer: Server = new Server(makeServer) + def makeServer: ServerSocket = { val random = new java.util.Random def nextPort = random.nextInt(portMax - portMin + 1) + portMin + def createServer(attempts: Int): ServerSocket = - if (attempts > 0) - try { new ServerSocket(nextPort, 1, loopback) } catch { - case NonFatal(_) => createServer(attempts - 1) - } else - sys.error("Could not connect to socket: maximum attempts exceeded") + if (attempts > 0) { + try new ServerSocket(nextPort, 1, loopback) + catch { case NonFatal(_) => createServer(attempts - 1) } + } else sys.error("Could not connect to socket: maximum attempts exceeded") + createServer(10) } + def server[T](f: IPC => Option[T]): T = serverImpl(makeServer, f) + def server[T](port: Int)(f: IPC => Option[T]): T = serverImpl(new ServerSocket(port, 1, loopback), f) + private def serverImpl[T](server: ServerSocket, f: IPC => Option[T]): T = { - def listen(): T = { + @tailrec def listen(): T = { ipc(server.accept())(f) match { case Some(done) => done case None => listen() } } - try { listen() } finally { server.close() } + try listen() + finally server.close() } + private def ipc[T](s: Socket)(f: IPC => T): T = - try { f(new IPC(s)) } finally { s.close() } + try f(new IPC(s)) + finally s.close() final class Server private[IPC] (s: ServerSocket) { def port = s.getLocalPort @@ -59,6 +69,7 @@ object IPC { def connection[T](f: IPC => T): T = IPC.ipc(s.accept())(f) } } + final class IPC private (s: Socket) { def port = s.getLocalPort private val in = new BufferedReader(new InputStreamReader(s.getInputStream)) From 685b416b8e5ecdab0d62c2751fcf0eeb68e88e12 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 26 Jan 2018 12:01:24 +0000 Subject: [PATCH 086/120] Cleanup Main --- main/src/main/scala/sbt/Main.scala | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 75401eacd..4f4ef57d6 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -52,7 +52,6 @@ import xsbti.compile.CompilerCache import scala.annotation.tailrec import sbt.io.IO import sbt.io.syntax._ -import StandardMain._ import java.io.{ File, IOException } import java.net.URI @@ -69,34 +68,35 @@ final class xMain extends xsbti.AppMain { import BasicCommandStrings.runEarly import BuiltinCommands.defaults import sbt.internal.CommandStrings.{ BootCommand, DefaultsCommand, InitCommand } - val state = initialState( + val state = StandardMain.initialState( configuration, Seq(defaults, early), runEarly(DefaultsCommand) :: runEarly(InitCommand) :: BootCommand :: Nil) - runManaged(state) + StandardMain.runManaged(state) } } final class ScriptMain extends xsbti.AppMain { def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = { import BasicCommandStrings.runEarly - runManaged( - initialState( - configuration, - BuiltinCommands.ScriptCommands, - runEarly(Level.Error.toString) :: Script.Name :: Nil - )) + val state = StandardMain.initialState( + configuration, + BuiltinCommands.ScriptCommands, + runEarly(Level.Error.toString) :: Script.Name :: Nil + ) + StandardMain.runManaged(state) } } final class ConsoleMain extends xsbti.AppMain { - def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = - runManaged( - initialState( - configuration, - BuiltinCommands.ConsoleCommands, - IvyConsole.Name :: Nil - )) + def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = { + val state = StandardMain.initialState( + configuration, + BuiltinCommands.ConsoleCommands, + IvyConsole.Name :: Nil + ) + StandardMain.runManaged(state) + } } object StandardMain { From 7b11729f8c625c9f0b689034eaedc73dbca1c692 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 26 Jan 2018 12:58:58 +0000 Subject: [PATCH 087/120] Cleanup State#process --- main-command/src/main/scala/sbt/State.scala | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/main-command/src/main/scala/sbt/State.scala b/main-command/src/main/scala/sbt/State.scala index 9d0e401c6..eb15168fe 100644 --- a/main-command/src/main/scala/sbt/State.scala +++ b/main-command/src/main/scala/sbt/State.scala @@ -238,14 +238,16 @@ object State { def process(f: (Exec, State) => State): State = { def runCmd(cmd: Exec, remainingCommands: List[Exec]) = { log.debug(s"> $cmd") - f(cmd, - s.copy(remainingCommands = remainingCommands, - currentCommand = Some(cmd), - history = cmd :: s.history)) + val s1 = s.copy( + remainingCommands = remainingCommands, + currentCommand = Some(cmd), + history = cmd :: s.history, + ) + f(cmd, s1) } s.remainingCommands match { - case List() => exit(true) - case List(x, xs @ _*) => runCmd(x, xs.toList) + case Nil => exit(true) + case x :: xs => runCmd(x, xs) } } def :::(newCommands: List[String]): State = ++:(newCommands map { Exec(_, s.source) }) From 621ad2b5532b3fcc2347178cf366ff25e986fb8f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 26 Jan 2018 14:54:39 +0000 Subject: [PATCH 088/120] Cleanup TaskNegSpec --- .../src/test/scala/sbt/std/neg/TaskNegSpec.scala | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/main-settings/src/test/scala/sbt/std/neg/TaskNegSpec.scala b/main-settings/src/test/scala/sbt/std/neg/TaskNegSpec.scala index 75549783e..e3819d9eb 100644 --- a/main-settings/src/test/scala/sbt/std/neg/TaskNegSpec.scala +++ b/main-settings/src/test/scala/sbt/std/neg/TaskNegSpec.scala @@ -7,15 +7,19 @@ package sbt.std.neg +import scala.tools.reflect.ToolBoxError + import org.scalatest.FunSuite + import sbt.std.TaskLinterDSLFeedback import sbt.std.TestUtil._ class TaskNegSpec extends FunSuite { - import tools.reflect.ToolBoxError - def expectError(errorSnippet: String, - compileOptions: String = "", - baseCompileOptions: String = s"-cp $toolboxClasspath")(code: String) = { + def expectError( + errorSnippet: String, + compileOptions: String = "", + baseCompileOptions: String = s"-cp $toolboxClasspath", + )(code: String) = { val errorMessage = intercept[ToolBoxError] { eval(code, s"$compileOptions $baseCompileOptions") println(s"Test failed -- compilation was successful! Expected:\n$errorSnippet") From a69c3d6e13ce4a41e9fb6edc5dfdabdd89b7249b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 26 Jan 2018 14:19:14 +0000 Subject: [PATCH 089/120] Format build.sbt --- build.sbt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index fbfc0ce51..9b39f5aaf 100644 --- a/build.sbt +++ b/build.sbt @@ -24,10 +24,12 @@ def buildLevelSettings: Seq[Setting[_]] = Developer("eed3si9n", "Eugene Yokota", "@eed3si9n", url("https://github.com/eed3si9n")), Developer("jsuereth", "Josh Suereth", "@jsuereth", url("https://github.com/jsuereth")), Developer("dwijnand", "Dale Wijnand", "@dwijnand", url("https://github.com/dwijnand")), - Developer("gkossakowski", - "Grzegorz Kossakowski", - "@gkossakowski", - url("https://github.com/gkossakowski")), + Developer( + "gkossakowski", + "Grzegorz Kossakowski", + "@gkossakowski", + url("https://github.com/gkossakowski") + ), Developer("Duhemm", "Martin Duhem", "@Duhemm", url("https://github.com/Duhemm")) ), homepage := Some(url("https://github.com/sbt/sbt")), From 3791e489b232e9e936d07077fdd6a216fc9392e9 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Feb 2018 14:53:05 +0000 Subject: [PATCH 090/120] Use Def.settings in build.sbt --- build.sbt | 51 +++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/build.sbt b/build.sbt index 9b39f5aaf..7f08059b4 100644 --- a/build.sbt +++ b/build.sbt @@ -40,32 +40,31 @@ def buildLevelSettings: Seq[Setting[_]] = scalafmtVersion := "1.3.0", )) -def commonSettings: Seq[Setting[_]] = - Seq[SettingsDefinition]( - headerLicense := Some(HeaderLicense.Custom( - """|sbt - |Copyright 2011 - 2017, Lightbend, Inc. - |Copyright 2008 - 2010, Mark Harrah - |Licensed under BSD-3-Clause license (see LICENSE) - |""".stripMargin - )), - scalaVersion := baseScalaVersion, - componentID := None, - resolvers += Resolver.typesafeIvyRepo("releases"), - resolvers += Resolver.sonatypeRepo("snapshots"), - resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/", - addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.4" cross CrossVersion.binary), - concurrentRestrictions in Global += Util.testExclusiveRestriction, - testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"), - testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2"), - javacOptions in compile ++= Seq("-Xlint", "-Xlint:-serial"), - crossScalaVersions := Seq(baseScalaVersion), - bintrayPackage := (bintrayPackage in ThisBuild).value, - bintrayRepository := (bintrayRepository in ThisBuild).value, - publishArtifact in Test := false, - fork in compile := true, - fork in run := true - ) flatMap (_.settings) +def commonSettings: Seq[Setting[_]] = Def settings ( + headerLicense := Some(HeaderLicense.Custom( + """|sbt + |Copyright 2011 - 2017, Lightbend, Inc. + |Copyright 2008 - 2010, Mark Harrah + |Licensed under BSD-3-Clause license (see LICENSE) + |""".stripMargin + )), + scalaVersion := baseScalaVersion, + componentID := None, + resolvers += Resolver.typesafeIvyRepo("releases"), + resolvers += Resolver.sonatypeRepo("snapshots"), + resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/", + addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.4" cross CrossVersion.binary), + concurrentRestrictions in Global += Util.testExclusiveRestriction, + testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"), + testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2"), + javacOptions in compile ++= Seq("-Xlint", "-Xlint:-serial"), + crossScalaVersions := Seq(baseScalaVersion), + bintrayPackage := (bintrayPackage in ThisBuild).value, + bintrayRepository := (bintrayRepository in ThisBuild).value, + publishArtifact in Test := false, + fork in compile := true, + fork in run := true +) def minimalSettings: Seq[Setting[_]] = commonSettings ++ customCommands ++ From 359a0109fd5b5b87f90f4e9ef916914cb25cabdb Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Feb 2018 17:00:45 +0000 Subject: [PATCH 091/120] Cleanup & simplify actions/generator --- .../actions/doc-scala-instance/build.sbt | 4 ++-- sbt/src/sbt-test/actions/generator/build.sbt | 22 +++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/sbt/src/sbt-test/actions/doc-scala-instance/build.sbt b/sbt/src/sbt-test/actions/doc-scala-instance/build.sbt index 68d4abb6e..497ab5d39 100644 --- a/sbt/src/sbt-test/actions/doc-scala-instance/build.sbt +++ b/sbt/src/sbt-test/actions/doc-scala-instance/build.sbt @@ -1,8 +1,8 @@ lazy val a = project.settings( scalaVersion := "2.12.2", - scalaInstance in (Compile,doc) := (scalaInstance in b).value, + scalaInstance in (Compile, doc) := (scalaInstance in b).value, // 2.10.1-only, so this will only succeed if `doc` recognizes the more specific scalaInstance scoped to `doc` - scalacOptions in (Compile,doc) += "-implicits" + scalacOptions in (Compile, doc) += "-implicits" ) lazy val b = project.settings( diff --git a/sbt/src/sbt-test/actions/generator/build.sbt b/sbt/src/sbt-test/actions/generator/build.sbt index c347b414e..04b2a1b48 100644 --- a/sbt/src/sbt-test/actions/generator/build.sbt +++ b/sbt/src/sbt-test/actions/generator/build.sbt @@ -1,13 +1,11 @@ -lazy val buildInfo = taskKey[Seq[File]]("The task that generates the build info.") +scalaVersion := "2.11.8" -lazy val root = (project in file(".")) - .settings( - scalaVersion := "2.11.8", - buildInfo := { - val x = sourceManaged.value / "BuildInfo.scala" - IO.write(x, """object BuildInfo""") - x :: Nil - }, - sourceGenerators in Compile += buildInfo, - sourceGenerators in Compile += Def.task { Nil } - ) +val buildInfo = taskKey[Seq[File]]("generates the build info") +buildInfo := { + val file = sourceManaged.value / "BuildInfo.scala" + IO.write(file, "object BuildInfo") + file :: Nil +} + +sourceGenerators in Compile += buildInfo +sourceGenerators in Compile += Def.task { Nil } From 4b36bca5ec6e37ac86655edf643b86f12ce6fb37 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 2 Feb 2018 14:35:47 +0000 Subject: [PATCH 092/120] Fix label so it's copy-and-paste-able --- .../sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index 69a856d89..dd9de5bc1 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -316,7 +316,7 @@ final class ScriptedTests( def runBatchTests = { groupedTests.map { case ((group, name), originalDir) => - val label = s"$group / $name" + val label = s"$group/$name" println(s"Running $label") // Copy test's contents and reload the sbt instance to pick them up IO.copyDirectory(originalDir, tempTestDir) From 26014a527ef681e5362d97d7eb7255ec71d85578 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 6 Mar 2018 19:20:31 +0000 Subject: [PATCH 093/120] Use Def.settings (bikeshed) --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 7f08059b4..f313fb9fe 100644 --- a/build.sbt +++ b/build.sbt @@ -40,7 +40,7 @@ def buildLevelSettings: Seq[Setting[_]] = scalafmtVersion := "1.3.0", )) -def commonSettings: Seq[Setting[_]] = Def settings ( +def commonSettings: Seq[Setting[_]] = Def.settings( headerLicense := Some(HeaderLicense.Custom( """|sbt |Copyright 2011 - 2017, Lightbend, Inc. From 15f449849463a3e4e774877e51989be922988936 Mon Sep 17 00:00:00 2001 From: Maksym Fedorov Date: Tue, 6 Mar 2018 23:01:31 +0100 Subject: [PATCH 094/120] Add more configuration axis ScopeFilter factory methods --- main/src/main/scala/sbt/Defaults.scala | 11 +++++++++++ main/src/main/scala/sbt/ScopeFilter.scala | 12 +++++++++++- .../in_configurations_scope_filter_factories.md | 10 ++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 notes/1.1.2/in_configurations_scope_filter_factories.md diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index b9ab939ef..b67e62f52 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -2705,6 +2705,17 @@ object Classpaths { visit(projectRef, conf) visited.toSeq } + + def interSortConfigurations( + projectRef: ProjectRef, + conf: Configuration, + data: Settings[Scope], + deps: BuildDependencies + ): Seq[(ProjectRef, ConfigRef)] = + interSort(projectRef, conf, data, deps).map { + case (projectRef, configName) => (projectRef, ConfigRef(configName)) + } + private[sbt] def unmanagedDependencies0(projectRef: ProjectRef, conf: Configuration, data: Settings[Scope], diff --git a/main/src/main/scala/sbt/ScopeFilter.scala b/main/src/main/scala/sbt/ScopeFilter.scala index a303ca09a..1139a176c 100644 --- a/main/src/main/scala/sbt/ScopeFilter.scala +++ b/main/src/main/scala/sbt/ScopeFilter.scala @@ -10,7 +10,7 @@ package sbt import sbt.internal.{ Load, LoadedBuildUnit } import sbt.internal.util.{ AttributeKey, Dag, Types } -import sbt.librarymanagement.Configuration +import sbt.librarymanagement.{ Configuration, ConfigRef } import Types.const import Def.Initialize @@ -154,6 +154,16 @@ object ScopeFilter { selectAxis[ConfigKey](const(c => cs(c.name))) } + def inConfigurationsByKeys(keys: ConfigKey*): ConfigurationFilter = { + val cs = keys.toSet + selectAxis[ConfigKey](const(cs)) + } + + def inConfigurationsByRefs(refs: ConfigRef*): ConfigurationFilter = { + val cs = refs.map(r => ConfigKey(r.name)).toSet + selectAxis[ConfigKey](const(cs)) + } + implicit def settingKeyAll[T](key: Initialize[T]): SettingKeyAll[T] = new SettingKeyAll[T](key) implicit def taskKeyAll[T](key: Initialize[Task[T]]): TaskKeyAll[T] = new TaskKeyAll[T](key) } diff --git a/notes/1.1.2/in_configurations_scope_filter_factories.md b/notes/1.1.2/in_configurations_scope_filter_factories.md new file mode 100644 index 000000000..3effa6ac7 --- /dev/null +++ b/notes/1.1.2/in_configurations_scope_filter_factories.md @@ -0,0 +1,10 @@ +### Improvements + +- Adds factory methods for Configuration axis scope filters + +### More ways to create ScopeFilter for Configuration axis + +To create configuration axis `ScopeFilter` one has to provide actual configurations +to filter by. However it's not always possible to get hold of one. For example +`Classpaths.interSort` returns configuration names. +For cases like that there are now `inConfigurationsByKeys` and `inConfigurationsByRefs` to create `ScopeFilter`'s From 251e5ab26eba540548a4104ffcf15ecd3718afb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Gon=C3=A7alves?= Date: Wed, 7 Mar 2018 00:27:22 +0000 Subject: [PATCH 095/120] Filter incompatible aggregates in cross switch commands --- main/src/main/scala/sbt/Cross.scala | 26 ++++++++++++++++--- notes/1.2.0/cross-strict-aggregation.md | 8 ++++++ .../cross-strict-aggregation/build.sbt | 13 ++++++++++ .../cross-strict-aggregation/core/A.scala | 5 ++++ .../cross-strict-aggregation/module/B.scala | 5 ++++ .../actions/cross-strict-aggregation/test | 5 ++++ 6 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 notes/1.2.0/cross-strict-aggregation.md create mode 100644 sbt/src/sbt-test/actions/cross-strict-aggregation/build.sbt create mode 100644 sbt/src/sbt-test/actions/cross-strict-aggregation/core/A.scala create mode 100644 sbt/src/sbt-test/actions/cross-strict-aggregation/module/B.scala create mode 100644 sbt/src/sbt-test/actions/cross-strict-aggregation/test diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 5d3de17c0..dcff5d449 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -216,12 +216,30 @@ object Cross { Command.arb(requireSession(switchParser), switchHelp)(switchCommandImpl) private def switchCommandImpl(state: State, args: Switch): State = { - val switchedState = switchScalaVersion(args, state) + val x = Project.extract(state) + val (switchedState, affectedRefs) = switchScalaVersion(args, state) - args.command.toList ::: switchedState + val strictCmd = + if (args.version.force) { + // The Scala version was forced on the whole build, run as is + args.command + } else { + args.command.map { rawCmd => + parseCommand(rawCmd) match { + case Right(_) => rawCmd // A project is specified, run as is + case Left(cmd) => + resolveAggregates(x) + .intersect(affectedRefs) + .collect { case ProjectRef(_, proj) => s"$proj/$cmd" } + .mkString("all ", " ", "") + } + } + } + + strictCmd.toList ::: switchedState } - private def switchScalaVersion(switch: Switch, state: State): State = { + private def switchScalaVersion(switch: Switch, state: State): (State, Seq[ResolvedReference]) = { val extracted = Project.extract(state) import extracted._ @@ -291,7 +309,7 @@ object Cross { } } - setScalaVersionForProjects(version, instance, projects, state, extracted) + (setScalaVersionForProjects(version, instance, projects, state, extracted), projects.map(_._1)) } private def setScalaVersionForProjects( diff --git a/notes/1.2.0/cross-strict-aggregation.md b/notes/1.2.0/cross-strict-aggregation.md new file mode 100644 index 000000000..170207536 --- /dev/null +++ b/notes/1.2.0/cross-strict-aggregation.md @@ -0,0 +1,8 @@ +[@ruippeixotog]: https://github.com/ruippeixotog + +[#3698]: https://github.com/sbt/sbt/issues/3698 +[#3995]: https://github.com/sbt/sbt/pull/3995 + +### Improvements + +- Make `++ ` run `` only on compatible subprojects. [#3698][]/[#3995][] by [@ruippeixotog][] diff --git a/sbt/src/sbt-test/actions/cross-strict-aggregation/build.sbt b/sbt/src/sbt-test/actions/cross-strict-aggregation/build.sbt new file mode 100644 index 000000000..6c9759d4b --- /dev/null +++ b/sbt/src/sbt-test/actions/cross-strict-aggregation/build.sbt @@ -0,0 +1,13 @@ +lazy val root = (project in file(".")) + .aggregate(core, module) + +lazy val core = (project in file("core")) + .settings( + scalaVersion := "2.12.4", + crossScalaVersions := Seq("2.11.11", "2.12.4")) + +lazy val module = (project in file("module")) + .dependsOn(core) + .settings( + scalaVersion := "2.12.4", + crossScalaVersions := Seq("2.12.4")) diff --git a/sbt/src/sbt-test/actions/cross-strict-aggregation/core/A.scala b/sbt/src/sbt-test/actions/cross-strict-aggregation/core/A.scala new file mode 100644 index 000000000..6c542aa30 --- /dev/null +++ b/sbt/src/sbt-test/actions/cross-strict-aggregation/core/A.scala @@ -0,0 +1,5 @@ +package foo + +object Foo { + +} diff --git a/sbt/src/sbt-test/actions/cross-strict-aggregation/module/B.scala b/sbt/src/sbt-test/actions/cross-strict-aggregation/module/B.scala new file mode 100644 index 000000000..d262f7e5d --- /dev/null +++ b/sbt/src/sbt-test/actions/cross-strict-aggregation/module/B.scala @@ -0,0 +1,5 @@ +package foo.module + +object FooModule { + +} diff --git a/sbt/src/sbt-test/actions/cross-strict-aggregation/test b/sbt/src/sbt-test/actions/cross-strict-aggregation/test new file mode 100644 index 000000000..fb3a4d9cb --- /dev/null +++ b/sbt/src/sbt-test/actions/cross-strict-aggregation/test @@ -0,0 +1,5 @@ +> ++2.11.11 compile + +$ exists core/target/scala-2.11 +-$ exists module/target/scala-2.11 +-$ exists module/target/scala-2.12 From 1fef1a5a6a29605e2a0c15f4fac35749ee210936 Mon Sep 17 00:00:00 2001 From: Hiroshi Ito Date: Wed, 7 Mar 2018 11:14:49 +0900 Subject: [PATCH 096/120] Fix #3990: Add CLA instruction to CONTRIBUTING.md --- CONTRIBUTING.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 190d5a0cc..0aceadc45 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -246,3 +246,12 @@ cd vscode-sbt-scala/client $ vsce package $ vsce publish ``` + +## Signing the CLA + +Contributing to sbt requires you or your employer to sign the +[Lightbend Contributor License Agreement](https://www.lightbend.com/contribute/cla). + +To make it easier to respect our license agreements, we have added an sbt task +that takes care of adding the LICENSE headers to new files. Run `headerCreate` +and sbt will put a copyright notice into it. From 49b4c3a3e0c46266401ac1dd7e050f2f9b06cbc8 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 7 Mar 2018 15:31:24 -0500 Subject: [PATCH 097/120] mima --- build.sbt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.sbt b/build.sbt index bb6df2c9d..85e006027 100644 --- a/build.sbt +++ b/build.sbt @@ -203,6 +203,11 @@ lazy val testingProj = (project in file("testing")) sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats, mimaSettings, + mimaBinaryIssueFilters ++= Seq( + // private[sbt] + ProblemFilters.exclude[IncompatibleMethTypeProblem]("sbt.TestStatus.write"), + ProblemFilters.exclude[IncompatibleResultTypeProblem]("sbt.TestStatus.read"), + ), ) .configure(addSbtIO, addSbtCompilerClasspath, addSbtUtilLogging) From f24e5f21363f5dfaf1d92c3499c574071c9078d5 Mon Sep 17 00:00:00 2001 From: jvican Date: Wed, 7 Mar 2018 16:13:23 -0500 Subject: [PATCH 098/120] Add test to confirm leak is gone The following commit adds the test case used to report #3143 in a big projects with lots of cross versions and modules. The execution of the test is instant! --- sbt/src/sbt-test/project/cross-source/build.sbt | 8 ++++++++ .../project/cross-source/p1/src/main/scala-2.10/B.scala | 3 +++ .../project/cross-source/p1/src/main/scala/A.scala | 3 +++ .../project/cross-source/p2/src/main/scala-2.10/B.scala | 3 +++ .../project/cross-source/p2/src/main/scala/A.scala | 3 +++ .../project/cross-source/p3/src/main/scala-2.10/B.scala | 3 +++ .../project/cross-source/p3/src/main/scala/A.scala | 3 +++ .../project/cross-source/p4/src/main/scala-2.10/B.scala | 3 +++ .../project/cross-source/p4/src/main/scala/A.scala | 3 +++ sbt/src/sbt-test/project/cross-source/test | 3 +++ 10 files changed, 35 insertions(+) create mode 100644 sbt/src/sbt-test/project/cross-source/build.sbt create mode 100644 sbt/src/sbt-test/project/cross-source/p1/src/main/scala-2.10/B.scala create mode 100644 sbt/src/sbt-test/project/cross-source/p1/src/main/scala/A.scala create mode 100644 sbt/src/sbt-test/project/cross-source/p2/src/main/scala-2.10/B.scala create mode 100644 sbt/src/sbt-test/project/cross-source/p2/src/main/scala/A.scala create mode 100644 sbt/src/sbt-test/project/cross-source/p3/src/main/scala-2.10/B.scala create mode 100644 sbt/src/sbt-test/project/cross-source/p3/src/main/scala/A.scala create mode 100644 sbt/src/sbt-test/project/cross-source/p4/src/main/scala-2.10/B.scala create mode 100644 sbt/src/sbt-test/project/cross-source/p4/src/main/scala/A.scala create mode 100644 sbt/src/sbt-test/project/cross-source/test diff --git a/sbt/src/sbt-test/project/cross-source/build.sbt b/sbt/src/sbt-test/project/cross-source/build.sbt new file mode 100644 index 000000000..2f3894078 --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/build.sbt @@ -0,0 +1,8 @@ +val commonSettings = Seq( + crossScalaVersions := (0 to 6).map(i => s"2.10.$i") ++ (0 to 11).map(i => s"2.11.$i") ++ (0 to 2).map(i => s"2.12.$i") +) + +val p1 = project.in(file("p1")).settings(commonSettings) +val p2 = project.in(file("p2")).settings(commonSettings) +val p3 = project.in(file("p3")).settings(commonSettings) +val p4 = project.in(file("p4")).settings(commonSettings) diff --git a/sbt/src/sbt-test/project/cross-source/p1/src/main/scala-2.10/B.scala b/sbt/src/sbt-test/project/cross-source/p1/src/main/scala-2.10/B.scala new file mode 100644 index 000000000..fa8ad30ea --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/p1/src/main/scala-2.10/B.scala @@ -0,0 +1,3 @@ +object B { + def show(what: String): String = s"String interpolation is ${what.toUpperCase}!" +} diff --git a/sbt/src/sbt-test/project/cross-source/p1/src/main/scala/A.scala b/sbt/src/sbt-test/project/cross-source/p1/src/main/scala/A.scala new file mode 100644 index 000000000..8b55ead57 --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/p1/src/main/scala/A.scala @@ -0,0 +1,3 @@ +class A { + def show(what: String): Unit = println(what) +} diff --git a/sbt/src/sbt-test/project/cross-source/p2/src/main/scala-2.10/B.scala b/sbt/src/sbt-test/project/cross-source/p2/src/main/scala-2.10/B.scala new file mode 100644 index 000000000..fa8ad30ea --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/p2/src/main/scala-2.10/B.scala @@ -0,0 +1,3 @@ +object B { + def show(what: String): String = s"String interpolation is ${what.toUpperCase}!" +} diff --git a/sbt/src/sbt-test/project/cross-source/p2/src/main/scala/A.scala b/sbt/src/sbt-test/project/cross-source/p2/src/main/scala/A.scala new file mode 100644 index 000000000..8b55ead57 --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/p2/src/main/scala/A.scala @@ -0,0 +1,3 @@ +class A { + def show(what: String): Unit = println(what) +} diff --git a/sbt/src/sbt-test/project/cross-source/p3/src/main/scala-2.10/B.scala b/sbt/src/sbt-test/project/cross-source/p3/src/main/scala-2.10/B.scala new file mode 100644 index 000000000..fa8ad30ea --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/p3/src/main/scala-2.10/B.scala @@ -0,0 +1,3 @@ +object B { + def show(what: String): String = s"String interpolation is ${what.toUpperCase}!" +} diff --git a/sbt/src/sbt-test/project/cross-source/p3/src/main/scala/A.scala b/sbt/src/sbt-test/project/cross-source/p3/src/main/scala/A.scala new file mode 100644 index 000000000..8b55ead57 --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/p3/src/main/scala/A.scala @@ -0,0 +1,3 @@ +class A { + def show(what: String): Unit = println(what) +} diff --git a/sbt/src/sbt-test/project/cross-source/p4/src/main/scala-2.10/B.scala b/sbt/src/sbt-test/project/cross-source/p4/src/main/scala-2.10/B.scala new file mode 100644 index 000000000..fa8ad30ea --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/p4/src/main/scala-2.10/B.scala @@ -0,0 +1,3 @@ +object B { + def show(what: String): String = s"String interpolation is ${what.toUpperCase}!" +} diff --git a/sbt/src/sbt-test/project/cross-source/p4/src/main/scala/A.scala b/sbt/src/sbt-test/project/cross-source/p4/src/main/scala/A.scala new file mode 100644 index 000000000..8b55ead57 --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/p4/src/main/scala/A.scala @@ -0,0 +1,3 @@ +class A { + def show(what: String): Unit = println(what) +} diff --git a/sbt/src/sbt-test/project/cross-source/test b/sbt/src/sbt-test/project/cross-source/test new file mode 100644 index 000000000..17db5c8da --- /dev/null +++ b/sbt/src/sbt-test/project/cross-source/test @@ -0,0 +1,3 @@ +# https://github.com/sbt/sbt/issues/3143 +> crossScalaVersions +> +version From dc4f93a7c3ecf2e2491aceaaecdbcec8fc8f7df9 Mon Sep 17 00:00:00 2001 From: exoego Date: Thu, 8 Mar 2018 08:14:39 +0900 Subject: [PATCH 099/120] Remove unnecessary GlobalScope --- main/src/main/scala/sbt/Defaults.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 2ab1b4259..b48c007f2 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1999,7 +1999,7 @@ object Classpaths { ConflictWarning(conflictWarning.value, report, log) report }, - evictionWarningOptions in update := (evictionWarningOptions in GlobalScope).value, + evictionWarningOptions in update := evictionWarningOptions.value, evictionWarningOptions in evicted := EvictionWarningOptions.full, evicted := { import ShowLines._ From cd9f0d271171b3cc8554ba36001f8f2c070b2c1d Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 28 Feb 2018 05:39:06 -0500 Subject: [PATCH 100/120] make sbt server extensible Fixes #3890 Here's an example: ```scala Global / serverHandlers += ServerHandler({ callback => import callback._ import sjsonnew.BasicJsonProtocol._ import sbt.internal.protocol.JsonRpcRequestMessage ServerIntent( { case r: JsonRpcRequestMessage if r.method == "lunar/helo" => jsonRpcNotify("lunar/oleh", "") () }, PartialFunction.empty ) ``` --- build.sbt | 1 + .../src/main/scala/sbt/BasicKeys.scala | 5 + .../src/main/scala/sbt/ServerHandler.scala | 67 ++++++++ main/src/main/scala/sbt/Defaults.scala | 8 +- main/src/main/scala/sbt/Keys.scala | 2 + main/src/main/scala/sbt/Project.scala | 3 + .../scala/sbt/internal/CommandExchange.scala | 6 +- .../server/LanguageServerProtocol.scala | 155 ++++++++++-------- .../sbt/internal/server/NetworkChannel.scala | 31 +++- sbt/src/server-test/handshake/build.sbt | 15 ++ sbt/src/test/scala/sbt/ServerSpec.scala | 37 +++-- 11 files changed, 244 insertions(+), 86 deletions(-) create mode 100644 main-command/src/main/scala/sbt/ServerHandler.scala diff --git a/build.sbt b/build.sbt index 8de457067..a5f333fed 100644 --- a/build.sbt +++ b/build.sbt @@ -498,6 +498,7 @@ lazy val sbtProj = (project in file("sbt")) connectInput in run in Test := true, outputStrategy in run in Test := Some(StdoutOutput), fork in Test := true, + parallelExecution in Test := false, ) .configure(addSbtCompilerBridge) diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index 1570d392b..393397f3f 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -39,6 +39,11 @@ object BasicKeys { "The wire protocol for the server command.", 10000) + val fullServerHandlers = + AttributeKey[Seq[ServerHandler]]("fullServerHandlers", + "Combines default server handlers and user-defined handlers.", + 10000) + val autoStartServer = AttributeKey[Boolean]( "autoStartServer", diff --git a/main-command/src/main/scala/sbt/ServerHandler.scala b/main-command/src/main/scala/sbt/ServerHandler.scala new file mode 100644 index 000000000..07fe8616e --- /dev/null +++ b/main-command/src/main/scala/sbt/ServerHandler.scala @@ -0,0 +1,67 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt + +import sjsonnew.JsonFormat +import sbt.internal.protocol._ +import sbt.util.Logger +import sbt.protocol.{ SettingQuery => Q } + +/** + * ServerHandler allows plugins to extend sbt server. + * It's a wrapper around curried function ServerCallback => JsonRpcRequestMessage => Unit. + */ +final class ServerHandler(val handler: ServerCallback => ServerIntent) { + override def toString: String = s"Serverhandler(...)" +} + +object ServerHandler { + def apply(handler: ServerCallback => ServerIntent): ServerHandler = + new ServerHandler(handler) + + lazy val fallback: ServerHandler = ServerHandler({ handler => + ServerIntent( + { case x => handler.log.debug(s"Unhandled notification received: ${x.method}") }, + { case x => handler.log.debug(s"Unhandled request received: ${x.method}") } + ) + }) +} + +final class ServerIntent(val onRequest: PartialFunction[JsonRpcRequestMessage, Unit], + val onNotification: PartialFunction[JsonRpcNotificationMessage, Unit]) { + override def toString: String = s"ServerIntent(...)" +} + +object ServerIntent { + def apply(onRequest: PartialFunction[JsonRpcRequestMessage, Unit], + onNotification: PartialFunction[JsonRpcNotificationMessage, Unit]): ServerIntent = + new ServerIntent(onRequest, onNotification) + + def request(onRequest: PartialFunction[JsonRpcRequestMessage, Unit]): ServerIntent = + new ServerIntent(onRequest, PartialFunction.empty) + + def notify(onNotification: PartialFunction[JsonRpcNotificationMessage, Unit]): ServerIntent = + new ServerIntent(PartialFunction.empty, onNotification) +} + +/** + * Interface to invoke JSON-RPC response. + */ +trait ServerCallback { + def jsonRpcRespond[A: JsonFormat](event: A, execId: Option[String]): Unit + def jsonRpcRespondError(execId: Option[String], code: Long, message: String): Unit + def jsonRpcNotify[A: JsonFormat](method: String, params: A): Unit + def appendExec(exec: Exec): Boolean + def log: Logger + def name: String + + private[sbt] def authOptions: Set[ServerAuthentication] + private[sbt] def authenticate(token: String): Boolean + private[sbt] def setInitialized(value: Boolean): Unit + private[sbt] def onSettingQuery(execId: Option[String], req: Q): Unit +} diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 55376c343..1bdee43ff 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -26,7 +26,7 @@ import sbt.internal.librarymanagement.mavenint.{ PomExtraDependencyAttributes, SbtPomExtraProperties } -import sbt.internal.server.{ LanguageServerReporter, Definition } +import sbt.internal.server.{ LanguageServerReporter, Definition, LanguageServerProtocol } import sbt.internal.testing.TestLogger import sbt.internal.util._ import sbt.internal.util.Attributed.data @@ -278,6 +278,12 @@ object Defaults extends BuildCommon { if (serverConnectionType.value == ConnectionType.Tcp) Set(ServerAuthentication.Token) else Set() }, + serverHandlers :== Nil, + fullServerHandlers := { + (Vector(LanguageServerProtocol.handler) + ++ serverHandlers.value + ++ Vector(ServerHandler.fallback)) + }, insideCI :== sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI"), )) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index d90311607..df551cdd7 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -136,6 +136,8 @@ object Keys { val serverHost = SettingKey(BasicKeys.serverHost) val serverAuthentication = SettingKey(BasicKeys.serverAuthentication) val serverConnectionType = SettingKey(BasicKeys.serverConnectionType) + val fullServerHandlers = SettingKey(BasicKeys.fullServerHandlers) + val serverHandlers = settingKey[Seq[ServerHandler]]("User-defined server handlers.") val analysis = AttributeKey[CompileAnalysis]("analysis", "Analysis of compilation, including dependencies and generated outputs.", DSetting) val watch = SettingKey(BasicKeys.watch) diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index b3da0452a..38124e5a5 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -27,6 +27,7 @@ import Keys.{ serverPort, serverAuthentication, serverConnectionType, + fullServerHandlers, logLevel, watch } @@ -475,6 +476,7 @@ object Project extends ProjectExtra { val authentication: Option[Set[ServerAuthentication]] = get(serverAuthentication) val connectionType: Option[ConnectionType] = get(serverConnectionType) val srvLogLevel: Option[Level.Value] = (logLevel in (ref, serverLog)).get(structure.data) + val hs: Option[Seq[ServerHandler]] = get(fullServerHandlers) val commandDefs = allCommands.distinct.flatten[Command].map(_ tag (projectCommand, true)) val newDefinedCommands = commandDefs ++ BasicCommands.removeTagged(s.definedCommands, projectCommand) @@ -491,6 +493,7 @@ object Project extends ProjectExtra { .put(templateResolverInfos.key, trs) .setCond(shellPrompt.key, prompt) .setCond(serverLogLevel, srvLogLevel) + .setCond(fullServerHandlers.key, hs) s.copy( attributes = newAttrs, definedCommands = newDefinedCommands diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 1daab4db2..2a4e1c105 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -20,6 +20,7 @@ import BasicKeys.{ serverAuthentication, serverConnectionType, serverLogLevel, + fullServerHandlers, logLevel } import java.net.Socket @@ -102,6 +103,7 @@ private[sbt] final class CommandExchange { s.get(serverAuthentication).getOrElse(Set(ServerAuthentication.Token)) lazy val connectionType = s.get(serverConnectionType).getOrElse(ConnectionType.Tcp) lazy val level = s.get(serverLogLevel).orElse(s.get(logLevel)).getOrElse(Level.Warn) + lazy val handlers = s.get(fullServerHandlers).getOrElse(Nil) def onIncomingSocket(socket: Socket, instance: ServerInstance): Unit = { val name = newNetworkName @@ -114,7 +116,7 @@ private[sbt] final class CommandExchange { log } val channel = - new NetworkChannel(name, socket, Project structure s, auth, instance, logger) + new NetworkChannel(name, socket, Project structure s, auth, instance, handlers, logger) subscribe(channel) } if (server.isEmpty && firstInstance.get) { @@ -210,7 +212,7 @@ private[sbt] final class CommandExchange { // in case we have a better client that can utilize the knowledge. import sbt.internal.langserver.codec.JsonProtocol._ if (broadcastStringMessage || (entry.channelName contains c.name)) - c.langNotify("window/logMessage", params) + c.jsonRpcNotify("window/logMessage", params) } catch { case _: IOException => toDel += c } } case _ => diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index 637bd97d0..761073591 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -21,10 +21,70 @@ import sbt.util.Logger private[sbt] case class LangServerError(code: Long, message: String) extends Throwable(message) +private[sbt] object LanguageServerProtocol { + lazy val internalJsonProtocol = new InitializeOptionFormats with sjsonnew.BasicJsonProtocol {} + + lazy val serverCapabilities: ServerCapabilities = { + ServerCapabilities(textDocumentSync = + TextDocumentSyncOptions(true, 0, false, false, SaveOptions(false)), + hoverProvider = false, + definitionProvider = true) + } + + lazy val handler: ServerHandler = ServerHandler({ + case callback: ServerCallback => + import callback._ + ServerIntent( + { + import sbt.internal.langserver.codec.JsonProtocol._ + import internalJsonProtocol._ + def json(r: JsonRpcRequestMessage) = + r.params.getOrElse( + throw LangServerError(ErrorCodes.InvalidParams, + s"param is expected on '${r.method}' method.")) + + { + case r: JsonRpcRequestMessage if r.method == "initialize" => + if (authOptions(ServerAuthentication.Token)) { + val param = Converter.fromJson[InitializeParams](json(r)).get + val optionJson = param.initializationOptions.getOrElse( + throw LangServerError(ErrorCodes.InvalidParams, + "initializationOptions is expected on 'initialize' param.")) + val opt = Converter.fromJson[InitializeOption](optionJson).get + val token = opt.token.getOrElse(sys.error("'token' is missing.")) + if (authenticate(token)) () + else throw LangServerError(ErrorCodes.InvalidRequest, "invalid token") + } else () + setInitialized(true) + appendExec(Exec(s"collectAnalyses", Some(r.id), Some(CommandSource(name)))) + jsonRpcRespond(InitializeResult(serverCapabilities), Option(r.id)) + + case r: JsonRpcRequestMessage if r.method == "textDocument/definition" => + import scala.concurrent.ExecutionContext.Implicits.global + Definition.lspDefinition(json(r), r.id, CommandSource(name), log) + () + case r: JsonRpcRequestMessage if r.method == "sbt/exec" => + val param = Converter.fromJson[SbtExecParams](json(r)).get + appendExec(Exec(param.commandLine, Some(r.id), Some(CommandSource(name)))) + () + case r: JsonRpcRequestMessage if r.method == "sbt/setting" => + import sbt.protocol.codec.JsonProtocol._ + val param = Converter.fromJson[Q](json(r)).get + onSettingQuery(Option(r.id), param) + } + }, { + case n: JsonRpcNotificationMessage if n.method == "textDocument/didSave" => + appendExec(Exec(";compile; collectAnalyses", None, Some(CommandSource(name)))) + () + } + ) + }) +} + /** * Implements Language Server Protocol . */ -private[sbt] trait LanguageServerProtocol extends CommandChannel { +private[sbt] trait LanguageServerProtocol extends CommandChannel { self => lazy val internalJsonProtocol = new InitializeOptionFormats with sjsonnew.BasicJsonProtocol {} @@ -34,54 +94,24 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { protected def log: Logger protected def onSettingQuery(execId: Option[String], req: Q): Unit - protected def onNotification(notification: JsonRpcNotificationMessage): Unit = { - log.debug(s"onNotification: $notification") - notification.method match { - case "textDocument/didSave" => - append(Exec(";compile; collectAnalyses", None, Some(CommandSource(name)))) - () - case u => log.debug(s"Unhandled notification received: $u") - } - } + protected lazy val callbackImpl: ServerCallback = new ServerCallback { + def jsonRpcRespond[A: JsonFormat](event: A, execId: Option[String]): Unit = + self.jsonRpcRespond(event, execId) - protected def onRequestMessage(request: JsonRpcRequestMessage): Unit = { - import sbt.internal.langserver.codec.JsonProtocol._ - import internalJsonProtocol._ - def json = - request.params.getOrElse( - throw LangServerError(ErrorCodes.InvalidParams, - s"param is expected on '${request.method}' method.")) - log.debug(s"onRequestMessage: $request") - request.method match { - case "initialize" => - if (authOptions(ServerAuthentication.Token)) { - val param = Converter.fromJson[InitializeParams](json).get - val optionJson = param.initializationOptions.getOrElse( - throw LangServerError(ErrorCodes.InvalidParams, - "initializationOptions is expected on 'initialize' param.")) - val opt = Converter.fromJson[InitializeOption](optionJson).get - val token = opt.token.getOrElse(sys.error("'token' is missing.")) - if (authenticate(token)) () - else throw LangServerError(ErrorCodes.InvalidRequest, "invalid token") - } else () - setInitialized(true) - append(Exec(s"collectAnalyses", Some(request.id), Some(CommandSource(name)))) - langRespond(InitializeResult(serverCapabilities), Option(request.id)) - case "textDocument/definition" => - import scala.concurrent.ExecutionContext.Implicits.global - Definition.lspDefinition(json, request.id, CommandSource(name), log) - () - case "sbt/exec" => - val param = Converter.fromJson[SbtExecParams](json).get - append(Exec(param.commandLine, Some(request.id), Some(CommandSource(name)))) - () - case "sbt/setting" => { - import sbt.protocol.codec.JsonProtocol._ - val param = Converter.fromJson[Q](json).get - onSettingQuery(Option(request.id), param) - } - case unhandledRequest => log.debug(s"Unhandled request received: $unhandledRequest") - } + def jsonRpcRespondError(execId: Option[String], code: Long, message: String): Unit = + self.jsonRpcRespondError(execId, code, message) + + def jsonRpcNotify[A: JsonFormat](method: String, params: A): Unit = + self.jsonRpcNotify(method, params) + + def appendExec(exec: Exec): Boolean = self.append(exec) + def log: Logger = self.log + def name: String = self.name + private[sbt] def authOptions: Set[ServerAuthentication] = self.authOptions + private[sbt] def authenticate(token: String): Boolean = self.authenticate(token) + private[sbt] def setInitialized(value: Boolean): Unit = self.setInitialized(value) + private[sbt] def onSettingQuery(execId: Option[String], req: Q): Unit = + self.onSettingQuery(execId, req) } /** @@ -97,7 +127,7 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { // LanguageServerReporter sends PublishDiagnosticsParams case "sbt.internal.langserver.PublishDiagnosticsParams" => // val p = event.message.asInstanceOf[PublishDiagnosticsParams] - // langNotify("textDocument/publishDiagnostics", p) + // jsonRpcNotify("textDocument/publishDiagnostics", p) case "xsbti.Problem" => () // ignore case _ => @@ -109,7 +139,7 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { /** * Respond back to Language Server's client. */ - private[sbt] def langRespond[A: JsonFormat](event: A, execId: Option[String]): Unit = { + private[sbt] def jsonRpcRespond[A: JsonFormat](event: A, execId: Option[String]): Unit = { val m = JsonRpcResponseMessage("2.0", execId, Option(Converter.toJson[A](event).get), None) val bytes = Serialization.serializeResponseMessage(m) @@ -119,7 +149,9 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { /** * Respond back to Language Server's client. */ - private[sbt] def langError(execId: Option[String], code: Long, message: String): Unit = { + private[sbt] def jsonRpcRespondError(execId: Option[String], + code: Long, + message: String): Unit = { val e = JsonRpcResponseError(code, message, None) val m = JsonRpcResponseMessage("2.0", execId, None, Option(e)) val bytes = Serialization.serializeResponseMessage(m) @@ -129,10 +161,10 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { /** * Respond back to Language Server's client. */ - private[sbt] def langError[A: JsonFormat](execId: Option[String], - code: Long, - message: String, - data: A): Unit = { + private[sbt] def jsonRpcRespondError[A: JsonFormat](execId: Option[String], + code: Long, + message: String, + data: A): Unit = { val e = JsonRpcResponseError(code, message, Option(Converter.toJson[A](data).get)) val m = JsonRpcResponseMessage("2.0", execId, None, Option(e)) val bytes = Serialization.serializeResponseMessage(m) @@ -142,26 +174,19 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { /** * Notify to Language Server's client. */ - private[sbt] def langNotify[A: JsonFormat](method: String, params: A): Unit = { + private[sbt] def jsonRpcNotify[A: JsonFormat](method: String, params: A): Unit = { val m = JsonRpcNotificationMessage("2.0", method, Option(Converter.toJson[A](params).get)) - log.debug(s"langNotify: $m") + log.debug(s"jsonRpcNotify: $m") val bytes = Serialization.serializeNotificationMessage(m) publishBytes(bytes) } def logMessage(level: String, message: String): Unit = { import sbt.internal.langserver.codec.JsonProtocol._ - langNotify( + jsonRpcNotify( "window/logMessage", LogMessageParams(MessageType.fromLevelString(level), message) ) } - - private[sbt] lazy val serverCapabilities: ServerCapabilities = { - ServerCapabilities(textDocumentSync = - TextDocumentSyncOptions(true, 0, false, false, SaveOptions(false)), - hoverProvider = false, - definitionProvider = true) - } } diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 3ad7f2fc5..e1dc9fac0 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -26,6 +26,7 @@ final class NetworkChannel(val name: String, structure: BuildStructure, auth: Set[ServerAuthentication], instance: ServerInstance, + handlers: Seq[ServerHandler], val log: Logger) extends CommandChannel with LanguageServerProtocol { @@ -76,7 +77,6 @@ final class NetworkChannel(val name: String, // contentType = "" state = SingleLine } - def tillEndOfLine: Option[Vector[Byte]] = { val delimPos = buffer.indexOf(delimiter) if (delimPos > 0) { @@ -165,6 +165,21 @@ final class NetworkChannel(val name: String, } } + private lazy val intents = { + val cb = callbackImpl + handlers.toVector map { h => + h.handler(cb) + } + } + lazy val onRequestMessage: PartialFunction[JsonRpcRequestMessage, Unit] = + intents.foldLeft(PartialFunction.empty[JsonRpcRequestMessage, Unit]) { + case (f, i) => f orElse i.onRequest + } + lazy val onNotification: PartialFunction[JsonRpcNotificationMessage, Unit] = + intents.foldLeft(PartialFunction.empty[JsonRpcNotificationMessage, Unit]) { + case (f, i) => f orElse i.onNotification + } + def handleBody(chunk: Vector[Byte]): Unit = { if (isLanguageServerProtocol) { Serialization.deserializeJsonMessage(chunk) match { @@ -174,7 +189,7 @@ final class NetworkChannel(val name: String, } catch { case LangServerError(code, message) => log.debug(s"sending error: $code: $message") - langError(Option(req.id), code, message) + jsonRpcRespondError(Option(req.id), code, message) } case Right(ntf: JsonRpcNotificationMessage) => try { @@ -182,13 +197,13 @@ final class NetworkChannel(val name: String, } catch { case LangServerError(code, message) => log.debug(s"sending error: $code: $message") - langError(None, code, message) // new id? + jsonRpcRespondError(None, code, message) // new id? } case Right(msg) => log.debug(s"Unhandled message: $msg") case Left(errorDesc) => val msg = s"Got invalid chunk from client (${new String(chunk.toArray, "UTF-8")}): " + errorDesc - langError(None, ErrorCodes.ParseError, msg) + jsonRpcRespondError(None, ErrorCodes.ParseError, msg) } } else { contentType match { @@ -230,7 +245,7 @@ final class NetworkChannel(val name: String, private[sbt] def notifyEvent[A: JsonFormat](method: String, params: A): Unit = { if (isLanguageServerProtocol) { - langNotify(method, params) + jsonRpcNotify(method, params) } else { () } @@ -240,7 +255,7 @@ final class NetworkChannel(val name: String, if (isLanguageServerProtocol) { event match { case entry: StringEvent => logMessage(entry.level, entry.message) - case _ => langRespond(event, execId) + case _ => jsonRpcRespond(event, execId) } } else { contentType match { @@ -341,8 +356,8 @@ final class NetworkChannel(val name: String, if (initialized) { import sbt.protocol.codec.JsonProtocol._ SettingQuery.handleSettingQueryEither(req, structure) match { - case Right(x) => langRespond(x, execId) - case Left(s) => langError(execId, ErrorCodes.InvalidParams, s) + case Right(x) => jsonRpcRespond(x, execId) + case Left(s) => jsonRpcRespondError(execId, ErrorCodes.InvalidParams, s) } } else { log.warn(s"ignoring query $req before initialization") diff --git a/sbt/src/server-test/handshake/build.sbt b/sbt/src/server-test/handshake/build.sbt index 192730eef..1267ea46b 100644 --- a/sbt/src/server-test/handshake/build.sbt +++ b/sbt/src/server-test/handshake/build.sbt @@ -1,6 +1,21 @@ lazy val root = (project in file(".")) .settings( Global / serverLog / logLevel := Level.Debug, + + // custom handler + Global / serverHandlers += ServerHandler({ callback => + import callback._ + import sjsonnew.BasicJsonProtocol._ + import sbt.internal.protocol.JsonRpcRequestMessage + ServerIntent( + { + case r: JsonRpcRequestMessage if r.method == "lunar/helo" => + jsonRpcNotify("lunar/oleh", "") + () + }, + PartialFunction.empty + ) + }), name := "handshake", scalaVersion := "2.12.3", ) diff --git a/sbt/src/test/scala/sbt/ServerSpec.scala b/sbt/src/test/scala/sbt/ServerSpec.scala index 7ad307fd8..5c25d1afb 100644 --- a/sbt/src/test/scala/sbt/ServerSpec.scala +++ b/sbt/src/test/scala/sbt/ServerSpec.scala @@ -20,15 +20,19 @@ class ServerSpec extends AsyncFlatSpec with Matchers { "server" should "start" in { withBuildSocket("handshake") { (out, in, tkn) => writeLine( - """{ "jsonrpc": "2.0", "id": 3, "method": "sbt/setting", "params": { "setting": "root/name" } }""", + """{ "jsonrpc": "2.0", "id": 3, "method": "sbt/setting", "params": { "setting": "handshake/name" } }""", out) Thread.sleep(100) - val l2 = contentLength(in) - println(l2) - readLine(in) - readLine(in) - val x2 = readContentLength(in, l2) - println(x2) + + println(readFrame(in)) + println(readFrame(in)) + println(readFrame(in)) + println(readFrame(in)) + // println(readFrame(in)) + + writeLine("""{ "jsonrpc": "2.0", "id": 10, "method": "lunar/helo", "params": {} }""", out) + Thread.sleep(100) + assert(1 == 1) } } @@ -90,6 +94,14 @@ object ServerSpec { 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 @@ -98,7 +110,11 @@ object ServerSpec { def readLine(in: InputStream): Option[String] = { if (buffer.isEmpty) { - val bytesRead = in.read(readBuffer) + val bytesRead = try { + in.read(readBuffer) + } catch { + case _: java.io.IOException => 0 + } if (bytesRead > 0) { buffer = buffer ++ readBuffer.toVector.take(bytesRead) } @@ -153,11 +169,12 @@ object ServerSpec { 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(10) + waitForPortfile(20) val (sk, tkn) = ClientSocket.socket(portfile) val out = sk.getOutputStream val in = sk.getInputStream @@ -172,7 +189,7 @@ object ServerSpec { sendJsonRpc( """{ "jsonrpc": "2.0", "id": 9, "method": "sbt/exec", "params": { "commandLine": "exit" } }""", out) - shutdown() + // shutdown() } } } From f13465246c382d5416acf732da2bfee2ed68b28c Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 9 Mar 2018 18:01:10 -0500 Subject: [PATCH 101/120] include the full body in debug message --- main-command/src/main/scala/sbt/ServerHandler.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main-command/src/main/scala/sbt/ServerHandler.scala b/main-command/src/main/scala/sbt/ServerHandler.scala index 07fe8616e..ce9429c33 100644 --- a/main-command/src/main/scala/sbt/ServerHandler.scala +++ b/main-command/src/main/scala/sbt/ServerHandler.scala @@ -26,8 +26,8 @@ object ServerHandler { lazy val fallback: ServerHandler = ServerHandler({ handler => ServerIntent( - { case x => handler.log.debug(s"Unhandled notification received: ${x.method}") }, - { case x => handler.log.debug(s"Unhandled request received: ${x.method}") } + { case x => handler.log.debug(s"Unhandled notification received: ${x.method}: $x") }, + { case x => handler.log.debug(s"Unhandled request received: ${x.method}: $x") } ) }) } From 0433440c59c735d2ce908aaa300461759f5d914d Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 13 Mar 2018 23:42:19 +0900 Subject: [PATCH 102/120] move ServerHandler to internal per review --- main-command/src/main/scala/sbt/BasicKeys.scala | 1 + .../scala/sbt/{ => internal/server}/ServerHandler.scala | 2 ++ main/src/main/scala/sbt/Defaults.scala | 7 ++++++- main/src/main/scala/sbt/Keys.scala | 1 + main/src/main/scala/sbt/Project.scala | 1 + sbt/src/server-test/handshake/build.sbt | 2 ++ 6 files changed, 13 insertions(+), 1 deletion(-) rename main-command/src/main/scala/sbt/{ => internal/server}/ServerHandler.scala (98%) diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index 393397f3f..5ceeec21b 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -10,6 +10,7 @@ package sbt import java.io.File import sbt.internal.util.AttributeKey import sbt.internal.inc.classpath.ClassLoaderCache +import sbt.internal.server.ServerHandler import sbt.librarymanagement.ModuleID import sbt.util.Level diff --git a/main-command/src/main/scala/sbt/ServerHandler.scala b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala similarity index 98% rename from main-command/src/main/scala/sbt/ServerHandler.scala rename to main-command/src/main/scala/sbt/internal/server/ServerHandler.scala index ce9429c33..47b4f4fbf 100644 --- a/main-command/src/main/scala/sbt/ServerHandler.scala +++ b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala @@ -6,6 +6,8 @@ */ package sbt +package internal +package server import sjsonnew.JsonFormat import sbt.internal.protocol._ diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 1bdee43ff..36299ac25 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -26,7 +26,12 @@ import sbt.internal.librarymanagement.mavenint.{ PomExtraDependencyAttributes, SbtPomExtraProperties } -import sbt.internal.server.{ LanguageServerReporter, Definition, LanguageServerProtocol } +import sbt.internal.server.{ + LanguageServerReporter, + Definition, + LanguageServerProtocol, + ServerHandler +} import sbt.internal.testing.TestLogger import sbt.internal.util._ import sbt.internal.util.Attributed.data diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index df551cdd7..82191f556 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -42,6 +42,7 @@ import sbt.internal.{ } import sbt.io.{ FileFilter, WatchService } import sbt.internal.io.WatchState +import sbt.internal.server.ServerHandler import sbt.internal.util.{ AttributeKey, SourcePosition } import sbt.librarymanagement.Configurations.CompilerPlugin diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 38124e5a5..ea3a6b942 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -45,6 +45,7 @@ import sbt.internal.{ import sbt.internal.util.{ AttributeKey, AttributeMap, Dag, Relation, Settings, ~> } import sbt.internal.util.Types.{ const, idFun } import sbt.internal.util.complete.DefaultParsers +import sbt.internal.server.ServerHandler import sbt.librarymanagement.Configuration import sbt.util.{ Show, Level } import sjsonnew.JsonFormat diff --git a/sbt/src/server-test/handshake/build.sbt b/sbt/src/server-test/handshake/build.sbt index 1267ea46b..fcfe026ac 100644 --- a/sbt/src/server-test/handshake/build.sbt +++ b/sbt/src/server-test/handshake/build.sbt @@ -1,3 +1,5 @@ +import sbt.internal.ServerHandler + lazy val root = (project in file(".")) .settings( Global / serverLog / logLevel := Level.Debug, From de690f4e41b14dc4b55e44c1ad4e5a43f3bdf0ff Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 9 Mar 2018 12:01:53 +0000 Subject: [PATCH 103/120] Collapse 1-line scaladocs --- .../server/LanguageServerProtocol.scala | 20 +++++-------------- .../scala/sbt/protocol/Serialization.scala | 10 ++-------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index 761073591..0083221b0 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -81,9 +81,7 @@ private[sbt] object LanguageServerProtocol { }) } -/** - * Implements Language Server Protocol . - */ +/** Implements Language Server Protocol . */ private[sbt] trait LanguageServerProtocol extends CommandChannel { self => lazy val internalJsonProtocol = new InitializeOptionFormats with sjsonnew.BasicJsonProtocol {} @@ -136,9 +134,7 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self => } } - /** - * Respond back to Language Server's client. - */ + /** Respond back to Language Server's client. */ private[sbt] def jsonRpcRespond[A: JsonFormat](event: A, execId: Option[String]): Unit = { val m = JsonRpcResponseMessage("2.0", execId, Option(Converter.toJson[A](event).get), None) @@ -146,9 +142,7 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self => publishBytes(bytes) } - /** - * Respond back to Language Server's client. - */ + /** Respond back to Language Server's client. */ private[sbt] def jsonRpcRespondError(execId: Option[String], code: Long, message: String): Unit = { @@ -158,9 +152,7 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self => publishBytes(bytes) } - /** - * Respond back to Language Server's client. - */ + /** Respond back to Language Server's client. */ private[sbt] def jsonRpcRespondError[A: JsonFormat](execId: Option[String], code: Long, message: String, @@ -171,9 +163,7 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self => publishBytes(bytes) } - /** - * Notify to Language Server's client. - */ + /** Notify to Language Server's client. */ private[sbt] def jsonRpcNotify[A: JsonFormat](method: String, params: A): Unit = { val m = JsonRpcNotificationMessage("2.0", method, Option(Converter.toJson[A](params).get)) diff --git a/protocol/src/main/scala/sbt/protocol/Serialization.scala b/protocol/src/main/scala/sbt/protocol/Serialization.scala index 75b9e7c83..1a62e53f7 100644 --- a/protocol/src/main/scala/sbt/protocol/Serialization.scala +++ b/protocol/src/main/scala/sbt/protocol/Serialization.scala @@ -41,10 +41,7 @@ object Serialization { CompactPrinter(json).getBytes("UTF-8") } - /** - * This formats the message according to JSON-RPC. - * http://www.jsonrpc.org/specification - */ + /** This formats the message according to JSON-RPC. http://www.jsonrpc.org/specification */ private[sbt] def serializeResponseMessage(message: JsonRpcResponseMessage): Array[Byte] = { import sbt.internal.protocol.codec.JsonRPCProtocol._ val json: JValue = Converter.toJson[JsonRpcResponseMessage](message).get @@ -57,10 +54,7 @@ object Serialization { body).getBytes("UTF-8") } - /** - * This formats the message according to JSON-RPC. - * http://www.jsonrpc.org/specification - */ + /** This formats the message according to JSON-RPC. http://www.jsonrpc.org/specification */ private[sbt] def serializeNotificationMessage( message: JsonRpcNotificationMessage): Array[Byte] = { import sbt.internal.protocol.codec.JsonRPCProtocol._ From 268b5111ab143fdb1ba1ca138dfc6b9425768268 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 9 Mar 2018 12:02:45 +0000 Subject: [PATCH 104/120] Format LSP methods --- .../server/LanguageServerProtocol.scala | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index 0083221b0..311eb405f 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -143,9 +143,11 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self => } /** Respond back to Language Server's client. */ - private[sbt] def jsonRpcRespondError(execId: Option[String], - code: Long, - message: String): Unit = { + private[sbt] def jsonRpcRespondError( + execId: Option[String], + code: Long, + message: String, + ): Unit = { val e = JsonRpcResponseError(code, message, None) val m = JsonRpcResponseMessage("2.0", execId, None, Option(e)) val bytes = Serialization.serializeResponseMessage(m) @@ -153,10 +155,12 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self => } /** Respond back to Language Server's client. */ - private[sbt] def jsonRpcRespondError[A: JsonFormat](execId: Option[String], - code: Long, - message: String, - data: A): Unit = { + private[sbt] def jsonRpcRespondError[A: JsonFormat]( + execId: Option[String], + code: Long, + message: String, + data: A, + ): Unit = { val e = JsonRpcResponseError(code, message, Option(Converter.toJson[A](data).get)) val m = JsonRpcResponseMessage("2.0", execId, None, Option(e)) val bytes = Serialization.serializeResponseMessage(m) From d3ef452a5f7288eb03205d0a13789f0fc2a0821e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 9 Mar 2018 12:02:52 +0000 Subject: [PATCH 105/120] Extract jsonRpcRespondErrorImpl --- .../server/LanguageServerProtocol.scala | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index 311eb405f..8dc514d40 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -10,6 +10,7 @@ package internal package server import sjsonnew.JsonFormat +import sjsonnew.shaded.scalajson.ast.unsafe.JValue import sjsonnew.support.scalajson.unsafe.Converter import sbt.protocol.Serialization import sbt.protocol.{ SettingQuery => Q } @@ -143,16 +144,8 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self => } /** Respond back to Language Server's client. */ - private[sbt] def jsonRpcRespondError( - execId: Option[String], - code: Long, - message: String, - ): Unit = { - val e = JsonRpcResponseError(code, message, None) - val m = JsonRpcResponseMessage("2.0", execId, None, Option(e)) - val bytes = Serialization.serializeResponseMessage(m) - publishBytes(bytes) - } + private[sbt] def jsonRpcRespondError(execId: Option[String], code: Long, message: String): Unit = + jsonRpcRespondErrorImpl(execId, code, message, None) /** Respond back to Language Server's client. */ private[sbt] def jsonRpcRespondError[A: JsonFormat]( @@ -160,8 +153,16 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self => code: Long, message: String, data: A, + ): Unit = + jsonRpcRespondErrorImpl(execId, code, message, Option(Converter.toJson[A](data).get)) + + private[this] def jsonRpcRespondErrorImpl( + execId: Option[String], + code: Long, + message: String, + data: Option[JValue], ): Unit = { - val e = JsonRpcResponseError(code, message, Option(Converter.toJson[A](data).get)) + val e = JsonRpcResponseError(code, message, data) val m = JsonRpcResponseMessage("2.0", execId, None, Option(e)) val bytes = Serialization.serializeResponseMessage(m) publishBytes(bytes) From 5f56fa9f14ad48014f2fc7ce686d9a336e672f66 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 9 Mar 2018 12:06:29 +0000 Subject: [PATCH 106/120] Extract serializeResponse --- .../scala/sbt/protocol/Serialization.scala | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/protocol/src/main/scala/sbt/protocol/Serialization.scala b/protocol/src/main/scala/sbt/protocol/Serialization.scala index 1a62e53f7..21d798b80 100644 --- a/protocol/src/main/scala/sbt/protocol/Serialization.scala +++ b/protocol/src/main/scala/sbt/protocol/Serialization.scala @@ -8,7 +8,7 @@ package sbt package protocol -import sjsonnew.JsonFormat +import sjsonnew.{ JsonFormat, JsonWriter } import sjsonnew.support.scalajson.unsafe.{ Parser, Converter, CompactPrinter } import sjsonnew.shaded.scalajson.ast.unsafe.{ JValue, JObject, JString } import java.nio.ByteBuffer @@ -44,28 +44,28 @@ object Serialization { /** This formats the message according to JSON-RPC. http://www.jsonrpc.org/specification */ private[sbt] def serializeResponseMessage(message: JsonRpcResponseMessage): Array[Byte] = { import sbt.internal.protocol.codec.JsonRPCProtocol._ - val json: JValue = Converter.toJson[JsonRpcResponseMessage](message).get - val body = CompactPrinter(json) - val bodyBytes = body.getBytes("UTF-8") - - (s"Content-Length: ${bodyBytes.size}\r\n" + - s"Content-Type: $VsCode\r\n" + - "\r\n" + - body).getBytes("UTF-8") + serializeResponse(message) } /** This formats the message according to JSON-RPC. http://www.jsonrpc.org/specification */ private[sbt] def serializeNotificationMessage( - message: JsonRpcNotificationMessage): Array[Byte] = { + message: JsonRpcNotificationMessage, + ): Array[Byte] = { import sbt.internal.protocol.codec.JsonRPCProtocol._ - val json: JValue = Converter.toJson[JsonRpcNotificationMessage](message).get - val body = CompactPrinter(json) - val bodyBytes = body.getBytes("UTF-8") + serializeResponse(message) + } - (s"Content-Length: ${bodyBytes.size}\r\n" + - s"Content-Type: $VsCode\r\n" + - "\r\n" + - body).getBytes("UTF-8") + private[sbt] def serializeResponse[A: JsonWriter](message: A): Array[Byte] = { + val json: JValue = Converter.toJson[A](message).get + val body = CompactPrinter(json) + val bodyLength = body.getBytes("UTF-8").length + + Iterator( + s"Content-Length: $bodyLength", + s"Content-Type: $VsCode", + "", + body + ).mkString("\r\n").getBytes("UTF-8") } /** From 6ceed00f48b85545071a7b2490c3177914017670 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 12 Mar 2018 17:49:43 +0000 Subject: [PATCH 107/120] Cleanup NetworkChannel --- build.sbt | 5 ++++- .../internal/server/LanguageServerProtocol.scala | 3 ++- .../scala/sbt/internal/server/NetworkChannel.scala | 13 +++---------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/build.sbt b/build.sbt index a5f333fed..2f21cfe7f 100644 --- a/build.sbt +++ b/build.sbt @@ -451,9 +451,12 @@ lazy val mainProj = (project in file("main")) sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", mimaSettings, mimaBinaryIssueFilters ++= Vector( - // Changed signature or removed something in the internal pacakge + // Changed signature or removed something in the internal package exclude[DirectMissingMethodProblem]("sbt.internal.*"), + // Made something final in the internal package + exclude[FinalClassProblem]("sbt.internal.*"), + // New and changed methods on KeyIndex. internal. exclude[ReversedMissingMethodProblem]("sbt.internal.KeyIndex.*"), diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index 8dc514d40..929ea21d1 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -20,7 +20,8 @@ import sbt.internal.langserver._ import sbt.internal.util.ObjectEvent import sbt.util.Logger -private[sbt] case class LangServerError(code: Long, message: String) extends Throwable(message) +private[sbt] final case class LangServerError(code: Long, message: String) + extends Throwable(message) private[sbt] object LanguageServerProtocol { lazy val internalJsonProtocol = new InitializeOptionFormats with sjsonnew.BasicJsonProtocol {} diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index e1dc9fac0..9e5d3e718 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -46,18 +46,12 @@ final class NetworkChannel(val name: String, private val VsCodeOld = "application/vscode-jsonrpc; charset=utf8" private lazy val jsonFormat = new sjsonnew.BasicJsonProtocol with JValueFormats {} - def setContentType(ct: String): Unit = synchronized { - _contentType = ct - } + def setContentType(ct: String): Unit = synchronized { _contentType = ct } def contentType: String = _contentType - protected def authenticate(token: String): Boolean = { - instance.authenticate(token) - } + protected def authenticate(token: String): Boolean = instance.authenticate(token) - protected def setInitialized(value: Boolean): Unit = { - initialized = value - } + protected def setInitialized(value: Boolean): Unit = initialized = value protected def authOptions: Set[ServerAuthentication] = auth @@ -74,7 +68,6 @@ final class NetworkChannel(val name: String, var bytesRead = 0 def resetChannelState(): Unit = { contentLength = 0 - // contentType = "" state = SingleLine } def tillEndOfLine: Option[Vector[Byte]] = { From 9c0ac90ee99c2145a42fc6af1f7a3aa03fc18990 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 12 Mar 2018 17:13:17 +0000 Subject: [PATCH 108/120] Make Watched use State#handleError --- build.sbt | 2 ++ main-command/src/main/scala/sbt/State.scala | 2 +- main-command/src/main/scala/sbt/Watched.scala | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index a5f333fed..a66cf9996 100644 --- a/build.sbt +++ b/build.sbt @@ -383,6 +383,8 @@ lazy val commandProj = (project in file("main-command")) // Replace nailgun socket stuff exclude[MissingClassProblem]("sbt.internal.NG*"), exclude[MissingClassProblem]("sbt.internal.ReferenceCountedFileDescriptor"), + // made private[sbt] method private[this] + exclude[DirectMissingMethodProblem]("sbt.State.handleException"), ), unmanagedSources in (Compile, headerCreate) := { val old = (unmanagedSources in (Compile, headerCreate)).value diff --git a/main-command/src/main/scala/sbt/State.scala b/main-command/src/main/scala/sbt/State.scala index eb15168fe..a3e178dc6 100644 --- a/main-command/src/main/scala/sbt/State.scala +++ b/main-command/src/main/scala/sbt/State.scala @@ -323,7 +323,7 @@ object State { import ExceptionCategory._ - private[sbt] def handleException(t: Throwable, s: State, log: Logger): State = { + private[this] def handleException(t: Throwable, s: State, log: Logger): State = { ExceptionCategory(t) match { case AlreadyHandled => () case m: MessageOnly => log.error(m.message) diff --git a/main-command/src/main/scala/sbt/Watched.scala b/main-command/src/main/scala/sbt/Watched.scala index 5424c8d7b..fad1b6a8d 100644 --- a/main-command/src/main/scala/sbt/Watched.scala +++ b/main-command/src/main/scala/sbt/Watched.scala @@ -112,9 +112,9 @@ object Watched { (triggered, newWatchState) } catch { case e: Exception => - val log = s.log - log.error("Error occurred obtaining files to watch. Terminating continuous execution...") - State.handleException(e, s, log) + s.log.error( + "Error occurred obtaining files to watch. Terminating continuous execution...") + s.handleError(e) (false, watchState) } From 00ce32f102092d1586513270c64d8f570d28ae80 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 12 Mar 2018 17:21:22 +0000 Subject: [PATCH 109/120] Cleanup CommandChannel --- build.sbt | 18 ++++++++---------- .../scala/sbt/internal/CommandChannel.scala | 2 +- .../scala/sbt/internal/ConsoleChannel.scala | 2 -- .../sbt/internal/server/NetworkChannel.scala | 2 -- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/build.sbt b/build.sbt index 87b10d702..c5e115e31 100644 --- a/build.sbt +++ b/build.sbt @@ -84,7 +84,14 @@ val mimaSettings = Def settings ( ).map { v => organization.value % moduleName.value % v cross (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled) }.toSet - } + }, + mimaBinaryIssueFilters ++= Seq( + // Changes in the internal pacakge + exclude[DirectMissingMethodProblem]("sbt.internal.*"), + exclude[FinalClassProblem]("sbt.internal.*"), + exclude[FinalMethodProblem]("sbt.internal.*"), + exclude[IncompatibleResultTypeProblem]("sbt.internal.*"), + ), ) lazy val sbtRoot: Project = (project in file(".")) @@ -182,9 +189,6 @@ val completeProj = (project in file("internal") / "util-complete") libraryDependencies += jline, mimaSettings, mimaBinaryIssueFilters ++= Seq( - // Changed signature or removed something in the internal pacakge - exclude[DirectMissingMethodProblem]("sbt.internal.*"), - exclude[IncompatibleResultTypeProblem]("sbt.internal.*"), ), ) .configure(addSbtIO, addSbtUtilControl) @@ -453,12 +457,6 @@ lazy val mainProj = (project in file("main")) sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", mimaSettings, mimaBinaryIssueFilters ++= Vector( - // Changed signature or removed something in the internal package - exclude[DirectMissingMethodProblem]("sbt.internal.*"), - - // Made something final in the internal package - exclude[FinalClassProblem]("sbt.internal.*"), - // New and changed methods on KeyIndex. internal. exclude[ReversedMissingMethodProblem]("sbt.internal.KeyIndex.*"), diff --git a/main-command/src/main/scala/sbt/internal/CommandChannel.scala b/main-command/src/main/scala/sbt/internal/CommandChannel.scala index 6b1b8e391..54c65cfd3 100644 --- a/main-command/src/main/scala/sbt/internal/CommandChannel.scala +++ b/main-command/src/main/scala/sbt/internal/CommandChannel.scala @@ -23,7 +23,7 @@ abstract class CommandChannel { def poll: Option[Exec] = Option(commandQueue.poll) def publishEvent[A: JsonFormat](event: A, execId: Option[String]): Unit - def publishEvent[A: JsonFormat](event: A): Unit + final def publishEvent[A: JsonFormat](event: A): Unit = publishEvent(event, None) def publishEventMessage(event: EventMessage): Unit def publishBytes(bytes: Array[Byte]): Unit def shutdown(): Unit diff --git a/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala b/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala index a0356234e..3f039f270 100644 --- a/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala +++ b/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala @@ -40,8 +40,6 @@ private[sbt] final class ConsoleChannel(val name: String) extends CommandChannel def publishEvent[A: JsonFormat](event: A, execId: Option[String]): Unit = () - def publishEvent[A: JsonFormat](event: A): Unit = () - def publishEventMessage(event: EventMessage): Unit = event match { case e: ConsolePromptEvent => diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 9e5d3e718..75c42f542 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -260,8 +260,6 @@ final class NetworkChannel(val name: String, } } - def publishEvent[A: JsonFormat](event: A): Unit = publishEvent(event, None) - def publishEventMessage(event: EventMessage): Unit = { if (isLanguageServerProtocol) { event match { From 006527d246649a5d72f95200576334e904545271 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 27 Mar 2018 09:08:52 +0100 Subject: [PATCH 110/120] Re-order SlashSyntaxSpec arbitraries --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index a4c802934..ea1bcf2ea 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -57,16 +57,42 @@ object BuildDSLInstances { implicit def arbAttrKey[A: Manifest]: Arbitrary[AttributeKey[_]] = Arbitrary(Gen.identifier map (AttributeKey[A](_))) - def withScope[K <: Scoped.ScopingSetting[K]](keyGen: Gen[K]): Arbitrary[K] = - Arbitrary(Gen.frequency( - 5 -> keyGen, - 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) - )) + implicit val arbAttributeMap: Arbitrary[AttributeMap] = Arbitrary { + Gen.frequency( + 20 -> AttributeMap.empty, + 1 -> { + for (name <- Gen.identifier; isModule <- arbitrary[Boolean]) + yield + AttributeMap.empty + .put(AttributeKey[String]("name"), name) + .put(AttributeKey[Boolean]("isModule"), isModule) + } + ) + } + + implicit def arbScopeAxis[A: Arbitrary]: Arbitrary[ScopeAxis[A]] = + Arbitrary(Gen.oneOf[ScopeAxis[A]](This, Zero, arbitrary[A] map (Select(_)))) + + implicit def arbScope: Arbitrary[Scope] = Arbitrary( + for { + r <- arbitrary[ScopeAxis[Reference]] + c <- arbitrary[ScopeAxis[ConfigKey]] + t <- arbitrary[ScopeAxis[AttributeKey[_]]] + e <- arbitrary[ScopeAxis[AttributeMap]] + } yield Scope(r, c, t, e) + ) def genInputKey[A: Manifest]: Gen[InputKey[A]] = Gen.identifier map (InputKey[A](_)) def genSettingKey[A: Manifest]: Gen[SettingKey[A]] = Gen.identifier map (SettingKey[A](_)) def genTaskKey[A: Manifest]: Gen[TaskKey[A]] = Gen.identifier map (TaskKey[A](_)) + def withScope[K <: Scoped.ScopingSetting[K]](keyGen: Gen[K]): Arbitrary[K] = Arbitrary { + Gen.frequency( + 5 -> keyGen, + 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) + ) + } + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = withScope(genInputKey[A]) implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = withScope(genSettingKey[A]) implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = withScope(genTaskKey[A]) @@ -88,29 +114,6 @@ object BuildDSLInstances { implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = Arbitrary(genSettingKey[A]) implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = Arbitrary(genTaskKey[A]) } - - implicit def arbScopeAxis[A: Arbitrary]: Arbitrary[ScopeAxis[A]] = - Arbitrary(Gen.oneOf[ScopeAxis[A]](This, Zero, arbitrary[A] map (Select(_)))) - - implicit val arbAttributeMap: Arbitrary[AttributeMap] = Arbitrary { - Gen.frequency( - 20 -> AttributeMap.empty, - 1 -> (for (name <- Gen.identifier; isModule <- arbitrary[Boolean]) - yield AttributeMap.empty - .put(AttributeKey[String]("name"), name) - .put(AttributeKey[Boolean]("isModule"), isModule) - ) - ) - } - - implicit def arbScope: Arbitrary[Scope] = Arbitrary( - for { - r <- arbitrary[ScopeAxis[Reference]] - c <- arbitrary[ScopeAxis[ConfigKey]] - t <- arbitrary[ScopeAxis[AttributeKey[_]]] - e <- arbitrary[ScopeAxis[AttributeMap]] - } yield Scope(r, c, t, e) - ) } import BuildDSLInstances._ From 51ff72872bdc7de92ace2244d090ef032516c058 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 27 Mar 2018 01:21:38 +0100 Subject: [PATCH 111/120] De-generalise expectValue in SlashSyntaxSpec --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 32 +++---------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index ea1bcf2ea..2c843d989 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -117,33 +117,6 @@ object BuildDSLInstances { } import BuildDSLInstances._ -object CustomEquality { - trait Eq[A] { - def equal(x: A, y: A): Boolean - } - - // Avoid reimplementing equality for other standard classes. - trait EqualLowPriority { - implicit def universal[A] = (x: A, y: A) => x == y - } - - object Eq extends EqualLowPriority { - def apply[A: Eq]: Eq[A] = implicitly - - implicit def eqScoped[A <: Scoped]: Eq[A] = (x, y) => x.scope == y.scope && x.key == y.key - } - - implicit class AnyWith_===[A](private val x: A) extends AnyVal { - def ===(y: A)(implicit z: Eq[A]): Boolean = z.equal(x, y) - def =?(y: A)(implicit z: Eq[A]): Prop = { - if (x === y) proved else falsified :| s"Expected $x but got $y" - } - } - - def expectValue[A: Eq](expected: A)(x: A) = expected =? x -} -import CustomEquality._ - object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { type Key[K] = Scoped.ScopingSetting[K] with Scoped @@ -296,4 +269,9 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k)) check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] } + + def expectValue(expected: Scoped)(x: Scoped) = { + val equals = x.scope == expected.scope && x.key == expected.key + if (equals) proved else falsified :| s"Expected $expected but got $x" + } } From 42b61a4f8ebfdcb3344bfc83812da3216f5717f0 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 27 Mar 2018 02:14:33 +0100 Subject: [PATCH 112/120] Dedup keys in SlashSyntaxSpec --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 145 +++++------------- 1 file changed, 41 insertions(+), 104 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 2c843d989..804031cb3 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -82,6 +82,8 @@ object BuildDSLInstances { } yield Scope(r, c, t, e) ) + type Key = K forSome { type K <: Scoped.ScopingSetting[K] with Scoped } + def genInputKey[A: Manifest]: Gen[InputKey[A]] = Gen.identifier map (InputKey[A](_)) def genSettingKey[A: Manifest]: Gen[SettingKey[A]] = Gen.identifier map (SettingKey[A](_)) def genTaskKey[A: Manifest]: Gen[TaskKey[A]] = Gen.identifier map (TaskKey[A](_)) @@ -97,16 +99,18 @@ object BuildDSLInstances { implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = withScope(genSettingKey[A]) implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = withScope(genTaskKey[A]) - implicit def arbScoped[A: Manifest](implicit + implicit def arbKey[A: Manifest]( + implicit arbInputKey: Arbitrary[InputKey[A]], arbSettingKey: Arbitrary[SettingKey[A]], arbTaskKey: Arbitrary[TaskKey[A]], - ): Arbitrary[Scoped] = { - Arbitrary(Gen.frequency( - 15 -> arbitrary[InputKey[A]], // 15,431 - 20 -> arbitrary[SettingKey[A]], // 19,645 - 23 -> arbitrary[TaskKey[A]], // 22,867 - )) + ): Arbitrary[Key] = Arbitrary { + def convert[T](g: Gen[T]) = g.asInstanceOf[Gen[Key]] + Gen.frequency( + 15431 -> convert(arbitrary[InputKey[A]]), + 19645 -> convert(arbitrary[SettingKey[A]]), + 22867 -> convert(arbitrary[TaskKey[A]]), + ) } object WithoutScope { @@ -114,160 +118,93 @@ object BuildDSLInstances { implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = Arbitrary(genSettingKey[A]) implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = Arbitrary(genTaskKey[A]) } + + implicit def arbScoped[A: Manifest]: Arbitrary[Scoped] = Arbitrary(arbitrary[Key]) } import BuildDSLInstances._ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { - type Key[K] = Scoped.ScopingSetting[K] with Scoped - property("Global / key == key in Global") = { - def check[K <: Key[K]: Arbitrary] = forAll((k: K) => expectValue(k in Global)(Global / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll((k: Key) => expectValue(k in Global)(Global / k)) } property("Reference / key == key in Reference") = { - def check[K <: Key[K]: Arbitrary] = forAll((r: Reference, k: K) => expectValue(k in r)(r / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll((r: Reference, k: Key) => expectValue(k in r)(r / k)) } property("Reference / Config / key == key in Reference in Config") = { - def check[K <: Key[K]: Arbitrary] = - forAll((r: Reference, c: ConfigKey, k: K) => expectValue(k in r in c)(r / c / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll((r: Reference, c: ConfigKey, k: Key) => expectValue(k in r in c)(r / c / k)) } property("Reference / task.key / key == key in Reference in task") = { - def check[K <: Key[K]: Arbitrary] = - forAll((r: Reference, t: Scoped, k: K) => expectValue(k in (r, t))(r / t.key / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll((r: Reference, t: Scoped, k: Key) => expectValue(k in (r, t))(r / t.key / k)) } property("Reference / task / key ~= key in Reference in task") = { import WithoutScope._ - def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((r: Reference, t: T, k: K) => expectValue(k in (r, t))(r / t / k)) - (true - && check[InputKey[String], InputKey[String]] - && check[InputKey[String], SettingKey[String]] - && check[InputKey[String], TaskKey[String]] - && check[SettingKey[String], InputKey[String]] - && check[SettingKey[String], SettingKey[String]] - && check[SettingKey[String], TaskKey[String]] - && check[TaskKey[String], InputKey[String]] - && check[TaskKey[String], SettingKey[String]] - && check[TaskKey[String], TaskKey[String]] - ) + forAll((r: Reference, t: Key, k: Key) => expectValue(k in (r, t))(r / t / k)) } property("Reference / Config / task.key / key == key in Reference in Config in task") = { - def check[K <: Key[K]: Arbitrary] = - forAll((r: Reference, c: ConfigKey, t: Scoped, k: K) => - expectValue(k in (r, c, t))(r / c / t.key / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll { (r: Reference, c: ConfigKey, t: Scoped, k: Key) => + expectValue(k in (r, c, t))(r / c / t.key / k) + } } property("Reference / Config / task / key ~= key in Reference in Config in task") = { import WithoutScope._ - def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((r: Reference, c: ConfigKey, t: T, k: K) => expectValue(k in (r, c, t))(r / c / t / k)) - (true - && check[InputKey[String], InputKey[String]] - && check[InputKey[String], SettingKey[String]] - && check[InputKey[String], TaskKey[String]] - && check[SettingKey[String], InputKey[String]] - && check[SettingKey[String], SettingKey[String]] - && check[SettingKey[String], TaskKey[String]] - && check[TaskKey[String], InputKey[String]] - && check[TaskKey[String], SettingKey[String]] - && check[TaskKey[String], TaskKey[String]] - ) + forAll { (r: Reference, c: ConfigKey, t: Key, k: Key) => + expectValue(k in (r, c, t))(r / c / t / k) + } } property("Config / key == key in Config") = { - def check[K <: Key[K]: Arbitrary] = - forAll((c: ConfigKey, k: K) => expectValue(k in c)(c / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll((c: ConfigKey, k: Key) => expectValue(k in c)(c / k)) } property("Config / task.key / key == key in Config in task") = { - def check[K <: Key[K]: Arbitrary] = - forAll((c: ConfigKey, t: Scoped, k: K) => expectValue(k in c in t)(c / t.key / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll((c: ConfigKey, t: Scoped, k: Key) => expectValue(k in c in t)(c / t.key / k)) } property("Config / task / key ~= key in Config in task") = { import WithoutScope._ - def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((c: ConfigKey, t: T, k: K) => expectValue(k in c in t)(c / t / k)) - (true - && check[InputKey[String], InputKey[String]] - && check[InputKey[String], SettingKey[String]] - && check[InputKey[String], TaskKey[String]] - && check[SettingKey[String], InputKey[String]] - && check[SettingKey[String], SettingKey[String]] - && check[SettingKey[String], TaskKey[String]] - && check[TaskKey[String], InputKey[String]] - && check[TaskKey[String], SettingKey[String]] - && check[TaskKey[String], TaskKey[String]] - ) + forAll((c: ConfigKey, t: Key, k: Key) => expectValue(k in c in t)(c / t / k)) } property("task.key / key == key in task") = { - def check[K <: Key[K]: Arbitrary] = - forAll((t: Scoped, k: K) => expectValue(k in t)(t.key / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll((t: Scoped, k: Key) => expectValue(k in t)(t.key / k)) } property("task / key ~= key in task") = { import WithoutScope._ - def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = - forAll((t: T, k: K) => expectValue(k in t)(t / k)) - (true - && check[InputKey[String], InputKey[String]] - && check[InputKey[String], SettingKey[String]] - && check[InputKey[String], TaskKey[String]] - && check[SettingKey[String], InputKey[String]] - && check[SettingKey[String], SettingKey[String]] - && check[SettingKey[String], TaskKey[String]] - && check[TaskKey[String], InputKey[String]] - && check[TaskKey[String], SettingKey[String]] - && check[TaskKey[String], TaskKey[String]] - ) + forAll((t: Key, k: Key) => expectValue(k in t)(t / k)) } property("Scope / key == key in Scope") = { - def check[K <: Key[K]: Arbitrary] = forAll((s: Scope, k: K) => expectValue(k in s)(s / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll((s: Scope, k: Key) => expectValue(k in s)(s / k)) } property("Reference? / key == key in ThisScope.copy(..)") = { - def check[K <: Key[K]: Arbitrary] = - forAll((r: ScopeAxis[Reference], k: K) => - expectValue(k in ThisScope.copy(project = r))(r / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll { (r: ScopeAxis[Reference], k: Key) => + expectValue(k in ThisScope.copy(project = r))(r / k) + } } property("Reference? / ConfigKey? / key == key in ThisScope.copy(..)") = { - def check[K <: Key[K]: Arbitrary] = - forAll((r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: K) => - expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll((r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: Key) => + expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k)) } // property("Reference? / AttributeKey? / key == key in ThisScope.copy(..)") = { -// def check[K <: Key[K]: Arbitrary] = -// forAll( -// (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: K) => -// expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k)) -// check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] +// forAll((r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: AnyKey) => +// expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k)) // } property("Reference? / ConfigKey? / AttributeKey? / key == key in ThisScope.copy(..)") = { - def check[K <: Key[K]: Arbitrary] = - forAll( - (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: K) => - expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k)) - check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + forAll { + (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: Key) => + expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k) + } } def expectValue(expected: Scoped)(x: Scoped) = { From 3c66f39744d054b073dd9755abb111455b372589 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 27 Mar 2018 09:14:55 +0100 Subject: [PATCH 113/120] Allow SlashSyntaxSpec to be Scalafmt formatted Previously: [warn] /d/sbt/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala:44: error: illegal start of simple expression [warn] ) [warn] ^ --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 804031cb3..e68d3ba0a 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -13,7 +13,16 @@ import java.io.File import sbt.io.IO import sbt.SlashSyntax import sbt.{ Scope, ScopeAxis, Scoped, Select, This, Zero }, Scope.{ Global, ThisScope } -import sbt.{ BuildRef, LocalProject, LocalRootProject, ProjectRef, Reference, RootProject, ThisBuild, ThisProject } +import sbt.{ + BuildRef, + LocalProject, + LocalRootProject, + ProjectRef, + Reference, + RootProject, + ThisBuild, + ThisProject +} import sbt.ConfigKey import sbt.librarymanagement.syntax._ import sbt.{ InputKey, SettingKey, TaskKey } @@ -34,13 +43,13 @@ object BuildDSLInstances { implicit val arbReference: Arbitrary[Reference] = Arbitrary { Gen.frequency( - 1 -> arbitrary[BuildRef], // 96 - 100 -> ThisBuild, // 10,271 - 3 -> LocalRootProject, // 325 - 23 -> arbitrary[ProjectRef], // 2,283 - 3 -> ThisProject, // 299 - 4 -> arbitrary[LocalProject], // 436 - 11 -> arbitrary[RootProject], // 1,133 + 96 -> arbitrary[BuildRef], + 10271 -> ThisBuild, + 325 -> LocalRootProject, + 2283 -> arbitrary[ProjectRef], + 299 -> ThisProject, + 436 -> arbitrary[LocalProject], + 1133 -> arbitrary[RootProject], ) } From d19329666ac64c9a28a1dd5be83b7a2f55c02794 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 27 Mar 2018 01:27:25 +0100 Subject: [PATCH 114/120] Re-use Scoped.scoped* methods --- main-settings/src/main/scala/sbt/Structure.scala | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index bcedc4171..7091e3fe3 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -628,7 +628,7 @@ object InputKey { apply(AttributeKey[InputTask[T]](label, description, extendScoped(extend1, extendN), rank)) def apply[T](akey: AttributeKey[InputTask[T]]): InputKey[T] = - new InputKey[T] { val key = akey; def scope = Scope.ThisScope } + Scoped.scopedInput(Scope.ThisScope, akey) } /** Constructs TaskKeys, which are associated with tasks to define a setting.*/ @@ -657,8 +657,7 @@ object TaskKey { ): TaskKey[T] = apply(AttributeKey[Task[T]](label, description, extendScoped(extend1, extendN), rank)) - def apply[T](akey: AttributeKey[Task[T]]): TaskKey[T] = - new TaskKey[T] { val key = akey; def scope = Scope.ThisScope } + def apply[T](akey: AttributeKey[Task[T]]): TaskKey[T] = Scoped.scopedTask(Scope.ThisScope, akey) def local[T: Manifest]: TaskKey[T] = apply[T](AttributeKey.local[Task[T]]) } @@ -689,8 +688,7 @@ object SettingKey { ): SettingKey[T] = apply(AttributeKey[T](label, description, extendScoped(extend1, extendN), rank)) - def apply[T](akey: AttributeKey[T]): SettingKey[T] = - new SettingKey[T] { val key = akey; def scope = Scope.ThisScope } + def apply[T](akey: AttributeKey[T]): SettingKey[T] = Scoped.scopedSetting(Scope.ThisScope, akey) def local[T: Manifest: OptJsonWriter]: SettingKey[T] = apply[T](AttributeKey.local[T]) } From 35decb6ee5e4f7333c268eda941a002c507cb587 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 21 Mar 2018 15:53:08 +0000 Subject: [PATCH 115/120] Deprecate Scope.transformTaskName --- main-settings/src/main/scala/sbt/Scope.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/main-settings/src/main/scala/sbt/Scope.scala b/main-settings/src/main/scala/sbt/Scope.scala index 0bd3e27c6..e3409fc99 100644 --- a/main-settings/src/main/scala/sbt/Scope.scala +++ b/main-settings/src/main/scala/sbt/Scope.scala @@ -236,6 +236,7 @@ object Scope { def showProject012Style = (ref: Reference) => Reference.display(ref) + "/" + @deprecated("No longer used", "1.1.3") def transformTaskName(s: String) = { val parts = s.split("-+") (parts.take(1) ++ parts.drop(1).map(_.capitalize)).mkString From 25988d22567b24d27c56d8becc4b4ac6143fb63e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 26 Mar 2018 23:57:34 +0100 Subject: [PATCH 116/120] Cleanup test/ParseKey --- main/src/test/scala/ParseKey.scala | 216 +++++++++++------------------ 1 file changed, 82 insertions(+), 134 deletions(-) diff --git a/main/src/test/scala/ParseKey.scala b/main/src/test/scala/ParseKey.scala index 1f17d9288..23d9d0778 100644 --- a/main/src/test/scala/ParseKey.scala +++ b/main/src/test/scala/ParseKey.scala @@ -8,166 +8,114 @@ package sbt import Def.{ displayFull, displayMasked, ScopedKey } -import sbt.internal.{ TestBuild, Resolve } -import TestBuild._ -import sbt.internal.util.complete._ +import sbt.internal.{ TestBuild, Resolve }, TestBuild._ +import sbt.internal.util.complete.Parser -import org.scalacheck._ -import Gen._ -import Prop._ -import Arbitrary.arbBool +import org.scalacheck._, Arbitrary.arbitrary, Gen._, Prop._ /** * Tests that the scoped key parser in Act can correctly parse a ScopedKey converted by Def.show*Key. * This includes properly resolving omitted components. */ object ParseKey extends Properties("Key parser test") { - final val MaxKeys = 5 - final val MaxScopedKeys = 100 - - implicit val gstructure = genStructure - - property("An explicitly specified axis is always parsed to that explicit value") = - forAllNoShrink(structureDefinedKey) { (skm: StructureKeyMask) => - import skm.{ structure, key, mask => mask0 } - - val hasZeroConfig = key.scope.config == Zero - val mask = if (hasZeroConfig) mask0.copy(project = true) else mask0 - val expected = resolve(structure, key, mask) - // Note that this explicitly displays the configuration axis set to Zero. - // This is to disambiguate `proj/Zero/name`, which could render potentially - // as `Zero/name`, but could be interpretted as `Zero/Zero/name`. - val s = displayMasked(key, mask, hasZeroConfig) - ("Key: " + displayPedantic(key)) |: - parseExpected(structure, s, expected, mask) - } - - property("An unspecified project axis resolves to the current project") = - forAllNoShrink(structureDefinedKey) { (skm: StructureKeyMask) => - import skm.{ structure, key } - - val mask = skm.mask.copy(project = false) - val string = displayMasked(key, mask) - // skip when config axis is set to Zero - val hasZeroConfig = key.scope.config == Zero - - ("Key: " + displayPedantic(key)) |: - ("Mask: " + mask) |: - ("Current: " + structure.current) |: - parse(structure, string) { - case Left(_) => false - case Right(_) if hasZeroConfig => true - case Right(sk) => sk.scope.project == Select(structure.current) - } - } - - property("An unspecified task axis resolves to Zero") = forAllNoShrink(structureDefinedKey) { + property("An explicitly specified axis is always parsed to that explicit value") = forAll { (skm: StructureKeyMask) => import skm.{ structure, key } - val mask = skm.mask.copy(task = false) - val string = displayMasked(key, mask) + val hasZeroConfig = key.scope.config == Zero + val mask = if (hasZeroConfig) skm.mask.copy(project = true) else skm.mask + // Note that this explicitly displays the configuration axis set to Zero. + // This is to disambiguate `proj/Zero/name`, which could render potentially + // as `Zero/name`, but could be interpreted as `Zero/Zero/name`. + val expected = ScopedKey( + Resolve(structure.extra, Select(structure.current), key.key, mask)(key.scope), + key.key + ) + parseCheck(structure, key, mask, hasZeroConfig)( + sk => + Project.equal(sk, expected, mask) + :| s"$sk.key == $expected.key: ${sk.key == expected.key}" + :| s"${sk.scope} == ${expected.scope}: ${Scope.equal(sk.scope, expected.scope, mask)}" + ) :| s"Expected: ${displayFull(expected)}" + } - ("Key: " + displayPedantic(key)) |: - ("Mask: " + mask) |: - parse(structure, string) { - case Left(_) => false - case Right(sk) => sk.scope.task == Zero - } + property("An unspecified project axis resolves to the current project") = forAll { + (skm: StructureKeyMask) => + import skm.{ structure, key } + val mask = skm.mask.copy(project = false) + // skip when config axis is set to Zero + val hasZeroConfig = key.scope.config == Zero + parseCheck(structure, key, mask)( + sk => + (hasZeroConfig || sk.scope.project == Select(structure.current)) + :| s"Current: ${structure.current}" + ) + } + + property("An unspecified task axis resolves to Zero") = forAll { (skm: StructureKeyMask) => + import skm.{ structure, key } + val mask = skm.mask.copy(task = false) + parseCheck(structure, key, mask)(_.scope.task == Zero) } property( "An unspecified configuration axis resolves to the first configuration directly defining the key or else Zero") = - forAllNoShrink(structureDefinedKey) { (skm: StructureKeyMask) => + forAll { (skm: StructureKeyMask) => import skm.{ structure, key } val mask = ScopeMask(config = false) - val string = displayMasked(key, mask) val resolvedConfig = Resolve.resolveConfig(structure.extra, key.key, mask)(key.scope).config - - ("Key: " + displayPedantic(key)) |: - ("Mask: " + mask) |: - ("Expected configuration: " + resolvedConfig.map(_.name)) |: - parse(structure, string) { - case Right(sk) => (sk.scope.config == resolvedConfig) || (sk.scope == Scope.GlobalScope) - case Left(_) => false - } + parseCheck(structure, key, mask)( + sk => (sk.scope.config == resolvedConfig) || (sk.scope == Scope.GlobalScope) + ) :| s"Expected configuration: ${resolvedConfig map (_.name)}" } - def displayPedantic(scoped: ScopedKey[_]): String = - Scope.displayPedantic(scoped.scope, scoped.key.label) - - lazy val structureDefinedKey: Gen[StructureKeyMask] = structureKeyMask { s => - for (scope <- TestBuild.scope(s.env); key <- oneOf(s.allAttributeKeys.toSeq)) - yield ScopedKey(scope, key) - } - def structureKeyMask(genKey: Structure => Gen[ScopedKey[_]])( - implicit maskGen: Gen[ScopeMask], - structureGen: Gen[Structure]): Gen[StructureKeyMask] = - for (mask <- maskGen; structure <- structureGen; key <- genKey(structure)) - yield new StructureKeyMask(structure, key, mask) - final class StructureKeyMask(val structure: Structure, val key: ScopedKey[_], val mask: ScopeMask) - - def resolve(structure: Structure, key: ScopedKey[_], mask: ScopeMask): ScopedKey[_] = - ScopedKey(Resolve(structure.extra, Select(structure.current), key.key, mask)(key.scope), - key.key) - - def parseExpected(structure: Structure, - s: String, - expected: ScopedKey[_], - mask: ScopeMask): Prop = - ("Expected: " + displayFull(expected)) |: - ("Mask: " + mask) |: - parse(structure, s) { - case Left(_) => false - case Right(sk) => - (s"${sk}.key == ${expected}.key: ${sk.key == expected.key}") |: - (s"${sk.scope} == ${expected.scope}: ${Scope.equal(sk.scope, expected.scope, mask)}") |: - Project.equal(sk, expected, mask) - } - - def parse(structure: Structure, s: String)(f: Either[String, ScopedKey[_]] => Prop): Prop = { - val parser = makeParser(structure) - val parsed = DefaultParsers.result(parser, s).left.map(_().toString) - val showParsed = parsed.right.map(displayFull) - ("Key string: '" + s + "'") |: - ("Parsed: " + showParsed) |: - ("Structure: " + structure) |: - f(parsed) - } - - // Here we're shadowing the in-scope implicit called `mkEnv` for this method - // so that it will use the passed-in `Gen` rather than the one imported - // from TestBuild. - def genStructure(implicit mkEnv: Gen[Env]): Gen[Structure] = - structureGenF { (scopes: Seq[Scope], env: Env, current: ProjectRef) => - val settings = - for { - scope <- scopes - t <- env.tasks - } yield Def.setting(ScopedKey(scope, t.key), Def.value("")) - TestBuild.structure(env, settings, current) - } - - // Here we're shadowing the in-scope implicit called `mkEnv` for this method - // so that it will use the passed-in `Gen` rather than the one imported - // from TestBuild. - def structureGenF(f: (Seq[Scope], Env, ProjectRef) => Structure)( - implicit mkEnv: Gen[Env]): Gen[Structure] = - structureGen((s, e, p) => Gen.const(f(s, e, p))) - // Here we're shadowing the in-scope implicit called `mkEnv` for this method - // so that it will use the passed-in `Gen` rather than the one imported - // from TestBuild. - def structureGen(f: (Seq[Scope], Env, ProjectRef) => Gen[Structure])( - implicit mkEnv: Gen[Env]): Gen[Structure] = + implicit val arbStructure: Arbitrary[Structure] = Arbitrary { for { env <- mkEnv loadFactor <- choose(0.0, 1.0) scopes <- pickN(loadFactor, env.allFullScopes) current <- oneOf(env.allProjects.unzip._1) - structure <- f(scopes, env, current) + structure <- { + val settings = for (scope <- scopes; t <- env.tasks) + yield Def.setting(ScopedKey(scope, t.key), Def.value("")) + TestBuild.structure(env, settings, current) + } } yield structure + } - // pickN is a function that randomly picks load % items from the from sequence. + final class StructureKeyMask(val structure: Structure, val key: ScopedKey[_], val mask: ScopeMask) + + implicit val arbStructureKeyMask: Arbitrary[StructureKeyMask] = Arbitrary { + for { + mask <- maskGen + structure <- arbitrary[Structure] + key <- for { + scope <- TestBuild.scope(structure.env) + key <- oneOf(structure.allAttributeKeys.toSeq) + } yield ScopedKey(scope, key) + } yield new StructureKeyMask(structure, key, mask) + } + + def parseCheck( + structure: Structure, + key: ScopedKey[_], + mask: ScopeMask, + showZeroConfig: Boolean = false, + )(f: ScopedKey[_] => Prop): Prop = { + val s = displayMasked(key, mask, showZeroConfig) + val parser = makeParser(structure) + val parsed = Parser.result(parser, s).left.map(_().toString) + ( + parsed.fold(_ => falsified, f) + :| s"Key: ${Scope.displayPedantic(key.scope, key.key.label)}" + :| s"Mask: $mask" + :| s"Key string: '$s'" + :| s"Parsed: ${parsed.right.map(displayFull)}" + :| s"Structure: $structure" + ) + } + + // pickN is a function that randomly picks load % items from the "from" sequence. // The rest of the tests expect at least one item, so I changed it to return 1 in case of 0. def pickN[T](load: Double, from: Seq[T]): Gen[Seq[T]] = - pick(Math.max((load * from.size).toInt, 1), from) + pick((load * from.size).toInt max 1, from) } From e30c0fbd1eaadd18ccad59b35316402aa2e667ba Mon Sep 17 00:00:00 2001 From: Heikki Vesalainen Date: Wed, 28 Mar 2018 14:29:22 +0000 Subject: [PATCH 117/120] Update to Jline 2.14.6 This version of Jline fixes three things for Emacs users: - ANSI colors are now enabled for Emacs. - Terminal echo is now disabled for Emacs. - History is enabled for all dump terminals. --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 94a583ea8..10fc91517 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -97,7 +97,7 @@ object Dependencies { "com.eed3si9n" %% "sjson-new-scalajson" % contrabandSjsonNewVersion.value } - val jline = "jline" % "jline" % "2.14.4" + val jline = "jline" % "jline" % "2.14.6" val scalatest = "org.scalatest" %% "scalatest" % "3.0.4" val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.13.4" val specs2 = "org.specs2" %% "specs2-junit" % "4.0.1" From 289077a40566aa5e9c7a19f04cd66ffdfff780dd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 16 Mar 2018 17:42:02 +0000 Subject: [PATCH 118/120] Re-introduce Command.process This was an unnecessary removal in e83564a6b71639c7e51bbc002c2d083cf8c9ab5a. --- main-command/src/main/scala/sbt/Command.scala | 10 ++++++++++ main/src/main/scala/sbt/MainLoop.scala | 9 +-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/main-command/src/main/scala/sbt/Command.scala b/main-command/src/main/scala/sbt/Command.scala index 9d9be7a33..6ee8e0e44 100644 --- a/main-command/src/main/scala/sbt/Command.scala +++ b/main-command/src/main/scala/sbt/Command.scala @@ -163,6 +163,16 @@ object Command { case Some(c) => c(state) }) + def process(command: String, state: State): State = { + val parser = combine(state.definedCommands) + parse(command, parser(state)) match { + case Right(s) => s() // apply command. command side effects happen here + case Left(errMsg) => + state.log error errMsg + state.fail + } + } + def invalidValue(label: String, allowed: Iterable[String])(value: String): String = s"Not a valid $label: $value" + similar(value, allowed) diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index 02897f7a0..e2c1fe0f2 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -17,7 +17,6 @@ import scala.util.control.NonFatal import sbt.io.{ IO, Using } import sbt.internal.util.{ ErrorHandling, GlobalLogBacking } -import sbt.internal.util.complete.Parser import sbt.internal.langserver.ErrorCodes import sbt.util.Logger import sbt.protocol._ @@ -148,13 +147,7 @@ object MainLoop { val channelName = exec.source map (_.channelName) StandardMain.exchange publishEventMessage ExecStatusEvent("Processing", channelName, exec.execId, Vector()) - val parser = Command combine state.definedCommands - val newState = Parser.parse(exec.commandLine, parser(state)) match { - case Right(s) => s() // apply command. command side effects happen here - case Left(errMsg) => - state.log error errMsg - state.fail - } + val newState = Command.process(exec.commandLine, state) val doneEvent = ExecStatusEvent( "Done", channelName, From 120ab651344b45fb1c90cf418a7e0f204a677285 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 3 Apr 2018 16:06:27 +0100 Subject: [PATCH 119/120] Remove unused type param in Scaladoc --- tasks/src/main/scala/sbt/ConcurrentRestrictions.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala b/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala index a827c139a..7313eba62 100644 --- a/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala +++ b/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala @@ -122,7 +122,6 @@ object ConcurrentRestrictions { * Constructs a CompletionService suitable for backing task execution based on the provided restrictions on concurrent task execution. * @return a pair, with _1 being the CompletionService and _2 a function to shutdown the service. * @tparam A the task type - * @tparam G describes a set of tasks * @tparam R the type of data that will be computed by the CompletionService. */ def completionService[A, R](tags: ConcurrentRestrictions[A], From 56ffac22b7c92e13975eb59993c50d6a12dc9cc1 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 4 Apr 2018 01:29:56 -0400 Subject: [PATCH 120/120] document profiling tools --- CONTRIBUTING.md | 154 +++++++++++++++++++++++++++++++++++++ project/flamegraph_svg.png | Bin 0 -> 553287 bytes 2 files changed, 154 insertions(+) create mode 100644 project/flamegraph_svg.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7dc91fcdd..1471d269d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -231,6 +231,160 @@ command. To run a single test, such as the test in sbt "scripted project/global-plugin" +Profiling sbt +------------- + +There are several ways to profile sbt. The new hotness in profiling is FlameGraph. +You first collect stack trace samples, and then it is processed into svg graph. +See: + +- [Using FlameGraphs To Illuminate The JVM by Nitsan Wakart](https://www.youtube.com/watch?v=ugRrFdda_JQ) +- [USENIX ATC '17: Visualizing Performance with Flame Graphs](https://www.youtube.com/watch?v=D53T1Ejig1Q) + +### jvm-profiling-tools/async-profiler + +The first one I recommend is async-profiler. This is available for macOS and Linux, +and works fairly well. + +1. Download the installer from https://github.com/jvm-profiling-tools/async-profiler/releases/tag/v1.2 +2. Make symbolic link to `build/` and `profiler.sh` to `$HOME/bin`, assuming you have PATH to `$HOME/bin`: + `ln -s ~/Applications/async-profiler/profiler.sh $HOME/bin/profiler.sh` + `ln -s ~/Applications/async-profiler/build $HOME/bin/build` + +Next, close all Java appliations and anything that may affect the profiling, and run sbt in one terminal: + +``` +$ sbt exit +``` + +In another terminal, run: + +``` +$ jps +92746 sbt-launch.jar +92780 Jps +``` + +This tells you the process ID of sbt. In this case, it's 92746. While it's running, run + +``` +$ profiler.sh -d 60 +Started [cpu] profiling +--- Execution profile --- +Total samples: 31602 +Non-Java: 3239 (10.25%) +GC active: 46 (0.15%) +Unknown (native): 14667 (46.41%) +Not walkable (native): 3 (0.01%) +Unknown (Java): 433 (1.37%) +Not walkable (Java): 8 (0.03%) +Thread exit: 1 (0.00%) +Deopt: 9 (0.03%) + +Frame buffer usage: 55.658% + +Total: 1932000000 (6.11%) samples: 1932 + [ 0] java.lang.ClassLoader$NativeLibrary.load + [ 1] java.lang.ClassLoader.loadLibrary0 + [ 2] java.lang.ClassLoader.loadLibrary + [ 3] java.lang.Runtime.loadLibrary0 + [ 4] java.lang.System.loadLibrary +.... +``` + +This should show a bunch of stacktraces that are useful. +To visualize this as a flamegraph, run: + +``` +$ profiler.sh -d 60 -f /tmp/flamegraph.svg +``` + +This should produce `/tmp/flamegraph.svg` at the end. + +![flamegraph](project/flamegraph_svg.png) + +See https://gist.github.com/eed3si9n/82d43acc95a002876d357bd8ad5f40d5 + +### running sbt with standby + +One of the tricky things you come across while profiling is figuring out the process ID, +while wnating to profile the beginning of the application. + +For this purpose, we've added `sbt.launcher.standby` JVM flag. +In the next version of sbt, you should be able to run: + +``` +$ sbt -J-Dsbt.launcher.standby=20s exit +``` + +This will count down for 20s before doing anything else. + +### jvm-profiling-tools/perf-map-agent + +If you want to try the mixed flamegraph, you can try perf-map-agent. +This uses `dtrace` on macOS and `perf` on Linux. + +You first have to compile https://github.com/jvm-profiling-tools/perf-map-agent. +For macOS, here to how to export `JAVA_HOME` before running `cmake .`: + +``` +$ export JAVA_HOME=$(/usr/libexec/java_home) +$ cmake . +-- The C compiler identification is AppleClang 9.0.0.9000039 +-- The CXX compiler identification is AppleClang 9.0.0.9000039 +... +$ make +``` + +In addition, you have to git clone https://github.com/brendangregg/FlameGraph + +In a fresh termimal, run sbt with `-XX:+PreserveFramePointer` flag: + +``` +$ sbt -J-Dsbt.launcher.standby=20s -J-XX:+PreserveFramePointer exit +``` + +In the terminal that you will run the perf-map: + +``` +$ cd quicktest/ +$ export JAVA_HOME=$(/usr/libexec/java_home) +$ export FLAMEGRAPH_DIR=$HOME/work/FlameGraph +$ jps +94592 Jps +94549 sbt-launch.jar +$ $HOME/work/perf-map-agent/bin/dtrace-java-flames 94549 +dtrace: system integrity protection is on, some features will not be available + +dtrace: description 'profile-99 ' matched 2 probes +Flame graph SVG written to DTRACE_FLAME_OUTPUT='/Users/xxx/work/quicktest/flamegraph-94549.svg'. +``` + +This would produce better flamegraph in theory, but the output looks too messy for `sbt exit` case. +See https://gist.github.com/eed3si9n/b5856ff3d987655513380d1a551aa0df +This might be because it assumes that the operations are already JITed. + +### ktoso/sbt-jmh + +https://github.com/ktoso/sbt-jmh + +Due to JIT warmup etc, benchmarking is difficult. JMH runs the same tests multiple times to +remove these effects and comes closer to measuring the performance of your code. + +There's also an integration with jvm-profiling-tools/async-profiler, apparently. + +### VisualVM + +I'd also mention traditional JVM profiling tool. Since VisualVM is opensource, +I'll mention this one: https://visualvm.github.io/ + +1. First VisualVM. +2. Start sbt from a terminal. +3. You should see `xsbt.boot.Boot` under Local. +4. Open it, and select either sampler or profiler, and hit CPU button at the point when you want to start. + +If you are familiar with YourKit, it also works similarly. + Other notes for maintainers --------------------------- diff --git a/project/flamegraph_svg.png b/project/flamegraph_svg.png new file mode 100644 index 0000000000000000000000000000000000000000..2fc2638c35b504181297f8df5979188f6ef17490 GIT binary patch literal 553287 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%0Dlw=2%f3d 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 z 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
>Ge+!b54BV(Hu$}li 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
x;_YYX(QXorgBVJV{z4UcKQbMeJT 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 z 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
Jyk0x9{q3=vEy)3}`obbs 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%0DZR3$EL&%4V?rJi~7+H*l7G=7?jy;Ho{FW7 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�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 Y45=7mEa7@bjPQ8LOM|5