From ca7171ed17dc3b862fe79cc8a6e0fe535b861555 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Fri, 1 Dec 2017 12:39:59 -0800 Subject: [PATCH 001/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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/176] 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 2e86eed151a33abd4a89196dc2e47b5957b51e5d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 27 Mar 2018 15:06:20 +0100 Subject: [PATCH 116/176] Split BuildSettingsInstances out of SlashSyntaxSpec.scala --- .../scala/sbt/BuildSettingsInstances.scala | 131 ++++++++++++++++++ .../src/test/scala/sbt/SlashSyntaxSpec.scala | 125 +---------------- 2 files changed, 136 insertions(+), 120 deletions(-) create mode 100644 main-settings/src/test/scala/sbt/BuildSettingsInstances.scala diff --git a/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala b/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala new file mode 100644 index 000000000..6ca193ccf --- /dev/null +++ b/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala @@ -0,0 +1,131 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt.test + +import org.scalacheck.{ Test => _, _ }, Arbitrary.arbitrary, Gen._ + +import java.io.File +import sbt.io.IO +import sbt.{ Scope, ScopeAxis, Scoped, Select, This, Zero } +import sbt.{ + BuildRef, + LocalProject, + LocalRootProject, + ProjectRef, + Reference, + RootProject, + ThisBuild, + ThisProject +} +import sbt.ConfigKey +import sbt.librarymanagement.syntax._ +import sbt.{ InputKey, SettingKey, TaskKey } +import sbt.internal.util.{ AttributeKey, AttributeMap } + +object BuildSettingsInstances { + val genFile: Gen[File] = Gen.oneOf(new File("."), new File("/tmp")) // for now.. + + implicit val arbBuildRef: Arbitrary[BuildRef] = Arbitrary(genFile map (f => BuildRef(IO toURI f))) + + implicit val arbProjectRef: Arbitrary[ProjectRef] = + Arbitrary(for (f <- genFile; id <- Gen.identifier) yield ProjectRef(f, id)) + + implicit val arbLocalProject: Arbitrary[LocalProject] = + Arbitrary(arbitrary[String] map LocalProject) + + implicit val arbRootProject: Arbitrary[RootProject] = Arbitrary(genFile map (RootProject(_))) + + implicit val arbReference: Arbitrary[Reference] = Arbitrary { + Gen.frequency( + 96 -> arbitrary[BuildRef], + 10271 -> ThisBuild, + 325 -> LocalRootProject, + 2283 -> arbitrary[ProjectRef], + 299 -> ThisProject, + 436 -> arbitrary[LocalProject], + 1133 -> arbitrary[RootProject], + ) + } + + implicit def arbConfigKey: Arbitrary[ConfigKey] = Arbitrary { + Gen.frequency( + 2 -> const[ConfigKey](Compile), + 2 -> const[ConfigKey](Test), + 1 -> const[ConfigKey](Runtime), + 1 -> const[ConfigKey](IntegrationTest), + 1 -> const[ConfigKey](Provided), + ) + } + + implicit def arbAttrKey[A: Manifest]: Arbitrary[AttributeKey[_]] = + Arbitrary(Gen.identifier map (AttributeKey[A](_))) + + 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) + ) + + 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](_)) + + 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]) + + implicit def arbKey[A: Manifest]( + implicit + arbInputKey: Arbitrary[InputKey[A]], + arbSettingKey: Arbitrary[SettingKey[A]], + arbTaskKey: Arbitrary[TaskKey[A]], + ): 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 { + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = Arbitrary(genInputKey[A]) + 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]) +} diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index e68d3ba0a..b3b77c71d 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -7,130 +7,15 @@ package sbt.test -import org.scalacheck.{ Test => _, _ }, Arbitrary.arbitrary, Gen._, Prop._ +import org.scalacheck.{ Test => _, _ }, Prop._ -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.{ Scope, ScopeAxis, Scoped }, Scope.{ Global, ThisScope } +import sbt.Reference import sbt.ConfigKey -import sbt.librarymanagement.syntax._ -import sbt.{ InputKey, SettingKey, TaskKey } -import sbt.internal.util.{ AttributeKey, AttributeMap } +import sbt.internal.util.AttributeKey -object BuildDSLInstances { - val genFile: Gen[File] = Gen.oneOf(new File("."), new File("/tmp")) // for now.. - - implicit val arbBuildRef: Arbitrary[BuildRef] = Arbitrary(genFile map (f => BuildRef(IO toURI f))) - - implicit val arbProjectRef: Arbitrary[ProjectRef] = - Arbitrary(for (f <- genFile; id <- Gen.identifier) yield ProjectRef(f, id)) - - implicit val arbLocalProject: Arbitrary[LocalProject] = - Arbitrary(arbitrary[String] map LocalProject) - - implicit val arbRootProject: Arbitrary[RootProject] = Arbitrary(genFile map (RootProject(_))) - - implicit val arbReference: Arbitrary[Reference] = Arbitrary { - Gen.frequency( - 96 -> arbitrary[BuildRef], - 10271 -> ThisBuild, - 325 -> LocalRootProject, - 2283 -> arbitrary[ProjectRef], - 299 -> ThisProject, - 436 -> arbitrary[LocalProject], - 1133 -> arbitrary[RootProject], - ) - } - - implicit def arbConfigKey: Arbitrary[ConfigKey] = Arbitrary { - Gen.frequency( - 2 -> const[ConfigKey](Compile), - 2 -> const[ConfigKey](Test), - 1 -> const[ConfigKey](Runtime), - 1 -> const[ConfigKey](IntegrationTest), - 1 -> const[ConfigKey](Provided), - ) - } - - implicit def arbAttrKey[A: Manifest]: Arbitrary[AttributeKey[_]] = - Arbitrary(Gen.identifier map (AttributeKey[A](_))) - - 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) - ) - - 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](_)) - - 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]) - - implicit def arbKey[A: Manifest]( - implicit - arbInputKey: Arbitrary[InputKey[A]], - arbSettingKey: Arbitrary[SettingKey[A]], - arbTaskKey: Arbitrary[TaskKey[A]], - ): 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 { - implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = Arbitrary(genInputKey[A]) - 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._ +import BuildSettingsInstances._ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { property("Global / key == key in Global") = { From 25988d22567b24d27c56d8becc4b4ac6143fb63e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 26 Mar 2018 23:57:34 +0100 Subject: [PATCH 117/176] 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 118/176] 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 119/176] 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 cbb953279ccac742867c9d76acee3ae5a46e0455 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Sat, 31 Mar 2018 20:28:09 +0200 Subject: [PATCH 120/176] Intial implementation of CompositeProject --- main/src/main/scala/sbt/Project.scala | 8 +++++++- main/src/main/scala/sbt/internal/BuildDef.scala | 3 ++- .../sbt/internal/EvaluateConfigurations.scala | 10 +++++----- .../project/sbt-composite-projects/a/A.scala | 1 + .../project/sbt-composite-projects/a/a.sbt | 2 ++ .../project/sbt-composite-projects/b/build.sbt | 2 ++ .../project/sbt-composite-projects/build.sbt | 13 +++++++++++++ .../sbt-composite-projects/changes/basic.sbt | 1 + .../project/sbt-composite-projects/other.sbt | 1 + .../project/sbt-composite-projects/test | 17 +++++++++++++++++ 10 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/a/A.scala create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/a/a.sbt create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/b/build.sbt create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/build.sbt create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/changes/basic.sbt create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/other.sbt create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/test diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index f292ca0e1..0fe82b457 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -119,7 +119,13 @@ sealed trait ProjectDefinition[PR <: ProjectReference] { if (ts.isEmpty) Nil else s"$label: $ts" :: Nil } -sealed trait Project extends ProjectDefinition[ProjectReference] { +trait CompositeProject { + def componentProjects: Seq[Project] +} + +sealed trait Project extends ProjectDefinition[ProjectReference] with CompositeProject { + def componentProjects: Seq[Project] = this :: Nil + private[sbt] def copy( id: String = id, base: File = base, diff --git a/main/src/main/scala/sbt/internal/BuildDef.scala b/main/src/main/scala/sbt/internal/BuildDef.scala index 96c4641f6..4194613ae 100644 --- a/main/src/main/scala/sbt/internal/BuildDef.scala +++ b/main/src/main/scala/sbt/internal/BuildDef.scala @@ -17,7 +17,8 @@ import sbt.internal.inc.ReflectUtilities trait BuildDef { def projectDefinitions(baseDirectory: File): Seq[Project] = projects - def projects: Seq[Project] = ReflectUtilities.allVals[Project](this).values.toSeq + def projects: Seq[Project] = + ReflectUtilities.allVals[CompositeProject](this).values.toSeq.flatMap(_.componentProjects) // TODO: Should we grab the build core settings here or in a plugin? def settings: Seq[Setting[_]] = Defaults.buildCore def buildLoaders: Seq[BuildLoader.Components] = Nil diff --git a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala index 6b4e77f91..872b8e33f 100644 --- a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala +++ b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala @@ -123,10 +123,10 @@ private[sbt] object EvaluateConfigurations { // 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) - } + val projects = definitions.values(loader).flatMap { + case p: CompositeProject => p.componentProjects.map(resolveBase(file.getParentFile, _)) + case _ => Nil + } val (settingsRaw, manipulationsRaw) = dslEntries map (_.result apply loader) partition { case DslEntry.ProjectSettings(_) => true @@ -234,7 +234,7 @@ private[sbt] object EvaluateConfigurations { } private[this] def extractedValTypes: Seq[String] = - Seq(classOf[Project], classOf[InputKey[_]], classOf[TaskKey[_]], classOf[SettingKey[_]]).map(_.getName) + Seq(classOf[CompositeProject], 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 = { diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/a/A.scala b/sbt/src/sbt-test/project/sbt-composite-projects/a/A.scala new file mode 100644 index 000000000..528ffce71 --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/a/A.scala @@ -0,0 +1 @@ +object A \ No newline at end of file diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/a/a.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/a/a.sbt new file mode 100644 index 000000000..67966f6d2 --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/a/a.sbt @@ -0,0 +1,2 @@ +val aa = taskKey[Unit]("A task in the 'a' project") +aa := println("Hello.") diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/b/build.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/b/build.sbt new file mode 100644 index 000000000..3c7dc5056 --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/b/build.sbt @@ -0,0 +1,2 @@ +val h = taskKey[Unit]("A task in project 'b'") +h := println("Hello.") diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt new file mode 100644 index 000000000..cd56ca9e2 --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt @@ -0,0 +1,13 @@ +import sbt.internal.AddSettings +import sbt.CompositeProject + +// Based on sbt-file-projects test +val cross = new CompositeProject +{ + val p1 = Project.apply("a", new File("a")) + val p2 = Project.apply("b", new File("b")) + def componentProjects: Seq[Project] = Seq(p1, p2) +} + +val g = taskKey[Unit]("A task in the root project") +g := println("Hello.") diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/changes/basic.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/changes/basic.sbt new file mode 100644 index 000000000..c128b140e --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/changes/basic.sbt @@ -0,0 +1 @@ +lazy val root = (project in file(".")) diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/other.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/other.sbt new file mode 100644 index 000000000..94e5f2363 --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/other.sbt @@ -0,0 +1 @@ +val c = project diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/test b/sbt/src/sbt-test/project/sbt-composite-projects/test new file mode 100644 index 000000000..dbf2e5289 --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/test @@ -0,0 +1,17 @@ +> g +-> root/compile +> a/compile +> a/aa +> b/compile +> b/h +> c/compile + +$ copy-file changes/basic.sbt basic.sbt +> reload +> g +> root/compile +> a/compile +> a/aa +> b/compile +> b/h +> c/compile From 25bbc5f488f608e102907cc4f222f26257c74254 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Mon, 2 Apr 2018 20:46:07 +0200 Subject: [PATCH 121/176] Add shadow tests for CompositeProjects --- .../project/sbt-composite-projects/build.sbt | 13 +++++- .../sbt-composite-projects/changes/shadow.sbt | 40 +++++++++++++++++++ .../project/sbt-composite-projects/test | 5 +++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt index cd56ca9e2..7bd373aa4 100644 --- a/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt +++ b/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt @@ -1,8 +1,10 @@ import sbt.internal.AddSettings import sbt.CompositeProject +lazy val check = taskKey[Unit]("check") + // Based on sbt-file-projects test -val cross = new CompositeProject +lazy val cross = new CompositeProject { val p1 = Project.apply("a", new File("a")) val p2 = Project.apply("b", new File("b")) @@ -11,3 +13,12 @@ val cross = new CompositeProject val g = taskKey[Unit]("A task in the root project") g := println("Hello.") + + +check := { + val verP1 = (version in cross.p1).?.value + assert (verP1 == Some("0.1.0-SNAPSHOT")) + + val verP2 = (version in cross.p2).?.value + assert (verP2 == Some("0.1.0-SNAPSHOT")) +} diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt new file mode 100644 index 000000000..9bf54ab9f --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt @@ -0,0 +1,40 @@ +import sbt.internal.AddSettings +import sbt.CompositeProject + +lazy val check = taskKey[Unit]("check") + +lazy val a = (project in file("a")) + .settings( + version := "0.2.0" + ) + +// Based on sbt-file-projects test +lazy val cross = new CompositeProject +{ + val p1 = Project.apply("a", new File("a")) + val p2 = Project.apply("b", new File("b")) + def componentProjects: Seq[Project] = Seq(p1, p2) +} + +lazy val b = (project in file("b")) + .settings( + version := "0.2.0" + ) + +val g = taskKey[Unit]("A task in the root project") +g := println("Hello.") + + +check := { + val verP1 = (version in cross.p1).?.value + assert (verP1 == Some("0.2.0"))//Some("0.1.0-SNAPSHOT")) + + val verP2 = (version in cross.p2).?.value + assert (verP2 == Some("0.1.0-SNAPSHOT")) + + val verA = (version in a).?.value + assert (verA == Some("0.2.0")) + + val verB = (version in b).?.value + assert (verA == Some("0.2.0")) +} diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/test b/sbt/src/sbt-test/project/sbt-composite-projects/test index dbf2e5289..868ecac69 100644 --- a/sbt/src/sbt-test/project/sbt-composite-projects/test +++ b/sbt/src/sbt-test/project/sbt-composite-projects/test @@ -15,3 +15,8 @@ $ copy-file changes/basic.sbt basic.sbt > b/compile > b/h > c/compile +> check + +$ copy-file changes/shadow.sbt build.sbt +> reload +> check From 120ab651344b45fb1c90cf418a7e0f204a677285 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 3 Apr 2018 16:06:27 +0100 Subject: [PATCH 122/176] 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 123/176] 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 z314t+(DbW)uRJjb#8BF5 z(iU578FS!!NStu!IpV9~^?ZDY(l0~G_-%Z5+B0|4ZNz4xa2mK`mpbW~Sj+%6JxUTn zy!KS7%0Yt(^DEKmFRQ&_7w%5D`)0Kkha$^ps~L?V&!U%R4(EuPhfv_}t^5pKsVsoE zyYfU%x2K+iBYHhL+j)d&kJ5DpL6+C#viFKR%+&Jl z1y6OsW%VB*f+$tTmsT%zWPq`iG=c90(SFC=l@HR$UZ6>(yrWqAJ7Bw$d_mZ(_Qu4G)amGt`e^2&a*B#^{G;@G*MCzS*_njB`p01PS zWkGuuj7P(Fnm7KolP?`@oS)!;(fo=S@?GY~@Uc7Yd)A6KBJVQqd*m35zCUW(pKsy& zS!?8I>#G$BoxCyKaJa{$Ac$I^yL)*pplN?G*v2g&)?nfBX8R# zTIhoTo^Fqrxj=9uUhD8LNL}5~u5{DRMdPSubekNbOXd|z##JdUnglT;Lp&B58hpsu zy)q0>?%zEqP6(Of@DnVnSyB|1ty$@(mwMhAK|{m2BhM$%*Tbl!`Wf6Fw5|;?Tn`zy zv*24g&YFP4Eao-fSaewL3hlyjAw7T5uqcnlP5j}f@)4nRVI1a_i|V~|lfh+(+xebM z+Ryoa=a~H%2Q0t0emn{ip-m2!$0lH#B{qC|My1w{oRlSJhC}l|$Y4yza27`C2YWE# z01FJn%gHEz=e+;;8UVuO`caE68RWe;T>iMr;#&YN{EW4oyj&x8AKK>CQh#!q2xPJ5 zlhD=rWksq`z+eWEy?x^3PGI+S{(&ATYV#_B{EX`~@a`{~K4_2meZKG|$NU#AFe=x% ze9lIZC5@?;oRbN@9HcWRbe)MVipw$~xXT%vG8eWgyLeED_)sdj6;uWV-oUWuGFl9a zzT_92kv1Kw-v_Le&fTZrAMNUM!mV6@s#25L&E7y1SX#{KRbF#Vy%;HyOwZt~bU2gs zAWzq)oxpnZ?Wt{)fT%4c~}*+SdAFE`~GlUdqAU zh~dO$)Ro!v^x?gQ6dV))gUZh9*A?)h$XJ_l&RgQ>XPz^?B&byU_&NCS&6A%*%OSxG zG)kIi+gQ~PFG-`^ey|19$Oic{p^rB^& z!#B#Da?^9uVBaxBGS72gobf^G&+!Haol5k9eQ;+F%GPFZ&fTA_Q0N}}#A&b{J!c3{ zi$BeZpo(u7M6H^?g|pxuXl9{5`S&(>WIskZbh)qkM{5cUT=8?e%+)_l(xml{gV~A) zA-cl-wiEa+?^ILT(_Op;l7VAPH(jf98)2D#FVPSB{-SYw0?oiQ4y;E#Xyv>4uC-eg z;oAqn>KPnik@tb!nvVf*3-g^WRJ%g-a~!4^==BejJ--&6#$}nu4fM=Hg`q-Xd-&X9 zbC0d6uGnu_w47_2E^&=7waNkgO;@!Z+KU9-vp^nGieY6d1pOm0(Fo!+82jC3HkEe> zAMl8`#!$m{(AKfgf{WEaN7MpQ!$y2ys`kYbIF;8w`}rLSmfGO`no{x3U+;VkRxZ)A37VpB^M_DnNS$Ncs0kHsW!hu%1g z$h~~;&gE<#d+yJH*0?D0xAJ!q>&l7~$)Ps$<;d-|yk7W4osyOLiv{aPp5`gV zXW;&>EXn;mVmV`BS?*j0Et1OKIH^Gs=HuwS z>^L*_&Yz+@@)qc5ksUp4(-Hjoz9WGTFL&o`-uI#%NoeT$TqN0N;x%|Kq!f)g>k2q2 z{_Jei$XtO*qdlYK*@-aJ=@tw19*`8kJ1TBFi}#wHkIX!8Uku$1jiwCEE~L~Rjx2&r zTFf(J6tQ`{;;^YT8vy-s=7XIQ;)^Vu4$gvt|XAiS{r^$8czM9Kx)|^?feNAUDBFC;{8Vvg>nht^ljQVRRSg7;#h-XfV?NUJXvYyKRUQ|3a!6j{;I?;WCqPF*cnpD?28iVubh0>YaIn#C82R1S}?mopDx{Cf(Lpfch z&4meUBdjwHS;;6df6dt@urQ2UdM{X!Z9JG7K2<5hW$B=+*BqB0Hd+xYrYXvAe-_9D zA|`ioQ;g=({H9IL?~ot8?+3d75UeLCMmY``y>y@mCP7O0X#uF`*4~&%3O+fxUEBGI zYnRs5Y4pB(llG3CrflQ9p6r>Y3|rq-w-1H0!0_A;?_({&08`mg%t&m==-2)sae}A- z&C%O2W%QQ6ilKP5h+~Dt3!;r=ek&v*Q4;=37YOKFerQkGAF5n%r7=RoVw7ROqdr;r z{Unq~IAH(&fgW~D$!Fa2$&s*-3DHTzeu4UlY-k)0`Q|A9Z7k`SE``!;rk&?efQ?m} zjjWLx+P(g`p2sQ5JGe*ebH@h>>*`dtGS#=jXJ(0KPf9nZh(R6DGTi>jVOJ2^gYa9f zc7m_iAO+?i@K$Wj&G{Ofz>3FMaq{2fp5J^6)vPnolCOVA3((#nYBhuXJjIti@@tdU zJWT{pY2|E4FM7W9m##<8W>PkWmxlVWXsDdlzMd(ikqrbd-yk&Qov;EYJlUl9XDzfI zRt`J`;FcdbUQ}JNI7mt(AK3duomL$kV=+QgU$8oZrp} z1ynhlD)wKHMpS0w3R+h^)YnGX=Dh=YKGHh!|Gma!Mj~=Iu}bqID6PpoN2~RsArf6! z;#WqqM4zNI>$-gfd%m+nK1G^g5dwzJO2LAHCe)&2=px z#4T;EdsY0ti8M}&KyUWPmSEKiIj0vSm8IOYS=!*M>&k}@MZhwwuePXwD-)1L zdG6Us?Qf;wX0a)=McPMb%_Jc0nyjZ^q*`D~=F39MCaXouGYGe|(UiFtQksK>_fIP!1RgaY%aEA4h=I z_gdZju0+iX@RzZYT+Su(wv4+2<;jtW)_!CW(_%MJyIe{RxT}Kny4M~vA|Gs~B3IHn zz#o`|{oI@-tPr(a>(37GPq$q~YyMgAzj%^7R&~VwExUT+lw;cPW`{d>mokU=p9VvD z_l0c_OjY{^o1Kk00k6B2*R3&B;t@h7>F%Y#TXiOv2pLjb3%qAvD*-UQt?3?5?%wMf zoSc0f;5~-|HI{WT3_H~|?bF0O6G4oIly^bZo(X-^k>xhHU-Jz!E69y-@a^BP*akbe+$AqH zyX|2I0GtwV6kLE){Ga27anx(NuCVdhq4T$Vu`Qi7+O>GfvAXX4%Z{-)- zL3t}{IN&nR?!El$t~s0A-T)rW3B4B3yh8V7C6-Sk17ijCJOzk)8w^$0j(TYnxA<6n zbI$6Hpb=w=lrl*XkevKWZB@0l#etckEHo*eYx`3iP);WS6c&_4YSYuILJVx<4wpt- z>FCTLeDFf|Gm_+lFTE-HPdOci*3#wAVQ8+3(O*VC@3(0!wm=dh+bT~S&~`6BVv&Ko z{qyY7OHho-4Ix@F4Vr9Q@#ADLyA)~ zo3EZuwPcmtpTBEaX8zcIGAC!_7Qnlf{N0+M8x{#0BHpT2pNIV<9jSG#90{n%fVatI zHq?4)!-etv|4T;e>EiHXe15F`;Hv%GG3z>NqBoqDNjy1j5J!AK(+Cyp#Zb)jE#}r+ z6g@ea)j)~eg$>20u>|$vP%hp`6;cBv>6{VNkC_9=8(bD zgXpGTybrQN8fg2^1N8Iw)bz1qJInZ2=N1#u0v)-{%EzV-!h;!Pr|e11j08a2 znSpBAV6a@+078a$0DAF~?6`uS*p#Y&#vd3P9YJ_}(Bg+M6pI~2P8^WH9=)y1Y|fl8 zZ<;FDA7;ywS;Kf>Pn{7;40zreVd*koE01Ss7?%=Zq!hPHng$B;@`ui@wvm!PGlUTB zut{X~<~Bc!254Cfq!dTpD#g;fo{{DvA?mO-w_ZSr0Xz97cfJkxp9~WFHVq5M`O3+$ zOq;(WeJ=sYcJ`r@5#9T9_AvuHQ)9eRrQRr+6#4cU$q|ep1c=p%93V%J%(7l*mZqyu zWGWH>uYXxD8zJ6K11>9QhS1#`4if6!cj(dF>}E9*p8*Tf>LSf4ph(W{{g^oM1AQKI z(G?o)v@ryRIy-|bdSYB+W@9?p4F>-sYzQI(r0aBQT$!HC!UhZlMw!BQCDTC!S7lqo zrJ(qKqSo|*5_8O;^Q2V*Cnx$*=!yFAtQ&4SIF1GbW1rSPxXe=&d3WFEa(qD!BX|DG zZe)Q@f9`5!k}ujD{(jW1Z9}Rq!=|9ILZ;StP0GgHimP!M^D*l4Cd*>JTfjypHjl)j`KK{*b@Fz1~fGL7bpj8LQ2X7b|M@0co zG0)c*M8dV3bt)dQk`@3dFJMY<-K4{omU+JX2eC0Ub${)PGhbo-X8+@r@4CN%F=NmY zi6FP~0Cf|^k1hf}-m{l-d8TpbWh2)YvqMrsl_;-oml6q-IKbf7y1NtCNpHmu13Dt!=@R9M%4L>nV*}-swjXZ)~ zfv6vWM5+*cqJoh!Ml1O1pNLr8LEBH>_GO3SEqAmyIYDrFVT4X}AW&7C@Y>95>~(xw zErVfOG21;NnGJ*E=*WG)T6)lVDX>r)Up@gmh+IFn>*edFkL`=COc~XH9%KMcT(-%n z4%Yjl_V)Vg$&S=n_KO6sFg?I)>PVkXe&A_{25?>`z0`m(zfbWMdZ!wA>0o{*L=;x= zSg^kAPC1qvo$Ww*1*z#^XyGy14%~DK%L@hG; z4o(G}=PapXSk)sj2z9m!8)2n&5U5D7G}m!c5eU7_W41Byam014{J=U#JP&lqsaPQ& zpt)e|#dU^;D>wY6L2)R4fPUcJ4t7M=KjYzzGs?z^zW@ahj?$V!(}0RgU3_8zRHbIK zb)H=@2H|{3T#Oos0H5!hRg9MRRy&UlqeL&xK$IOw<&g2f+-L6D3G?;-L|?r3^=y)l z`3Tf~)iz|I&XJe~YGi2$Qa!)p0RT95L-Qi&i7H}jvHrLiy@q*y40bVck-XPr&2$dH z$KLLd4KExmy#Lh6#Ozm#gvXhLy?2)P6|SVxs2Na(raqENgcw)d_YaUX z>@rU{rPA>Sxj&9p?O)k-Pf)UtvT=l7eXI1LZxa~~p%Xe}#ZgfhLdToyZaPXp=zSj5 z&GQjv6pv(;oUlRx4tl}4)6^?*WM-j*2FJos^QA&Xte#3C#=O^U<)6eZ!QIS^wP2U@ z1iZWU^iOFpB;ufri*Ssi%vD1JaP8Yb0k`Z+mZZk7O4UqS3^)pgwVz_F2HnPF@e_m1 zyD-=z$DTxOaK&h%V<3v9Ah(h)sEz#Y1&51<32=TmgdF5X^#=zP$F z=|PXVt+0>`ob-pmUQzfbEy5T>o>NGEn^{nZ!0cPaQJ$7pzg0G{F?=w^R;RpkaI?Fq zz|CmaG6DMg^k7H$G!kqIKe*If(Jfi$KWtS*?ZI|YH=EB(vkd8ue$yBp57x{3H{_?4 zYF2t(W4!hw4Y+X4>6@Cgdu_fKySPqZ%_dB?aZ5Wu$tDp+dF$M+6mM=Hltimz-B(EZ zn3cG1qW#*}*XA*%J1V(DZJoxsjBiWb%okGa@U3$kO={t^)0UWcB--G~>;5W#Ax>Ft zGGdSTXtv*c%JOV*)vry6*|>Im?&o=ah&zj%%Y-qi=T1gDZ(X6A4}N@8Y0CHQ0j2|B z4o>3=96Jb=d_=i!_E!08(Qq7yj2_y(eP4yL^~^Tmn5351`7(X)IJgJj3i zH^B`(rXOLM;&r3fo$y5a&cztsALC6)Yv4}?ku!%fc&bHb?JjUVPn^YcBH+o3*d|>s z+?Vg11MP|2%ZuJ+q3Hz1WG6Z>$C0N6u#^vNF_~!nhpWM=Mx@zOKqvPYOBuas#Je~+^97ZdHwDSR!4h3?r8IJ_^#8_C(Z zo9MN!jYVghu=ST^|18`e)w*F=mx&X(`UOz73<)=(Mz1PZHyTW`j`{n2GXAw&94wbG zwGk_2UHm8np&v=8RA+-Q^oho{nR>r-<;)Fx*1Qg^8>pT4%=M-4*s$>@k~%8t`G_iw zri5@2zktD>_4K5!FT}p3wa3GmdTP%s7Gkn8IZEy;4+HGp!ny;2{D@E zOczVX8d7Xx2~pq)9toNoSqvQc60d1AT9bh znlM1w*H&c2Fo-gi6Rl5I4e57%=6ZfFJ6c;6t-*@lVW-*f%?mzUCE|=C9N&^)6`aI+ zwZHs=?!0mIobQ}Wg&0=WS6jXZQtCy5yL~Q?lJ-xrkprJ(NdJkEEAG@&++m*#)X`9{ z9$q35N{h6-R7!@4d^Cnyot1G&ib45>mpxToaiSX9ge=9D!<9G2 zo5yh)0WwETf;AAHGUpF{1^+UmHxSn>r;68_`Y7_jEtt4qv$CetRApaZ_!ng6>EQ@R zFNvt;<8w}$+ojAngg zYI6XeQ1X%)t0~1ML+k-ye-^SRh}JQg-Xw$sYlfA+a@(}oA0l{vC`qua1Gdh+`lN&r zDar?%HJp>_=b06)nM*v9dhmfBMg?4br-y|wpxk% z#0&eAn!r>;v4S2pwk`X5Z8)V&$N1gH)}=|f)Epz`ZvHPbANA(v6C6LLo9s>}W|gAP zU9cA|4Q0Qp;j7!{6BftxPympK96#%9d-KT?KUP*G2k3Lyuu^ebsSZl18OeHkh*j5z z=LCI70=@D_;SD9;FK{&}wvc}4vF0Gl9en^>i@{sU1D9^{76;Ub4D!7Fm5f=nrD}iO zLYT<3){lfV>0OF~TJi%@ojy%qBni2YPpChNbwRVM1vkm&BKgD?>3^h4ML4L@set^m z2Gj)Q>{MuQouA78SgdBs-<^6g;e2#i)L6&H+QS^O7VHA(uY6V6#{g2F@1d7}4lrqz zH&$FCXGpW_4fe)6tp-m9olkWz!INmqFrE-PoA}wMH?9@+U*x}EV1~eH{8VxH?zcY~ zEs;wccUC>ZM=(PTELN001O29+{}zGUV!o}nNy=+bslV;8vtE546JV4gm?8IlZttXf z(k>6{%_c%muP1F1pSA5QmnN7lA;$97axo~n+i*@jmmYT2ymhYRbme)zd(^Of5N*t$ z`ylP+O$=8T*#LFLu~2)FgjxUQ55|{~{P6+CR&ks3EscdYr7oo2^-WudDvf5PIT|)M zl|d-k{i!stEVo~u#&5U8UjEjQWNV@Q?L>DOSXs%@bR$>2wWOf@EvMt$_atv5lgZgo z0HN@xd$EBs&lSQvoK;@_f*M_*o6YAq!QI9=;QB_%z7fDn3@FRi#%=U#+^va)*NmP#g+pjIEsz-8;8}jr*j7U&}-ov%PwDl=%O2UP68Vs z+|6tjsr-pfwT1^JZULQoQKBvml0QsWYyo)xv*Udj?cR zO+N8e!d7W?-l|ov%Hk_)`fN4@)#2REoibL`17^N2R~vQzfJI+z3Zw2Qs{xm{t z7rnRC04pe0Dit3E^nqoaT$X%E;eJ;oqZLxPaz$&v4_ZuccY+0W#BiU&*=}Vjvs`r1 ztPOc*uROXjH*j7=vaiwxjt`93+(=#FXVW{+CT~?=NXeu>uJ2%0bHJx<6?yAYX+Q-< zsgBg!ig!cqSm_GBbGV+KL8@uPKVCJ&hHISy8H3cA-Zst$iV_d)q|jHt`;{0ui}KTq zxu8|(1yz|>(mcsVKO;9bpmr~vcM8q5l|et@gn0Z_==0Z^OB(7!>jj*kb)gX}zLk1s zo08nt{BBkp+}?AoOo=Wgo^T8ly9NuCj-Ab|iz6<%|3xQ+ z&qkGcaaHD(-(pb=O6;D8c)$*(s4zTr#4}dl8JFi7hV!cWT(r$Xh6{Cr08?`Y5XKlU zW4J98@R2ns<;F9kJS4EU%KPjxiM-scX61_p?zWs^Cg2+3Q5A{t++hYsy4e)hw{{bHB&p>K4-6cH3#igh(J#vuE|hn6cPS|wc%|J?p|_io z+IFVcl<$lxflu&1yUd0Xk58GqLBE^gG&e1~as-obYjei%35V;IJ=5#K*PRDf`Qe5& z5Tt>NkpN|_o`U+5HPsO30b_~;E2Q+*YWZoq#R!?_hHrO|tU4%3q{3+&-rS>e_RtyX zYL#C3EgA@O>7mZocT^JJ#9 z!;L}p?qE-tlSKad7-zMj&cxd7@mzcRm8#6;VK%JY|Es}yllO*#Q1GwkCoinUXjQCq z7&t6ZrY07jdnVxG(?%_U<3hqdW3gpq?12;6WK;qQru;$?Inq{Wg}P%&T}ooXtFRw9 zWq!Ul4w`*4C2g70v@4UhKn@rS%Sl?=k)<2mf@xzDP4nFa1&7gmLa24%a+gy_EhPQd z)-03{1&@dE!$>jUfsn58+bk4M>AcKzYAti&d8%Nc$&QHJ+uc!`@^G}Jo=5R(8--X| zXQ`*9t$gkC05;N`s#8J*+2x?UFES;R%yvSir^nhr@TfiIQs@q+RyK4haT!g49gf7`>};+6hE^v9uRA1Mqv~KHoBHo>^=z zV<%Tpt9#)a@uk>vEDYT)U1XM%_ur3%4E1^E=*QQRotxhmKPtP0>k5wMq|jPFwC2v1 z!Xs;w#aaw?&0*q(wFJMh_+%6xJyJn7_RkzPb^XdMq4y-IXMN` z{73zh{-l=JzNB>`q$XC?+}b7KlK0QkBA4PkI;E)k%rMm$dCfPKd==2h_~F>UxwiMmy)iy>4BZZZi)nYVoN+26|2><$uLvy&t8R%?7#Ku#vuwH1N_X7KJ7OYO5+ zxW(FVAIQkD2x7!89@QG$E6bYa_(p4p^DY0xibEoWE!3`|GI0>c zv9?^~7P|4A+PVMzGazId?lC|*dKfR`^gs>$U?az{x(J#V_02YhiD_1azdHD_k;a4% z+nLl%2?`ng#94eJt{_HL*{t+)gvT^e(p55FBOCPx&-CT~nM6^zyV$PJYBI*-4BWVN zrz`QaE0@AO)*_?eW`&JGHJ2W9lN7@$v*x~(?pwjYtj+=A+kp|-{N>6Qk~(=I4jis$ z!4B3;_|5z2ZRmd+P7Mt3@AB0|2C%(ZN1ENh&i;ZK$Nj#lyEq1vHm6wO-xywX|9#+t z540T8u%!xU4?VxbVktni*3QlacX01vFm~Zh;u1Rf{@b3Q-N$JPByDKQ3w1s~uJNcd zx=7ilRqrom%59IuoEY2yzy73*N~6DK+1sMO$&}Nt$ZfLj7z>@w**vB}X=R6jt?Ge% zQQ50+a46h|KPqd6B&0%s>yj%Kj+wuBZH`;pEgGxA@4i6;%OYTe(rDGta0`L1bm7jiyJ~i(Z)mgC{MjPIC>l5BWb%bJDiP5)5lFq<8#p zb$DFfnU=IL23q+Xc~zkF>AY=AJlr}0*P!UORKU?o_TSaJ*4 zWaqgsL48^2LuYUA@BBHVSu)*f&nEUHcSt#_$TpMHlNywOtzy{AeYlyNsxY_sHqBRa zusc#l86f+B$2(&ti#FzRssX3C74kjVR9=n3*EL6rUgac>1%&wYUhwta&0%>VYK1B$ z7@z7bzs$!qZveg0iNIp8gmSf5bl4Y|VenYvG(b<)18 z(?vJiM)%3B@9+Zly_b@p$I%Qqh}($xEL@uPcIMF{>YGYuv|bSZW&yCh&KyO|km3y5 z)fZu~7*SONJ7SWW36q61dW<~=%QokxLJ3$(vq_!Osns&ZZqel@*Bxe^@L6%p<9|v^ zB*;Xi>tc&GpD&u6MRVIv{N2-@)k>%KDe2wG1g#|5MMoH7fD4veZll4uPX%|H_X$ZrW?EcfzN%R+wUeyy9&=w@$W+g01)wO(bhm;=hGGY^!SW*2p4gHgtNHqvmjvpJ%ojZzStMdGm-n6BW%k$jnOVp#lUsqJr@dClS!F4kxDx+0pq~Dev z76=Pw`Q~iv9xP#GaFKEpPUCP8BRePkIqP!v+_bEN$GdRt+RS^VeUo8-W-DxnBEMFC zT&}InJ$=7<-%~mWy<*j@R`pKHfvzp5_9svMp=@s~zIyHI34G9R-?+-)GU|~;nZMEN zT4OMj=H6}qf5~0gsHU%MFEkt*EpkNk=YI&>MY!mbJ?(ugG9qgUr;=(!7y_x+R{p|` zcCNy5`9b~-HEqV>$TNHnb%$C!2wiKgZ#MVsNh8f_c7FGZzl7?!(aD%~@Bu>^jAE?E z)y~xS0d&4xRI#7Vv#pBR#L|+DjnPaKLL>Do|HKnox^PTbpczc9_xQz=`fwGwM)CmUcB&Sbpl5u>bO&B@W(7A|CW zum99bnYrD-X-OGhg$OiXO1UtPsiC%E_k)5Oz4nNN9%}?Zi%2TzL{z&KuSH{Z>SA=W(D|$?>#a)@r%WJpm{)HCO+gZbb z3sr%h{r@oaj=_;dZQFKi+vdc!t%+@CV%xTD+qUhbGqG)Zf-m>;KKRy;{?%1o-MiMV z-q*Q~<7BgNxhr@y(x_tpe3MRQ<)7cN=5GCKbunud8eS4$LVkwkHB+5ofPw*=-;!XM{gVj{O;Yx49 zdvQI3!fHNM#V%*T_`CRLIJM4BS_mWyK;y9xi^fgGB@%_&t1utnvwj|=XPd_^0~@+B z!f8qN7l%Nfa@u`bNgyWgzD95qDz#1{z&2)-r9k*#yIn6B|NDX<8wi;2C!#nWdLxJQ za5v`9Kn6~CvXQWYMW{KtZvr%a0gFHzKnv5n4>;%H!GI!2y%>6FLxF${5e8@vM^4FR z15%*8vXRh8=xQfP_tV@)9*~-hcB*U9hX`$jg#T-|2k?Ld_=4ZWT3r8SRk}>Q-H|}y zFQ!U|bwtjh*hfk76(SeO%u%tLK1nYapsNg^q^?9r`rxydX z5;pb{QB6rWQ@F=!+z{?OfYLZ_S7>%5%DCaoDi$Sd@Ie?$S5unGg3SeST0P% z7%&9IC^%g$PN~NS5Bwt_V875@+{NdxQ?wbv#?wC6FwXHc^bhq*>gS0CGfD5-^_!38 zrma3u{iMGzbxyMbPklk&p~krWOmbeE1@i-%Vc*!41ernIdgdwN#%mXCCchstC81du6hrH(ACHC>=j<n3c0FTh5j(u{--6B_bD*fgq72CxYsF?m)kjv;AiXvSxdv<3gO%|>TZoFaWf~g zvJSj#wjFQ7H{93vOYpwJ7&PK%AQ{xGqUv$OiE&fg{lG?Kd%SE7V4#>|86qXp#WsoP zWj74^A|{;G|6VHu zz>HJ3sj#h|O;|V09E^(7kt41VD|-1>3MAH{LY)Eo{0hG9uE+_l5!oXp7q-!JU+B5d z9g9Gh%YW@vb^c(l++fg3^+g=1o&gwqT@3csw*Uy9X{S(`>PXa z_jZ?c{y1$WrQT;u7y43&$-SO>6P{WHDsDu#YByGAWOG)ZjFyyul}d6pZ6|g#MP*!b zqf}G5g9MYSkvyJ>Vt#XCN*>Ua*m}8qv@ymfnyQ<2$?{Jb&2>Z?DOGy-4dvE9e%RDt zFN%dk+9)@=!EB#EZ{2W7PEKB%8#QKYTaP zT7<7oNqLK1J0H(V_P%6ZJRT`e_4xE{EVFGM+tQXkE~+h zRuCdDE{__Z#2(ZJ?QuR2n-H5Y?EbHf3-4(>`E86(yTgni@% zD?=JjkYT)k5!#L6)sRFR6MV*UI42U#%}zm)3Y|$Z4GTB0(o~j28520TyM4?sI$BfjHo{k|38~}kP@`hxfA0ao4SkbWx zf!6B?>q&!%0xiWJ3u78USnJW4;|iq)R6rY6M*#6}xGA%gWCO#|B6=`HCi}0n91xKg zBYJIts&dY{CccO7G)n$yPm;~1B^fu}b}N}gwXsk_kI3EKCK@UfCeuLKWw;jL9tp*w z55f`}tfH~QSmlk)F_lP2E3AwS{yC>%0&CFCMRqaa*I{aQ<7C|)IEjFf^i z1kLx$6Eo1uy?;oBpq({I@T_ZjrUl&Gk-Mh6H3mAPaaVq} zD8)X^o~Ah(O~yT&2o(H$nPg;3OjU@cN=Nr6h&d3u?ZwR;0w#0&bnAmZgay+ zzLsTYi)r|56|SO`{84C*W;Ne$;D%i6a7W1;B2^igI>xbfY5r!+Hsag*%%Lu5w^k!| zn^Sr!O44V$vZ4>lnS?dvg3*;Kc!0%8=wSibma zvl{}2teIhbB@!DpQ*KF#{E^%B9Wk*Sc`*z=1gLa}5vP&)_)5jvLo@5 z{?#f)h8wqNP|sO_JyVYanzq~7l!8meg*bwQty6Ii_*y5iZ+fTyIpQ)mD*tCno?)+S ziJz0f+twgH)(wD_{qxiQwJQl=yEnc~Ewq&t9Xr>hKcR9!Pt++hsuM;D5EI;06YAGW zur1;C7Sr2>%_o0yoMM84f?i*$apbV;%%l>xU-*yZAsZpONcMH&k=lLF@(sG&b}m)5~B=x}TYFgLJ- z&HAvGx${N*IHI9h44-j?@)&o5ynaNf2# zQ%Tm8|7o(e<5~NY#FHuAE6*`UvEZmF!}eA357hpz`_{#qq{C_2(s9va1)SpK+{hFp z)A7p_z8>Jqg#%s!ZV2e1{V#D}E1HG`dk^K8QOjMLZGF<#EiBh!H_@^m+{hL9LV!#GQB93HuRo=GGKR3GKYi$tNq>9dpA+mio7 zPngmwoh>8(Ve~bhPVo1895X`||xTJ9j4qz^^aa!G|24B>q$OWgZf_$m@X2GLN{F zqDsps`aiu;DcF_EVhT4;!6< zpBSWDE&<*}O8n@uf;cP7Sqn3yuDip5qfN~JcuShZ6Ale|BuL|UikecsTske?_&cV# z25kA(^R$=3*s5zmZKKJof{lF%8#k^>9$N!64}k>~X>WzzR`ELE_0Ltu)jsGm1)hlz ziHXlj5B@%7a@n3bR1m2G?Jqlu4TE2o;l&2_lNXs;4Ag;sD=jbux9qJ>JS~Ks6#j)t zSKCIzc1d!(UHD%iEfbEe9txg=+rgmw!VVK2cVgx`63uVAvhtW)PNH7-AlmR0cnY}% zb9rX3FFHx~v9D7uM*M2|6$C(0BoXb^%JbK}m z>v7W|MHb5@9_AG{RyyQ5{E&V=0#_9?)H{vM^KVF1=h(B+XEB<%n@|M+v7KQQrbN&9 zh(j8+Iy3~IsOH?E-!vs^)i$LO$2Ad8$)NI|N;`uC$|;TDUnyh(wvI*nGwq?cv5^pV z3cTbh3f8XTu$QDUBHu(RTatC6#4_sB0xvI3)0l?Zj6rIo*2?F>8_gU>u>Jw!5BvXQ z!0n5~pNPYH>GdI;zMt+pV*kuzjW54}Ss>`0@wENU`_o6BVA(sd(t~~g7K)KCB^iUN zew5n3wAn5JXl>sx-u>;OVGn_v5<~q25(t5z3ziX69K)eWxB5E1A~;{yS%wY7%{rN#X6Y=4Nob5D{x1>(@(Gaxq+lPvxx8zK zcKC7XX2Ilthjj=SvVlN=^f@5WNBxqg_}K1*THQ1vkJ_Qg5=7QWYPuvcO}e;MI_Pd% zMESiV6w^`nV^6mKYDk9DOGs_^3C;^?Dz3`-@{PFBsD>P&H5noXNkxyV5lbg_z1JF~ zCeq?D2cya++*Wehh|oN5j6iyFF(>3K!pny=M%jwZJXUrc)!hqWnd0$_46I}GJ2RjQ ziXjWq*Ya}{%mAt}V)WD<`-0A*nY0mT9|d6k_A{$WLFN0248P_pDOM9%tPb;>L)aLqvEH5nO>G>AS-O^e%D$gHmZW=vj8j^PUH-??b@Oj%^8ME;l ze=7Yd6QO+O-b%_c8J;$*gBzd5_m%l@^q34(HYyQH3C1U;C-8lC4HgFy<9L#B7IUXc z9S`Ct<>@Y4ZJOW{&JxeGH0&KYe9ywqxyu8;(_St*pJ6^MkpIRiy6CLB$oyUZ)0bLl zz?zpQ6M#lqCS#?^XQ2-l1)SfZAAY-@Y}&p#NH2PA0!Awz3l7F-&h*q5p9=T?%esh& z><^F)_L42pB=?iTA;CV?W75v8opn1;9&GROKzE^X_XviKRz226Mw$bMNNQ@kPahfH zYkc^7>`tIr&3}s$i42c2@Ok#-(Iti8a+%!B5zI1iTH!v2H&|spKdeV~2B>jusrNvP z2pmHjU*~F%#dg8QWpl$KtmY)>d2r)70e{Vtt$mxEJ5J6gFMRnDF#_Jqt|!#34!&3n zEx(p3!YIwYD24tBIT)iG&Y3E+z)DQWAtNfHl9!QS0uB&vgg;jClPIhS(2w*yY#n}E z!OD^VRR)OAPKv|rDGBaki1pWEV%J`s@@Tj^?%inxd!JBH^e_?g8^8Lshtebm>iqAM zCo1sZdfBvxSg`ooorBGrdVTu*mY=EnW7D>Kn$ptxzntphEACrusCO%~X>V+=^3Gzs z0)Z60Y0L}Ip+x=RFsdZJ9C8W8LH&XAriSb66}KQqXF9^1S+54PkD1ei>o((^rd%k)=`Um?v;S*}$E|zp*ZnDC>pXPPIen+fZfY>$_WTCS(&2qlTun`~!PZ={%S2q5F z&QWCGW-7Oq$)Ajs^sLydk2IBluDcooZ#~l8-kwJ4P9BVfzWGlrTbtdswf5J|y>CR@ z#3ynl@qdpa?Y&3@U%GX4YN6$KSlEqy!e1@wMlX%j+d`n;jhxr>K(&3f74!p#C-jK@ z`$-E1;=;tH!$3>z=LyTjn*iQh|58lo-;Td6*gx8@#L}hO3E?p6$HU=>vwqAWCV2vZISJl_S_qEg;WQm|+e;kMwS3U+ZO(ww7qFe)89dkLb~wXB zDSHnIf{P0Y6F^3H!L-|B#Q(Nd{0M4$$Fg)fC=s_#Rk*6j(NE45xJhd8ki9q7MHlE= zl|XI1Y_4?usDQZ{h8`<5^GTb35Z3cRCZQ6UyDo*Dl3#Xhe~)<}Dj8Q;+`j$oP_`59 zuv0RsQ%?Woh55pZJs+MPU4vS7IU9{oO`E&0c%yD39#L*lA&Oj3vAWN!V=WX>6zG9E zE89OwmIzGQ%CzOjtJgj1wG>-{+9x&Nmzmd=pwA4Hs6^N-Z6jcBCr!)>Vc&>G{obM(Xyh5!T=`EH5Gw1YY&=!(B{$6p z?FSC#ivq&ot8s2QT;g&I0THfidM~Lt-o$eEa=8ri$q&_*x*6E~zqcJ&V2Dye6sU|e zS+rBko&m&atXhiir6lK;C zJO%oi(3=+7y)ye37nzSa5FM0|v>7?t^ps3E%z>ok+_&Q%`t!BZ_%0uX5WmBA!CLms z=T?S?d3}OgX0BwT&*2Zk7#jCTe?knwyFj z&x#fA_QW#YN;$k~)CAH_Ujf$5BT?aFx)iXtxK%h*@oq8X^bV$Vk>SF}*T)=qF>{xN z3jSVY#S2M>IW&MS1&`KTiifFem`p6Wzjj0{IGg|9#~=-BenC>3`cQ3#^7WTKQrd!62=ihN5fI6!3+^V*mj1b!}# z%~q6&$H>RHpB_=pvybT|gnr1!{=yK!eyLd9Ui$~u@n-|qg|E=ND;^s{u5~%@wFIRD zmH6$xiQI@5Qbo295}S%AUaivBt+f!gkD3HEgLM8MTOM}1BncbaK|Y4C|IZPD%?haSO*OH6^Vy@i5un%ZM1t>lnmSJu zv;fXeja+E1{GEMltVQ0ipo60t7b;6x645YS`z&^1gsU(9OEAgfx8Ws^8#2#G$jiv% zM1yx<0Bddxa7nJZD3t0&#EYUUxy40$&ob~St#k+LV$=@NXl72eWe_;#xjI5Ne^i^y z9qtik;>?CHL~?0+MI^%6TNYMx#=Q_zF$hI0du8k|0LsXGgcKqNSJb4&lvzl zJCR%Hry#l7q`%G)X={OMcguW^w)$r?G}K&BGp}Lw^Miv7^c~!!5eMPgO@j+g_91N= zScYiT@Dw)yp&+EfawduCM8Tad&PcM0&pX-)u+88)HT{rJO2-wJ?Qt(7o}Ny<>S`Bv z(}7DZM}x>Mk`3I(izIfK+qtYK|Jjn)NMN>>C+sHCnwWL3E;Ojlo;EgaPkFwZJtT*B z-Biki?1#c&|1WI#PvQ`;7vyhZ^jchKUpkaeV@v(+q6k=}-rEd|VY{&{r1J;72<~AN zxDIQ)l~MOu3D-sA=l;t?YS?3GmN$JVPf?{Sqa@tqqDO-6zm)0_I?pmR6%$O&7+tb8DKVl3*->SYYqj>$#d=|Cu&@*JK{vHnnosUE(=;B8RcQL-^mbwb)V0cEO21C%QdyuQQ}tNcl;Tmh zWIy@Q?^qribT8jpb5iPN02}kyK=h!hJ3x^n(6^*1l(en%f~HP2*v(m| zswu?espeH!dde7y4~Ym=p%Dp}=VqClTo6Jrbv}>?{Tq*QjIA6DbX76f4ye=pV}@Rp zW9ApN+-ymt&BpG~8s} zW1#J0PN48B7}xb;kgdE_(*+db1;a)+QD&z|4Rlwn14>qQLkYbAr?xo20c??wh+pf@ z^#Q&c89`Dh&nJAG@*ont#w+AU4%47w$upl3b z?x`~W`#{1721XW{U%KdZ;Vyk!4i{}UPwuVyDae01i}dlGiH#AKqM8QX;A^K(n+ZHJx725gn8Dby53gWyl4lpuA`jx)F|lKXV% z!{Zf{bM(W?_K_aFw^LKRf?B^-Vy7O5ayI(~80md)!t}ihc2^<&O=K^5mn`Hchrz*Y zNx^L4X@0ZQpV`Wx6cr(h#oHe96B`~%&q#jh)f@F$&rEKd_Cm=aBmHN!halIVy0zKk z);671OJ4sQeXU->5|7ismU4Wj#Kq^XphB}WW2=|Tqej*jha}HNz!739PPfG`y8AbL zPdu#DND{YNNQ*ZlbolgAE#>`a_VLcg^mZmuHqQ+cF+$?)4)pObwa6cG1W;rpNAAeT zMOdC;pu4Fs!6!$N;r{N0=;;pTvF;~hMa_C_W{)t3#FUd!-pp&ifxaHh#E;O#cq9L@ zVh_7frT3s@%#}L%8LR;iH8chY?r;^B?mOfe!Zt77ng_M&{%W%L&eeMcb``YKvr`y} z{{f4&(2yZXBgk~5k1oJ!Xtnt!OMZvIa!BpqTzt`Ip#Vj;rf^hWj_zv5`UvM$l1_S~ zsAr;ZMd3khq#E=SJ&Wcn7Zyz{ouG;*zik{JF5CWz?F$=T~4%E(j zW5|X^i08H28A~}mb68z&L?DS+nS8~Lquf=l9_WV<$0OR-Cq1}ASLG&El2Rl|07KnH zF>xcGYp20p5cPA3|K$k#brVfQJ|a!7;YbUF_Lsq?uRPuu!5J(c2D4owO_S6NV-(wB zSQQQZPvHQ$;J-H_?PMlnWKl@=;cS`@bfk82q;|GyP6Fx05ms6^86p}e>i;`!!3mOY zhzeudu`0+dEcDz}_M>#bo$i z*D>O#zDgUrRY{^VatmA9Wlc^=NvgpU3{OOoiiWHbvVC$Ut8W(t&2fm~aJf9rQ*J~R z5}^UY5Y>c^)jFoq21Z%?SkDYz6A4P6^l6sD$tc|(WoY)7owE{w%4VR!6!$VS1sUNN zPPE84DpLCsQomYu=-)wxvt1tMd_JY?82f=TH*WZ5k4?#dLw3>HV4FyIV4X?LDI@jl zvsg6g;s(mDb2KW2X%F`(*`yD4{fVzY^x_uMAeT;^S-oE3PdJ@u1L)KXcs|KXac1}U zq@S@EhziiZ+~ryX%)v&B^<>K9O)^nDqK_?(A)_&CX+~9J19&Bn9cuVCWvr1cXO2bH zk@BO|ocNsNJz~e3q7?e#8phw_H>W1lR-P;LQRn#Z0GkE(EbYtR?du${XvUI<`WpXq z!l+?Ra#FHg-VuIH`-BAACpg$~>$hjyZ_?a%Y?I8{cCL_w;zD05mG-;Q7LT+{X7kH_ zNhc(aEtAY4@py1updn#O+)mzB{2708%qbRw3qu~=V%WbyaH^$HwEbG_;PO0#phoWH z@j0$*LX*;IFNGKO>!3|T{?72^$~4Thd$_+>(h~SqC+5gNNyv3X_vY{y!>RWyHb%XM zQ1PWvA(n82=hqw(vNuN0XK)$XgV`mXK3jAYhFHgx+7-e^et|{S%i3?Dg)D)Raw7w= z9ajy3xcnqOpr!aYrGoNEr&^6Wlvi_rkq@MTxMvpK3?Tho&MSpgAhfW5yOvl%Om<0T z1w%SQnwu=Zsmi{cu}1d)pj*lPQ7H-e_-KgMjKvAqGi(F5jH%ZOzXB>MMrkw(@kBw8 zxHk_X4t)%EHdmy?Vp5FGAJ;Qaf4I-Qk7E3|Ko$<+=rhL*aFZ&EcPl22`705FyBD!X z$Q)n7B4gDDu%i1P!n+O*6%fjD9ijH9X#@%b_znP2O*(xhRv3v5)qXmZIvJu#1E2#l z8<7Uw9s74JP6(4NY5!>QS&%CM(>xp+wK5#RU~1YCw{7@mvMxk>ZRevo73w%^*~@h{;jBZ&lfRNuX|fzr8_?`Ix~Z3*)@>9HVu}PooO49E)^K3$|ap zfGB;FDH_w7hv{I7{s6CR&a~R(j+cco2q9<^NCJ>KJdU+|LI!3glH+FtIlK=K4hi0n^D47Q+~7jpQE%hHh^D(k?Li7k~e zJc%90NDQw%)Ssf(SM(6Sj^Gf>V9PevYV}_}C$!J*EXI2} z?vt)WDdz~L#QCi?n)Ujwf*bk^UI=CB=h9^39O3JbR#aBhPRe5$?z6Mb(ptgs>oY`} z$DK-$>y6U#Gq!_I%oAhmPV>}>(wbB>v32kA;p*&K5bfB%F4E77UAW;G>kvL&Xck)Z znp+Uqw9UDqf<}1bGPGp%G-T(Z4Me1W2-}vr8a7tW;xMk-e?8Z}tN#eXI%ZKAke_z) z@#)Hkuz{mHejBo+6?U+L_1Srs{oZ+1eANFcf4z12WFeI7^~~187)%!rvJT%{3@>ks zU=tdDky>un$`+q?df)jLdrlO%Ut4`SSn(0~;JKZlSb4_#lN($AL8+=ur??c&H@>~0k=ety|E41*r-sgMKS4*Cwhse*Unez-M)mu!=2IC6& z-L(1Y!AUG+6JprLyl71Al_}!2oI+*Mqj?go4R|9Uc9Tw0^bzr`)9< z$gqemze>z`pIKW={nfHqfDl3xRa_zYa>Vi-MC+UG=QojQ%4Gy2$tmnXDe2M#Q{mEg zQZ1H=$U10cQJ$;MhKR9wfmE&WHq>iL$ux}d3=O`W55Z$BWZTkcqA!5-X*AWZrAEpL z7n>S7r{MIvR+mx5XY)ao@9&|kFi65$B=npY-*q(5q5!L}rfAwN*ebCwFZKNCKdGA^ zlw^Bd9Bv{A6QVtDmm_?x%x4_{mMZb!fHBvOHcXG4&-)m3A@Lp&2<;IM1w zofn%V>a%e7*`}*Ku;WWb{v)`)<(|IFes-K^d7i1rxB4pESRHO&HHW5lqX5}W@QriM zjaogj@|+@DNFN(7@gGtf>cw7^YA6h!+hS)}nhP%$Cc4|mwb6I>`_7ve7?UKumaPs} z+aOgSqu#i36qVamHDgvNdqSnauHPYXy-;Xc=Qb9>Jz9d>jiOmj7CKBnNAo+oZA()W zei!q3VmkW|-ASz~dbKjWeDGVAlZcVzfc;<_W}yV673+t$U0d!tfpq=;U#J$M5d0&N zXPjOVn64v*Z7sw84}PojO#{;&5vCH+Cy|Hu5ARv;@7Nczxf0Q-iubtVZ0BM&2+ZXD zz3gIVS-M_L-eK*{!Oi>=nqC@M9idQGMF8iS^BHqiPS>%p>J-xh%ki9F2YYDF>TYPp z%!qo?oO4D^4rCwvF(ASq`RThnw^h9g*4#I4(Bv((*3eV$zH@5y17Dg z9&TCDy^j%daVD)U-!$fRMs47h&JO(#QERBwlBuwQ9J3;oj!@`D=<&nIQW2`jU6SB$ z-Z|=`A1MKE5gdar5nIUW5f0nm^UWoJ&{ zk6h-o@wPwT+*ilPQ+tOWmdOsGMdupTxS-E4L9eq-Rz0DPp^|-BQ@vC1>44}bd~FP$ zSiln~;4z*t#TO7HY)ZF}pCvj5a(RrWM+{wk!Hz{R8btOKdFkjSwzU*eaMm}>8(1RL zUh#Y53{RdqRwq^-a>H>7{mHQx46?newqSm+-mHq{q4Om8(NqQ=4sMP;3*!!PJFN3) ze%+DS`NTj zdPlhs=eAn0xl*I1toj;F3}6{b5N|ZbRv$F|ZdXG<6P|4~GI=D?EGh_M!fG(Bn(Y8)Q@aadT3q@(6%M zVo7dakl}D&@rx&Z;cRq03(Z)tN?PUdyThmq#~HpXR1Q!RV^+4)#;^s9b`NiIFYqHg z7rPWjCu`~7`}ezTaW7EVp79L)3!mH1!`nuMZe#A#b6%#;qPpdxT%lSZ*oMdBBR#!ad5+~BcR19I_r z;XDQmVtS8=gVE^PvXJvkzo+H~H5dQN`k}gK(0S==TG)C9@3Dt2vT)<74D@(YHNr2d z^8~BIO2F*?UZlQ{pHfS^%5JTh`mK7kW&ga7tgV_m%hTagTGUh%M#3jy{6_CDvJ#)c zSg>A+4zXO`(7A;$$wXB}NN3d-`~-i|pW4c9gMDils3W|l*xW5!K^{@L4mgm1`L8Hd zJI&8{|CdCbH9AN8nJm!f^5*6lKIl(9R0*rxjh?j)JGCOl_BP_nv2TU&d+gAjwu}tz zj-gC1*M9I;_Gi+!n(E0c&%D)>33FNG%g$d5C7j31(zZCei0uYGhkwtfblQWklk6^g z5bF+W<9k>zxFlanJ3f|rwm#&&1~gS|!FEUA`0k~J9oZM?4C-k^N_O^zM+5$C5S7af z5c6HHajV#D=Byjo;|*Z^Rm zr-csXzwDL*e;^R#`ulej$uy0Pw|D;5nDchl|JR=GJG-H@l1+FGh{C=h zn$?n_3gsygQf)puC-1hwc)yKTUb3S$-0nr%>Sj+UM}E;4yFMi%m|ZGg`d%q2d-lGhb6MZOX?qa=At3X1sQ<%_cYh&anE)5K{0Z)?dan~369@!% z_hssnCV1GepOx5G0+G8ov;h_jf zT*i@OIwezaaqd+dCQS8XCFF0mX2xAaZi7%>%|C8oD^8fq@r-2-h&**1@B#l?yN~$u z(4Q+Y9g%6q%FTx`jp4Jh64R(kKwk^yQd~_m#ztG)wVQ%tAiu%*%Pes?H>c8?)~7fB zofi$2-^|U^omq;V1M^D}^*+1HOj%Xgsejxps7pPk%q*l)8&=vWen&autS-1?LW78h z?V7Ay8wn&2H?Q*-7D`9e7hz!)8v|s_w*oM8B|nc!h*1-ba*sPRVuasPx!6OZb=-#k z+MrwHZ{P5=v(erxKF18oGJkG=5U5T8e#cX~?t5_Zu$V6&twXYI_zi!D{tT^OTH_WsmWZv9KI`WV?b>WV$DbkT~jxz@2 zYl9FNA;H~LvHs;DLw9yj72W5d{g2zvmreb4PN&w|_oetv-B5FFu!{fLQD9(#Yt?>6 zCk`$jgAY^J{PDEgJ4O!bJpb5|>R)QP2r2d4Ew!bHNvEJ9A-i+su-?3-Cz<895fraE zV=(4mp730`se0OqAMrirLzkA|BYLlOdurTe(M*=T%Z25C@I=D1dk?WYipaa45_4#- zy{{?IL3Q`>b}o*>$|3@ANQyo225FnAR6SBTqV;m_`K{*8Ox?`nS6{4#-b%+lP}do+ z6Z*@i=pBA;AFsfd%=`VhhB2J2wTh4KLhBNx%WH5pF?4xo7}-x5jb4-KW$RIdoGT)0jY%_4Jf)aO0(sC$#w-Hy*|}{! zHF3p%*JXHS3A5Y^R|HlC?Bp{&Otv>a zQM>u4cfOgn8^F;oMd=4Jw9RJtxxCJc_6H$dTbVpuSBQo`zMKUr&keAMD$Ztd6#FCgbnv z`5LduT_HW18&5H*y*pcUS)xz1deo`n#8Ls;FjeQ-odoT^v}5#fp51%h zk%A;?z?aR9hI}v|acRQR0LkmO((*4{{GLPs2}*Xlov64iFn0@_Y4(mPoy)rTnb<5Q z+$aM?o=W`Wtrz+&FrHd`Npb)$Z?SU9Gr8r$i9@3z9K!t-gZ^_Ro8$RkK-OG-#5W zypfI-Dz9IU;@G)KDgoHXhSabmMkY6&Gqb7sAvd}rD2_^@3ZQ2U>Vrd}*U6epPh zS}23> zTW*@=CD)CO$sJ`4a^PGdi&1TdkE1~`j!z496d}>&?O#NmBwPkYM(4kKl8q)8; zffrm;%kiqjD%{>DZ1cNIus)<(ngQSLR33k#VVN-6q_@%nrJisFSL6jOHs?u574_E+ z2d}nb1pfRdi0iL09BH5@aL5y2-|zFZ(UqUk?s^fxXx^7Be(ULbvJ0FC`S0x+z=H@Z z=6WAlYhs*LdSmkUTH@tZ&Vw`IA4St~MB277)XZ=s!$P1&nlD?+Rg=-ZT~ydvFNNWD z!kaNkGK7ea{?uHhjD%-w2kws6^2dys)viCG^*AyF%2#F*vQwv82+BUl`wZ6fF|S4w zfY?!k==7g$WZn&stJTJ!htvw2#Np@kD!Kf^yQ|AwY7?aznwNU)NRtf4Q1D{&AQ-t0 z0IaoQx{aWdL3TyX~!*gZek9|HNlHSSiZ#cdmE6MS<~F>h*HMdGPfe;-O6ap z-pk^wV)Nuz6N<=LI?KS2=$9Sy!Zsj8Hbs=aqwYp8|CG{@7=bK>io6yrz3iraO^bxQ zN5FroqyFGtN97$9JyY+!fNIUy3zbA+89L}**oap$-HkRzr=?6IP(Ahp2Zbh`%lc*G z=d>;uZh!Hdd1_G6E8zS3-BR@@ zf_HQdQD+R4vw=bHb@3r8GO8Ub@SOAfZRFangDI;ZjYi@Bj0Br-U{IA!r(cD~%=l+T zw-f!de@K#;wO3S2!&;;!H&IsmOdiqP=6{atE6(@$5#Rh zW?_2u3OI4ibd2Vi`W)BV-PHj0NyfiVzE?vo-Nl*yF5gvnPY^aW}{w3H*UTofI$dV3f&r>r?pelEG$ zaf||*jcI_F2Vm`_pWQG_G?3wa@#Q;)BkCKZhd8-$T8%NVSap@v5u-1f8zUO#bjWT&L$;>rNNL>YOojJ&i!@2k5{;YhH}f2$quHO;d7gJ- zbrPii-_>RWGtn#f3)yr_5Qx-Y#B2s8jLw-WU}Mbx6}8W=nG6M*w#&}$qx4!e)+TmR zv{WIZS1Cx%A`BWjz>A78LlHiluvm&2ne}=|&P45WcY^cf)(w1-W{<=WC$BXKKN9*T zI+~_V7gWLZGHl2DE5UBiqMlWj9W+AKJ2v{C$hzNC&FVocnfoU>@q16@B;7RC%=_68esm)6a99^najqG_jUI{|fU)X0+#Wb$s1lXuOddP_N06w)+5J)Oj$b z&vRdQQj!>*{Jr*E7zf`?FL=9(s+v0-J`_DK1WZJU#4@pwtH!*J}&zPeE@K0R@r{ z@vAaONRv3N41SBpql|;qoL59b3mdBOF=?_2#Gi%wJ8trlp0kn6aac6mKk(=Q$ zmjV9<&4&2O$JsrfoF$+oy^Z$yZf$R)mX={O$}K>Qoc9lK<3I#c(vorm+Vd6s7=Cw8 zEClnuSGpYl&KlLmVT&d*uzpZd4h5?=YAU!1LwX_fFGEzYwV_=I zN{#rTW5Vch!1o*Rmps6K3>Di3eJ|P&n?-|rC}pmZSvc4!k>%LnHwIP@p6N#rfaavk z$}kBv5YfvQ%ZzxkG0fB)QG`sWii+U!$S6BA+@xu-v5ss=1jC|=y*GNa8$vvLLIdU1 zRAts&EX7oQZkBz5`W9E0YY~bg;Q?M|m+Syiwj2*=mWk{bw>7F*QNf#?-BIp{?VhzU zahvTxO+AM^>N(Ja=f20BRHs@<_fTabi$u%^#s_UdB4ol$2b#{;lYjERt@@8; zE>}Pv$Wmw2`Y=*ZV*x<_?+SCmJ68gW_jBNzSchyd>HPs=0l5vkRn*S$^c6QM9S6Hk z>+>E?a&iC5EsB9|#Fb8AemU#J}w-dDuA7ovU%NU$E0P7!?dr3C8S=vfP;gdJh z!A>EcATV7RX_=2cU~s#;;a7k#>NYi+S-VjlN7c{L2^;=YvX&hGYu*9HLtn_A72|~8 z1j6aRsZUfKY5^!qi}8M(NqM*%mNyA9jYiC)Y#b0Iz;%$AU`Il~TJQuyk}9LP_f>4e z`e)_*n&Yf zR?amy&nAYeLd|*BhLD2{&!9IEBnS`1Dqd!Z;5?%~9C}b$lqA<@0~rkg?RehvqBjf6 zYCs46$#rh@30OfG0a26< z3ze94#y4J=%$?;`C9##9)QBryN++3a329srchH3I=$AJuJOi8b?#>N~clA^u;YgG2 z2-~Z*;Z*lN{G){lXE0|^qfgFD+P7qzo*~&2vxM2{5q#pG^o-lY)Omww5>9UUJ5s08 z`Nhc@T}4YXo;V-7e}GZES9SFv-I=toc^lAsaoVM!ZgCz~M?tQ9de6kiIE_o`pf2xi~c{R-YGhiXbIPiZ96NrZ6_kQka9gFCd%xv`a#48VRh|-0+BdiP&q2!5C--H)cP zMjXQMvv@%rXka%K|2|uq%XMEo9%PhD<+2f|F2$0Sfg#b?>01Sma&N9PZ$K^_b#dP~ zQa6=u57?&wbO8JgX&Bm8I<-$0zfZQMw?2w@w+94<1C4W;=s@M>%s7%9u$fa3w7Q&v z$zDg)^Obij9@%!hsuvPDQ0f)~ zl>(-VOc#+^kGAyNRlL>yIgPo`qS0&48dVycW^I6D^l8!8mrnZrX$A%g=VIlB6bEK! zlVm!3A_E2sV z2}kcG?;bv?Vt^wOu>wI9*S7*Ti76)Tqoi7{(I(i`{jxN(WIQ&ZchokOty6#NfYTxX z*Xg%`#&OP#VYzj5<$Y{ zCAX@Ei#F1lra-NIS;oTNUBYW5)R0NDUE^h6Bh|b(Ge8$0<~#Fj6$$JU^10ACCU=LK zbK_-H)n^$N-AvTa?AHm6F|)A5CD%e&J5c$rZrv$HP7n4gM}(Fz(=W0gBAuWUX5&p& zr;BkXR|1;Az52%<5$>m+;2Y;Ae8Us8dfNxo;ajwiEvy?q2L48a0O4&?c#?r&qnBF$t}yEbo?QPLmLrJddy1&!L(wJ;_RK=qv6abpITS z(D$RmeiX{Ol~?Duxs`HQ-To~qja89$J}h_Gh1;P0kQ75%?E?QRG+Z8XZgIl-l)E-% zHX{T~o3uE*v!ALDoIgIOJeJPE8ieeTsu1OD;Vvw<=~fCMaTL%?t5n}wA2qxD85ayg zNO_r^@Bb!30Nmzo#xMj1P41gK7DM`O8s|NNBFu_D7CCmBZA^cT+Z1Nu{j*QEBLl^UhYtmrHJ_G4d7~`*RnY*`N=NO(a(SdyNNIfDw#(%s` z*j3Bce;PfND|`A0ZxMW&Efq4!-=W{&@xi;^tv2XBl*QKW5KVBZS#Tl_8O-WtXz5OdjbZqaEeQI@aI%;=G=z65T-3PYnF*m8P>c~^H$YAk(tD_HqC_wj}WY-VEl zieE+;I$+{H2z-^RZztryxgk8(2CydvG3v_=Qr@*|I4K&Ggl0_oL}SbDXNBBGO2E!9BSuvP^O$Opmj;&lE@S3M6e&&iPB zFn*}3fkBRtgB7P(t%4y1*?x-BIDu8=UcZXVKY$>+7wJ9@JAuHI@p){`mJ=&F*8DUY zXJ5aX+TOq?W8Ya{%MMMr!v=t_+4iv@NbpdUe6}$c?L{9(>DvAUWs!8I7bzE z@pw%fQo3uEN6;)VshDro^>du3#`f6qkY9DS{Tt!sy>Fdir~raR;~|U`2O)%fea`ZeJmBdoTO*Kx(DzUczlU=q|LNJiL}zl z#q@7S>@c!Ev5bADLo=*4&~lY}=U_b$nE93Rg%TcLK8q1??b}-ElK$6dLX^ln!h8l$ z8cr!&1J&c@S4ay{rEMoJy>ahg4=+}5RQ43i6rH8Z4r&mW=&@YxbM-rBm`V#xJ*O_0 z$N0|H{qpw?Q4+9fJl3F7L!w3fT81s`+StUk1F{danI{|0;mncG7Dr&{q&Qmhq#?+A zo6zt1tWoFh76I1sO(_@|-YT|&Mak%=TU3D^YJq63{mzHDezfLsExXy}UVq55I{D4Q zEMpch1C`Jk&ziFIoxbsEj>QE6>zWRa|~#kh>HA6{P)&L^Fja6cLS@Q z#$+jFg+QRQ!`5Q`KBjGj$odYbhf}aL=WR=0wG9}4wS|#il-{4__eiV1lNC&;iB-VT z0u+|9S2j(NOCyEEmR}st#9IEo1SL#~{#!vqY2uLF%FflqtVFMHdP`<0j7D+l6Jlq< zI*5_yw0AZoT&mRcN((9h{>Pv&m)^T5+nJafzx@nYS&5Cs_&R(uvg8X$>3?K`OjvTy z-r*Q%K^tT5 zBqL6bK2sfEUF~H3!9m=zw+!C$EaaNi6kvETnOU&G$%~-!S6lKS$;F~7W?Z!MIlarl z{$C;pCFN#ZYuC{A5Iy=VK^sTY3F6mr00&bBjSlNj33#-3NQcn6lZT7vLAlAP_qCMU z(45-`&5qBP-$7DSv`2~5SW>|QoH}7g4q}A3>Bjx>ky7GwR-ENFQ23UfOi1HweeD-` zGjK866(U3eWye1pxF7gRK#!c{Y zrQeRACTN*MWm`Oz$=sPV`&~9iuEao5AoQRm!?x4~())keXaft;Mhl(*ld*8ftZ;uX zXvhr&b5@bD4o=A7v*{N?e@1Abc5@e9epl7$0w=b?-=-77bb3PeP*VN;5o?^AD@|;; zF^{^-$P|F0U7ovDez~d?J`swSw{(E6hY9q75By^@Sa>RsSJz4|Ve3=B?Nfu(Smn)7iue4csgqBPk)-u}QqJ`2o#_0*Hddf)7AI*?!1 zEEIpysQgoHf05Pgzgq2PmZ5-8ZM0VN1;H{D8Ou>R`yk4-5Nu>g@+=d7kBOlxYi;EY z$wU_WX}np4L-{@|wEMx>T~X2(tL}c+o?b?mS%nQZm8qWgDVzKSjrncHJUUH3MOTPyubtIoVq{qscnvfmw%^@^6ez;y?|T+2OD zDEiZSYT;0KEP8I1wl^p;;m+#HJQBLc`b*1c<~?U(wttW#jw>vU(F`sfJ3yIU*@Yre zi2e}QLFKxpqn;7P;b|6>8X{P9`TQP7(+nK8vyX<*`|V%lv7&_aTEp5l9B_YyC9hbQ zXT?rR;^QkjU^G|1$7jEl^=HP5j0e5DqS>Nd;eqPb{UJ`yj05>Kd0nY zgIYFQicpU4nd-8d5T5IC`MK?3B_$??wMk>>uk=6j6j&+dF*Ytrz!&(QI_=^APY{Btl<8%UeBl#3|WGD&k{3{|glNo?PXW@^gc8 zQ(b4UOuV3ToV`)5cR5!#bkW23e!6$ zo1+uT1=kH$x#L(%P8y%VT1@307!a=Tg#S0mi39uiRjcIUD6}_J-RB8k$1I9Cc+IIFOjMlj!MAP}p zHRW>?$l#N`^KX;TydQwuC&`E&A|?3`X&QY3nD6#on*$?Mi=_}Bmk<*2cSKmV2D-XH zogQ;%IhtEvrUlk^f#jszyhRN z`$NzC8Wcw%X;*>{ej0(LUms@+JjL96?TR+du&dfcos#dz?uSs^1CQK|~~JuPyGyaT)k|nZ0mw+r8i?1T;WahhomW zTHpc7aJK071~8EF9wY_J`Xe4u}tX71fr7dVx?$ z`b{UlaR85W15dFIa2u>afG8BD4jWZHK0tZF6<{t3UAAY*IJNlnAgh0!-#t;kgL`f; z9K1vdTbrSNuV~A}TNuwFzkeaf>h&yDP6~*;yf0iSHLJ56FgD*-B10SpViF5Hk2{mnt;}l1r<{d0@Nm&V9(x6RpaMU|W*%QK z0$0I##LvizJ#5Ul-bX})luuZKmookR0L-TiuQp;4?@ByiPDSCd=h3ynxS#`=fl$U% z$HN=JXMRN*4n&sH>?LmMu?q~|yK|B|jvAg2&8{D^x`Ww!_|xI88YNWt zOrBxCUD`D(W&aM-0qrPxRuXpevoh1`XGXfa+e__5Z^clSdK4xfF-(XEyT9d2Ry7TD z8Q2nxV%73^2`9dvk5}yF$eJYa4<#nm^t&e)N6hNMGGYd@4c8Mo$hSe109wFqV$juk zZuNELEK-U4F%yuJgMMD9Sr*g(_{=M(K*2sithK7PpSRrH6`IigXMX-~9PRgA4Orgw zm`5d@LXn*_N(HM)I~!yqQk|efYDQBq{Tqx*B8mEC_>*52);S&gotB(0eL>ooCQ(&%>@`QE3?4w&w-zcAxd+#ql@KFHe zMS(GTn#AyRMpI6XZLctoUreOyCzm(B4@^K&(>(k?q#9T7=rUyp-PqsRJ3VqsZp}6>UU(E(T6&TZ$epOf9 z05B6sY>wtC=>{zy5^-BUIP~#OMD;@Ud=xE8w8+NwFJANTPTt(U9G-=J-it;4d5Cio zzT$;4it>iSokCp)*L{RtcDO!C)jZV;N*23sr`7!g9hGQ6e?paXYq^R@7|<~-);F_b zU=AH6)w{He{9he8??*`#rXgsKW9nEL{_!MK>88Z$&Nh@rKUdvw#P#9{*<`kq3ngVi zX+6mY0VEnkf5Y1SYtQ}X0&E(bg;{JAkD*?>rV8^tT^=xJWp(Xf2{*7o=t=V|ktEe2bdXJo4!sNt0b+{xF_H%ml%wA9#;9qobt(v-` zXsf5m|8Xz{a#pZI9X3}BR+qSHZvSsp?={Pfz(Q~fR>{fm2`ci(Oxn9+rW|q! zYQgoLrVgxaVKM;IcaY*_rFeb%G^njKpN+MPNzNA-2mn&w2?MM_r`5l?z0WkWY<3kj zQqq4(y*Coi=CW3I$t>#j>DO{`^E%<8GYG*Ch9#pQg~_9v!FinrkP^r0VhxSK5Is(K4V@_~^4LTD`!7i$ zw4YJT`Oy(ZhVr16>l5N~l1tKFjU}bkwIAc^%|H^qmyjOk7$zG43WPC$WS}2Ll}I+U zf!gdQchbbmJ{iB)W`9ub_DsHVV^(;hvm6NR2{U;WbB&%*mV6qqnH>Yn;wm_LGt<+4 zJ8tVIkzAsZz-SsCSmV@5duZo5E_CAb)gCohyD0&QJ%s0 zPj^(z-wp)v1ZG)&m7IptC_241?=R+$D{ue7*l%-?V^oV{)ErWC%;J8X*F6Wk&%^$kwI^b|xiRSQ z?&zC*x*eERI5k5pRce-`L2w?vs1+T?m95YU()VhAo>i3CnXMh|aEgY&CmswdmHcy6 zO{P3iad?SMHvkMl5kR=JI4-Ca{D4rtKLT8)?`WQ?^FOj>ns-;(b4*~eiwY%-xWm#r-F+Y=i44R zFxvcsl$=^s_`mV}<|5Ya0|aJeM19pOBxa_D3G5*gH?P3xMq>k*Xr%9UwUc=bIpQVl z=HzoK)35Rj(zqBGAM$bD$`33;a>1U(Y+lE|T^^#L&$w_%{jmeUIk8Ks_BsMQo9v?3 z7c$B*d_D9LwV4UC3K4e)bCfH7mD-YJJMNl!c=VOKgA8=lu#I##ZVu2Jmo!rm?e_I8 z^knLqVZrYxvVo^KPE_&$)^-!RchKAsc;XMjIt6Cxonf-SAz=l>B9Ic-Vh|hclWo^NMUJH;w(sbEIGoY zLW3TL5X>haIsV%qFai(16l`kEPDyJyE>T2=VPjD!I#BfL7BJT=Ph6gO*?u*zC%nj| z6{t$k;BaU{OP)((<6C9U0hA10<^F0-Mnx?^#IPYTL&cC)+vg*72pvOeO!fZQ@g}&mJrifY78~CzYjm_i06cZi# zTNu)X$Q`RRWsS2YZ5=-tGvbo)=$;@Rccr>)#hRQCCZ-fpF_3~Hi|@B&P|9fCVmLPZ`#&1 zvaK^NK2Z!kcs8M2^VAy_B4zGb7{Aa^gy>|r7^Gm5usXs1pgaQqU4FlcN&^x3x%^S; znz>3ya3#x&qp27FS9UKj&4jeT5TImtHOF0mQ7`~roLWaPe72=C;d)5VD(`_Hg0X#C z?qa-gy`9*XSb|u1qxgX|gjYjSg@XTsD~iL0fBij6e+#=)*0X0LPSQqW>Ox+ih<%^4 z@@EXCS#*X?+Ic5}ulThssp*iu!EX00<25&B=Az*8aXl9YMiZI80RBWChlD_L%3SEH zN2NqRpM3P=3(p6ND;BAxN{!u0-PxkM(L88Fuuw+OOE6kikDxHw7Xqxb5O|g-=U_I( z5NQXBaU=4i;voe6f{WA>7N^KXWpO~*?r{n{^#em9DdFZGXl@fyD6VW2_`8$wVwpt4 ze%o!F8HxlWgAVSr9oCTSZG$-OV%j8QGU(fk*#v}X=q_n?2dpHc9uhCv)n}i7uA!KY z@4Dm7io^tiFYeTdoS4wpKS$@PNd7`I?6{3OVM|VWy02i{;T>N#!1KRc>_DVdj3*G| zFp8VoOdSImr?V))VRSjYCUu~Y5mR{w6+yzMeRPXR95Z0K& zgAFZkH6LRJ7EjnIP&OIF7*_~@zD|Y%_v0mEA;yylQd=@Ol_F8p$NYrX-i{ye>C$3P zB{=TGd5Bp=>D(f*Z{|}WCdzKpuP-3iBQI|Bx7HN0@ApeBDzmil1E0*|s=HvUv1T9=ZgvXf7=S6Op4BKDYOm*8>AWxf z;c@8V)I{Jh+?HzTR`}UQ)jELdV+b0ZTmRX8S?=;{OF=?fw0YKzoA#=eu9su zQGHF)nnJ=+gkECeDk>+u_+I!N+UTQm3}533ls>%1Jp|;L2S@OBfjogfhOIvY2G*c! z=?0JWdH){zLabI$dgEzmLt{~^2LkUW*B@m}gze2~+VF&!!CdZMrhB`fLl6-h%n^*q z5{c0vTXpwmXG_I&gipKbPvxVcUZv%LAoA*EO_OTXaH`-~u+M+@`c|!(r{dwl24PNk zlrBVLsVZW7Z+0IJBP?-ue&ypc@n#>vczJdphdi}qwmIb)b(REYVzee*Ur9Wjm_!Km z{jf-OocGd>_}2c~MjV2iDC~|IC=_LGQ@)XE{?#Zdi!4nh1L(l$DZk&j@)2eQxS)G##=Z%e>$j3L=ZTXxn zAI8@xQ;Gd;offbb0qtqm13FWx#E@=YTpea+N8N2{6o4mU6aAqO1}qzTSmF4Fnxawvn|;4Rdd(jFz(6UnLnh(!;xpX zuNSo%BHh0A3$b@I2_Br!;D5?j*OZarOsU4spTesylrpl?Y<&w1MxV~BoEqmIn^MLJ z4$+sKe-Ub|Z&j0Y!w-_WzBnA|n#lGMSbkY#DUd7Af|{VJ5`&6L1qtt{wbE2L`YbU*aK$*5`d#eM{&_`}^2xa8f1Vrx(OF^0?+ce4s zK<$o=2dvGLjRNlZ2z4BVZ1Oqf+0ZxT?k@B-=cFa)Deu<{O68pce6;MdU`TNLY}CUO zNV}tB&PK(npmqHf_AQwzG3lO!jx{`br6KIGEY?b>$01#$-W`&zr4Y*iV}}mdKDqRy zU+zyI?VQQsg0`1_OR1$Xp{C3Z?pD$p7(mRE?|ciFb$s+IjBYszlRR;)hQa=Q*Q22R z@i_9d!mDy7v3ZbA^Mk3gAJ?jYCE^T8Ba0^CPpHS5FAO7E8MeNVoZO-(*$k(%Ql^+| zh3jSp{Q{IxrB&xVIc#1hko%QG_slSEf5ZQQalK7S7P?@6wakI!#=e z8McL-J#q8^U@qhuBGa(PZmMo7 zmuJPRDCbx{?7r_%g<4U%)xShOVt&|2$JqR_)J;d_R%Vgk5#Gf@S|@64MyLY)q*Z#V zb~se{0r(HomP2EoylWV?=`Dl)?IK(kQI*$l+MZdccY?Iu=o2PnxT@eEv_a<>jA;!g zPVHw81#iRwo1~1MlC65W`Ie3Q03ZbCeC*5IrT2R*PZd%s(%XUiAWppq_ zp*m#Vh8uM9eT%9_v{cyIgCsUzFK=d%^)gK2$I>)ObO28`DmMOy8PEc#d-6en$qfeo zUN`87X5;den-#a#x0bTJ#|8PTtSGS*bCd?Sv}Q1Gx0so3A~zo(gJQQYe^?o2smLHu zo^rU@awvtW(hDIA5jV~qSeeXR9|)ZaG0^!rmEJ(?3p9LrP*A4}ZYE|{onu|f9d?g_ z0zG#vTJx%1Zln_9mQIn?O%);mmrTH3QUzS$VhX5ZJ>gN-&n+a5sMzc;+^e zbbt3$--Bj*^T(-E($iLaA&1GIAEa>7tQSE{CMFLwse8kr#d;pY z?fsI?Jhb;p-(kusLX^I;joNSeqq0pp2P&G!{AU zY%7%?DA;&c72O}7J>ht_*MF41J`ZHeNRrzYrFgEEZ)1w}T!!1ZxvJ9Dk*k`xc$Poe ztrvpr_>$2!D0d>4O%O|E)+iDGp*7p-+2mdHkMpM!dXXZf$onhZLXQ6zePhFF{=h{=@v}YA;G+eC-$f4O;ik`e>1Ze$JrtHcH`(`5 z`9-tV-Tb*kXJBW8d){n7R#NmVJLTBuLqEEHFr7b*{gWOVl~(H(4!={phzn}}cROy= z#sCPAnm=VdE3=T2-DzJ>L!;E{fq>aU#*utTQcBzN^P2yF5Ch!E;qNs-O~;?%6=+yg z7r6vjvK@X!m0)LnccNiv80M4wnF{v0!w#O)aj?4?kL^73*B zBOZR9XZEMn-Nx`{d+%#ULpounG=)Cg=ayV*_KmM~0t!sRQ-zL#=qH|@CW^>h-*;~ZOSpT-51N|M za7FBX{6%ZYwp-z%`k?~mRUP2HK_+1Rk+Q|H-}EC}GjKty9#lopbtnSP^aUIg60{{B zABM4+m#!)2c-*?l;otaJ?GJVOiD8pURQ(r@R_nwDm?d`dez12V;M-J|Di=q-{i{&( zyPDp?8SiznwF}EH$0FX%^iErtQl1HZz!|}QSo%xWvNabT*MbUc7Ww1+zZs2R4f+TH z3;2|5Nk{11u-u%UmYNFziM}s)rWl=0%PJV8r+j2_yw3%3EqA(1FH2(>1wHTT2!=9d znzp)r9BIQ{y_YXlk&K##xRM7fYK!nIv|%ibwt|;#Zi^Wy0eV+%w=n8y*=ki&+M(uG5oX|y*tz)0T!%z*}r)I zFtp5y;A@&oVDq|3J^5YX0ViO&aC-vgy^42~6iN>}W93?9y8(d&XBma~u$mo)^M7Qc zwej_!NT)N7o}J|LCi$2?@@9TjGC=d>flty9_H zt>3>0j?#j#Dy9Zek>odDJ`iXyuFT-wJ(%SXsdut)Rg^0T0x^^s&7PoLeJxZ^w2_;d zB2414ttDb@)EB%92}J|{cu7(+hhetxR&F3SfKCLqgEHlNTXmG&!oV5CY3Nen(*$?O_aR~yUfrZY}b2K}9PRT2y0MzDT-kY={SGSuv4j2bqOF^S! zkELBzU%2+9EV#HgXT#;a4>#Q1HdB0wj=8O}C>_eOr-#GZF-57yVKcizm>VH;@vJBe z075zz9M5e9N1DfjO2*ubkfndxqEryOw|!J%V=5D{O1L`1{spbXSsfx zXgE3aE7oxp>$DM3M)lUtjS4@ZEUYo4`)$hnIs9D1XO$-;6in@(7lX7>q1tkWI;AVollTpBkfRxrH|^PrGNzZJtQWO=_->&&qbr#?@Fw_M84f3}D1K zzGtnc{OXR6&KGHRMOg&}m50Gc%t3h3!9JmotE2Q<(#y-$@BZ-i(P}|;`8vnBcCHpE zN=Z`DTgy@P?Mw2RILpm-YYgWAhmSgeAyzee3R(jjo>nEqk9PV)|B-htedD^L-Eg_~ zK8nEu>rPV+NnGC;ugydKtv(1o!bMg?%i6YPsk`2NV&_RQX4C8CY%ZV{$u<{D4?$OV z-v4!1L_mP3Kc2UP%z!*&-j2jPf^TNENYi#=3;hP`N2-ydPhbsHTfW!LROw!kXv7d4 z0gLmr;mrko+h?ZD&CkYC=L1d!uFRBfTRV)!65`w{XvO6%Qy%U*WWiSw_R%ZrSOF(O z7;IP>>^`ttP{5Y|(IvLGS=n!FC(x}QC8DoRoC_O@?vzOJ-3F0bVnF17bk29dHwo-^ zni1HIaC7oaq$+43zqT(xj(>hdPkVNPPWDr_b(Iq-m7AX6> z?B-;pIyvDb2xKxbk1;29PtF#Cy`wn?YW6{~tCk4_h_-SMe~0v{bBX_&>9MZ!%L)YP zZ{_INar}~LFT5U}ez>qU!HvgpjY_N!*cU$rX9M^6?jiH$(^Zgyulk>4PjOCob2+f{j2zQ6FJ{Ay{Rs7%%u3%c( zI=z82Xf|eQJ zr+Qqb31J-7ORkXM>8wU-nEkLC85y60kNgHjASO^fS(pWMwiO)(bCOs3a@JZiFYM_~ zKStRZnPJiN8dL9GKbPB4AU#~x z_s-o*RkfJznZjYZWK5$Vc>*CJ(kb6W?+eLJ20T78lK&M7X_CR-@?=AZ$)X%q_q~1a zW?{9cjk7ugS}}!W5pXAzz>P#;QOYZEvWB};LHO%T@wZ&R-{DTuKVP+}!-J89CB|OGnR_hgLhu!aMSS|h$kJQIhK-nJd*JalQkUHzE#e(Gk`|{A#o&b0` zo(}&b(SJRrFFs9W14yLwwSG3?wmTreYu5dpR{6@ANZB2Y3pz8V4m@|m7l;E^=BX;x zb`*f314vuXF&YnEZx;@th;E}DIK?vi>sU7XVfwGFr;F7$a|n>xF0w`*8||=jzg=M! z?cxk~#9ogbXNEO0b27b7w|VcZ6|JvSb$Eek8iGU;@7r^O^00^8NjJCeBqhUNykk#58);pC23-B0~1^WoCBGl0;dZt~G-o;_4G?ss0dLC%7!irMl zZUA`?QO~*(=}$k1=(LrFwnOFp=Oj-^gAdqK6wmkVEuu>OM9gRqzT@PxVbgi#W%9Yz zkKPZPBwc^yG$QmT3)vm!2I*Lv&TpAlD%kr?2Neb`!@VKMQsGK=O~>m>gcFgdSnW>D z8I{UFtt6@E^Gcm}cOM=r8SVcD@(~{vLNp|X9Wgqi4f^TLr%Nq^fpUJ9l&N_NM}V2| z-=bav#qvsCNyxet`g}pWI9bUKmF&t=*%4W|caMeSZQ#)u#g^XR!C)A=9JWHO0$K~| zbQkBJ-Cnm~GyBN7f#AoXB#1p!nZV>2f6hs!uewhaW=f{X|EHf&X0EJX!)Co9cbB1LogTFuG_18*#AFVe(gw>6r&!F)7Cu#ma&Z79jeo0 zb>^5DV)swQ`&N`GsB^2|POBUC{aXWugd>#EcGVQUD>|yQjix^{=Whq;B-SRRyljl5 z7*0|%#%#R21+X&XCAb}C^|r@?D~|g;qDoDFZOrcXE&Inn#8GDxE;_EVQ5|=NqZL%e zW4#HL47qrd+i!wpM6*j2=y#hFjEk_}|1A>hw1yOcq~_bfNdr>kHQcbwmCIuxTPtc@ zKYV$v{Xi8uCN~uEm56w}TsE3pUBj3hnuHG9?S50GB0a)m{r`tweaw90SGbiI!ZSGX zs6p8rJPl^*#EmSE%k_TZYhg@}U-#WYKfASvN*^QQ)yn{vk~*3g-@zk(4KSLRUTKNq*y3FA~b1{T^>;46mGvFKoX4 zUJh-db=G4>U4FC>nSPPTjGT@nucVq&l*r=jVf@i)N~WbFLR_;aHGb&RVZN+7PsyBO zK-cK#0GcHZjnJvTOxu~cHa8&7?lu$Xqt9B@E#zO*m~J*_N&HqAiaWaB4H4>feZb;? zun61Zqq6t=iCr_*5SBS2fi0UIbBKWBQ#5b$>Y>U;;trfu zn1uuhw-ARBUF{=! zNc@hebCe&?fmy+(nIH|5XhJs7%PCe^^J;egEdNRKB~NU0@1@xLF9K8m2~=Ckn(4$@%ntVFFp00DJJ3Jod(Wxw}H zZDr_i7}8IgA~LkG-l*28rC~1(CmVr7@G(z<@6-4i-Z4IiAJ{Lz$diP0tUkjf<77%W`fE;E+;n=F9KOUQ3o&^2jrmQShHcvhPA~ zR5@TCaclLfj3U#EqZpHF+a*kE#UGGQ3tM4A>*K+K0^n_$v5asRPmFth4y@Em{84W-b1H&$s-yK1STP^L=Bm0@SH9IC%JpIPq`s4RpU43kz>#enqAKbvR z7DtyuU^%F+SHrM8&l>Y5wMqN?4`Uv3pYeCTri;OSY=)P$eK7zQ1Lapu#64PV(VSSSBM7o(?VLGbdd>$;`KQEhWf zeO%%NoJ@{_iGR+0Y;=3+%rE_uMzhK1mVLI#r9njxF`^iZHeK`gKo z?3t4Xo$N*|(3V(#pe2mJ102LUjR%3fA6|d)7GpADXt7#O;WEKgGkID-`HLUa=g_@7 zJ+8z?{N#fGU}H$&Bx{k;vvV_45!;QFelf9Cy2I_?*$qT;F#5@_{Xo32d~7H}xYgh+ zO|9TRoB);hbuakVDV%n|Y)yvIJ$vY_j+&T>{;J9GW^DCgkg<91iSbbNl_D`iZK7-g zL`Gi1c{t{$q}ZANFWlY4sy&C;-Yh?Tn13Mgrh~z~qm~}PoO#u1w1=3R;{d-w_5T9_kd+Mzlggm{R0xgh!v=O^ol>?OJ$5V^aNPt2QQ zaRUXF-=XeV5k9v&Y4o@W#4ga%1KRk0-{f%J&hJZ1{*`Its~rR+UTKVn#qzvpBP=OE`BSS~0UH8a8x|@f<&i02Fs2t? zj0p8S9NvM?xTzLzV3pguICCxj1@P?d7o{6HT>Fw0fj=TUi&1F}D&SDI2KMb=`!nIa zkEqr#I?kFfWE{7LazDja;D+zFQEmXU<*{}-l9HB+<%8L1Ie*|{Y=+9bqi9KA%@5b} zxPvcgw#0zMpfVIAs>6$1%_`Sy`Tgs9H!(()3#mZ#mlB^&JvngP4oP?Jn?L%7i5wa z9JmcsHdDj}=-`Z>Rkx7NI>yUefSrAL6B5V|qei_U797UX^RWgM#KAsr?3jPqU-_C6 zf*Y3s1Zu@71P4}z4|`drZEe@CB@^sD1nd^PTK^WuF1yKPTbmmYFo`jDucCpUarj2s zyiPbt-sjCU_b&% ztn`?2y%;M|t)d^?Bq2xIMLc^JX01e|Ghzezx z*1?g~zp{t+b&;map$mDY5!bh%E;yvMr+_T+GbGvu?@d}eEfVJrb!G)m(&5~e?e!`oKeO3p7M3#u?w10vI z4CzgF(?RI>@L)PULczD4sAO?XS`E;J)h%kBpa&z5l9r!~C(js{hcngGE%n7_rnt?ht!epBXT8d>XNeYWZtVZaI znJfVhp%0{=_SY92*$=Yw^lyLjXfm-LIeQ=kjs%RpH2Ys9Y)H0zTgRKq{eAHQ{ESNmnyQdmyD={q zNq@q@Bw02rlA#+dvX~_qA-}7gXn9U@itzEo_HNoM^nS;;*hRUj4yNmETYP`8n$ZQB z4+C_QFcoa+&#sViCqMl_Fui(e>YvD<9s$7$_%*PpV`tWM1oIY3h+nz;V5}a*N+4qA zu3Ue4e;R+xUlTCFc!WEX`2(iYM)^sER@_M#EWv}6P4(2GG=MBsR+coW(M2c6aUmE+ zkX)H`43o?n+)ZS+VPtuO!U5zU*CaS>QsjaVglY*W)7$}uUpYN2C-a>8*L2F1Z-^0S z{}#(P&e>uJ&^6u($2e)(jZL3_))~(k-d;c8sLFIT6tLBor~bcYSJDq4m!!|(A#hyd zFwrQaUsYKecrcDetgZJcP~YQNI|Dt}?J&(hzW7gglm5O4hF`U)>>CI|>kp>ztH*r3 zEh)Qi9}97#nC+8+XF}O8rujN`v&+}ShOLCq1PaEP*a#r=V86u+)62~dogc-C$_fZkk8t!31O~1-N#i`s<(H6M{E%UaC!oOvg=^?8<{zd%w>hY5E9Q9C z_O!~czbHAx)rqR?CA`#x$~^LW7WIbM^0 zm+$f6d4aFM2EUGKe7N@c0phsZ{pRlA9j;{>ScD`Z?oYfs*6DvgMxJ))8iz5wS%QgE zh2y>=He5z!Hf($47JAs(%rw59_g~Fd?|J;qkMOy~FD>O&&e2b_=YLUC^g->aGF_Er z5)7ce627t)HLfAomoZl@lRAxu5kbrN*)96^FO@$vQq2zW!54R{bjBjt9EYjSfS0K& ztJObMQ?)1p30TqM12s*_xry2*ELN)eH!z|Ve-^2%^}KS>9dVs z;5l63v1Fj2%l=FoK5*MiwJs@r^o!VcqlC8J$Q=4i6;pGsV4*$LwSb(a@;=sny+Al? zo^WdS_`gu~)@YAmVWQv{uD_3cUHkDr;D?? zN@#<>Nwfzb*}7?F_}8ipt{<@(vu`5c-@ead5ueZdUbm@L8mrf@w z)z5cf%%9|4*v#7_M|e^tc=PV3ZfAM!)EttNiZ7ae_#x_JXf-S|y6*8>rtvG?PeZ*^ zL+RjQ`eas~YP#+d<=RrR!i9L?A}#h1+|!fHxLr284axsc_vSxDE5lIW-)r9;B^`FV&zzmAgP*mng~>$E z`#%vSa6CO~ukuK_{_1_ccU(ImInMn}1AA3Rzi#~*>@^Tf#*^OVojuPJ_sZ~$${Jlg zTr0Fz77IXZ!dcBlfSfa2D|OYiq|rjNL~EmP1||Bk{pgt4gab$+bLCrdV~!IO4iNN6 zznR?qJKF2Qgd;TC=JRdFg?E;@x)%pm$*;ujOBD7!RvEfAazfd}ciiXuuIX1B6Aq}3 zC{Fy^vSSB*u&X8<(AF>wTj>4kRGXcdaDby@$n~nNb#x=d-47eJyIh!Xzz4>4cIplm zQl*wWVQpJJzoe09uHe-Gyq;++MU~YEzX_HBJges08ls^dH9_~zyr!;!#O7!N*#y@O zEnDHbLw<0(=70+m4sfcdnvLom5b1fS)#I12MptLg3SPe|8W*i^!X>%{GCEJ?cQ3nk zT5(Kq`i$zrX(jI4T~HOm^?>|%v9^px%*M8D-C$c!HC}aY+q=&&hkpkC%=nkcl1?A7 z8M%8AeNmf61#{Ni4UKg3PwFij^*g$1!a)Y$X*^1vIz@HfrG^oWVyettnYxHqrLX;_ z>1QzEfDYd3uNv3cFesw0T%juAuZX&0ljP#|H{EU<6AqA}$Vc=iPm<>$UdL-})+Jpn~;Hx~SJ$_uD>rB+&)Vpm=IKYI85x$*oRU2KI za9Vv^zQSEgh@_O~6*_mZt%QZw;E?vjD%FoYG~s|LROuV^+xLlboSSf@JhxHN$w6}+ z;;Hi*l!`9Y03DQl9y=?z_%pQ?UIu3-9HG_8^ObJ)TL_9&UQp`Y#J-k=rAyl64OV4h zYUT1;D@f1t*Fi2V9UG+(l&&oYFSd6P+`(}{-XD^1Pfv||dX0uL$3~_Jy3-f6mk+5< zfOrSg$KYB^HJsMqD%xQv)>Rx=JFR>il5oHqT?1wl4ho{8)vH^7&@ftvV=E3O#lh9Q zF$u&S2HLQ53CZA34mPx)OdFY4BJ&A8d#gscJ7O7BsFRNqkd+AUv*Ii;r3IU_Ha5B4 z64>(rLrzcN7LRRg*r{UnXaH%aQGu`T{+bMGCS%WJRMJ?lubvTJ=fc>Uz=y?YMJ2(wun!YzT&aksqf-8fXr zmJHkM>L2sai8*44?=0@(Lo?SiHr1fou;AXzP+_rQ3A+~GoMaPWy>Mor85!Pfy`hPr z3}34mp+7{}T0)O>6w%Rfh8gB=%4YM_EU8>Dfo8}O(>7jKd}z-8HoFf>@yn$$u#&6; zed_$aTWhG!b|$kXvbs~!?iSXH%=^M=(Iwk2h&)wrVzuq4CDB^;<#k6Gw#v zbqvt<4quK7r-2(m0}O8FA_d0b&<=@JUliw+*;sL8jdNNgK# z)umG>AGWn=+kQWUX3dEXrFeZ{WH@{X|s zgRo@N>T1b5ky1Vq%^5dto4?&#&`~5^abfX`wqxZT$vuwoCLx&^kMbKsNp&HL7&yr= z+UsB%mIw(C0?ffimLi)DrS9v>6WGAu6?HgIE#}D*H{A`DkhYzRqq#` zT@4MIZ^UB*QdfrHT}n|^-CEg&SBl^RiTlsw)cGFd?)(!MVNK+z(}zXcu!K=B1whM9 zG&h{sherHIhG!o1Mj%ve)$bSOeu0Gve>5>=Ww5ZD-=U`FqZ3D-%7a;|F$>3z{CIeX zFP`>O{QJ4~r3X&uqKM~a&3Yw4R$IJtIe3${{JtbXp$MF|euUwWYsKZ|@?nRfKo3M~ ztR2a&_f92Y-967?ctH%G~MC#YzN> zX3weJ`~1h;LS6o#urY1etd}AT8tx5!IpV?U@Z6W+VDyc6Bw-Nf5TjjfSSu~~C$8~g zKbSCRo(Huj{}epX7tiD#5$ZzYGhW078?UAD)P6MVeJMOps<@%_Lg0{rB3^A>-iz&i z*}~eV3P7JxuVoHe;w!AZn)?ix^e0X|5u#Q3rhk&)t8J@%>(qrGv0p3h)A30&JR#Ed z-`twm*l=+}>AH65UFiwpE9VY)MkKW4zF3J+SjyV8=uF%LZ2RBQoVVH_f>OfLUr38E z0P(FwXxmtTo*M7t{C!YuzI5=ZD?s7uTg-g&MKjayfwmvO#7^{J^)hOJny)^IBR+KVe_^JeGys%hY;55+hHDEE;(hp>{_-?3* zdp93c49G56ykLL2U{{TKLc-a#bjqGvXMdiO|EG-?PTLG+^@B2SB5)eGX*A$4v58@Yr^i;qtWas9wCVlBb?vyGAt-t2Q{}Q@9uTLs;>cCw z#vh%U^?mr27Y-GkF}2o3HH9cHW{5>iaMLK0E9lLq0kZD#!)KLo6LY3d{w+bcuTf!| zcUSZiirYAG?0d0S{=Bc~93vbO6bIUQAU^J)0R!fFu!FDa9T(A@v7z2QG~vwO8d?AN zkz8dge3cV7#S8a0%1y#_^|>g2{Oy;gB%|||Ez4UJ?^kiP3QGv4N#%2u(pRz<@0con zbNe@+wa}p&K0GEvoXxoaz$91o_3z0s4W__s0!- zAV^BImkc{n=$AI()bz=}rnWqCC?D6Tq^_u@?5Uii>(KwR_Z{$U6j|f5t7gkxvfO(w zu^roSdK{-W5)udmjsQm>aCDC2qa1gX`{3ZXJN~(&e()U}5WY|X3F(FO#7?u5*l~?} z@0MiAYP;J1n_WqkWXZ9e=5U&yy_%hQ@6DUHJLS!LGb_pU^AwX*S8La2#pg@>rfi>* zv97ypZ5ivlNHL%1njIN3&(C|dr*E2n^7e^- zL;O3O!N8&8p`T0#na|u9T=(*EZZvUNS%Q|APCZ#3lMTSrHwZ7T-d~4ZLn(bpd&6If zx@Czowr0kaT`GE`hvlE;%yn@|V0=q}8xF=JkNEtNG@hGOEDDX7dOQN}$;Tmb#o;VJ za8AgXJoksPBWKiU*`{Uw@mL2JPc8}kq9q_m@Cgw4qTl06GCbdi$GkK(Dpix7kip4X zo&CBnNlJ7os;M}j30x&I7PmB@u<6tAj72yvGOSOfs!(Z_LYU3;RB2Eam=uum`%3iU z91rI!zPKbO!7>|llJ81(NnqSbfbIkX2q}`d34?@3WA3Oa)A(=} zH?hBmx4&1)$15;Cg}2SkI=?u50=}E^@6%sx=rKGI8@kdTRR=~-g|Z;v;yV9*0=<0? z9^!GpAGg4B6n366?fhdI7(HP2i$}M}3B>oXL8rP(HlWD|;(go<2C(q|JK*F7x#15| zKtL$(K`O-}I5y>4U1*SDGRes>dAmi1!PBPV6Jrm;Ro zn4N*9m-R7Qd^ye-5vabFelU1SSsd1CG80l>Zqm}w*@~qLg{a;)Ad`IoYU(SIZ}uI& zLW)LV0kKO~_;ltQ22SK}qLMhn`-V6Y`WLWBj&pM10fp~o1`(wNs zRElanB?4U=Y1$aOFFR7l8#_>DYNRvOhEjamD}Hls#5Aajd)z0fl{3zCE0+a&JD^PV zb^@zYK8#|OG>?$Hi>)kg!o)cA^2t51a1-rcBKAuW548h3gMrZ`v`;kvqe;IY5{_Fl zjV2B%%QSa4px|jQ26%!9mZ#*LY&f%56`kOQH~+plcj8_6S$J*^tA6DQS4P+>xh^gV zTo(z9TP={<*?LcXDo1&Vv&*h3lHOohQ4B&E-E7*<8}|Em@>G}b4F>iqq_!f(LwG9o0M z53W4iCkKgvp3;Azw0xLzIGZlmm+rcEN#JId0N?M$M0D@TeIMXT1iv;j?S0&5IsA|q7o$2Xw)^^&1I08Fb{ zd0;$IlrMK0ku^G5-BAk%LH%y;>GPmSA=yM(SRO}JgC>q`T?m&o%E0$~J^|f-`oKpR zFX)x&X=WJ`SYN_V%3}&#;2{rvpv~1NVRovyJH-SBgL$Guic?Le5789XG~hLXENvMq z2FR$-qhD8)hBYH$)4usZD)NnQDQvY1KBqRTtF|S6|AEjfJ+EE~ARck2<&oZf_MwxdYm_WOX1m6K=eN7umiS;(w*{~4s>K%A$WX3N# z8Q6Qg?aXtvMJwxIC*h=X8UEfj@3s>dL)XjuxzQvzO#t;53{DH}E2}MTLMiE3g=2%q zBtF^>N-dlXkx{P2ZvEKV7xso}B@?zfR_u zHM6WdhR}T1Y;W8>I2S|znG#TbUy2@_zT{=RmdtA>E`0;zDGtK<@xJI(<2Ism7s_}Q z+$oJ@S33;48TrdYf^hrwJ6U^*g``ea$kC+9Y$BWBy0|3p3zvY~toU2%+Wjx=W`nYC7@g z3EyeUq7!VU(Ib2qWL(4|>alrpwW_-H)YSc(CWSxxYmCa6$8KE(__6upBrOjGF%~^T zd*n<9T6AmJh=+A6bYqkRl!r{%V`tleXmBytF9>JGF0P4%2Rd-BG5^tgpJ_{?*+*$n zjnvTDjq5R7KKe|g8}PEmkm@w zZ8b`sRTaf*x&x|@TI^7G?X5+~cj`=dc#dBNhUXcWimEFO(FyoT5=w0cXt3U-L(04; z87k}5(gAF7<`;r3l8|{zR~du0OEW(eUPw40Z_e;fvEu~|g$a=1(PZL@TY|XJ_&RoK570pc<5fA8KjmXq1J01W0I$1uFRX58d8Z_eKS~MvNI?#L|? z7*q!?ojQ!z{TBMphZG@STAynI8+CT=ew^E40S6n^?~d8kSaV$~fZcKD03OC ziI@Y`cP?tju^+YUJ=O8}jvbG6jc68cCIDN8F0HHThOdE2L|z&AbN~uHq@x@z5EY< z+-RC1C^zo;)jnr_z_!~FwM`H`2q`uixO(x5(Y zsbY(@-F7%yqy(l`@_~AzQK{|wgBQyQR2dyHa1H(R8W% ziWR_yn}nU6*3|Eb-QC1?`;KAQ(L}|LCQ$LyrqRT@27_@j%-0vyRc=$#G%}Pm|TNuDt{-3z;5wWC#7f!sFSv%?2%ndFS9{%x6^!`mikK zL6e4N^p;J3(Kr9TL*T}P@`7O*nHC{lL(6#-JOiIO2ExKxbf7Y zOB@H_Q5hNws}=aJcSiJEBhx^c+`|pB@vKADVlC_&IvWec$EV<($yjhEki<&<1 z)b^xLz@hKX3&{si%$k{Ca5VJ*e5})&rYJC@XK!sB4z1B|SFKLmw1#?a*R}~~xspRS z+zx$^2EN=7bltiXEW^SjW*NRRJ;eM#FE0O?t9#gn+-R80zy`9JX@P$#Iht7ld(~z? z2@iB~fc589_}J9ORfKh^HAOA`EfE!%oSI)aw+&nh+fz<2Ke7Kg3=;U}%;ae}VlODy z#1c!z6>bg*j8iR+;d?~?^NyHzXCOKPL6nBn_8Jbmd}SP$is zc!@U?vJ~%?qsL~M_e*fF{DAkDF0r856R~8tBI8p6mO1G7WN#dB#di;RYeC3=n}#_) zHyX!A-%^jK;Lwy{m@_3p`-%;pR2@#!G>=Eu$Mr^R^0OJd`F@%8x0tKM5B%U3%Z)kl zGQV(OM$n(+KKFi>uj>4VYLp#-%<^rE=)tqG_ps_d~yWn7y$^ zM|^O0k*0inZ^{Omf;qgKsRG`2-SPNc7F5Xaw;{E5>Eml5_cArI!XGY*W%%aYC4ZvW zd5c+wu~Ca_q5#b}KwAkGzI_{(RTV~2P=~3`nk7k_bic_&n3DGbDR&NgfN#T?T0aIi7E34RpL9H1Du~ zEeTN?7?BVozQ@7xIb7qpss@xin^il_lyKH!6PP&-96Dc~zvQ>Hfg5tf~_>P_>7lum$H=zXhJoA158|Gm(#O5Au_~3HMBa}VbrKyqE#%j&*cqG`- zuRQP>K0ZWZNN3;%b+u1qH7-qWbMk`cuq|k`fkn5XiHo?A0rV(`G9;~(7LGsYFhSL0 zA82yefT*b?B0mZNJL*URbm>Fu;E*zCvxBZ0wxnsB zb8u|)%y&a!Y)2C_oReR6oN`=WCm60|mjuSI1aPC{4v6CugUcTd`6XEZK7of87)^MN z;Aw{DaNXH6g01O@@dslr6U6LcL6-r#f^1LZAz*h{^I1O)0y))8y?Kr&lpdrzMc{R& zF$51n>_vL?8s)>|iiyh$%9?@)=`oTc{oDx7&%lIgV9&^c8#RNse;Zj6+?!xcoN)9F zV89iy0_5OHe1sbEB0c+!DtBT;Qvfew6dM4n`LT4 zxz;2i1tPZL2namJxfAS$5|Ay=b3{~Zfk35#c50h;74FPj@ue@%n7p1MrQUw*ruHi% zEe!fIHt3Bx!0YEm2<41oY!uu_(DRVayNj_EYKr z{fu2V=*EbwE9wc(M&{s#Z2Ay$vgy|giTe@$<~tYM%p!$%n>#g*$F^2;{Tc{&59F@G zUlSN}a7+An&X|#JEaEV&Y$S|fmcXFF4y z1W2%)KmQgqHy%X|E&8gYfC{&gnV-aq18QMp5L~?&52+FSs2zlRq${I@_5zvVIeDeu zqD5qG6cOqGXV|1*Lbi;|TO(fX&yzR<6cYT^;-!Hk=P}_7%07LSXh8@MSI1d^naV<8 zZXz$n8UW^kDRjKQQ4(Pcf%E{vKL)`kIPsfc9lVJ_{UMl<1t4iUO0>))Wqe#08D9$5 z+x?cpb&*%@Vz=3#KUfp7i-+-oSGg~;OB~y6kO049!9v(VZy(_G3nhc?Q?J>55*Z7$ z)5(;vmEcW!PW8;r5~pQ*qydFAaUxu2drgTGMS=3L%4f+K$OiEILx+7aMo4oSodtgu z;3R%5_lU_@AL%sd)llIqw8t3nCna~fFIy#8BqjVtO9FJ5>HJBPcv$Si2Oq*Kju$mN z%$Re-1VQ&n?+_9~pa5_61O995NF|Wb(Y$c@OW;M@Wesz?LO4H!?dz;zpvp4@IXQf+ zHU}F0{f#z4%Vl!X5%!>hK6x=SE6bRgW6XftG-F~qjbBS=VYzW0C~{nG8k3M~oW#YU zvyH)@aYDg-`|iaVsq2>RMR>-Nktv3lP9-3O&(TlgmPf}x;pRoj zq*CwTErplC+1%#lt=bLY+-f<+Y5608af&<+GVn~#9nbXq*#XO+kiv92RTMujaP=i{zwaHO$Z>Q*;eW{YZq7DC$mP|Ls$sO| zCHCRURxtzI5@R>r%P062iq<$PMrB}VKaxFm&N^e<#C42b#Lkw+4M#Va5k1likx+s9 zeUR5sR)&8!taEiny@05gyVIJkx<}ZxHW&*90jByXOzB#sT&=+0Dl3HI;7{U#AB#S4U!LUOn?zuHg0rx@|IqurUpyiC^%AeVi^L`HBFIJ)?0`Fs zJx}!h@x_40Kji(NoEU|76Abfn=xNjG%nvmi%W2b@U3SE;@Yaj)x>K}_)Df?@zx#F6 z`~Ez?Vq;|`U44gO*-*-*nySCIf6}0Dvd)_6Ir;U_-)+|H-mcxRXDI8e%f?}~a=pi0 zeR{=*-CuN2T@K-4CH_v|v+p=m64EFk4eHK3!aG!M|#Q)Us zk7j-AKg0g?4E@G3ZQci+pZ`AKsa#LI8e|E5M#cRtzugx88KmeL!xPPa2or|968JO+ zASaZ6L>vB__^mJFwHF=R$vgZ!T7Q}-&L@k8dahe&1|t8v=%rDNy{XdzaM{TFsxM#_ zIYW>PTFT#;|4nC-ZYAg2VQo|Fuhlg^6Y!v2`Zc;e!!P+kxaW5L;v6ci+t~Mb?;k&q z{I@@8W91mW=)WsJ%@(Ffkk236|GFXcwIop-=nQ{Vf7(R1Jmc|T79U%}M-?ui1~oG< zo3MGHqoTk8?{Gf^k>#2>7Th1neZxOu3*_~QfY5F;E^1e{|8MU%a@xk_;Ia&WMPk0! z%xC@AUly|6*!78K(<>fNT^4d|0Q&_ej_^;zT7WO(bE{!qGHu@*e~Gr7W{T&aDYp}g zM_5EHYWkYl*3Q^;IG~VECMVv9RsxZC>Ca}7IXQ&QqVGSn|2{`ugxq9){Nti~ck&OP zB+h3rN%zyMKwQp>J~)v7rD8MZ@-f9d>m|u!9OMPMT&`1m*z)fm;y&>)FC{u7a-Gw= zczr*J_KG`&5&V!mH@pN6p_%H~(D%R9`a15`;T7aCM?C#0zXu%VLgG7ZAMH~dVJ_nx z<_z1Fe$z-G%OmZNq0ek3_n%@e+%!zP7`>qq0B@!RXy!5^Pf4g==fC$n^++Hi`3L$U zjY(Wc%sfn;{7V0$jF7D1E&rnP`wd~Q4Q2KMzr3C3x`moMG|QFpOO-%Ab@p4su70K; zDhNxWD+P-d@Ml6Cd%krd_#zYCz_bou`SaRR0N}1|sH=(?ERwmduJ*fz~~wM#=algs7{EFJbc}=Wf&C>=+KPuNBORc zno4V$V35pLpJ_C-R2%9cy-bJC2DMUb48IsCgDwYG zP<`PlXRjkHuAfmC=}L+Xa_SDzV5)-DBXo7Ggk zUEMQLaRFpu$1oA#|!0+R(_tZsv%c3I}*#Ts>txe-4#_ zZ$+iCrk1g3Ci@M?ITa2d%c)D>8h3&U2k{F;#d)$~m|`s|9Kf8?6zxzQ7>vMfCQvrj8z> zuN(E%GEGGYkNyR85nN${eZg2>ro}UXp`n@AQOc_xn(j*Z1xvu9!a*IuV{4QCCz!@V z9BXmhoZE6Ri8Cos?Abw}@3APfk+EufW*qL21}z%t7bnelre77Kwo9?TW$J4VE&5rZ z8~d;UiX0e}fF>BP;W|u*q@)M67i%7rjN^1`7-w;qsViv*J(L~1+BjgBGW@M_WX6X9 zqsE$!#ix`zv4DG;l=*NP3nXLnlueT7cJBZT&3?!ntp5-nKw7wJkx9X`P;eO0Se=64 z5a6uqHN^xLu#A9&%8G-s&Z{jt^fLXerJB1{kf8J*bImL>4eex5ObbV8w}U*pA(9 zb-SB83y1Vt=W8ymQ$P<(e^*#IbvR{!Deq|AI>wRLx>8Q!v}#R7`2lP7!qB;* ztiBQ$ru&~QY_7(0i7O7Y21?myU5dK-agBrdJ6n=%mFfPeg@qfP!Co+KrxAhL`Ar;?Vs5)jpTpCZF-afY;>aYHvjbHoK4O1EVTy@D8j@O`vVP?cxRO^&o zJGWZTc64Q_JI)mYYgF@{8!zI;@GB2g2b8key1B{Et^lKgeIQ%wj%>G?z;F=VE6VVS zsU=jEex2Jm6y1_P@(7{rJ)XO(_R`2PMhyww4ma+@m7862Bfh1d1h+|NM4`U|k4k5` zuJm7|1UM_4b=A1Z*j4Fyt;t@?IP9%J5$5mMT+4!7v|SQ#Nnm6W@Lsa$)}yne_^VJ? zCn$HHL<{DJ2Rc|Jx;h28sJJ9>VQt_Hto$z zPTJ_fbIv(c-`w{0Rhhv$DPua~3kN3*L_>EIl7&ylz)ZIjlu;-15D+w%qCeeH|3Hlj zT{?eXUDL+?p$19YD)Yn}lam*YWef4B*&G!D4^9D%=HV}x+XU4IC1+~wrs-7cKWn@{ zjurEfc;&Js`N6|HZf(_t;%A1_9&HL6{SCPQ`I^7Ehq0>~&ZFpz`S|c;x_$KI9AogI z6l5-?|8V7rx%+$2iEna`oc(93$;d|Oy1ppfHN9N-*4CYOp03zo(%Pu<){+N)%12H6 zWP1u;uI}zJznRz@<|+jE%oRK~p~y`3Dv)%Ma7o~rNMQVRdFE;oYI)<|%g=rSzC)6T zC*vplE`&Wdqp|Hvj|;Dq!x~iF`}5OQ4xXTB#v9%5HCH*Z)Jpq68+yFKUtHs;6;C~X8qrvJXRC8^hJODp`v}u z&$GL=0@B`_mid6xJYP}OKBSs(Fw}f|vq|LU=hxmsbo1Bf#@2w)sQNx>tJwtGHN7PF z2ZcTC%?@N;{Xg(q-)#e9hwM*j5etRPv4+Yjgx;>MIYSx(g5#gY3L7bxSLLJV*J0{x zG}iaEmrG;gcw}d9!`ph##f1EoW*7(kUrY&G=`QH(s(l?QT7KzeKZC|I;_W~$ovP=9 z>bk91PY8TFEi4ZgtNpB|Gc4F&*w?X5%U=~8Dr%`I@bHgR*1iqClG5-GA_LfCq%|AM z%HC&99|``EPe=GWI$K(^s}((*?&Z9p>h?8XdaXFt{~|+Q63XA|8MxEi$4d1jhg6wY)YTg$76)MT8x;t3 zZ|$GY=YQA=PbTKs)a;kS*~eYi`Y-P@FJ1s`H|f(A*{i(`dx{Q&zVXb_V~2&hkmNaU zVug+DX*;qX<$VgX8iu;mQeG4^BiW5?Z9e&QcfcCA#`R~L5K7pvDC>Tiptb(^Z{PtP zJoG^LfJ!?3t2BwOr~1{yC%?c(;0a$X&$tC2FW2+ViTarMD7TLCFT1_gO-*sDILeb^kuXmp+VEw!%@et`a>wEOL{ z?gr8*^$wZ`P~oz>0>kg|c0=lp1r?1x(W!eTrhgu$ezBy!1k`idP$TbprM!F_&uglu zyO^MRdX$^$YAbvazDSI{yRYf-rheQIX(W_Jf0{O7+l1uXn1;uT`sDWW1VXQ9`vQe7 z1apPsK<_KP_{u=|2k9B#CP)h|U3`w~A{pRiJ5s z(petjX>Oj$-eF%x`3P;yS$0(x@o0KScSq}uof1$@5A4cUCoC(yefhyu{{D8$29OJ1 z8x!{}TljHO#rkcf=ZvDv-~^D|V~I&mO_;FS2P+K-_3tP|x6Tjs!PV5)RM&4F08toK z1DAfjcw)Ui7SK1uS3R@0wh9>o{1frvMCl2SrA&G%%mc|UM(w&F%~?^sX63=D?e}gk zIs-X*Xf)1P)=;guFcQm$qc=Gr;|Aj{vPJgW9%!)I;SjVT3C~U^QTW>AB zb>V}8f)(GjSnOJm$p%8(S^6%TyxNJL1j_m5gFDu@=WM%m^{EBH`;Tn7yM=v-jwn;A zlDNG^s>Fr)E0+B^F&b*h7NKvbM)MP*7Y9h@`^qwdbB@lEVgC+FwIBJfiH^+k^@@=A z=A|zCJ`!YQ?1eRv=;&L*B`YOcO?r&^bGk!?L`kx2I2=iz!Bv?N!MxluUQ6SS2KoZ}%Fsx#W5B*(p2%}Hi4r_~(>%Pg-2`olCgc^p+(SSc z$zVyyJk(RpZXVVk;)%5EcZ2-z3}h{AG)hRA_@W=Iw)m|Jhv~7Ah9QdQerKSspV3gS zH;6<~x~{{1+%lf(>_zd?Fgu4p%fq2H8@2ow;{vh;{7A2$zlEZL3PnFK?|?F|xffWC zdt2reCvwl0Nv4BVfdfA-5EYaaPOa!HP|6*i?_yVF9>e%!`^k+B*xOLb6HCP2nc+D{ zb7ZD@OT?0c8BsZYvNe-tFF?ho)yBZ!;04G#JwTQj5R@rqc~;dtqnhxv*^=l+Lzil< zOT@yqj8H(of8r!~9H1fbJp*s?Micx)b7XP3FV*|cR^JV|3A4OJ34!U~OhrE*YF9X>%&Tn$Ru_JiEKBzGof#Xk z0C8v8xTR#EpMU<^Z^xyd+gb+Ccj0@l_Um5%%~wfhJIdR7%T4n=GQck!)ot*d4@j>mS4Y(gKoR&qO7i z3icI*29uqio3uF4H!mjpV|U}%ea`Cdv}{*~O9Izk0+w##+7~st5_rOC~8($Bmc zf0bBtk#R}jhDm@Yej*q>Re9kH%z@9dDbaVjbAv7c`V#9O=#~XJpyA+#1Ruh|aBLu| zI$KcqghWp3GpCw=%_AS_mS8$t!$@N@6i|UIePtgQNLqhiHBbdEc3$EHv23=9GSAa) z&^Kv@j94T=crgWBK-=b2wg9&)L%n?PyqU4`V`R&xRsW?Fznvg(cHR#?&{Ks1CO|=$ zC8N|s0;*;&BKQs{Q+3@aAO3_e)VkdLDDA)->#X3%Hd|67ooU~t6gmoNlJn);6!*eUZ)HbhvW{?l#_tdi03wk@Y z7an`7U^g_P`LkAkA1RWC%x+%QeWs&wRes^!d*Gnhi3fj`n(0tg zAxKAPo-}ghnyO(I3(W%yTn|=m`KHwb4!|uT#sum5aY@Kz9TLbmYEe~!1r zKyY7@E<-!3zUMdm{QTAe002M$Nklq2vvu!(Gf>*A8YO)mldA#{&HSJGMjVp(TaP6$jCtyCjz5kgZz+aZWzd6S&DZi!5W~sKGtnP5Kh8dyYEq<3_=B*+ z5Y!(pFF~2hyHdUJWKgAex7H4rY^^^A*0VX_LoxVr+Q9IALeo%1sWvGYziy;rpb4QU zAJQRZK};|hyVO(!Tb#}OlL+(+T4RjZ>_2OhXTEHEPvgv>G+d4-SrW|)sSM0rw4m;* z)N-XZ3Y;xL3zg=@M9l9>+B8MCe&gZO>eNgx44r1y((nx;JDNa+n^k&WL{KTyh>aJh>RT zA_?$ezuOT-Uk_tw=_-Bcl*LZYcm_^+4xN4Ac-JcP$&+q)l$2M>jVhgd_ql!h5Z3&V zw(*YE_-)qbn$V5+efIgi=98nV%0Kt~44(ku3N8uUEE1sm+n#M#RMUj7K%9bik&&zl z2kZTsYNJta)SqsJ^GiaqV2aEW-hzyql-hpU&}k$zin6UJYMxX3$4~(XQlzM$OWw>F zJ1Jv`uLL!ezTH0{rW#JRu@E+;>GrjtyCco6 zp*V~1M~2z~ZG%=@t%E;JGwHq%8D8m9bP|MK*SD!2Oe@|}+972^3Sw#N+^41c^_qRn zAPr74G3>5w+^3})47whjs$7LF_F$xkvKrb7rxtcJ;HisEm9a0VInezLQwvMXRW|G{ zFPz#s^wSPz=&Scj^_;)sd|SDel!_%;m|gRc+Ks*CIvT2YxD?JW$;<_PgYR_@Qhw0d zuh45-4TQRL{}*V+O1x)ygux>#2K`*qR!bRLHO7eGaCC0VGtKfIgHfTE?6(jG;XLRpUg6tzgQ8g;L)>S$#M zt-AihBM9XL1{xhwCIetPS5fgpuR$@;yZJmw1B!>B{xAylp_IBsDAr#^P`f&NBg8y!kx5`x=8f8ga*H#o1XF+f&rYuqF zSj(=WQfX9Uu^ilw%pR+3sZK7O`U|6!vRJ9t(o|JT(QmZA5BlRblj_O$+qlmpUaI+l zjbH!Tw2#gSF;}un0@qdomPLnaTg>RG3%!FI^5<1@h~QBf9)}RCr=450>l~XJ_5S=g zC*CHbFV2PGl7LGBW03$i)6m}vCO&D>(2PEz z%iajux#D{;LZF4wGBD4%Azn@0F%)&{bmuF+W8uz)ZbQZTrgLPA33=jri17cZ^gsu&!%QziiKKSz{}zS8u! z)x|HC;_{|Tz=p_(vCW4c1}5qE5l4td%cZLKEhOT76!H z;S0djhT(9q^s;7mQ_yB(!m`tf4`w%lH^aiVC9?|WoXtM8^9v3hWo=8sF%KJapDj%j zBrh{z@`CLLrvM#rJA{|Sta);{l%nEQ*d8|c=3z7sRK0#Tw``7YVXxi6J90a={hZ3~ zkVZ-KE0RURq9Xo?z#ks3nIXVC}Ot`x+Tc8Pw(Cgo}CEo{axbJgLlMY z>lxoWU2t;YPbYD5b^fiB7gxlCxsnX<^(;BLGZmkQjOI2ceY`YxF}FGb*VDVFa5F@l znVo<4qSpiV-nJEU#umqL_D>th2(3g7a6Im_5J0PH{Wh+e;>Yrq-fwH^#N=JUC4n0+ zf$^%v@x@_(SseBzO~XC`k*QIkxY$+Au^=;AZ5&L5?#!Hy#mV=b)Vy*4&_FN0ku ztoMv2z;~`ki7^`LE?AcYZZZidPE?`vY+#xQ4WBPx2W^w*LE>EoUqYIB7Lt(sfQp^D zm@3B$cxLIkhv1{+Jkk?j=0I}y+Xd*k73TdC92_s=%XJT!P+)3nN5qohii}GMKv(Gs zJdy8<8QK2@W;X%}36hSah;LYQ{LAL>JipBLWECpL-P8ZeY>6JwNxb7O%rER@b_f)~BT3V)GoII**_kXF zcd^jKw>+=tlVBqPvSR7G*^+*m5%DqNn~(?dX*2G{whk1T%Br0l5iTnmgV_Y{r!yx# z%up0y!jQ=-U_6JEksvQ3`)C+N6MT1ojOM5i$$?Q{21e@@bq|r@Uk-lJz$`tiL)umu zAxrIeUXdqpr^E%plH}|q5WV&n{hO5rnd9g zLhZDp5f)MStjSrlCI^)i?>Q8`_$hclaWG0{Y2PhRLsY|^`Qk5Uv!_|I8D>$lH|9(7 z!nF^=_H90CI%x){TT8}c&GLKJn1Kf=NI{-CFs^B7nzq8_OQ-j(HAAzaFyFX-T@tt< z5*Tk<9M@HuMZxE~JljMVG(4c2uunAOxPDy{a7o}gNPupL1jIc%8&?CHf#9;Fr%+go$q;E^i?rVeT4r7cvVVRCw031^mlFsHLG^Y|+ z@P|a~syrnBx+m~?4l6@iDJ>?R_y}|^LeZhQH9Kw^xFL)>@XVjg<}leJ#`3sHA1K`c zqPonO_*Lu(jZlmSJ{!a?IHb(D@toC`vN=^`keJH)Bp7ot-Oj3u=38gKz?juo;|$7i zId=T5tBlL`(!_gx6tQS01{51JP-T}hqDVutEz!eg>6Xp?kpeNZELyX)8kk-xQ z<<8M|dUYopO=_HiJILUk9SnkyC&FKLu@9rRvf2|E-Mu>_g7?Xt!P6VA6>!%PQsHC> zFDLhY3fQ;QB1})hblUg`sc~ROln1!J*#@E2OKu&vg|ff%Ku$Xo zG2uosY^%5O*8+TZ!e)vtw&Fx&%cNalzNEL@I4FOyxgUeULcon0L%J6`3fKRZU^nQx z5hAW9;TZ5FN7~$+y*3^^r(z#T9T_jbkRhh6ksw|N7~#sOsOwOSxd=w+6Z^UOX1xAF z=W`{jF+L#^|BYQ4$6@fkf6Aom$QI)kkay70&b74k)i(A(jwgQ1!94Ed&+EwJRVxa* z9d3mJMt^R0&5ih$sknfhraCMS`YYkc=2{NXU5PiN1VDf3XZo!<$CELFo#X`|*tN-v zBremBOms^H{7ezP1|yk_kXV!B;yxtSC>GE&eVI}0(*+cGDFJJhU|}*Zekj?ik^&;Q zQ?xAI4R;1nqHwtS5Z*H6pcas|R(e~c5W`r9nVysH^}FqM_a#E-t;-tTIv&OgUgf^% ze#sqyeAox%IDl%j*6;RUzN22uF$PY(N4hF%2&>lMJc7K3c>(>hDQ#k!U4P=!cUpI&S9oPd>WHE%Q$Ix=LitZ4Q+V#B-=LxcZS;EWYe@ge8K8 zpCw957bIu7PXLq&?)Y&`6=p{8!U#kx7R??Z9cwUuLJ~Zpz1-GF4&svy8SrPxh+)FH zM=VJ251ti#HB>kY?J-6q$Y+T$?;*)upvaAu1c-Nm*Q}Wy;5&1f;6r%D@uMySG}dgX z(RUs-ozHH2fzETG!Ct3LMA4H8{Ep2*Q$kSS2+LVQ%nl;#I~-U6nLR8oK7I)yB=h6fkjthr6Cs|#Buz9XvB{1v_xY>_MUKmx;+}Pj=UgrxLIlPL zqaYq499_@Gp$I!t$%IPPU7K>VdBPsT_j z>^xb@`1vEh7$Q6i;f{A2FAJ_P!OkF)Q{Y1)jzSn6m`cYv%5kOrVkNLfuoScyu6nT8 zdbjXa5o=8$lW$BR~)^${j+M&&7fYFGZo6fA?90M{4tSkoCr>3x)HN4Vzeb|6fbjV<_ z3d$L!FEKieLeCgDL!6q?cvuOXLbSD^VC2Z$w+m+C<;Es^s2*E65ts}ii0WYwCflW_WWU{cPc@^XRq?)7&82&g1jjKr6Mam@s zmjqlA7+wP448=KOSP%3@_#O{yV(F#j5*}W#whI6Z(=GQaGXmSublu*h62MCt@HV_d zd^(-Unui?NG%OX{J8<+9#l}Z`)-Dw1mh)>p821d?erdLy>bkG)>30(Uu-`pjP7He2 zSco`_z#??6KF34t_Qk*t{fx>P*p;M15i8vm5<2ur&&L0a_#2qKb}J7n>Hal-%ZHM! zC7S+^+1`%2th0iU>>gry;lRZmx}Wt7Wu0}|xIZa*K)jkDh_AFib{qQ~!h;{(GU41+ zqJ<7~oh8;VjbBk)+87&Ft%0FC$m2~}!&_#Z2=NV?&)ewE*FAsxoBo&X;4S;K@BjWw z@|zj%HVza@Lj6bb|NX=G2Y?4PVdTG4zwvjEKS&7&{jbBK z6{!D@^t*TQmiLMkrvw)s9JnKj3Wdb?s4ttD_Q&}5q!adS%^@A@#NvT+rBSuibB=Z5 z7~-Gze+O9WyzaE%ZlbmyrcN5U6ALV9R>;9hus^crY7C$1r z$2xtO_$6cg=i1G!#&&BMw#t*%i`Fz!EswUo{HpA?lih7B2^-yhD&yhRy%=M}uXM|Q zVty__=O&`02bk4>Tc9awrrPfCvhPn04-|Z%+{`&uPI1qC(eE*?&|+hSLa+R&^^0$# zKJo#N^zjs$4DIiBeljgED~t%9&NCU-_ERUe)B82daG$FIIZWket>fQeR~sFpzqjG3 z7lWSI&zvZy@i~01@XM9}*sC8G-TN>3H;2?GOj{0q*%fxx?usPO#Heh|=L}TcRri zfMQ)aTsz6`UP3+;zK_CZzaA2u8kGtMD6pmVps-*cb$l2V&e*Gv*O+#54K4bzDNw{# z?Y;x56JWh5*W&TDR^L#nt#XLCn>lKy!T}hhY@zr6sD+u<=u|ks?Kbv)r`fq#x7(Qt z2QUW4uvNFWlj(8DlC#8}^r2x?IKTph>L6WE%`{k3fXQN!p`^{&QAamIdW9ZO+RAnK zY8}*8`x_e#EiHz&lLLi-%fa+A_;L3#_)XZ_zXz^|^(PAi=Z8_@0H%}dg*OBuV+%)B zr*w?2NK>JL&U zuBi&AtB`BdxN90==wbS{Q2WN9!U25-F((XX&(bBAElY(|wM^4x!NvyAN_T#z+RnAC zF1q{w`hT*cb3t1MDjXz2L#sE8l#OlrpY2pQpukr7%o(b&}ji4ClLj$ zfU)2lRo1|?a4dlu_8ql-cohyHL52lXVWIJ&T}8U6URXNWqd7913a8V{OI0HrLI|}+ zUOM(s&yKw-xTQ6v3Jn|?Z58yDa;@4X#VT`*QGK;s(QBm*QM%szficp$E@_Le?Jv_`YNPPtdyS!?gYK@-*PPIvh1+ui+|Bk8iV{Aao%Vq1 z+mKMBZ-74b5?$9$J6*mXg5*lLxg}sx;h-8YLVY#xBaBuM$5SP6yMn70U1q4DSnCS6 z9TTob7|4bM?syC>sT-0rq67nBQV$$L>$e>{KCgR(gP%hRpvVmvHIT*4mMDfcm=F1| zgC9VSM-Fg1w1XV^Z9k)izstf847&zA6kY9vRXa%T6cbA#=e!5EoSPHF9t+OB2^JWL z*gR!8%Y+RUTuxi#t|}vzn1($og##Prsv*YSQ)|ug(J=;if>4Sj#ZschIf{E-E$P=T zqJaj4ohtE_^Y9#%8zeYLu;`}upL-DC#>O$MW*onbT&}Y#5TTu%_vH*_BEsh-AL#-gCP_x+wy+y9_8#%jT@KE#$RXj9j1fZu)}ogM3}f+-(KCg#%R+b zV2##}KKOxKQgp`4^wk!~VLBYBGPfF~2X73kVAoL$K$|uPE(_i(%Dj$a&cVOdN1MPc z*V94-;0^5SJ*xA5FvV*hxc&_q8rPFRfm-m^7;zZ-+Hfsb>ttBr2s8CHhZp^<7}8|= ze<-WXw;$gW@9#bQCApQdPP?OavAE9j7HScgYthP|{ zQ(>ojhE(Tr6dmT%-Fb%(Z5@2&5b7_vv(H{A4pDDh;(&3uVV!7fC|Z3+IYd61ZFMcD z?LAhQqVm&gx{Zi1e0}7|gzjOqIb&%{{pR7`hbxnZQL((}>|X1(pl_a6SXmAX8~YwT zQg>+-t8C6WG4?MnD%@pVimGNEYy@x|Y^!YSop9vHXCv(i9aFSQB^sNHRu>F?OjaOQ zISPF~cVd>2F>)t!YS~f1GHP>)M#BCj0X`^(g0*Y|qG-lJ?p0>gMQ(gZfV09`^TLhD zSEVUhlf8y<##@N;1lR}E62b7zJO{lf3~sIQ9) zmjtd<0{E2m2;PuGJJg?671!C$U@3X|dnjW)_*3!x59G~VX@7F5H75?xt+TH%&f|N) zqOaBHtKNSB&6yT-mD?$goC}8zn?$2NT6^}E<9+DNjzy=6KkCp?SJ@DBg(4X#Iv+OS z2lK5&$02IJYOHRwecZ?>|Eyc|=Rh|e!(Wu0wZ(VHJ2F@%Ra4vWn$7ylVBXb;5wnoH zXmEZ=)OFOL;P_cM#b{@v8+&mil!DBqX!Bbt?kZ8D(&MYksz2)+Y8SJuGU>G?d(twH z?bQjS2wG*pkf(HYh`Xq`ByjB{Fn(WJrlzy7wkCz;%>B*IUQRd&GW8(P`kBPYqN*cI@0XY!LHaF(Li6 zq~H*w-g36EE;jdBKaMH&mh-3UBIi8oXE<7V?9Ou5by%ACx2)8)KIZw#K=YHjgdG9q z+v`{vS5ce?^yz%elg`adFB#C%3KJ|AYFk1wt>Gm%@ieD-3MJV?D`DrUX zsr?sEf_vr*r%oRe=tE+szJe!|#)8^5lr=F9BdAX_bi}242lJ_;)B2d7WW!7#`nQ%Hc%lk$ zq)$$n^t%vu_%bzSzyW2VxA)TT3ok;66MWG{Bw2(DqK*~q{}r9}`9$>t)P#4uTb^%J ztqX!LLRbe7MgFfUL53jPxW7zHy2l&xS^2ny%DRi*1TWdpiF>o7QU|pWYxHXoCkB+A z?>Cfv2~7C}t`CoSLdxnHZN1-!s^6*ySNo86ro=CGqYqaXmm~CAX;}f!5Ewq;d91Jz zeci>!P|7=S2$0cO*WFa&pAzp$c63#~HsG_I?0LQxJd%T7%Zgp?CFp1`{XH0A&R_hU zOgrF}xFNz@uWbAK#mddtHWj{Ma?E_J@T)#9Yl{dA681LzpmJN294TxlJ1!22QIx#~ z8)XCHK1&Rpj3uM`tf=r^){!A8F!A|>AU4rXIQ`pd(Gyd=;A`?u!oaTDGtW0crqB8b zNl*BLA_{Sbl!-n?^BWb_d$lOp3zZ|!d6*(yP+W5zcBt=>2(5}E zhg2DzZtaeaiw-2|{MxePZo#3dQam#ZPTU;jU0Z+Vd5EL%_ov3>u}=W$<)2-;@Ge$B zeV<5AUhgMl#~5I{^7$rsE?&uy6Ab^Z;F7@2C4uYLyNymRRNwh|r~8*@EGU>e`&r-i zZAx|sfz`m;s;=Ov+j1v8t|?x6u6LAfSEJ*>1#Kq*ryaN*oa^>KMFQTd(h?_XE56aP zOUgPTDS6J}MJo#CB?VtRo>dQyG9K~bj&}~UDA<06QdT`$?U{|wx1hCdkzdYRShRe> zkKvWi?Z0@|>=shdRE*+dCb9$Ue~J`c_51}(fNp#AaG^SS?wQ5&cgFJ%cB#znV-?>r zG27APAw zJ}Ej|ESaR*UR4U0>P`qr>&W!H$KNlCmR#tuQj{0*e4qoc= z3Z5q6i5TsL*4j5ucgj-oc1}qwttfb{lO1RE(2!>yn7`zBUP4G&K~9sIWy}*7n99~t z6dRr4w4Lg9a`ANwRIN>mnnT zi8(py4qQSDGi6@57IZ^P*Dh?1Vg|IW$Lh6K22?HYiz~XY`rdBYLR4Z&P-a=R=K+1%xB{CZ?n^$b#Hp2wx4|j%6AGb z?DEMxFmuL%OzHc@1)t*84Nx6H^Yqy;DQA0D+ms;G+SNQ>_;vB&lE8nU1Vl-`!84?=e2xR( zFZQWWF`^ZXS~4IU4g+rrrBNdl%`sg(=k5bCHf zZSd&bRD%8(=LO65uHcfuFG2z|7*HgnNaF695uCm^UFyNT+o5?olf$ySCAX(for@}p zv`m0ZHW!IgrP2(EY$8}=5YgFs=AtBh0c*O0`v7D<-&2oLr`+5 z4xOp*EJc#6H4-63^TLDUZ$b4JwA|O-6MkAT^OfkRM`HtnjRZ8izbwgF=kJ>loUknl z)w7E!eBY=if;|w`->4&HA<#NiW>v)UKosAWqL*Wxw^O?B<1VkiOo^E;7RE`FKF>gh z3c8deazvS|Zv$3meH!bZ=;<>pGITEDwzmqClhL8$Tf+|29qMempmyj^DOwO6nGM}Y zzg8{)9k{E4+faw)Gw+p9X@*b8@6v-&OZRo0CtzV-+Z^jF<{o!I*0+&? z6Ft0F#HY_f4HxuAe?RFgBueu3N%i(i1bh4Wu+9&e;SakeCZ$B{afHJSK3s_Cx5b6^z_Q(kioMqZ~%a4md$Il&^ zP}p|7x7*?QF1A(1mNb8a*~J0ZYx48{UP;~|PsBh|$@(_jFV$0We_{aYR6uoTMa!2c z;(33ukfw!V>7Rw@XE<-hsQj`Ey)-puzK1y4D_|-3Epr+rN7Hq6N#N#?0IM90LsmsT zaW4fQf8;o_n-acSo%Je=5-*m-t}~jc$2vOa4Z1yo`y+5cVPX$RO^B2zB@Q z|3Q%-_)!9;@*!#K$42{!1Oi{LWXTmG8@`9(NDSmHauH+nHv31Av7^aNZE z1=BMt4M^ZEMy0rPrn2^@dBv>%XRx>zS|`_#F)$Yhbx-jpDDw=UI+^uS{Vf+!^26{W`enCxtrAjc9fD{)0paQCt1{HLoxcCIZ4`R=Lcyn)p#MwRN%we~ z=2%$u!inxVaDiumD?;iAA2(6aT8Cn$BtzEV)4>RAFi+I@ZprKkT7XYsS;0S0)s=(p zsBHLMNq;%0z&>8dULo^LlttcVQJ5&<*8l<|b_-x5-~!q(`CJ`z+^eH~{MgkrI}Bu0 zJkjH`!K7=lNVXTczoQ%lXTS^%SIX$`1(fyOh~PP(Y^do#Nip80$Bp4RO^lovoqJd& z&+jV!ePijGCU9>{L&61YFtgCEEG)GMJGQA&WX@e9P9l>i#M4kd%QiMkx`ImrH=hK|ox`}}obUTc_AL+6x-PYRTk)BDOZ_UdVPhNw zbDyY&X33?DJKkLkxg_wPCxPBg=g=c_?s#1aRVQ1?(v6_V;o!C~S&9zVZXw&Yp{Z|R z4UOz<0Tpg)(VFlOJQ0!PR~%&_^(QX&py{*OL^i>7aY^8&mw;Pt{L1EdL#I|%*l=Y1 z5x*I$V!2ted9JJ~KqwCy0v~lE+RZ$Jrv{o{K`DEoHJMW`D+w!faXcUfPTmt|GelB0 z=b+lr!%dM5if@b2!?`P80n-E`+fc>Vpa18tzedh-+?JD|*q4nn2f8Y+af?3sna7Dxto2izVUx>cIiL1-qZ4LsD1jKaZ(tN5lF>_XgjvSFAsX(<4qC&3uS$x9Ri{c1q+_n~In6h~kX zb{K-x>Bc$~H|au{2i}CHQGW`iE1N)ew3Z;BjOmWmOAN~Mk|m>>OH_C~R;37~4(4Wl zUW7x+yhs_UkZWlvTbxPt;>siZJ^fZtvR!^D9|$Tk&MI4yq)A+k`D#^VbJAsgIr$nx zJV8LgALRu3TfekM4`92NicZ38kXC!}E<@Cn?UKNFl7LMs8b6FNHFYOnYE$-+A{n?G zz|jaKA9q)LRHp3fqG_F>Z{x)l6c!YPS=Rh`YDZKVcW^Tx?ugpaP=nK5Vf;#9aPS(x zJi0g=l>~S`2%YGc_cDgo-m2FwuxrGa0OR35@o9AN(ZZhPrjsWb_6$1ZB_*vkDE5^d zJpi*22)fq$x}(0eKG%e9eR%Wt>)TBO@1>gWgErS(a;=zRVY$N1ApuHWc3*jCrJj_y z`%Z+JBxpNa-`66q)X_Rxee}}FgD7PNZ29JS-iZc2=};*Rx;6@x)>fhkVW}hUC~eS* zC|NMNxbKB_MX$lAG${91l>9K93TG`$64*S^4RwaxlU%RV$hVe(G_d9e zU#j?_sAQ``Up?^u+4~N_D30sz+1=}>a;m4^1?nJ>K!_%qDFz#iu^r<|f)gAZ$8qe$ zb_!|EKXx43pPjfRIPNAuu&Ktz^j<|#K?M~gq25k+y0YbavwOF?JKc!`1D44SVsCcl zy*D$z-6?P8&8)E%*8Vy!!dZsFc(`I(fAF)Iv)y3islvC0N&{oGLIDr#L|E^{PJv3g z+H2v{WBo0!7hgij`UD(0zz8>V_lcO#cKeJ8=J&-hJ+&+9IxA^1Na3FiqE>(I#AE=u>%2`aN9(Edvu^(@_DWursF zQPt5^U430NXP`~HYpOQlV@`Z=L=Zg5UQ0d087$qa%iMK1z->!4dSHaZG!%;@Ly@Jj z&s1aTuf%_*dZe9;`k!v>g0IP0%Pu`MHmLmAJKL!s;!8@3p2220=83xI~8h*~$6 zS8g@gYOLn={@%0L3Q`$_n|h+=;-0KMjWzgLyA;`39mAO#%5h3<++=jVsJ63)W=%Fr z`K1dlbTWukP7<=s*y_9MU#)>x-J8wmP|~G134w1F0oSJZTg{4d;$)DY|0hTGeqs+x z`(#d_jO(XBohv<%U&@E-a<-0gm|-}xN`46e34t4jfY1j+dI9%LjDF$3R~g5_A9EX6 zMLG8Kz-rr_x#-nX=|8kI zr6eJ6RR|~oBI^#$I4|&mMP+OXLRI*#c=py}J~aOInJFXjJFlIR8~K}4J2Fnewyp_@ zRj^~tc~H;6mQ!J+j*_Er4|T+ueg5{yar+C-O**{mBR&m;GXDb!;}t508i~UPWC~Qg z(&&Hp_{hH=*_8v<(I{#@9B3hERrb{_vi9O}6kRmlrJh1Qi*Nl(kO5c!zs#MneD3(R z9S8DZKo2f?;>lzstUP&=%mqi?@=nT^%XifR;q++G#FYHjL1jviD>!)AkeIrw`Jc9f(a34vjY z0AF8+U+%czu*}-|nHGkj2?{)`d+z+(bHTKqF|m}!9te3?-!Xpv9q@}gHX=E3 zer@9LE0B~U34xmv0mG4Mlr$yyks2!MqYToA&uWTcs! zwXizfN)rk#P~v@K;HXiFd0!;vS=x`?X3O0kDUMV$iwhSnhIq&l6|`dVq6frg#Lfuc zpN9|G&Hq3zgR~u=o&yFaMvR*)6gb6e;3!q-stHqn&9eA$6C|%qaEY-~&&7gp*2IZ{ zaLXk|rJs%(NwcJkzBH;&zuIL7n_^z!)Ok2ZJUjlyH;4>cJ2i+0lhXu5t(_WS0d^9> z0&iSs%DJgYkOW1N4y`r#rKtrxn{W|j;#(>({T!H2fLLwh8&e|;G^dbb#+M)t9u_I^ zvZpUJqLgvG+9?PTqNZXp7Z>vMq@tA^oT@4J2i1vJz$lNBkrK|UJCSH(2)PC*(*-GH zW^~%x=yXe0*-|cbU9hN!IncxJk86<;a+J=wTrO}?fobQa3hy59My#3;@eGU0=J-M# zr^FaWLfJJk{IGDL1e{%pCf!H~3}*zywsBZy61qLuG3(}Jh{C;s5dE6tzy=vd214ERsUzgH&~J*AmY$}B{2%MS#qvl=O9)&q1VE3%J+1h*+tZ=2 z(&8Ejy_<}p%T4;nCd09TSDYW7-I>cn63)@y{4zv(?qn^!m*U5)fYHBC4(j;8ioC$# zO|p_w4DRcB7DiAByd>z_j9?<7$BK6GmT^uhoEPrv6AXG3EC&v9Bvx8n9R=I5NWx$+ z&rXRR3u=Vx%iv1u@lI4ogjha_D;5I0gujgNl)9yohm{phB2>twleBU+fuNtm`y~hhKTY9HaAtIU%J%L7cm)7S z{*@4}ex#<4HK|=waTew5SEDN{qcg9{fsc}q!xMxkF+?PzIlFy3^paxWb@d1(Qu5`3 zBa9&}STpuLMHqbnu@cGq5#G-rJP4(uN@@vDgM!OR2O+R1b~&mmj#m@Hi$sO5nlbgq zG#MNin&!C@U5RLA++Xvqgx;N2OK2^G&A=JlBYeBp5SJgQ5T05*-^5>@N=>@d2!9ex zm|UES9H_s_L1SYVQl}eebDZv6yijC@z|}Av%O*(TE3-)TcDTouQ#-QV(c*S2<;nGG@&JdT)7p0=zI=-ZG@Vu^3tsgElrH2 zMx{&`CEg7yk22;jxi>UJ%Fu!>SC*i1{`8`Ws%ziydC!H0^jgcjGY!OW{vnCObrnc` z=g1KQIlb;aP8pQB=DMdcOcH=LpXD`Yk^rku{KBgEUO?=%g+P+?Fouy4G9{sevz;lz zL+mgqpp=bki$Eah*TeQX%M^NK{02mEVPPmZm&x5fmll%$y;fwD=E=##sHE)LBL>Sqpt5ShDNfDo^^4vzv zC8k86FgJk^vWS{>hiZ<3xZ*w{lnlO0ee0ddxuGQN?(^*AgnXfLb~phWg|5KeN==I- z+_UP;4_elAYC;MT?;Hqf*cf?sB&koN;vhT?f3*vxEAiFYY0*Ic6eo|4mPZz7#tJGN zkOdR?aR(FlO<3qZ2d+hmf(f?Kf?fx=9$h#a3~z|XC|?4n_>G75Ym7R3lsY4xijI>- z&r?rxV+6G0Wzp&K6df6uB^&7$dp&N)(>WkkxatV}@(WiV(~2P?6iAjujLe{ufhCT? z##8xZb}SJ^AaWKt(MLJXWD&BkSh-+??L(tS~C{ zUjN(O+GQv$1U*!CJSx$;F0ir;%GeMy(H-oXa14(S$ zLT2U!ga_`UQCcpC8~-SoK24G2%p&+747d{%Il(_(oqMZxy5NsQVbIcoKT;kpgbbf+ zAlQ|uQgdY)@kF$cDf^q$0={u$O{9c1%9(K6sba! zm@JRiQ_ztnPgLQl%v>98W`j6gHjPK8A;`msFtTtYm+K3-o|@RuIrJsg z!dZpFo49^hJcLfy7p|L_#)@n(A8+LPwVqZa%*f)lj^_-81zE)Tj7@AEp zXALIt4300Z3-#dTI8JtLbM8yhzR+|s-GBh-&%X44z6b<-2Cvci!YI*52uKM0uOT3I z_t06x3Y$=T=yQb**LhIkjEs$=Ax+rmMf@=X z-b(dX(>rHuWxvp_7)d2Q)B1lu3|Kl^VV}e;<*L45_9hvl8*I&6`*uE`{LBu`!5(73 zy2hJKF&ui($(B5_9sRxa-EQUz7)1$}^*}#;Z}1&K>cD^Zt$Qu>cX~4P6j5d*Ot&CB z!|moOnc8>FYn#|+=Wv+SUZ!4hLD9HJb_DcZ=cvp2bzI;=dL~rP&tyBgi5`#i074Nx z{ubIpemv3(fu)wpSKIzoXKnCC14F0$|F=hTk- znT4a-^nWYXmyy-?*%oFqBf2@mqusyyIOtX2g(APAdQZ=V-fyul6!egW|5Ls7OY@Hc zS-(%ZK7X%k?LT6k|Dt>M=RMn5&g-U*1V!u<5z909%TJZ6AVWkVc+I2Oi`u2RZ?{!!?Yq`>m)I6(0w{8!Z0v2+%-^)UWZ;Z{2!AqBju+%b zx0+ge235dqDoVJAzV*e{H%crOnaYu?!ktE%bdKD>thyQAx_!SpidoY#daa+NnHb zAue-P`)E32G4vj;4#rh~)%{1=RA{7qAf`+h5d3dvF#}FzU*KIP^Ucp`?x8lG; z;>Vr;-D^3@A@;oW0>n0Q&DE&hjZC=RX#L7fg@bSi`7izD*Rlgws=@&;Xl~nO`{2); z3I{YGc8Bah2iYy;!N)78PeZ71AOuvM&w9QnG2>yG=wP%_N>#TLoek6_2&^R=;aWpB zR8n=I!h!#Q>;zm5+<+NwGuaOGC1eHgLKL`e0&hd==GFE8_G|Zlc=Q&N{lW%vRM-cO5sKc8k5{w*UyJ za3J=u>=ayKc3-^um{uLr2(xRuOb5@H%Ygl;sTfPMoUCOUz;u4AWw(!VoXOg`&Suo+ z9CCI39oBtcS@(&&<#hEI<~`R{g|k)km`zt;pah!3$NisOg$f6Sf~IeK@9w>ZBVqx) zdHRsC1`RZN-pH;lypeACsOK|K;efq`ZvUWby_=kJwi;A82s}U^2ftgax7&@juiaEQ zpoAiWkahjr8t6+sOz-YKm$L6AjtMFpP*$Sa_s#1;g#&+y<@{k2e!ZccFSAsE$IXc< z9E2aUoY~gDM<9T9quN>r{w8ay8_TzTYe2?Y)Ns&vtkPU7WX4L^wyE}9R)quNVp(a4 zywa@*FtKGq=3Vr`AyhcRVl|W*eeB5yQ@;Tkyl>ypma-nR#U&qCT2Eb=z1&!x3_wL(W7_}-g&sExB4>rxht=Sg1Zu2gF9a`e+`n6L<|oEp!eKn z-VHrz3)2ocs-YXYQSVOn5cs!odq9N)Pb1N?7aapnE7Jj2HwT0E!}Q5|s<9n)x8+1BN@1Qb71ZzM1i>oXa(SRKD+^j1rP$m zP5UY^Ig}`GNnn|zT|Mwf!)z}py{pg1&d;L+P~`gAeux4OPL5!_>0>ZQ;!%PT`P8F$ z;qCW@9V{3~cWd;;2Z!{2Y;tMVkz`j_)FC5DuJV9t&gqH!L;4OhG#|fH0rWGD8 zctl<>27p`PYT&nB3h-j;A)SwT_9(+l*k45(K{NbmI)f3DJ1i51)+as6zluPyE(o9r zN2&-1miQ=5I2K5(gBA|I17^O8f~Sx5{5B3P9G(k9e3Ra&uzg$7!A>|? z`x-g2_tK6z2Re;cCJ+?4D_w8%g8>kK4xUe$Fl*=!IrhsIAT{-@2bA zaQoKJn#GwZU-UOI+x^4JGd2fPHW5IrtG& zf4Vj0i|y~&A$67}`aQCXvER@`SbB;V9BRBUP%ELH?woh1%SaGRcX3K_*P!BDRf=@? z>_Z1PIiuQJc3-bAt(bM&!9%lKhoCgA*|%%1Pb|gF1Rbgbo=+esH(WC zQVwFeY~}}XNCDq@1fbawPL*Q`7T0>Wzh8R3*~h#Y6vJum2aSohvE1rS)&5@`i|p0sCdV1r^?@H zx4B>=kPKYOAO*^h58S)^=)UKS(I3v4T{UO+>d-4VLBpxwZV2=1DDKHz-TmbnBB|e{>dXK+jTxDu{l8Ijz;<)`^L2~$%lmF((@X5}1 zJ|~2!r|Ff-GpoULN*BE%Y2;7Cc~zsa{Udex?{O7~dkoAf+5iAR07*naRC#jx0xk1( z`H2#Qo;y{1KyHgj81pQy0_>r>7L=Wlj6pCRZfs5R^N*nD{pU~b^@~pKKKs*COw^6#xV z_eM9$NFDQbQdk76tpVBbQobT<=ZzToY$61L5Z7rH)1{~AxFPGQX;a0%CoTY^ZpE0K zpN6X`+(dYkOm=nF{PtWqq?j3qN>Bj5*^u6UX758>_FI{zA2Mlw@@sysvG0MfASX2v zB!j(W`x~XlUxDrGIvrRpOtCAOZ1tszpPjA)JW+Jcrp(B6(zfXYSOfdNQ(XK!7yepq z_{H-l9;y-ApM*bUrQGcYXaj~P%cAmE;>L<#`l_C234J*k6lkH~gAfh`vcb-Fw!Ump zDc+?pWklZdi6K06z3&`4^cQ}=ZboF*vzgHac--YJ^MhqdArb--0uln=5U@{rd5iwm z!Wl!&T~)oeB#qvjll?FYy!(yb&9&<<^3(axSkS>;lfYrG`aZO z6|6-W^UlP{2WL$BII8B^edkX)=J+cvokxlBqXrDOZd_rclq3YMJpy#wV~5U|;tTgr zo4g@Gwx`4Busp0nwl$_;kOWj{iwTF!;+%P(&wDRQh9rlVmB6Zx>$Seo7ryu8-sSDyB!g;Koqx2vF9A}V_)JVB z6cCpeF^x@VN?gVA$OF)$G&U z4<^qzIb-t6w+i8*O#Cpi_PPBP01l+v-qiV} z(3Ii3wykX}Jy1Y5g!u<#PjTRac7*F_5&X!(4|fkzU8VPOJHrBPBLD%IqutXBj1 zM$?2wjLiONP+*2S@P~0BsJ*ArBexVNAs``eBM^{}2n?T~L!~$^j?%?Fs#hbr@1m6m zgKw0z8Y~2tk(h~4`PXBR*yf#ntqgd+?{kIx6fNetOrQN54$a zdLAO-R3s-M@ZCZH+ua3`svxB%J0fjkx=tfZSVR_VNRAjCpj(tNb}FhmWoE-dL#HBj zdXO$#7m^M0m56L>E-uk!&2U%=hQb%v1!qGUV`u&`Iw(CTdLOg5IjpcT1Z=qb)3i|!1p8-4B&~}6Bsq;ZJ78qsGQEkSPm%ASo<}HCOdp z6N=`!HW5%VMY=WP;q$sKy3lUK<|c^pq#&MVBB^N-BaMu`Y^kbv-g9XR;}lc7IR}am%_It zx+{NjqPGO9mo6YPFm4vsa6qMyYg}eo9hxDM`KyKL7*tXF#k_J{kyq%|U0`+SzKaa% zY3XW|wI3GML7oQ(=7>!tC?*%{qTsl&0MG?3Z5(R$AlX`d31y@N*w@)G2tt;K(W$o_ z(s%A}KK|5&Q;TZBHgfFAoX`M42gYKFw%!&LnE*-{?-*Lh3~e|;qtb?R51s3U)+jSD zAVVLcz+4cGk2AxLZu`}alb<5(yx6F0IRNC_NmT9+h!^F2p$%VJHA|uS5}X>;@`@TO zF~VGxbEpcT@Ox5pL7+Fo)8@Qn-p(ogNC-#>NC^0dz%bR~pqMWcR;M}Dx#04m(5gA1Y}u(lR-_=0fMJ$<;m8|97T19?{IA|dddM?f(td3IGY z-DWYJsz3DTp@4!piF_Z%IZih*9!6*?R0I;W!*Y}TK34zNa;8@zaJmn3gAZnY=Ki_TYCgmX-%?ME6ND?=$6sqX&>tI1_vCoR97=iKFxMR(=!?avC6htX834t4k0REoyp+5N56Vu#P z^GBG=w-1LnO-RP0sN$z*yXQJio>bx}V^tq(vh{skd1N=@4+^lh{-7u7edlvc=+>26 zxBXbyQG-x*?Y5+?_0>19NK#S~0^bY)Fr2GeR@G5qBZ8IwIamde*{j8+-U=IIVfy!1 zpV)&&6o9rR(=JB+@3xtGZI)(OO{#^Ve^k29Dx51j5g(#Q<$HhCX6T~XUYqgjit-K4 z$M+9aDgv^3*j|zAo`<9K-8xfi+Yb$q{|>g7!!D`WP--$(7==Gm7a1@g8K0={0MkKB zfAi{MSQHBhV?9bH!=Qh!t=njzd(FL@DgX^ZlQVBPTeitytv2_!o4U?+g6g9R!d<+1 z!_n<2<*?t3Q>sJ2T6=Y~sf)J2nIcb_K?ef@b^Zvn`atoTZgVe7cbU50DZA+QJpjJ_ z>8pazi(q7q?m>cXsECh1dmCHexiojxKXJGPxgCh@`hcK;3q_XmhQ8`Pq11hKj50S4 zZ9U#ljK5)I+EfF{Mr6v#5GYVje=W=Qn!DaPjdeKC;=E*@kCZMF0ulle0=^>PGWF#v z)Agq#bQ;ZPl_j6qQ;Pmao*%{8C}?bJ%jVYdq5g3n#QX7<64yUFQVJ3R5(1tP5C?PG zC1WFAKCyr78K8@p2O~4bejbDZ7iFM-lqP-OuA#93VTHfc9eiLHSQ(+BNSO0yl!&Tp zEJl$N^PxrZedOvnOH!zWz_39;;TLuB_&ofkg9D4osAQOr!2Z$&U-SD(QOwJe6JhxX z)Q^c-`rn`1H|88{dmEEb4?EVJSJF7GW2FuhL0(gr*?`F zA0~kh6f%bziNgnE3RD^RM|)?k{*_Y)^Ur}_?^KZmWe)1;X%soZwVuLeuaZT31sQPF z{At>hWz%!p_8p%j=uZiGhvF58m3xxR21QKyN5bAm4_pM!sS(f`$f5CoHT;x7+piAn z0;@=bj{GJ*bHj{i*VR8rCw6;Q)9jP`Rs%_7By7MHj2Y9Dru~~c_0Zux&tYDK62>gV zdB;1_cn9szgkJ<6dT7rDxaCu1_$5#>H#$A@Q1LvhXGQ%q32n9Oy+Xku&=uhzK(FD6 z0}Z@Pe1Xl18#5-q$n3jw2cK&+a^1}EAkbYCGN0UMoY7PIhdM5ol%Mug{w+vbhTpBP7poM8NUvhhyGuSe`ga6Y+OZ9#|7I5D53lF}desXqu6m+P3AR+~0u`Ct5Aa!v5bJtO0;F zvS8Zm1-uXXJ4!dYYP8VziR>~NzuzuCJgsWZ2uK2wRbqM$>2j*(*g5gffJ}UsoIHCY z{zVb`-1Bn=D)Rq>AWr1%dHpQ=0zePK1%W1F zuw)2V-;y?`B-IKhZED0xAw*`!<$NAD%Fbb_W@B(%oMut>oZHfEMvmZxKpfbY#{6qe6o^&I@q-Xqyky=*oOB~0AR!KFglMQipmWg`{UIyH zhXjc#oCjWyK?1vT1st4&M@F|m?-kkF*BqU_9E@Uoa!|+JMi|~CgEl*`cJC^ib>7qF z8U=TNtt?=m9dV8yy6mp)&j248;4LGVW-!77oDQFmCC8op*Q3}WGVjJN-AV{Z2uKKc zL;$z3a^ChyML0HAdm>ag?4vXUe7zITi#Z?bL?C`o8X`EpIaRO!`DTx%4v}r@aetW$(ky}Pn`OkL;xio ziZv9E1`6)vZ1%76pnin6ybDhd2I0cX03mAL0)=q;2C(pOAqsfq{N@Fc-!dUw{YgIq z+lS%x!TFeHkCIfJy_c19GK#>j;$XyV96nDB97%}gFham}xr%i`0FB`%p6MY6A8z@O z2?cS~K_!9T0Tr@WNk7u_+vq05?G~O31%nQLBE&7~o7}1i!8A!0^4!F6%V{zsAZWx; z;~wAAQbas2&ZPUs;Yt-q`q7l7i|KI<_VrHdag^=KEz}X$uxobbR&fHb^|*^uI$j0{ z6Tov^-z;4p+N(`QaoV9hn%SaD_19Uj$+Mqjr8@7%udY9_Lca? z(kUYTctdl>DMQ9=@T{cW*n&%`NeEnH1fbdBV67KMDIAQu>;q8^Qd?n!#XBWG6cm~i zx3q;8RSt7L+>IFFxTP-L-gpEY<1Uw!g;4>10}_cC7@*{%p+-@evJpA*bReE09~Vvr zhfyI3@)%KGFrm{Ep)vA^I0X)zW-bWi+(#iIu}2TQl+!E)^EESdgN832>8~#Ep^&?$ zD8~_q9H~eG3DHDEGK%vk>Ndr!C^Et=3PSFX-7zZ8zqRH zNly%6VQat@xbFqs9?!$>jR#tRg@JQ2Rs7xy2NARy8BA&@8yv&HcXUt!8x7Y)HYSyc zPiK?CKam~_SASwaMT}rWL%HBg7RK!M7Dvb7#StnUb%3;uZl(-!IG4y`*SC}Nj} zV4Ik$bEDXFP&HGygcLSDmWYTZBc{tHx`_%SgX3k<>GG6NSx}y4lw0gIzXd>M(1jqj zfX2pw!4!Ui!qvyL;;CpkA)BTt%u%HS<2VgiNudJem;@?@M8rI0k&kkm$wGQ1`elj^QWJ1WbP~k+`SLp%$;a>H^N5Yl{QC=&TH__1a z%?>D<;y>OSff(+Gw25e-(G$b3i6>g7UmCbj!A~mbWuf;6Ep`)|MkW`^@*r@cED!u{ zvEFWPlh24G;h1+92So-UcdF;c$RY!&fFeJavQOdibCA8QNJR+`s1|1`@olDZWPt|1 z-qG@ibY+T*ME+)}b`d-XAv|B1Ggm!T@I$+ij6Vv9q7>mm$nd%PBW(hTDb(htsuF}; zDLDCD^Hg7SxzTh+rY+U2EP<@Q2C1gW3bW)R-J+!1t3v>0q9i9Fa5EtQz2{t25%i>b zI3WkdU9uP*3Ybn#0RLQK>O7_hJkgwf3d#phI0c9JxH}lM7s$rMG0~wY*ga0VzZnt8 zBeN$^W5qdnn0viTHeb-e$MPsHH1|NnxmDDC57P$rj@e_ zlgegfVF*^s+2@RhXKrsmt}L8QNyv>{zt%B*F0Qsg>fWBUkTK^s$HG}}%%g|b4B9~< z0}Nt3PF9Jy!TQWq4DLmC-#yMy^s;>cjuEYo62#Y%MKHZ4)>c?EWyG~UA4Lgd2Equu zL034RGB^og4feXOL=8@Kc!u;6eF5CSyvB{sa&@DxHrX)ka>NiZIxJ6yvtp!<~7f!EEh{Po!AR+L-fB*}h-f;|O#HK?W zfSEaqiZ{#|3`IX@u~9Y!D<5o&G?-Wlxrq_L{Z=Q{^Ro8cdFG-CR=fh>&Hc^tuXA+y z&()8l5{W-`ld}}6~nrqGc54)ukU4eBItY*w2CWSJ( z56qt#ID@x5e3Nj{KW|gcVvY10ZSTFC_)4@~e~KvWL#A2CeI*A{`MPiazq;0RGri6! z#>=CB61wahQ?arCORK|X&zWEde;5_KOnaAFt^Q2Dqnq%I3=Gzb=z>``5BV8FaxK~L zA8K1Gd~C>zgEpd&8?%s}+x?!Rn;BKO6mELaf(r0Y|qk*k8O>AA9!t??dk46$50VZ20S8&>R`K;xBTKA{R<(aZmnU<$A-`T z7XF)~)R{fZQD~dok`r$uiLm>rg-KpkK}9leo=v8&-!rT&ROS6F@CRTy`SZ3vKZY~*7z0wz__wa;|^-?O^MauslH znokaJ{!#O@%XRnew;pX`ntvbk^9VAmhv<8Y__z_ZF6Zvb;#`(e2Mg_C4NtcGK2jF( zhp;Dw*wdymuyLKMEgDRGMImo@eGK$J3|cC3ZB_3+B0n?4BNi#Y6CPMpS!}w+V^)o1 z|L>Kr{l@Z`mQ^>>Ew6XI`{%Gjbv)^`Xl01Kz;;zdkClFL~_3zUgE^)2S^e{tEK#jSLobi(R9Z<=hRR183 zfD@qx+?(vj#cUbek0g^GqVD)azNMD9n83yUz`6t``_5U-KX@aTa9fTeB>Pda?P<1a0gXRc4U}&xM2A!5-Yq?Cb|)bMH>T@F8-s8%YD*@=Dh~tE@F{ z;nJ;yfP{dAfPerD$>wRN|3G(-kuyDR{@p{0C6lPUEvB9SF>W?-_GTwm7WshRoiFtK z;~Cw`Y`NRvK?oY#Q|8iWSwyTna)Nq{1nmC;0`Icxk1!`WzNl)XUO-KsL1Ggyyr5A} zHNuQ+u|hca*fScXOeLsr5VxN`-pX{e&>a^n^&)8}*WJpsms0oy;Gw!P^QsICL z#x7gA-P>(@hfv|z&YrF+`{BQ0(!P6OA#tPSs~v`Yu+~x5Ujd8_TuTkQ=uyI86%NSi zY>gjuuU}`{dZj8H@U}6XYfPKB%lCRy;eZBKPIkx+bdcR1v*a!COY*=FDjaaZXnQ|# zyoPLWmH;b1NBU1T(3dK0c)(FXUx4d*+l6!Vc~IfN|Al2YTzi=Tjrt;U2?nx9ET^rU z4WhucGo9jfqwy>7@3b7;#T*`be1C~++rsV!;;V2p$AAh4j0erf&sxvd(hcxXHXFZo z6V=Xk*3pe+wklYUs;27PVz2qFg|&hT2VysO?||z?x(TkeHW$E$PPE2$5uV7N{)45q z3K$0;x10g`lc=NtwLsn6MI0WQ9b&7kb**&AX>$pv)&g-0-PT~cbXmsB5^U-F8vaDG zT3O>EdGXMXSR}qWPye|#W1oGk_39*C5m&LSv_xL%k);nAAzZlju)Uy(c_i~C5T1pB zHMBGKr{l)bhB~)!yP+9`@D8qLBR2rJKv~9CfeHtHxR$ZLW&^YlHx&*fKsRuRIr%yL zRWsWP?d=iUDIp2rx|2ODsBmz5p1rW1_ySZo@Wb^Sg;Bc1v{vwqhCnY~MHLQ0j`pA0 zV%#Z2HE}H$7%ZWM@jBBa3b}@!7OK64>HvY|HVhXu7lVa&(>SeOo9 z%WN51g#$_f!xLPIo6P_AZ5u*`=WzWR^!!Wo#J9b+|=_j`zJ@9RC%-QzLJvmi&P#cU|Ico67_zfQk| z!O&9)FQ(*%I|9&qZZ_^HvsSetJc6p{nqbZC-+gOE6%GO~(wEj5x4=C01)GO4YNfSy zx9Q+{YjqRdGTcim8q4Rq_fBi~wGZ?p0u(tTi>EpSJ%k|` zq3nZc-B#g5f?jT5`@R_HqX(?)j6=@v3Ua{C(?@{}DzH25>%<2%He!~3~5{QNOUufSv)`cNt zxxmQdyWPk4jJf@dy0#X|jmz}Tp%b6t-paDEcz5P*$6smf7CW$R^!8gz*U8M? zmF3X0Gu`t~9N&&#Z1!AZ&06t|Vmd(D>dU-BZ26_CJ9qCMvv=>9U6AzYS6h2cH?&H6 zE}e%^rg#2{6OM1Yd5GtO`j%ZZI+>|ElMxp#o>+VYJ|m6>phIkV?e3|^PcADywYc7J zs>umGXI9UCqQ3FO;zI_5p>*J>iH~phk+nk&ay(0P54B#(++9=YKF>uqH=WtxLAYu4 z?%f}G2zQq%-QQRQOl-ffb%Nz!RO+Dv0z(PNkub<{%bL($fq9fL*l>%LZf`yWOq_>i zU(n*N%}#(D0~r^fKt=NJQjTv7F4ra%58B$b5iCi$sS(gESa8o#PN%wN?@#xp18C0t zxCr;L?oExNRJi{M1UUFc&Lrp0ZVRQhw7y=Wos;YW0n=0SHcHtHhlH`n(uW=#oqvxU zCax?;YtKA=m>e-ZF4fEYlrkV~O+FQe>mxB4so`s@pnhMds#`TWafy%u74TVJ9(1PO z9Z_DpcX6%!k(|6Y1C>geF72#YR(ffAWI*;Xtd$fL6Y<6LSRl5tqv4IJ3ovJqb28$= zsL17!fo`90kO36@_voCUqfgYLNAimv$A<%3Hts1NK+j_$E`g*H&g=~*DZhmEd9f5J zGj$)iw<7w5{HPHG%dj#%#9p;yx?VVV`aS#{ z_P6agf8cR!j^Ve;nOgEo2uKLrJO~V5ortSyDgS**#V!NNj2y8%Gkr0%r*gErwf2qr z)6X};5k5Izj7iLNpN_hDo{Ury34vjcK*MtzkGK1UXJr(^>4|jTnWdlYS&Qv~gO`k- z^ip1Y4AJw+&OKF`xBXf#6s6~reLJc~EdI6LyyMu$TaNIyAo{UyP0L>%>KNMeRX*4~ z=0GfeYi@e&$o|LA0$Kc$iR1r1Rv*d|-RGA)s~am` zYJ-7(>iYEPG{W$DG2XWL^V7yKqwLPnBW7a#iaXlaiZW6X9j|esRvU-wgf=NE;Q4m= zUNtyoSLNCAsL?C1rh%Bg`lmVrUyKVxHp99Wa)n=*1_y&9TbfIr0j2KL*`I}(O#vfc zjYCHp+Y?j$^d#BRcK)}|k$3XEMfGGPh+ znjX)njY#Y&UDXVTrL0bi8IL=CqO0fPAIhq>n^C$yDnouVFdJd&ZF#-6^gXar3)DXn zpA6aSYTLcAB;nPpr0w;xkCWva&ULnc7Ke#FIK~cLM0@?g=MhS)EiE%DXU6vY)&;!) zrKNjyWift&NVjrS#v?%r$S99Y`O_C6|NiN47l|lRljesb7#lQyii|*QqTP_l@J4BK zTuh|0vu>S1b$5F5G?{rzxa_l2NB=HX=Z~TG6iE3$m{)KQ-8uJ!^V*H!^@AtN z(zE}pKVPoTm~1Hhw<&5Sd=UyM#wX-&i=j6hJAav#SDa1BNeD;?d>0WIu38Y?b!yt- zp4&&yIXY|FlfliO_44f>bXV0CZIOjrit<wdeorkihP=e#!XMBFw zz4hP!-p;fWKm7FQA^RLZd#>sb%E`#bvrEQLKU@E}s-X122TSix*3~yOu!!zH7X0ba z(8&+9KJw7f=@%D&bbx1Uc=Dg?AGgdodf$CVZ;$-*SJhRHvF}X+vs8@lAOcKJ>HM>X zd1-|^$B$d--?7EWzv7@;)z`L%=d8)kdc;(@xUAbmsEZPzcv1Jn#H~n;7$MB7G953q zs$U4f`W$w0RMOdbx~qFvOZ|_}^rDf`Y4{DN+grMKnC&$OG40zsSeihzwPS64fA2ug z$cE}-X9Ias3$DGK;?_ zgx5`gigy$#S90{0Q%9ffO<7$qar5ZVeP<3o%3Fuh)}HNcHP19sKN~mh^@N_6P9C0n z+B$Dc-kLQ1_VSZkumVB1HFv&OQPmhcc71O8>`P^f4;@{t0T6PYR9{?Kjjb7B&C#sT zzn+)3b7KCxp%+&jtt!Up@{DDrB|*9Cb2A?7s9ILp0~u$`7bl&nGR5Y8HZK3EDA193 z5SWgioh&uQf`oBvV&!|gOcsLc0z1-3{mfA9Y(H&QK-j9nL>@XUda!dq$A$*PVihPn zW<_fF*kD)mxZW2I7H+}st425+4X9f71IPKgB2{y zj1L{94Oo%{mDUFRE-!%^5G)E~df)9)ugZ!UuaYO~;{Tq4_7`^;NP#}-v#~LS8qKtX zoXH5SnwWB1fL~Tb(kdA`D5$SCq2T^$fl-eoL!!C=i2)FTA4~%fdiX_o`Lvj*(Iio8 zwDc-KiR!KtR+}*65>v_Sm|$RGonDplWBg#t&t8WWhR(TX#UpQv;}5A`&>hvJ?N z)hZZ9p$Yl30_`xt4$PkSTF|o-l4ts96aDqKhS>E=FKH1bL_Rsb=*@(vr7@A?xt=v0 zaUGm=c<8Q;ZN*^Dqz@Y%mc5W< zYg;a$M(r# zsogoP)ga^ca z1_#>n=HW-PtYG5gzMdVG=Uy*6u@<38*@bV33%MTXr636b34xmi0Y_6Z>qs(iuD-=1UI1dSJoR*%+|d?K%2i{`zGH8irh22{9t^^YgV;L(zP_leCssd@L&7Boi)#6nWyXR|A3 zb{l)EnvRa!{!w1+qxmq3i$6wzl?JGPo1*Bc|QCHk5C(A&FWs!eaI&5jM#iimjiCznI!NG7| z%VpeUGAO8Gu>1%rT>8!#dOdCvbVut?c^6B?hoJHvGZUi4vIpeL3*(DQ;^}s)xwz^0 z`yNtl#)?iqahO4SM_=~2VE>am5sLO)BgyawQg6#ploozH@v37oN z<|2yKC*a(g)*t#j_FOkVa|Vo`$9`T@H7rUYWx^W*1Kp7~3Mu@%hJZ|m(5~+GcGlX^ zTKwc5adgG`MNEDM9bCGz<<47$lP5V99+3%d3^nQMyI+6pt{UmEZH%krZU_QI&BfEJ+WNXkMW~;Dx*t>r5-$Uix*I8gxmJ5cB8q)X0A6Hk6bEU zco9v~Yn7ZLJ`C+|YOAE_mfohPPPHOWR*-yQRoRh1*f;!+_SC6Y zJNw!h&On>aHJ9b=tt^%M%|rc9H+I45xy9V}ekptwKG;Q>xq4(dCDvh#)$k1(n`u|= zseK3_^XfuD9j$S){1!9N@lg(x@gN{dcTHuA@ zB{S_WlsBF3?`t+&nhd?iI}r*|3-$;Mi(e`OP4!yA17F^doP>acz)gmLH~~1k%0&ii z{Xagm`(t~qGSGM(d>6CObmXkA%RtgMiqA1V21K>9?Cd z3Eu_zNL~U*m<$+v>F+N>f8IU*3A={I>0%fEC3^kr4-NzA+!WXp#DRuZRkZ^p-!mDe z$psx>rb+YPH2Z^nLa~-lfB1U=oXmf2#e#h!HhuDry|9zM&VgeHGIuU9E?xUc_-;Nl zXTkjg)AM#Z=|@7~1|y);_^myA9L(Ezm$Z6HAO(wOFx63cYHB4P7MQwz1aB6o2>4M9 z`}pw#FCp~o#L*`jVP6?6w+%R@A9}m^FmEUbf%=u%1&{Fda-$+s(@&Nxge5Tuj7Qt; zI*GWZ!)XvBbdhmkKzVL1PzH`VIeH)*r!i1j#5rAZRLZ1}H5K=sIqpa_=#iY%WLA|? znALvd^mNctz{QQ)6QkfwC?Q30EzwoD999dlqhxjuf9IU2z{~lEj(yVq#PUP+KrlHR zmZxYT=kAP{p9=~vc(wQNr%PR7S!HpI!divh&BMn{d$G z?JZXIui6m(Yc^EChTux(qA}tcD?`t@_#lj2}~=0G$wW5NdcL# z+3@t_1F%BQL{YrQ6(XcwGGUCh^yIR`_Q~hj83i&<BGa-SW3!wvkVQSU z=qD@SxzOM z>B>e$Wt2o^K>o3Y#&coeiqNMgPg}`Z86?NHl>FijC8mglh>60D^0q=D=hUqei*AJg z>^SE{2Y4y}Wn-t^!!d{?u}YR95G$P+5fzwG0){#Gy~2o{OtLYA9AI=1!x5B4zh+sm z%25&k2u>>~Fz(H9aiIMC=sfi*C_I=KJA1m=%f!VwKoAQ;>HbzU9!Fy5iXO+I0R5^$ z{WCl=N{8m_`YZ%K{)*FrH^J$FxZ3Kp?7H0=lNl|zf%hB=*p)!sNfTq(m z@VosKhC}YfS;*m#87U9L9|5}h);G6q!}&~k&+#E!sGB+%J^55OIX{?Y7b(ph4H>sQ z1%{KZPR7anJP7m{h*Zu3UFAr35(3`}0!q>`%y&wjLd4+hWE2UDLQJJO=#jC(Zl9%g zVjb)y{AE72-K3~;43^?8#8dt`4PifM#T(bvhJ$2spiQ`(6eZ~)8vvv8GMYmM+s5*0 zo#Xz>f8EjZgpBXc!bAEVbM7(F1G7D-A51MJCm|pq@V|rr*5ZT%;*{{~-6X zZU%pZ)6E4Cer>4BEzrl?0MbwE12XmlqkR;L4+I-9T^oYQO{|r(?-DYy*xCf|uZJgy zrvmMdKmdy5pRAi$;I#wDzX`(o;74lvSfiSBGahkcwclz9Pd%+?T9;7qJ%mstp^y>y z1y&L6iPD_?pbL`oFo<%Icr9+^P#bvXPvZR;0w7{_@FfKq{~oFUeEvZwNj#sbfPcJ$ z*XFW9NXL$M)-jt784$!?miLMXU_TB|FN=HW?uAcTsL;@;ET7)MAyZMMy)}JEg6kWp z=GYUU|;<-sj>=-V(V_-@Vo*|sS<&MPvY7|*09hOn@A;tJd??8I=_JqK>gFutF} 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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�(EtBl5gQgjgQQ`hC3?w#b6d2b1fGL6$ST0O7a_4#wcT5h z^mRo9EvHVzf>IApahtNuR3Ms2iu(_A#(2+g5 zD6Wv-UWvms5{}XPD+C7s1RG3Ir>^Rdc3vM<~mJF>~CgUv-L>i zL5an0%&l*emtd7tCL;h|D0TR}1lQg;<3{&9j?T^;LxthXeB~hB& zvfFaG-80E07^YhejX;k_0h-kB3I}H;g|<>|e^mEWIH$%HxE=Y1?iXdD+oA)}dRn>vU#{%0 z!vn}6FiKQgKO=)-`CVm=j9;BxyXy<-;58c`kumqQuGRlyA z2sOfy+jw+k{@$zJ&@Dy|uyC6sSYK556t>rZvdiW85;|BWH*?_FwNv`RuftYy@8le_ zE$wRWXJ7}^^j1X>nA7^%A&ZEWUd7yH1{R0NfRBXMxrK1u?gxUNjtE$31!XXO3^ZQ6 zeI7T~-K%@8fk73eZ7w>pHnf5{L~(9F6$89_xq62hzJPGj7qT|%?9@cAqg9UIqC>~H zJ`I2nbgrp7>6zexa&x|&>(9!!Vzi1cA*XFWmSr&EV&X;T9n)D>QYFjAG?0XT$Wl{8 zf0>&O-;-tyF=XFHhJFHS>m(c~ZXbJL6~H4n!uYTd9O#OPLvTyct-Xgvr+hWw^vC^j zGMK}n?tK*WfB7;b#`rAAKcBuh{IT?~e!G!J6YG|2dzFrQ3X2pi&)_l>oH>GVLE(cz z0t8WWeGeD;yT}V!W>wn$YTwXZrrJhXANyYC;4V zr4_8nct_jP7#4XAmLeYpBt*JsAA|n8Bqm6V`CZ<1PQ@z-Yr9$c_Urdj3&b0Ha51}+ zb0b@}Q=?0xkyvv;_!>&3ztsxo z$?LX9=eFy!XT8Ul46Qx}h>P#s)^*a$?&s0fZl{+5%X|Bcd7gXg-qyzqS5OJ4@HP%I zuyY)L^dRJ14V#~aeiB3(&k>OtwSb@c;*xM*Y}!==Fp?MF6A&b7sfd3bKZotzwm}RaPFFCxAW)2JYAyBciu#e|BXQM*2=)F&Uyn8Rx(H&Y$A$Ym7 zKawIKAj32P*+=Qm5QnV6?M-%SR$Y_|-q2U`FjV8Q3*CLg8Pe=bXa#KOMhlG*W|WAf z(=_VLr#=c3lS45r^qKfACOR^9+VLZD8BY3wa|H{FO0vlX?f<+tN@r~P?xgW@e?>3C z;9S*gE|B<`HVH#B=zmUJ+VC>}?Y4j8w*TIK>D8Lg0sB}!@MjYZz(DHZ^?izR5Euw< z=qPc*lU@tT@J>T2q9gbC8&F+bnHi|YVD$>o5MECHZWh-pT9q=;oBQU^H@#5)XR30N z&&T&32MVqjj@P%l8yn0DHtv2uhteV-M?Yh(t)$w`IX|txe0N@Z zGj@!8)F^}lF8SS?@wD}dLxG_M&C$7b;)0ppHA)==A@Pbz7UreT@8L=84iMQBJTxIe zAxh9E_3f<~B)G4EoB?G@)M7Xx%giXg55{iIxe*gMh(XoPY_@D9zS~3JFZ>mxqTc5E(^JOcu0lW)5D$sBke}JD=^s}8(f#woRk-Bc z**{Q^f+7V-uCrNbe14;vz7yYJp47IAP~n{cA8_;SCcNwc#9(qR6d7zd_98HC%tPB` z@E=T^*iE#w`*QaI0@HJwir=83Oe>|Aq9W~Z*qYT`EHm@R? zXU#0)uc9s@dG_RrLVa}>MTbLVg_mAQQ0zLxuE(dh7+CwQ?Ay`QK%N!5>+o^qWPb5w zn@c+F!*U_-AhO^%<^^8EM!C;`Y>mLXM|)L~aWf+ittRakV9#s0ERcuBcnEK($Ea>bJ|>GQIqKY}(-bIUpE^LMs1 z6ikx2SWTX>2-Wmis3rmnRC1=%N`14^d;{4XTWw?V2n2z-$^|jwvtCEdC%D+a*fCU6 zgFx}Io5%Kzc}Ivvru{-45+zM@tIN*Ow)C=OxfrCUn;={?864G2OBlXSEgroMS71Q`MFW3lM@1N2fte=X&56NArnyTUu&c4 zVw;MIi-rtFP;c5TzyE>8;Vw?KtrMK3qvyy8j0fW~Z7>sRdtxiNTh9{p2qI{<-dg%9 z{klEAmp}wemF#I?zxG8>{T%7j*=go*`WaQgH|c+*Fo6P19+lTK{R3<`tk2**W2m0C zK=nu|Zz+28Ps6ZyIEnB;e$k%GZ@L*jIP|OZgXx911a;sKAW~;HdQv_5xRadoHPrMP zgT>R9oHH37-|j2xBKn5kG#OHoty`HEC}qNLvWl-DQN*Aa2{lEJaw$ey>{Rql53@<2 zMgR2=HRrvq-$UAf?L+UE4f!>2cQ|VyJ~tP%=nc@V^W9G)y9L9^S&M^2dP2TdrgnVJ z9tsmGip++NKv8|)(>O`*jzK$81b&`;AthHSj`dW-RMAM%_Xw}L>HODPIYCOHYDE&% zVLy1CN)beTL@+>!Z0{os=-T+xpj;Qfedo6$&jC0{{7(cR2q_R^q&LQl_e^nyK2ou1 zQ|5V|WmN_ZY^FvG>x>yo5-I?1G$R9^gjgCwWWx9c5)Ffciy6NnmAf@|19{itr8DVDrX7~zfiOXPc<>i|} zAsM-XcBI=0QEGz?|C|TjMDfKryf?%HIhaOt#0|CnCJ?*L5cL0V+&KyNn^v3ea^E*M ziywSd+?&K-HF$m{_X&GE%yi|`lZX@%tz#A^+*=H&hjxU9*dON=MnBRA4)qPg-KAgM}TifjM7?SNmM+2!c6V;H1-eIZOd+P zRn}pKE;Gp>|D8ECyUVuB=8Ax8scxoRwu@R|0O0`qMBx61ondv$93^cchmH(~B)AgKx!7Uh*~S5Nz1op#NA z0_;{&x~Pi{=imOw2TepIURm(L4vkGaz|>T6L5$K>$PkExtRIrxOa$5N*&KhCMW5%l z3Ocbs(~VfL5eS89d3Pn{QZ(vJjaCd3WObHBLN4XDvk-j6;B*;s>#9f1u>82?I=#vf zooH`GAE2^KOZT_6>d|3jn*PqFend(Q7fD;30Kpr)!`rGxiohai{LfVv5I@G9fOw8o z?eLzwZ0EItwddX=4{ns$+-k}XK%^HRSL-hy!x;+5ZKIrZ@gZQTmP#vC6RrU7G%@#2 z!>Q2LKarONLkS-TX`u>=@@Lp?m5@7=_2dcO_Af>?> zH|_$|=n#xJ=W%4$-^c;E=t_sU1fug;J@a0EaYeE2z#&c6VGa5puPxaXs&$N=F!#r22tZ3;h?-`7kr9$~sn>2D_v9$E}c-He#el`>EgUTzp%Ll(nx z@6JNARcgU>eb1qR*?H&zsMO zFQk7CDE1SE*47503~2}7rUcI!Sv2Vxla^&DS|~#S39w&oX-|Inuo$AxfFa8}0>SO{ zA%))(ILjf5e$B4c!pUN(6cYnrBNJ$Z9c=&pOvTI-5X6^wz^clUKa6Ufef)( z!MBISinJVmyb-%2*h1D$2$JUAa`2K{hN5k>)0e*V7mW22Q34p7M|AB*lW${NY@MEm zmc5`NTP6vD(OC;2&DI&KslT7D9%n>w`RO4cmtM@3q>^rq=2Yl$j0m1VibSNxL(oVu z9c#Kw@n7qRkSzqJ)()yrQ<_)X0Z{=cg5%7}GCf)qlT%F>XQ=HR$malV0vEK?I1#0j zaGp%A+_!OzjI)}i@@x5e)I zziY5|QBQLstzzZ2=J2L~7Fc~(9kqXyFQB%fbSD{@whTSFhWY$2>;X(z^D z1f_pMIbN`AjnQl(5#Jer7WTc9%k7CuWa2J%yUL#TPvbYLdsI3U^uT%l3|^V5n7~Im z6uUS?f?FZ1k!?Q7fn0${lAgH6Ej$D)xw$;fW=nC8QzZO^mww-0ZS3=V;P(kk;tafR zEh@-8ru#HQ zvVh)AZi)0(=AoW-SFHc_PD-%GCG^f+I$Hj0Kn?R!1YYqgyonoK!&+&2EpuJ7uR!TT z5Btf_E3!nXvu_k1v6v=@DjU4tH6!z>#WkaZ2wk-m6oUpqk`xR?S_!NNNvKa=UZplp z+Jw`onAYfJ^XR@HL(Qc~V{>$b9r*vk7XVxx6b06X;4A8`fihcIj{V$jwYXa z9a##f&$s?2(r2u-DpJDFkFe3QE`|mE6ADd7mckZyI)ZaZf3&3rL$POufZn{qJy_1I zrydkn`E5o@Bx_!0y$gD3Ee5Cx5MCPFDxxs()zkcrkFFK^lBLQ@z$i;Z!H%o@u29T3 z3MG&HjneB+Q+3weD-=n1dcJ%ip_fvf^&I~M^utx=q)sd;_9Fd!`KeJ!onCo)cwCMN z6{B(Fmny+B-ei{*y(E8ubJ_}v#y0;mQcTbcr%PB?sb@VN=;A9BL(RrXq2JSZ7jmU< zO=F|o9Xw<>Jb@`~RiSQx1#Z;@*ExM$9Kz(6gN!^s$#d3UB>`q zb;iZAUzTW(S|$HWpJu;#DVI5^`fRmnATHHZ3Mh>T>r=|_fd0xfx++tQj;AWqP#>$E zqz4IW{B5qSQqFCTJ(jd_&5ad(ykt*lW6#4ft}0fYctn`k=TIgp5lPUuLL_GnCHYHA z!L!8=t|_qaAU~iA&ebC2b7c00QC4$r^eu$*8d}S1?kXmj3nq#H8|!u4`tHKGq3jE9 zgWs=RMo5wDlb~z;o@Mk0(oC0(<(Sr)%eEGi`#KCUb8}l zU6DnHz`F?$I&Evf$)hmvLTh;prFQSTNMu7}!po;?GwI6al~)0gH+MvDYmFqi=riSq z-fbemEInY04@b4y{hWXIrv$~u9u*^&I5pH^!6Z}?=NivPL~1Qm675rS^pkoM%0=d3 zumM(jmX-4VhpBVm&MfM- zbZpzUZB;6^ZQHhO+p5^MZ6|MRJE^eicHi&b9_tsJakkdn&z`gR{A7PskaVAaos>}q zUf8o|$Ts4FU8zLzy?XWaw+5jNE=0;RSxxaXIR1(!l~Ga#hSxfq{=(N0(vnZ!7)gn5 zgZz#(63^HCs#wUumqGfJg1!7Qr-}(tIJ_6T{D&{D7V53(D8~97NMfPfAKO(6+~< zb2e9)Kf)&PThuWS5JHTv@OdlAPQeFL?-yF9?)qw(0}-ZOa1lc3&pkWj<`@f_(iz6x zqc@|7$60zUZVGiZX*5m+*7w>#85|pY=RO<6n4GBeXkNhsIixF95`gp8!8NK#Kouja z3dHa#vnnT%gW^&jKBnWjo$&&MCwFL=yI|e&8fZZ zCr=Bf*f=hqM_r!H;r0i~-{7zQ`j8*GCXDfHm=O21!f+Z05t5Ooax!#vvt3O=g|M1f%OOP7IYL^Q`6Jdix} zz_P+lleHTl>bu9o0UJg}Oh}nx&z1d!AOkIj(*!n90}kxsF^$>_ZXG9VKSV z4aJV-jRa|sxp(q9P!UEs6}=N)CG=y$lxVp=Rq~Nfb-;@lT^^eDMqwXorKLQ9_S<6T z%%wN`qoHIRe1+T_n}rr-i7+s)bb!v}1h{_qTF@Q}sy`^{yQ9+HYw-o@_07_hIzx}*0 zs5GG%w>o#(_N}pIY(0>ox?7oc=&qin-_vk93aT`6eq_x!fnAf4z@e+v8%!x38pDHk z{7puGALS#W^tCl;=U`|zQ^4HH^b2q?5+o}Qp+_^sKO9!VjOtLHqHDxM9&nB}Ug-0s z%;};kpuP+`xJ!sF1J^KuJ#AZJs$>amY@vlw+0~F?gXHv;|wjs+$Aix zrO+?zVFEiM_?kCA zquILP#!;M^mq=#l&U=TDxi+{DM+*Frv_Miw3hPXcxhr-a+P)oNJau?@4;5-JJ=4qf zr>Gy>R2_&cJkx4cwN?_YjF`b75E<3MC2UHsl2!-U5k;X9f3lsTYCCBwi=A@C(GYs$ z*&y+5sahzTfm9_0+01bBbuGe32NjJ}siP8(6Zi>?KTLuu0o}|i2LC1euhS}$WH>$h z&I?5gc2Zi>W9y%dL^|cYO}1n(@&y0(+Z4Om#aQ*1S1ob_BL`R^*Q&)Jn84MJ&HAyz zq9JmbG1ot*DvIX==y{LYhczFM_3W-C0}}0?>y{w1*plJ+Z(jvd;sAekaW3r2-rc_A zfUAs9h+j%6aRs7$s~pBl&`Cs+?U#la`R9s4v=UlmdmC|k^kfFra!L^R#0eQ=SI4oK z1ou*XXfg11A42Dr!)=G)DUt$nT5G+ZuH9baKRaDt_Pb;r(WTtFd4Da}*3!!nxfFP! zFz)bhP4TT`HAR;W=&{ED8DFp|*{Tj>Vsw^RMh<8mNq1kOi%tO<4eGOoQ z?kg|m1P^leVYIszXLF5a%kBw;?GOgDS#^DQ-7}+wxgv4=DkFbREN+oghC48t#|=a{ z3ommuovHs<1#6=N`N3>mm;g4UtqwSWH9)d7ui5uuinWOWYlSfOusG&l$c*n|*)KrK z;LOP8X9u1c8e`m`Y(x`(CyQl@M9fD4I*3&XlRP~{uID#R*d`7wb&xe`w;7kfW27yN zmO*vw!o!3Q^uZTx&j%a30Qf0N z{_)MjT}m2!5Vl)kRa%+{7nlSj+wB5ZVNWnfha-^^OnNR3L814mcrtKBwr4oIO9JXN zY)Lo>X|on~Na@#-fo^>zC)dlbf8-t}Ogq_eCTmS;Pl+V+B!Qm5ClaRZE(pN?U4i;UARZPb8*NCd zieFq%BK!6xpIk8NirPo%o(2el`!rJD)zKccee@%upnnrTD7Ep$8DO@BB?&^plhj}` z8Y+Np?d>qhBl}z-{Q19NME#0v;#aT_{YX}4ZOFl7i*#e7 zWcr}W5$(B@4`&n4v)?5k6`2-|u>HDb!^^tn=!o3qQDmykCtx7iivatq6o3L}C@~W$ ztQP@66gP)%)C$$5eDX{IFr%*ZC(?R(f*C-cvh5GkwXwCX1N_`qFL0t)%37+^>pq$$ za!IRsfn}CnP*g_EF%eB9NU4P@uA|E5| z{dct!K;1#_pyY%9IS>)1u>}fpIAKK`C?Uc#z_R6)j*>hS-~y|q1YwFX7I)Gr!6eln z0rhq^xCRA{%e6ah#({HgJJOR^j3r1hg2W40Y>=hn|sCi4huja9Q=TOZ5cE53_s#NqQ8#*@N^kq>_lg> zw~d{Xa+iMv9KGVcSqq%4H%_`o(n+XJb8`C!odtM5MHfxnwT;gK{W1T0`4TOM1{Kxu z3d<>H&oZ&>-O|?dlq&Jec)i9><~Q;<1$_wqM?Q)m?Bynj;M-*HQZrBao9*I{?&lSs z3-pfjmy^Su*5v>E?0#a0DH5P$t+FLlbtTxpi!D;cl;q{;JS9sCo58=wt{^5U2q#>v zp)~7QI-~!lq4qP%uMg^0D!L&H3s!}PDO{?^hA_=%IeAL8`PJ%1%WRD{jlRq|d?yBS zpjk~FYkmnjF873ovnqYb7(GqJe82yv(O{elk5^82%(dtjLwU_+QhBXZ*)u`01I2D3eM ziC75wn~JL)1b;q%oD(CCY(%v@ISVFqU<@$B_TfJ4<&rK_h!)u`X4mekY9&E^DRcmI z3gEw*(mhn=TtT#EFZM9>Kl_ALv0ZTo^M=58pXw6>jZ;aNg6~bDZOuxR)8?*ty+cy! zER;%hNO>FMTQE38Gxsgy4J(CVrb?aKwiY?7jKZ~oJTB?#iZ$9&OFn%mhC=MNO_L>x z*$ia0lAMm$$*}$HctrIpxv}Vb->qdqmL(~HbWMzJL%}{dTRg!RltFoaQd7N@i8J6f zl{^?F{dT5`WB~o(Kclx6s=v;cb|E#w=w3cCk>lF6qVTCz2M+S#k!8xH^YX=cMENPV zuUw^!z_VIyW2skqXj15`VfmWh%}6d^lDk&nWwVi80@fab&Q*mC@7mWka2_Eir=boRp{yBk zw5T&_PS(6m)5)W;)xP}dRvE=b`S}~W3$jJ9+u1YttF(@-5^v?Vc4Bv11zJj!O^>;B z=6QxF><=z7F)!uKZkzai5}QWSm-M75o3YYnT7fCJ^tX;HHgd^PtaOSYxjfa&nn()E3+Xtr}&#!iel>ew0wb;NK zDIxUDiGJ}SO{^@f6kd@T3+Nuy@LQXkWx=-UDvuWJQ&){-icKIN7K%m`izO!bUu$V1 zS*~NC(TR0LT_LSx*qwD>2Gdg5w-ULI?l6mq5;pKEHb);Vo=Qeyc!S7|W{vtH_X8jO zZC4wGH_ZWew6H9#L7FIkiYtM*TJBX9=D*0K?9zeO@Rwt3sx2>1cx6F9s zT3nqn?wMzFxP}U<2e36KfZN)PEAq(*%tT3Wo+0fEQ-bd&A)Co zPxzX20=77bhF@Dk~84JYlRSkf^V%?j};8Wg=39dbjS@ z^6;0@O!Qr`mRd_g*-@li>1M$?dx1*(duGo(dq7fV zkL}OZ-BwWrs?{z2RRF2v;AiB1h3PZp@yto2?q_S>McWbDpKHC%CVD_})0-4ezP=DWIoMDbU5Z+bY+Zkb*mM|AKb)mHt;jO&+P$M@StUVVe&R#~#;$GTO({Sq&9iZnqDOcg+-P-7l#9FdIhFl^PG8NZoAvCi^^_(!x%6$M zSYvYxRzP4E@DGut`|OS%5zpFH{g^b&;VP~u1{Hku=(*OHpE#+>N(clgC!B*(#e8)+ zaQT|}_fF1>eae_FOsh6O-?cCV11SW_jiit?YxU|^2*2nGC&wHILGDmqj5(ELt@rq(&&KtK2kJJ3_7u znUXq#O7|&D8>?S$) z$90P)I@ZRN`Uyj6mBZ>|AGvMX{B{fCQ_^deB>1?EOnxnaI8j9xAP=Wdu=0KFXzi4B znfGT|(u2A2Ep0BxR(7kk!#hjPozJx46G1RVRMe1@+GUz=`crL!xX(Vc1;C$OY3Vrv z&V3-=IgFFbb#lr15l6@5IE>T~vU;m}-;~;Q+>Mph;$8o|q+o&?X(}e7J=&SS(;o|s zG1L9!MlbAe^v_}Fo>4ESXnEJ2_5abo7EyR+Pl3YYrr_S;C{^|w1`jrLkI%hU0Y_BVtxdu&!zSW@`Q z^`=ZHJY3ATG@;O491JIu)yQgJuU`SM6u#Rm7| zsXr>qU7xEI{>sPT!s9KZoS^IJ;8*5_d0hVIdsaD@9;MFPzBMZEE>GfD2MmT9J0p)u z2t`~b`SQ)@_nnyIXsK!y1AXlMtqJ`M+r z@ROS(qlMS$rS>w<=`Qbs0YKn(HT1*J~Ch`;dZLfZ==c^LF@ZJ{Q0JS}&}JS^tO7W78o zfOWA6*hx13L@tmrOYfIAL4=#BViy_@}*Lt#If>6ejWh#-=~ z$VgeYY0{Wv%XsWs67B&TLwYBg5{wq0Hm{2E)eDqeUIA{#%Zl2z~ya#1oQfGnjeur#=fsXZnPb~?CVbF+ck zI@KR7-8yEf5JM<m-g#C{6oK#KQOa1-o*}N3lW1SN!$JvwkhH?< zB;qoZ#{t5#y4n}1T=*pAe|J&O=rFsfSUEt4V2TzKY(Z=iKh;RRY}{i?2mtt-Y1>BNhesg?B}z;!$f9b5D#`iYLpy}|>k55LG8D8zGY zqp{RbQUEJ|1`4p}5F~f94&XuKflM-m%mi4|Pq`-`-9%oeA0CD)lpqReCo=Di*8R)I zN{o-7ohmSpoKesb-@m1U6rpF(Jl~|dLHLygbb{KPnMknA zVqo<#0&s$iC_s0R93DS<$A8;bn*jtQ;CO~y*ql*i7Nm|K%n`qDsexi zc14pLQGG-pKp$cOC?4PZfA+!cOGFOVZheoq{ilH`#(itVv0>MKXc2fA>AY6&-7h7u z;9SXhn|I=6{}3j~jM z$lk_^gB=djTIAUAhZtIc$ZYWB1@zL(6;VX{d}kO7ouKO_;i-zZ^%PI|+7w&bxmYAZ z?clhRB(m?n7^{P>Xafo^3(}a#rr9}J$$>#rU#0HHVB*8DjQLW(3T7%6+I14AA9c)y zs#lLD41Bp*O)o2umuyp0Kx-4rYY+|nj6xTsq?D}e>t!w$sziH&Y1^VrI zo1s}Ct%toU%ZQV_M6Qj=ic8Q2_|+7IJ%=k1>OyI?HD7)17^9@wzA1KKG#YkMqN*fX z_v;f%7_)@z;n3LB*mb^@YS@*^_Ga_ds9;S@R9^7wSMfr`Sy>zkn)8^uanP?Tw0vH4 z=(~AqW#WS#Z*%@2RRAw##}PIi5aY05RD1hSIkMewwLW=NIi$2tL&CxG{n>f3_MW6z zxd_@jSoz=MuxoM5aUJgR|f&_2FeswG+76(9iZzi-h?ZP}AKZulCa zrru$I{g6RuD*!toR4}{YcnjMb;^hCU+l&VNCQt?PvF88GE%6$$Wnije5CtVKjR_Xx zxvH*7iEp>?AbhZgkZPH{9=TWm8mR#ZqKQw$+5Z(lUFU&sjFrRDaX#|}sBv*18NvfX z3YYN#zxMlMd(iFCw$qBbVR0h3zaiRrWL@bTS~oSom3dxJFjtNtYmsb;iq+$?-0yhk zNXZgN_*WPwOLzWfTlW`1)QRhF#VsRx&l8qy&TydrTm7=4{U?;uk&LE09Kmbzt}izj zA|Bv1unvR}hm-l(pPk^{`zv>2hC_=6GwS>Xg`UimuuCN{EA$lEPI25AOV zOZ7f2S~ffd6045$!3c$d*jwoEzc=<+=>I}mT-?Lr!Qz1w(v5(?OIfT>HDKdKNr?G{rRhc!fv=AAurdF&g9Vpp#4xkU5Libb>EAML zjJ0L0aRd@R0*){eVxX;TnXJKk%wc5@EO&$(m_?E%~mbDdv#jUy`7QhU68thLwdowjD z4Z|I8=ro?V^qN6@Q>M}Kz1f4zuzJ%!kiyBBm5Ph5-O>!5dV|ur&a~AKtH5Qo9=J%t0B%-|WN|R@%TLSCuI*x9p6Ymo|>8mGOq}?$ys@^s|pLh+|XW za9EP5acltUM6lS?X38*XGZb-t8plC+)1tV;+1FfHQ(&FamWqOOQl+1t8Rq1!vKg4( zl*+Hs5=u54OsKLmodumm8K_aW&njkOl^JZ$vE|ncpWA|a;RRt_hppU2$_E-^6?+NI;=$ltgvE+GXlQ zK;<&%te6*kH#h`FG8Im4vjBXuD5JTD96O`bwb$i^;B~JeEv_>js9bNrLV@>MY2EAg ztfSxGJw`hy5$X+IXlL!q%2ExXwX{#2#bE|QLo4|2DyJ71Lz_XDDA2BfzJG-KzcD5| z77+E%jnRjSm)3%o^QF9@LkK^{e`#B6ezb0BTSn?bCmM|wVPWV+v8pl_6heyN;PWqMq1gaHy7#tZAeCyFJ{ODPBoUPVo*{` z0H%;Wt?gvFRImB27Rs5BIhEnnP&qg1CA{t6{NpATkzIYP$B?pkr_BC{mP}T8m+r|QGrFOne z?6=W*2>5yk^Jp`hZwUCYYa2Sz&qh;0sfL%N6J6z%8rG2PtkSwE@!}X^D!l8xuQ4-K z7y~z#j829F0@pSWQUEVP3p{0#+F*T_ICUUgIUSTh@g01F{r<`FQck|qIiN-<1#hTG-a z`LZwu5*Dt!#gTG(A2)E{uJC{fYC9-z0?s?GOdQ!+UOwHKmli-lG_K+VvP4=^z zIxOi2)o<^|Q1v_h2NhQ)arnDyk!%9CPKC){X}EqYv+!lsHKHme?Q^hJyeb7YZ zOKFKr0BM1<(j2vX)S8w*B28ux!t~TU`eWQ(7fB5^m{YYg!0M$`8!r2crb?FaM$|ZX zp(Af!&A_DI17ybaj#F`IBkVvpX__Nj1SKs<>w6Qvk+?(dUR2pVID5>f1h7ncNeoqE zcUR>VBK851HMx}(#C2W7qmP9S#R9jQJioppdkkvez^&rkSt3e+%&PeH>1LUXj8j{8 zZtW|5kwwmg9toPHC|#SBk{kv+SSTvjyaATCb*b?Y;Zl?Pcn8Qdw7zD z4$>6zn|1(u|A#oNSW5hs{pb)s{uK{7G`{St1j3`Wre?o9w4orTI~5hjEKWiv20$Na zsbaVzbmyInA!&PF( zrOqt-Ih4MOsfPvWjbmtQ$Cv>$);j46SH*|&dvP&7hay8Vfa6h?@qe^GlDxHUq?IL|)pmnFsB0yCeRdHhP@%_oVJFDB{ZFTjSUf9L4L601A{qc;O7y;k<%!Un%hpj|$>$|FU;bV{(K+z1RVnq#4EvSgldJv+y)4XmDC;a!8 zqiUZjDDS({wB-z(%PRJ^59Yb9>gKutI=1pW_z+4K0lqNeY>#N2@Hj_Gupp4$p>%7m zkg^;svjLo;TXF9iFJ)EAG?nEoc#Muqc@M>1%F_6|)6nBMFgi5AbY1nUrY;+{9q_3VpRIp{gB}jtHQ6dWUkpU`uDGpN~^iUEUnZ=^#%3yT@fNlpm`&_dc8 z-X~%l@2(MObM1L%xoFX1B$^#Sq1r`BxRkp9eb<#)yvfv~`NiB+IB!Z+>%ez6!`;6` zwFID(pu9AA#QFKp%2U{S;At>1NQh@8egjw@q))O-UyQ=W?_M!bgRO71U$|0{5yo`p zRTiHWr!0zY&VpJ0{34_$+4U=aVM!L$rA=;^;6!9>$2gP>|JH8hTSo}HG&M*`gQtOf zB68j+{%avB956vR>rGMjT<7XHlnxYQK@F*4JUR1OQ(8Ubo+=Xou!3G7_MA)V=Zg}g&NLJl zVTPi_|;^sU6j$wl7&v(aO(GUfq(ppT_sg6>?%$MJF;;a(ZXi+QJ{2cwDx*R%3<-g zjB{GG8rhu4hNvqVHeSAxUKdPX0o-L5og{+6FLQ_joecBySZO-lDy;-3z5M+q0`Q{k@a|@<>QoGTZ&N z6>RB2ci}IOwl{spvc@hol^-tZyLzmYfZW8&YQGX#_YJaTD3j3))56qjG1m{BEsV(c z{y+k^70qeV-{Z~NDRYIo>SXr|ekp!Cw}Fu50=b_1#xSh%=(wHA0(d8oVQ%B6c#g6Fp#!IcGKrijl`$FnPk zNNT?^VCxS-!mN01U}5B?BJ@^-85Cc;qX+>lZ>CWVw<&AUq3^&=F;6fMJuAn{e+WeZ zV)%Dj6-4VYlCG8@2P&n(@$6RB&&@sxt6Yx=EX~C3$Wsx(iTkepk)hu0pX9+R1+n>bhT*7Cs z*Q`}My*&^pt7wY>Byl>o3W8yH7;!L<`^hHD!M?4$e7hVlY?DNh9XKE`{{tBKS%F1FVp7P#5bpjS3h_&kBFn|% z3Klgg8^LnJ)Y;BsJLc`?hWv$+l64{(F;$0Woc+7Pa$PvlAiZ{=;3T=5#P_6mcb~Ja zHBfEnQkYYkSsJ6J*b^j=m6VI^^C~8+WIGeMHxAVV3g6)fndCA7$|T_W`!CZ`SJa+P zu3AE~h=K8^4mi3HbaWupD6!{X7%)jiF#-KuUZv=qs^93vS(FYJAO=z}9vF&sN)J_- zE1F4{sVL|evZPjQ_{mFtJX4&%2`xEUA_|1<*QVdQ?okcew3?qPc#AXX>`4tnWS_u5 z0*9&zY8EV9P&IP2WDKdOk=o%uuZ!_FQ~NwuRy8^+-Rf5oM}9!*`k9)zPp>O00%k~7 zNE^l#4I~8pZBrG5)!LtI4nrGBr} z!4pcOXt89_vkb`j{40~gpeGQ?1;cysOwhk4;F~4J+dAN7$u}4s@Xw%#^@<`vHC4KA z8R|xIq4$uTpy~_B;kIl&flc>J{#+`2^XM_SYqRazzCVn*(#^mpBIc**bo2QRLTUF5 zfyU1@t%(UsCpnmJdJF|1l#&;k{HJ-lC~*~VA@StM7%;7_yCV#Vw3MjU(p(ac zMNP}?L-HNBt9`2;#y;{I!2Yj|thb+Z?E5O76->&CFc;M}Q1ojuK8>Tcb{B^MVra0{ zuGx+WE!CgH?kwI#COH~j(>iTrro@_P?dqe`bz;=_ z#L(X4(>x7~xT696~g0$C)Ab=K?;`8!HUe&DdpH9YoMLE|N<(Bwd}0wP^FH_j2hgM}~_E znw8tIjcJR9DEN|4a!dH6-P!dYE(+xaHe<^8Iy2v)11wE2bBgSfWrfusnwFFXr2<^M zjE!wD$s@dxq)z>JCE5yk4I(w|sD@9CqyiY#MO83dgh)+9rnp5?sL~)7HbRx$kMDv090L zBbW2s42y~|c{iS4(Hs}M7M8F~%U{C;8D-{U}kOrKMbonzR1)EP70;x++iorkX>Rt%9= ziVdytVH+MT6B&12_#i=n*ZpXdr5vKCabs}Y_=)$n3C;Wv%1Wogfuj?!(3<7oV7N?D zv=CtyEEOXn(@Bn7<~3^`7lHK$E`9dD*+YjVr2gJ^OJvP!70mjaaYFZsTP~3^v(%z| zk#B+>Xi|ZB`>Y7O;(S_=2}9!)iB#cOfkj83&RsIq5I`A2q{? zD&UV?Mfq|vHRSkNO0+lNtbf=fJng>w+fQ(c zzG|?>Dv3x}t}^QH)glL)1mrZUOthF`WH%R(DNE{tH69Nmm7AOBw-Qm;{q;1NvL-5m z20hBGP0^;+co^>73&ueLwM6K73eo&wp!E;`-LuUcb#zFRN;5}16`Ae~PJ5`x43AcD0ZJ+;FyL93I#~0G)=#rN)BF{-lq1nN9Lx9@bua zH?Y$P0q~(MOh;7I(xlDIgE5;Z}EqC*fzHLdZR6X-E6 zg5Vg?+~LSYnJ5o)6JY>tO|WL8+kh%|7s&QV{I7s(WLR)BBJM$pMWwS-jE2gbu+>h~ zGU7`{_|SaqI;ZRX{{ed6Jc;|`62SAN+r+!XeJVdk<4+&G>%w_hH0+A^O_s5$<>Gz=eSM%WNJooPE+PSGYfc2Fm!D}f-KDY@rDDEV&u7-uFc$Vopos96(w>di~w#!wv0 z|8F2rBL&<$@YeAL#trn9ify`(;b1wn4qChtOv&*GPUDqZXMYDD@_D{akpCU3L=Hz$ z@$?>QlS&gP#O+LQX|9FA#B|dTJU)?g=}${H<)r+ zvGAC0<~ex(oo78Q=-f#=Ncbk_%ObF+_QnPshbR#c@rXlFfh`Fga1A}RkJ5F4;KR^g zi~T_=uHTwKG&uqC4F=X5t;2b7>|+ACSOo2FuNXDHuAQytBvKjeQ1($4R}E?{qjVxf zM$qZ;TTT6`bRt+VpQ7WtYd)--2`nluXte-8n3k*8o$QaXtlnR^$L5;GNJcdPYx&jT z2>J;>t%xS%(+7oVj(ZlOt)%0F|8Nm_AV2U>Twifnp#*-HizEKcPiVM?S7374apiOM zJB7~j?62;JyE4~-jnahUGb4#^{?DE#iyo~A|AnPKy6)20@l~=j= zmQSXb&n}_e1HGjua(4FFYHUr2JotqDJ$u(NhC}km1kJWDn5f3 zH@}oT-F}}Ni@H+6A*4f{hY8RdW@7j;85mRop^4hNy!faGE}=?qJ~mJ_x--6zsJu>> zE{^?E8fER@ueC!Ia{A13%GDb&A5Zrgik>MeAJ^I>W$k2iceTMW1yh09^JI3O52(}Z z0gBPemz6ljN+=jh+8@e{U-F`UgZre=;f!CkrXEnnmQ=%FFO? zT`+Km%vV`mI`6)AuY481=0XoZ>E@x=*}Zg~tBb#OT=WQL+cEPbWbXGZU+Y0)mlW@G zBm&XYi%OySfkD1~kVMz766jDy=lv%}-5*D^f_KBi2fw{MjzBZJ$=Ne<jW_cMWxbgg$R0#4L zrWGOM3A(V*p0P1fNZ*twK~a|0y0?NkYAx2oo$;{4S%wPED}0|nq0*%W!bap7B= z$?o4d8Teb@!@JN!3VG_fhVL^%UM3p{?*>IPH_OR*b$nAC4-;EQ zZ+cigP`-qXVdwm3t-I#CW0L=GuF^xrSd4gXr(&_nZFMV;t&@7CWH6oY#c+?64TrCw z&s~HODmeyuob`MPaDVSZT9+ShaJ<6#Ow*NOhWqllUYv6;^}<+s-s|AoAk2kz-teHI zriM)^Tgl#@`}KG}#P7Rs$`}^~`xM)UPcQ7pkCO3Y9K}6-?Xp*4y;H!HF&h5}cJl2a znE5&{a?0iUr{c6(pFHWJehN}wS_Yn{W6lb2=6oJkbMMN-2q?Rp-2buwDP3*Nx1;k< zg8>NGK2+^rGv?je3?AiF;eL3BHMYo95$~?Q`Vxj7vt3+xY31|l*Mt6@dw5Nle@O?M z?{MjNe>nGil#@qzGvA+xS80C`n(IGBH~pF0@$TnzEcrTYN}`)^O5FCgZ#TrvHtNsV z0WEQolpy{}(rKS6?e0jSmE2jD_B%?#n@IFa2a_OHS(0HMg?QcZPNm;!G?_~3srnLZ zsgg|`L>*${(!K)sRa+8chn@)f$3)DI=Hv^ z!FmDxMH_1oL5+WXv9UY!eF<2p!aIm$3I24lV&E$*uwZ*J^*cv69p^O#@^;qJ>(Txk ze4JwGi&qxNQywbdmo2@L1S!(Jv^3JU+tD9M6i=t)?(Cma73hzl28s)=qkZd>pL;VZ zyUP!y{N^uvb%ND~?G%9o5Mq23w2@kmuf8r9)+dl{L;qP_&GVIcrJxK$cwQCM9qe)* z_+&h&ke8A|u%yH!^m=~#>)ZIWtxD^>#Q(D!dPq3vqM)R{w)7tE+|l(!kVzniLa?z5 z_>uIq?9hJy_MCX7{pVNA`WuLh{z3ZN?*3qV`iZ_fieCKq|4{XhU77_;!!=sAZChQo zZQHhO*HyONnZc!YxqsPVB*j}67xRnoTufeQ)>C2%{F|Wv1X!?T0O|7C5V7iOD*yeG9LJR`-EK!2 zoI8i0>Bns^*LUg5MiPCtf*G8^>mmOieDQL3{5N}>m*BzdTN*7w$H&(IdnW^{@HMBkh$lyo=9ePk=FYBN9~2fujW=Y+xBk5*wgG$k8U0y*QGJbhk1tF z`QCXvD?GED!uy?;mKSGvG#~q^PNQ+u?sl6Q_`Z-tRSeG`#|8#tk^X89Lbw~RE)XBZ z87M#Z(ou^QIWAiB)O1t6P2j0V7%aD_m>r}Gug36Gy4^o74hr%OztFQ~;-rk9Z9AIx zXR_l(Q~AD4_Pt^N?gkoO+5OQ)O&cvQ84kdbcDpatm;_Vqo{o#9ps-^v4GKSjr!H}X zyieEg?K``+9o2gy84-U`A>l)G+Yv1-ayqOAKkC)uZ*5pRIx$hhI!FF*n$b~?#E_lH zb_i9x(>uEO4BVD(!B=aJbjCz3Yd+2i;{N%8lz07x78vQsAqaKz%d&p z73>E;5$gALHEFZ^V6R=X7O1rNMGYWQyo!M)4wd_(7gC_j!b|Dv6QfadyakyycIOPjo0pe} z!?}dcz#+V2n-U$T%<>v+OmlsHPW7~hy>0SJG&VtxqHd9~Zu2U}n>N~~MNv?3_tDzI z)0EE*4_i?h85@+3Ct+Flw)~v21!gN=QJ{2kL3D9rBC{4EX}Yj9C=V7IjrGw#UsvoX zEFJOf5YXZ)Rc0$hWlIq+=W#y@$gIgrMh7A;LSjSy`g_M++U9yvw9G{V;$39sWO=Zn zpg7d~l@o?a=6vs^qx=#Hc+bUpiTcrMx*)F8aa#EPBjRL5`-9oq=3^*)PT7s7+2I>> zb;Fq6lDI$Oq)Z0fz;wquExaFWDzEcPS_;aj1B^7j9hQW+E|8yE<|Wh!(yAk7J$;pv zGg#)QFp@IL!~M@;Jz?~`P#*A3af^{Gk094psN#AC0W`iwcp=9ndiBGX@OgcedlSjb zBB(g7u3yW-Aw7;9rj%#jj#fyP6CyQ%_x!lQ{D=SXz?54^}Jqq?ji#k<&YZdN-#(LoR0aam>0_(uH3r1`clyJ}2dKiHcX3Swk*_(M!U z)o>mibSt0qFWo#W#IDbiTe@%a5282Aj34-$KmP^?5IPO8?0b5CJ;f4f@}|MsBb-ak32D)#BDDR~(}rd=S}JlOa!~ z_UX+Qeq>I>7G+M}`Vh`~KEjW4zWu8ouKppI#%L}3oty?pJo+tGa0El9mrw`Y0Urw= zcXGr$=mwuWba1n)e4%4M@hYUUPO?AKaHHV-C>%KgYR=lYxGcYMD%7n3ctB$}709fs zkjt;bl>3K+@)`B*PE^y7 z?1y>DY2TXgp;EC|ZN2}kVtSduuDYvYX%tiL;Wc*sP4(bOO#Kmkc;NdJXz~&uTB2`v zx2^N8aC@079~as0=CBTvt^)2w$L)`rD8_#E15@b2+?jGX20`@WMa~9cUZhZfCn%w@ zZ^VqS;cZA2h11TTce?@f9cb^QsSknpshQ3ewtr{V+za(SUdYRJGZb(2+AO zyU`OyELgmwhFMr>1Scd-vn+n&9nsZoBx#{xPfaK&d9Lj&lw7<6)(n-)TuR zQp(BELZKbZ1o;%TbJnUsv%r+pVsfbMQ>2Z^@Wsf{Ihlc0&{4j!Ju+K58Ie*~X={A8NwbRZJ;H;CcR8JC=l30`gAU%aPc{C6q$zw zr5fQej4UEZo4D)qXw^T#?k%jp2pqX}YH;EC`vueUyT~hc`_$e>A~gC`-9v(WNF0uK z>hFa&kD}y6j1`njRz)tvy&5qxowQzi#p@X|YcEmiKZ-Gj>sQj)I|C=;MY_FS)B7z; zF$iy|q4Yrp)N_VkTP7eP0@-WF$I_`NTg`!0KaDPRq!fee+K_%UbtGnD4GNBF0(#&O4@SL^k3(fn59 z3wepW8tMAqSBp`85qpl=2wCJ8k-k;U=6ZBv9JHC3tpFr4SkFBR2`pQ*v@*qX!8dbyFK~-IAOAz5<{npO(+Vj@)!Ov zO8X)Q!+Ar|rd|n0a@lSXZlBdd(wpkhnsbGof3gbGpsfq8~h9vQh zl$;H!8e@Uv>kC|HAwSxPwMDb1(j;GXOf z8!?!WDwgX$sX^mU4UgVZcv>wYBR@)}aA`f*F?OhWl4wLH$HevJffwU_)BTK)BVZUr z%MqobVvb_O1Tt1JuBD&VRZ~ths53kg^cw}y%Rv_+L`XbhjYhWjNOF6n1ABM7(Gou{ zAugOlqj$22g20gZzI~{ak>P&+PnTN;9EY#Bh2n;4__8PT_0dr`E4Qzi!NLo6mX82G zME{8Dr+hyM&U<135wQc$Gqv{ot0Y!P{AuO_=@fb)=6S4s`Mo;_TKyNPBZ7?ZaS23< zZ7uWZbN;NmRJ-i|HOYf)5P?J9T%G!%GQLtc3JI1}_$HoLFXWtHwGa3Yk(cg9X^yQA zCz7YKe6j#aUuQ#>?jrqGM^Rdr?}SQk{^mP3v8UiIdg-4jX?QCGg+#;45St>bEI_&E zicQL@@iQcvX2g1KI;2_1NEUT6mXRuoycJ(MJY%J!Ro2`W;0%}ZV}zUgF4V$2D<9M` zprc-OmB!k2Mu^ww5(A132}1fDz!UDgUixuhnD;dBzM}F4(MrR?;$n-OR5g`16#><| z8{90ba?yC*hl*d&=v=%7m#d^Uv2d<>gGF+rMQ%l}25gRCxZzhme|=4KCmP<(8ZjoL zPg?q;9z-L`5S6kY3WfP3FTyFs6g8QqVxKQ7FuzzdW0qU4>=NaX%{a3v*ic`szH*k+`?#cVWKNA%mKG zAj7|UO$Wyo7X}+RsU4vWf+tRvUpScMbeFYWtanxAYzK2sA(+@pO2f_z3Dei#9rxC* zDurJtJ6QJ3sAQt?c%f81{uuCZGdUL$f4n)l=W3EDQi$aKec*OhtPE50&C*7cz++}` z5q|!az)N5Im_WxvMy|4`SEW)9n@zmK1#v~ieqhngc7E|I9k4>yVJ>H|8nMon3r$Z>n@yHTi4((~JHTHw|W*U=?&xtp@Lol!mqh0dh zex=4^PR7zkuOTg}v6X!HCMtRJu?UneFzIJKP`YNbzQ-Z<5rRls<3gcq7 zh5Y}U9{~#Qt3?5}p&if$PkuRYm1&suDe-!^!(-29ySw0+{dDejf_VSKe#IXWUh46b z^%BGS^UWeBm~~D|ibO8_ZyewGh{b_@$*k#ZtIOQMh5GgSTsEW*h43Sb_x=T|)SKkESZ0<;ocl0BTjCKaY zjNK51oApUIVvcctrm8BjG54S=ArSlibke?$xm{U_|hn!w79t7mhH}G(PN%aCXukwg5A2ilJ#9%GyahAN|zdoB_?xFx5d% zYJ5L%-Hh=x@_B=whqImNG=CvAzLOGPrd&@crFByf@6C)#(dw)Q!UAEBvBrL5~x3d)TD+Zy^j* z@D!3>aEyP%e3)%nu<#t=Yx3wbebj8V{@zDCIEw)_oi2D07U#IWHjo@#x3I%JEuB|` z>D^>Dc2*r(nIn2cP|m71@PMtJt(InD!;{|oAbgW< zX2^8Nud}kusJy&xLttGnJ16Fy_Nnn@#msW3pM+|}ueCPo_3c-eU#BHru(*pa`kIa& zo-r;ffPdiL@>ldt7krKe{cDHX%XJXC7;(F;juYua7#1=z@2~c=yD1D&pIucnKbWxX zE+9iD;0T?E$In`MmXZVh&eXRF8N9`ICG^L|fVHyzFPU3kh~TNFtH@6EN558q4y?t6 zdyb|VG*Z6nE2c~A-KyrFpz=hQzYh3*z&+EA`m00WYP*oI1vn}Tf9WADa`k=|F@J$1 z*Cn7Or0$<0*L{oSgSPtVfM6?3MX?6KDX<8@T4A#{HO_W)g{_}MGNDRxnC(#i9@U*f zX35En3GnP8s5!QH7DY6Q#cZD5@;YRufQYEYuG~B155~<(1Gn`YcGv~zg*i6RY~en) z7;c%|@?eB_xxvgDlys};!wF*UG0QuC@L+}H+ai2+@VTx!zttDH^ccIG;K!LDz~Mv3 z6`-a6KLU&s%)c87DKj-wX~rlQ&0SkL^j>TOrV8$%Vda|J>;3Z8?Bh-uo{>ddpyHJ4 zK4t~2G>ZlqC*y;B58DKm^p=pTOvd0%f`ZOMNxU{QP%D#yc@bS&;+`1D+>brsoOf(-MP;xXi^G}a41}i;#VeB z5(z$@>$ZxOs}w{USlu2Ze1g^=M2)gaG}1^pEt!+vt$KObPM!Jt!ZV7(L3?^31b^qy z>yOQ0+@I>Vc~KEXC~oK~oPq3OX0c2j0-0Kij^y`25w4CHHU%a?MdUmy!rDQpRZk#I z8&TIeYdxFGnUW+~6Ec?MSyEJ0b}lAIxK@nZUXmj6RXS0^+B5l96^MC93EK~?vwP)X zM>d`YaODenKA|H1DDSNxwzZ0< ziF@d_-rQ<#P^REY^nP9GNw8EHyt9KX1(w_!EE$ma2b4%MH|+rc3tGY541zK4{;z>3 zp}u}`Tc1bM!%`K7-I~73XkD71;Ma0{cp{J>2|+_(+3{X9&=YRx>cH1JKf?csn;KHE z8&Zen4|`H{#5^kg5cDVCWUU6UxPK@IUp$nh74)w0KM$p)gWy0&3jB zF?gb#cD%%ix2*HNV=RedTa{)IoVy$7!T7&e$h%HLPljC|H+o$gZ%qc5$IPd=EL$3r zPb_crTw7^XCbaxd5R5N%)eh6{smPTyh>fyHA`oE4j!1ZeSn&Ijm{bB zmjE5yFG}up^_$u{E@^4!d5diI!)3(gWfdllMvjs?oEZEHrL5EG6lGjt6l#n=fB!LmgsTrQ6ULl1vnq@ z=!`;Bs%V}vdat+qW{cd9NmB6Gp{3%)7NleJE#!vQ>~!0gy{oz2kyOFhWB4CI91ha& zOe=Yi0Y(vJ**f*0zOR2S6+~I28wUIN*TI+kbW#3VvQJ(Fz(Uth-O>>4z`3@O^Ou#Y z)>y|~j+%FKC}rjZnF|G(^9XYaQu21rWQFwEM=#5-%04wx_xEX8_tI6h%6k&@OhAuj>s7C)bc!Jr=tMnx!zXr4iLm*!&88~m_S(-Dk6UqtH5KaJySVR-XW$z8lwT@YszwLa_5Gb~QY|g=tE$}Uq%lU%o`Y2k zd|`i?X}eCw7IT$3q%{IHVewCn)$!W|JyNI zMgI?*BT*814jM^JuI~?I&L@OXS!e~Zv&vE{e3|LU zzU(F^F)gdC-wv9b4-;i$RqbRykXjjdIP`x#=>LovIf5e*^;r_zUv(s!p-;wpt2UXX z{{ywBsk@ii1vGxs#DRwg>Vc8q545z~@fA#MS8oZ($19W~BYol)(*|66t&WNq5US?S z;ilo{%8ng>>Vlm}Dd)fV`p(`7QSX`+TweV^y>L8i1TZWbsJ(Ie9qDbzi5;Tn|LMIh zgYZ{ta2as^KkMs1521k&krLJic)n!rc{4((Y#uyQ7w^8yVGNXSAd@C9wqlxTOB zt81v(Qt9xNCJ$dR_Bn;<&50X1RIGh*B9eEeOX=_(4n@Pl7(^cJ2G}YZM2I)}V@E!q z!EjlsGq_*2Mcb|@TkyHrx>jwrsj$@b9~h)Yma)SOfeKHG8b{KVUtb@h#nh=y`ELG1 zMUJ9fgSNoK%}s;0p<{PI1GUUG4{AV@W@SV1SUllYC8}svlX9J)QAe0m+@KlTg%7^i z6#`E$7MFq$H<=T3 zU|5CB&&=Y%J_Ih*zK)T7|oZ7bC?z%gQ@OFBD%Xl4t^7jb6_*IdA(rz~)LC!oD}K3;B)ERqV|VX6?L!R#h3V8#v*D^#OO;^NYqxoh}@ zUrcV}TztNK&>}y`S6EB)Ugin8Bd@&~;5y^+UPYb&eN+pYUQX=qk(jfY zjXW!<+exr+l#2qm9phyxiq?%bisDh}lqT+LjYAYTxH><){pzC8rcRxwU}1*b8)4zb z^nl&+{gZg5M?PhjY1`Nnm!c+it_;kFS>}KW*);Qp9Cog>GWUOz53A!p^SsQ~-3=Wj z;vGt^4hvhL4=26?xIZ7nTg$?3GW|vgo#%(&_tC1>MFXy)%~gBa{N~N(Y{9u5hNnWv zjgx_4aj0_i*8!w<+PWSM!1Oui=Tgw>GtzJUZHW*%QO8zG8}?4P_Tn2-xf5CF6eccf zQ5nXf5Vk}X`&0jxNs;E)!Hr-x6S1iEtHd^4;bmk@t&85n;?D&KI@os{R~il~w@`@5 z3TGi`0?U>qQ^TVq;Rn>-zq$e*j=~t$8k$=6YcT;5a4%55NKRHu4ab_tR|HyfN2DyLo24nwC^MBs#mPb zmKrqYQGSPNn-a%WW=dY)7vDNy@Y!D&9_mw`mx? zixj`enZQe~{h9M)5sn4cYtH%sr8kCZRz%J6cHo;gpBDz~`Iwr`1ixc^a z9|9K;a9-WrsfaIeSizF-`yQ1Yo6uubo(N;nlX+F#KaQQ+ECm~+8kJXFo9D2(epEX= zImdi8?;iBDI_I5!)+4|0dFIAW(7Jhklh-$O?ex5d>l{IH(+IV+U2SaB6dAS*KT%#z zdn=LJDYR5#aV-<7v*Tf@6WWyf7I*EFS-HS~?oKGstVzr8e+dWkM&VLnIURCHG|@~i<%9;rhZ;58E~XfoF0*# zs`70Y;SBJheZS;i*#5xJ(D^w)Jx`vD`&1eL4NX+!)lhqPsn|iG#XR-?e(}TNaZrse zWC@23Q!?pRSarR+y!FJx{1xD1s4B5WTZ8*ug#*PnEg}5}WMNE#Ef#Y50@aQ!UV|f_ zq`d1NH+LL$d%>iotheeVk{s)+K7cW`uKSC_eB(D?LoYZV1{?-;p?D`@?R7FPURNaQ z6FlVzaowVq2&}Ll{HC=H`>JcJVRfxINool$qD8OX_HvNhzFP}E>TTc{)+f)S@&n`0 zXhO?drr+C0UJMd+*(qWSv2*d?83(NiJr9g#j|>M>;rYzeu#>7$A4zM(Jb_o5EhJfz z)G!YWYRSSEhY!K!txg1j?Y7e`eYOHT%eD2vkuy6`1wc~oSpNN0$0sBYKwRt3=*KQK z)mG%7t7vlJc|n&6OB~^*>U>i=yf(~SX3$LTF(*rDVI>Z2<%T|;#Gm`|q`%>RZDNHq zxO-LNP<#iNfbXs+?QMGQIa50B;*1CGctx|m;4=`D$N75#Gw5#rm@>!nt+6kGVoT=h zY3Rnz$s8Z|{-XDsMuI+qr;bu6$|zy1G7>0v*Eh`k_v7VUi$IkpAtS$<{fylewE@Qv z3-jxc>F$rs$xLsXUN%0K#AjQ`Y^|4ScI+t?*JZ!(T%i316`I(%j4JDV z;*T%;hzL?QHTy2uit4^MmC`Ux)4EocO=2l~!2Jz&-tA^hhp_YyziQFh`_dc5M_yTd zCLC)MV?HMTuavp75i^w5QEcERq0?V|9^`ybUn=->rzJ2wIVKJJU`J=L0Xq}UsmuBp z59>z*0A*YFc1`ER4mc1wAL=2T%1q8>dSR)37C2o^$N5c+AXa_z@#*!Hxh zGtcnHvB;>KxK7~3tnyiX&ig%}C$20SrbomcoB5xDn*SI${~)BH>1t}~N;UpPX4XGc zB6r;Xt?zU>Rn5M!MMtx51)sJ zH+6$M;Ggb)5ZchVXlQ=!cGP#udH`z{;3gq?5}@FC*BEq@XE~5}1jbb}by3s(AY3^x z?ovamU}>O=biSH2CNf-7%L8$n%i`nEP*!P-L}wN;E2z@LGxcGod=W==Pnr7G>FTXu zwI|(%a^j;QIK+2jOmQW`D%?dom%rYtxVy3#;E87v<%v%x zYSpX9>G8665QWIN^`8u^Lp%@^rMB^W?Ad;nRZ&!CXm&&OGux-aK^mHh6H93j1!($i z=i%(+sesuxIrgQc<%iQa=UJj9U{yX*vcn%iSK5<||LN`VkEn#Trq;Vu=gMnapo|73 zGz;jb^94*R^&>0J$6w)dgt7>p;s|B+Dr&Xb=vm%w!*BNVo{*|T;P{@F6aQVU-Dw$r zQJwB`XUV~2x>z8tkQ*pCAG!?z06d1$m%|fx@8K|iCy~wFw9q%I)p`C%qyP=y7h%lY zP*Ctz8Nj4Rv+nhd?lxy3M*;$fqOkhn2bu`89WOTCafJGNC zZvo#Ti}w4^%O;z{%u=HHB4kA;^`E;2(Q&ym?;0w~Gzg!Pr^ zvzx(WiTDasngim5UxwergVy)4bi2{ulKL?L!vWJ!S+HOSb1y|W=_ypIlYWeZ`C26f z%>My!kiSj|>UOWuS1&L6Z`h+L#2K}qD1}2J$`;bMZpU)YAqAQ_O8~UDVg>m=QlV&J zIbHgw9R3CK0x&6uRP0vw27H0|$VhrmT<%cd(8>l0bkmDmTZU=c)TYKCacA^$7 zjP0;LhPTVxrc8oif-23uWXd|7o@HK$l2>-LdQ&CfP0GQ4?MfP>o%k?mZ}~2waY~(Q177c z^nJ_sk8ipkJ=h|G$?8c-6$vv=#^fLMq;^u`=YfIbv1%2irlQWe zJ~r-H{5UpSZfGibT?ZfT`-tRbWFf<0OdRe~qWqDq?$!Cd!A1ABS-9u5=g$fFVJS6I za_Y4%(ecNPl8fC8C5+Km7=gGQyRC?Gk-C4J68)>0$vS^jY1yHVgZQ>%T+vQ1?R&0A zJY!c>QR<`|3c1b7$9fZ`50zgRdG0@gn%@+~$niN96XRE}6Rs12<6Rr?NzCF)ZW%s$aq^svpX~hpPXd3xF$%3%V2}!$XCF4>d0Yvo;LcMy6`KLa22*O)6v6CA|UB zx6$y^orA*7f$gtCkOmLMoKL%V$gdJcUDgJ4$%DRrVM}MHkPuT-^Dm7fWAOCzlwI~M zNb_Nx)aqp*1m{SX)EPE)Gu1G6ln}BL(adRB+Sovlp6HLuuHwzQL{nLFV-tk$NigX8 zeitWLOqhpzNj`IDB_(<`V z-mP$$`8?gRPC?7;nAg*ZNcaUvdxq2VfaQL@{a#BC0}l|k@)BB;E3f#bI)=k~Vfu$y zQ|*1<8R-Zas2hYX+(?5!F_eT+YyQYoaT-xq>0n~sa2FH7FBKTM^d034tt?#9Tc-LecZVCRhUHU4PrgNgIAzcv$>&;C^VnVS z(1|=DIkRPzA!2l-K&{WM)qu-Y4gDyC)VH|U@A-jst4+&$O=NBg7GR}Oi>;&dJ+oiO zL1`@j8i2T^Qj~yUx<&loNPY?myr1?_vRe{h{q3`u#vd&a?A6_5g)Oz8Yk;Y-#JP}725S_Il`!aWA0;B^hFYMML90*#{xLt9D!A6o zOc`Rt*3OxrIU$Jqsp%r+Bk>Mo5lC^C@8Z`rg^|rCoCB%ON%f2I+_nf=$JST58>W~8 zf%`#LS*xFue*6~&4Hj>ywbtRND|agMPwx?=k7&A?Xt~>lyV>Zq+pyCOHV3ma7I8@y z(b92HV@4^fIq6^DWE^secioOyIBPAmvd>&YJ^0>$Vr`E5Ce1|5jZMX&D&qhO2+q=V z=oK*@hvNfWI2;<70^%J{2YbZ4UR|c%SoO z9BNyvLh&KOnAgE+tLqJz;UDF?|cWwg^lobtZvZg}DJk1^SU`tGcpw1;0 zGkx=qM6v23?q3B~?Q|gb@5khwiU*l$Jy#zGzO*rL8A;FzK>bGpS>l2ennSWnQ$e;a zXxe@~!GaTOLu7ajhyR;%qXYNFyqvzE7!7A_#37KES+?H9S={?FL}j*&354YoR{yBu zs^=AlWfYW3iNwoW%D1593n`+-QwXqMJVj2aV(&wX%X{ov(R0OUY*1CRV+kIJ3ZyJn zTP~{bp?V*-e#@!>2n$QQvBf@yajw9$575x!FAT21)-9(hY8r+18ZfN#Zt$-DNvLXW zA810&+v@+ZFU-bq*XXCvB3*)jZ*Q2CYyXZqL_P0Sa_u9$oX83cHbmCM-)$`&W%ux( zY|-1I(L;L{>^XQ8;Zjg28qGD14Z*hYS1Q|m=DHD|!j}y0RaPwk?c3A*zSbUt~{M$faXI|s`O6$2mDCY6| z5&GXC<)H}+IeB9LnWUdg3C0-B?OP3cX})mJ8W$P#@$Ky*d~E!)yc1<&)V~c`K0nhC z-D7Dy9-j|!>W43Gi)v@euBtzTu(*dw>C5z)&6@hGwfOE&L|L0r`WLv^dy%|)x+Vpn zf3XGR08t(8>Doi1AEpDsK2p!8a5Afj&@;&H#IF_*& z?wEyO?duH>R(uH4qcVcORKj5`>IAUK6=iWh=f{okY*3GCKRwpjTUwaK2Swf1LA}<+ zV|qPhnKzx0?9?g3r1I;mYCz6%J3orLJn8dR_v;tF8&qD<5a3>8<6bJmVZHYBfF z2-<$#2{#k6cMX#LDKPo;qK6td0h)FeTn(df@12mc~U0fpgJ-X~qCzKxj{X zsbxO3k(r3AYamT!rqW3Ou_q`6&KVPExUT@l{SSRMbv?o^T8wZyNRk~ zOq`T7^~qp0mTBjwqO-0JAp!a;7nf!*-71pxlQ@0h!&w^{=F5u9x8794IdC5DJ;bZd zT@2ivqG{b^!`5DSgf00Mp`dVOH_yQgyn~#=jy|K&1O&wGt31#x0q;g$vil!I)~}!F zRI-dqghwm)wo?S*MLEmtA$*nBs6x=s-4s0br&8>G2v#c3fkL8JPtT3?W+Op2Z@6pE zg(NGMo-s3xcOC8%mk_J@Puqn!5@@7G2j~po#Bfr%Nbhz)!f%f-a|>PK#RCKm$-ryrWM%4jf+4Fhu`UP2j= zp1J{dI{#~0cy}MVGBEiEmU?5;Q4HaB>uJtJ8q#?g64FROL4kMClVoH1FK(osZoI}EOkk9gepWu#+IpE!jNr|M_> zE!lzi;B87v4FBv{3}co7EZekIAa!#%B}fhL)NW*^<8Wo_=zD8mIF>UTL24R)B))bi z1Kc5@y)}Oz-Vo?wbNC9=AStY#zgGQt2H4=2*Stga{w9}0^f^$|7_^^$)(00uFzR)( zIu1cAXRRrHk5bNxr|^%FNZ5j0JwM^{QORoXozbT0SQ}X?wEJ%k;#*G(^M=AE*FoyB zP_!p8sE5@#K}Xx-Bc5RxZHL)m=0!eqo5Pc%_w^y@eLtob?A3>t{Nd^Rb5fHXOgIkD zNmh&+W1+kJO!V0MV#6MCqO@6(lX`l)847bxPD)V`GQx+DMp zQY186zZ(n@dq>5%U9n0%d#zebC9s4kT#v-B@nkJH=*Ce}@L zWkLX_BuAiR=l_5!+F}dx2z5h0tkT(1So{%`4NVNw8r5-B-FZh3Zrv(Yreafhitmw1 zd4t6ieW@^>6iQ{(Z)OqJ5-GHq=#&v1j8j#z9BivTd>jEq8#X=i{n`w5wxsbE^9Qdq zRlGv?cHSxaW#Ko)>zOLWGSi+QRfCfh||-eRkkp?L5n;k9BP&}Utl}H-@ur!G4%1bKn-SxGpLpR z4}B&`1|clUW#pnW=9PXEQN*sS!Z11DV|9jr7TMSK_p_C-XnJ#C<}j&aPG=10s+HwF z^u*#2pkEQrPYZt|!#aJQ5iU|OYf);QKqg81og32Pp*|Cep)xpaQl-wNfDKk%SgV=O zKPSd7?HH?GL+`D|S7a<7QR`5WEg=#SQvnH8u&K_Y(N;>DV)*!Q1hR4r%V1&+9~Rp&|FiH#aYK z)yP;{i$Leh+fd9BjDV7n?_Sn7BjkQZ&Vt5aLeu{xWm#*{#p1uIyjL1bT?!++$QlFZ z43dybEh~hDSD$6$pUtO_V7`ftMq8K>m$8vSJHs&pOs_)_=7>BOB?PxG}b-0#^0S^xrjARoS z+sBNFoIW9_4r0n^v$bAl(Sk12F~yD+$`nbpemXSD_N#;XqWyh+Pn+?kxq0f97`l6q zB02u{(Ytz-w2|*xpffPl4cU;me)XNv%jK^~#=m8myuxh1b#PHxeYAn8KH{M|r+x*~ z5k7W|nN`Y|V93;I>KV!lYT<%Gt^GcP`(9eV8rZ*ud#XD>9dc4DT;79=mcWmiigKiSIUOSwl{(?D8s9 z`V*>5KnkHfDNhZuR}CuD9;wCT>k)bLg+?V{$kPI(QZ!aA=HAe`L=J2xsnkQWBuSRW zQmP;S|4F4Bp>z?Uw#c+#ktqjLs5PD8aO{7Dd~Z_&&Yo5*GJyB#0J>9~sVL zznfkNj)R;wW|*Ntf_wEljJq!z?4|uBbE{7QYXS8bxwM&XA(PJ4`jp3=bj8Y>Z*|Vn zoia(aZ=0uZ|mP4QdKkH<%&B zF=;T*D^oxO6I^>XL}5UWH5r>S6M$DNeaAYs8dkj9>6&Z@R`TM^@SZ-eN2i1UIZC(Y zx1)eTcm8ny@5DyFSg2w`Jc65p==&D=UcrS}_*Eqk2ThaqVASv}8m&3SRtv5s`FC_&XtZx&F>%h_nXh75 znZy{zT1$t+4m^jfyc+zuC$bHA=Fr|&uy9U$GU#uqv!&{2zMk4J83bY@@%b^W;AlN* z1YS$+Px_lQ>Z&rC*-S}UZ_GwBQ2hp5OilC!Zk;uiq`De*Q_F2}w&bb@%c?6;S%7%i z;QUgXeS>crII~mwoLt#G!G+UJ3{*lqLRV4Rp>eg|ZG6GA5$u{3{{A2quFbcoH&u8)S>eo7&|8|;g@BrE=(vfJ- z9dNBKR1~Ch0qEam_R?K6V#Ch@P|(qny2u^RdI8oP63-+0=Klw?A(0E~^+pQ6n;zqLBtP$p_QR_G@r?+MPr zUj^n+FThrq-BF>W2(g0@R6eCzRNgVpMX|RoLm|z+YhPL(5ec9NA9C#b9w46tOgFg$qik2<`P-H!Xz)SSqpR&l0NTbk&pi`k3Fr<>eKE^^m-$fJTK+ z55VJO2eC2L^JdUN=;r1?bII}>K-u)XHI_2XhXlLRMaC75;=Vf*aILPj-vKa<~39WE&&3obElh%Egpil2#Tw$TdGRU zjSbD?d+P~IbQ+x-s=yfZJ$`SY7TrJGYu|1hxW8W4;KRZUe;`!8z!$=g@IcMb!wyhq zo1lz?a97IEVb3%Oj*yj|2a>mC^|J1M93InBX&kmii)~Ystbsm9NvUE$vM(k9DRmPz zwRiRE5Ac)7J_?Q#EOxYT(018N-kHcp*4?FQ5i|I=#M&yRN}082 zpQN z$rrh*;98^(o~=zNjV~q7?@#_s~tRqps#fps&seoigzW&SG=^8-YWwq*4z5lWMjaxZ< zc}~wh5;4=eUO9q{t~Wc`u4W87kIizWV6-D zc3bnO)w)qeoQ*tcZFOv09ox3kv27SVVqMJntTk&^)u>T50M@phw+$egKp;nP94~55t-U_{KXYcq2#a*~w8~FcWxT!3 zWu54F5qq%$_{Qk9+%{Mn1nWwg?S-XZQ;l5yx_sh6c-C6V@NRx*L1LK8=uK?cOiUJJ z`<+8l#mv*wti`}k5vZHGM%l%V%9QRvI^h+eojv6(iG+o|mdt`Lcp?u+>i*Rn>!B7V zwZuP*2Skbe4RbG~Qt44-17a;aMsL48{;v(bu zs37XoQg&4^`5|obbVOnK`f69~U~XjBfrUD20M&0%rHfRU$-?IRUl zc%3y&Ww|KG%w=d?95T3BL#47L_b{>8mwY>O4~1KibbS(G7a?PQU6vwZ{MWPE^(!tF zZysq-NkX18#~4IMqBWTWL$1&8`7wm(<{!aGyr$RETV1`^sb(3!nDJDIq17FzAM4R z){@98bU6M{LOpl)xk31Q7|#d&>0IA7p{2P5fAgsI*^~0`6~?9(13&=cgN(qXmERW` zhOt<4fj87^Akn3_)Ijzt5VA`_*n^e3dmU2Fn&ScNaPHN;S2`=DAwUmt#6>kl>|mf6_>t;1@Y| zmZoZeXD-g@Ho2+fK!Vg{hK42u8cdGLqb+(f10UrVNbWMLq?_D&@@P5iDBe3i;w)mG z@z=a1<(0-Pxd}|+*HCc2M1^+V%T(BW{-AK|&q6MG8?3$ccsgU| zY4ZAm_U1c{>N)4pzr&O)f7QJ*&qF|an6J5+;r?kAagBYa#bWyHGM@~c zlM9epeS5r5uB^f@e7Rg7-aw-E>b0B?`Q$Btj}q49@QghOZwWNJe=l0Tz6{QCIU#P7 z*loL2wWkVdiQTPBZ|TnU=y_R;icCa?hox3A=yxCT35~A@ebMCRj1-pzsBDqajPQqE zd539fdl9)9!j|jy+i1@!8(NfiTp!z-JG&9j$Re6U+qNS1*)n<5l^(|pf6_-`_@(6U z{8n{BQebqEyq74ma)9bcYPd6*4HOwawdPJQ?V9c}JG{XT+&Y**g63ejx!}vun{^YJ zkACwx{p;3|>e~50Was>pB)I<~YlCD}Zo$M$Vq#CPm(%L_RW5eD;f&}H}9-tulWPW{#{*?lP7_g{1+GFLnh;UZG~8X&8!P} zEZ3LsHrzok1u9Z}8yEd!^e|pC!B127LoIGv(q)MWi4h*2`?uG9fnB8dxql8|;~LMs z7P7YZ?)Dg^_p6dKTRWpmbVJD?+pDA3hK$BmOkLc_-}L#IBS-M?E|pN2U=MM4=#w5W ziYt%>fvh5RuBiolru13SBfW-vc(T3iE);E`mS;PU+srY#I zfO&Y9--!~p+80L5B9%pi_}1GRZf2&j>l-_?QY2VQE+!LlwxxV?HT^+B$u(7tdnsM~ zwZZ7W9}X%jX1Z_F=_W>#TL*x4Vt`_}gcXBf7CR5Pc`aYKu zmu)OdOK84Td5#9di7zAt zP=L7+EW_I8mY>EwnAL~r%nTeLiGMGJX>zNH!%Ww=BaZ8cWiMaC=NW*FPT_B6SOv8o zsj&2y$8Dcs;*7)C<7|NDU>;Zw8IG zg^$c*{Xw?pU1B0R3xyB2vj$q=KHwJ6z-rh^XJYIn7qBMdg%E!k4RD(Jc?Tj#PS@;0 z#SL+(>>_xZ`OzqYL>lEXZvqdxAb%0&!sw$gv)J_5&sERf8s0qO zHnAF0r?$r!pMk!qrLFx^1noVdH(s^bFJ`~N*w>*mxa9Jb_tW9$72gjK2bry>jm$&X zlq7m1Tb1G7qu)HYBJ8CVm`r{T%)JqMU_7`z4}Z-tVb0)VcY9gU7#S?|26|>~H);@f zsP^}@s4s-%D_oEF7ZF}Foa_9wX-f=jfHKjDt*xXYJ5y_biw=LW0S3JVpEImh%}T zcS<$#g0k{4DZj~1#qtrBs2FwB`aUN1UA6BpG+ng)pAKtMz<8&&;vCL_xQN@G7N8V#9triP?i%uDWVbsw6wc}AE^ZC~_64Q}H5`5PqZN=~1z_(Uw524m% zia$Ao#h&1YE5>PIj$E!(S$b-rx`UC3$fAgtv+jHu1>2JtCQ@$xoqqTX}nxq%Uhk}@!q=-g!-y@K7H%$dbxX|6qZwiaC zeK2?!8O`89;Kg_=B)M1LmlM&oN7?sUp31<+$sVl)BT%6@U+!WSS!h1*jjl@wuYwp3 z$saGXqea*bq{%((o1n#fVRLh9YuElnMQprWjn&k5qD~OCq42{@#`H|F>tP? zr{+^Sj-0lDt_DHC?`L5%AEQu-uNILYoV;1!mC|;W;|F(YQtEMdHFWB`$@gYiZ%eme zk*$IV!W39^Sm=kNtuoOL0d?ldA$c#7f;n) zaSBa_r=1OD1|&wiu8PNf>b*!?=vUlOE6s)iZ8?1poYe09;2qZC;vu*}7{jb*GBL&Yyo-cPaX(naIaOz;4Hi*kniQ%}kfm2q#o8X&1%nf6fO6q!AwjwnMfY!cUO z?X3XC*L`{wHAcpgi5? zRQn2D6}<&*q_TFl3)hoIq8`Eg$h&M%Uvm?;;bk_}!gm*_3}4}YzP+B8*-!d0E z;1Nscx@#F|@K#8kquLvjtCtEjCsLvOIIwtwY94FlVLKyraTQKcPm`s@K-v>gLB7my z(c}%;C2`j7oP?wikzi%sL->CxIWk~$bXrbT#@Z6Ql-4S#2dzZt^V|3JLZ&mLg|nH* zHBey;uiG(qv!U)9e<9&63~xJ>wpK#pb|8AK{*1+0>Y@sdO6Z9*Lf}@){=N0V-v7&w zKG{Gkcn!;n02FHq35y4@kb=JAzLJX_}ptz)ns4y}l zP;QsbcLbw<`vx4Ad3TF78K7wCZK#H-L15Ga6@m*kEwKv} znIO?u;%4!dyF0aeyU%!tkNY@&k&f4&U_lfpBqBCrn}z-ncz4_vJ#v|b^_pkzM%r;J zj*`RJ;EX)_tLYxkhhu@xN>A3tMs10+ys4?zoS80v$Vh6FR{4HtP8=_DpM`FAg(bKT ztG71KR}7&r4UO-OKkU0}9&D9e49YrTD4vMb*6s*iPRHjfcee@oN{Wx{ZZ zwgrZ|sH+-L@d5&;Mpe{YQ&^XKP>vL!5M{qrnh_KyK+oHzg>+wZ1wYk>* zjoIMofd4Qt9rD(4)m8WsV43ECb03By3w!jwvt4Sy&ZXLy^61NqK zxUT+TmF9x(gHQbf0n9lb$T@0?1vfTaywjX9f&}UC_#n-PXViVb$OVC zQj@WU{`?yOgK|GJ{MErKfi z>oZVid3;`OOU*v@R-i1o;bD`3%?r6{1-_q|7s+&yGwock>Ugb@{u3rSR(=-?trXzG zlGD*$+ixfv++!I?JSNjI(8mIE$PE z-=;KeVsz4lag~Y4=Zxk=x&`RKZeAtd3-aa~l|p4@b@GPf?s~otGp_xLWQ{TG5Ou$X zOJSb-AuTca1;v?0c6_;}5}a)xML2d-E7sqgda-uMXf&TiQpNd?bxxBVJdeVEk|a<4 z2x|kxtkf1x)^;0T_o$nFfho)@q)qOn+q+czzsemR>Y8}2IAiWcF&U+!(z)&QyvKr#Ms4;l^MX30Ll>7Kl~6fBQ2RT96(8q1ty>;ADo zM4{R7+8}RgGxbz_2H4PrXeAcm4m%c|i>x0Vq)-+)cave3AX_TlrEI60n3)?DG|mj) zA6vlOqF0`q_9h=O0jX;#7eGM2Nm8?HDo_jrIccCL{7O~vyl+q2B>;8A$L15o6=jdy zF;a8idyH+5jDMzI2p#pY=^-VG#G|&Q`4_w%P%6*i)C#pc$4v!%ly^EW?Rw5(jG<0+ zdL0$uEq}d-uUm3<$G0P}{9==mayfO)BBxM_9+TbG<+;Y!h|6+7Yfz1R^nEFl=lXOm z8oQ^b$2?nU+!g|WEv5<4+o7)X`Gq(I^WtPujzmbPy5WL+4P=#~Otj$-&c_U3Q-q)h zxG3nNS*dt(@s!!VVI#oZV^0vCF)h0&x32SpE>>Ui2hn9N{QSZ`aa20JU9-XhEHm?c z0AQ)W4z2{Db$S9(b$N1?l}wSP;TnAu<#rfUIINP@oYvYiWmu(WtB^F@HMq2$zjbSs zp;nh`B0sEW2|)?~5Gi73%~h>gCNX>fj^?hq<_DyE#G_+|0bG#KOZMtrBl)cRu)Lu4 z5w>Fa3cHV&Vdsz$c|K?21rE76SC5g33h4+q*uUwq@O5*ux@Ge;E_?#IeC{yh3Kt*1 zI7IN=qW{0q2||6&L#M|K6OR(>OB~k<_+Hwc!s6q^10j~AOP}D?u@~LC%hKt2qO|Vn zs>i{8YAl~Izt!;_>ctQG=AADTQ|05W=Qpq+es|YtW(Df1IGT-ymVS{?v-xD8?Eb8+=$s+(&@$8c5+KqCO zvxFPD{V+(hTt#|}p2E`S{klKfg4kQtO)wz4nCGktLj>XpcfAYuTYs1jq68S89*@E0 zm`2orVbX;?0R`ZH2;Fv=)m?x8lX~axB!+JgVGZP^_Mm|<-+jAWe4S*)q(~d!&{y%f zo7{A#8v!5P%^r`OJ5jhV_I9|uEG;nmohES-Ydf>cPQ+oP>=ul52ELg^{A!~3*>S>h z0Vho~JnLN}&N0LIxXGL3-9%?JBA~s%sq;9ViGzj7ePW&5{4IMvkh{*q01Gv(DG7`t{q$ zZpq(agV{tu=lMJdavKPvU#JdDN`EHeVb(wFveEMPp_r)|Vd3)^pGSkp5c3U2^ z6!|h~l_B79kq7uXnZbw4a-`P`e-z4-jf|Kl;`_pB#pqEIb0`# z{QkJPr_eqYoWS}rAj9=o6{T_6$ZxKJAJ~%=ob<3{->2fhly2Wv8bB4@ug>p#iIX<= z7BSxV7B-Zt$=iOVd8}!t#qrBXODLH%n^r2~VS52z|I?U*GTX-j`eiYra0hnok<=1$ zTQrapL#|yXcKbINr-h0E{??qgd2E+?IMl~rk+(nQW%%mtJ~rU>ad5}s?t*grl$^@ToR82P4Mi6X$KJ|I`TG9zlE&D?U)AkH8=<+Xzb;2$dPJ)0M{*%!I;TWST z>zz~b7SL&OsHNf$m74vEO^+dFS2Y{Bo|}x38@i-$B|*Y*T37`AJ+z@~O(;b3fs$e- z_h_I0^^O1IB6}G?7Jb=awK(y+d2N?{v)UklrcT+CgQB{{d(_4)HRqHVT`;hIX|OLD zQXY0XvDS4`{82j1xxYyrV1K*Q-}4Ctd`Uf4pKcanK6~gvcu7%!N9yzC-^=E z#Wb@ADW6+fJY$VA$wgjj1f10!K|o!E2v<4-u?_P4MPN$1cLS@-c13$s13PW1)`2f9 zyvm8hQ&w5mX1_F>2>@o=jFf6|QE|wCx$pIyd1`6@|sfU{CPgE0!*^ zwSDCom~1S2?CQ#b!g}A1K`V)M_4qF-@ zDsJQfnd1p`FWo&0gC-$WWcSMdKEO9z32z;lUV3hho4!C&;L7)$7qWI0TkcOZVYxx3 z@9l2i+0HDj39o6RCf=FIi3dISN2SwjCH4hnAiibHqhzZNC-Dyw5f~J(@E@dCQ{;4p zOfnv}ODZhBdvc3BM>(p5+5V1*C4~Y}NsHx@QZJKpvAco;;(AjRxRHd-E52eNy?C|4 zsr0xCIQS2;)s}Y9-no+DJNSlG2t~V`LZzgq)>*4+H!7Q*O$O^d;OF+x^<5U%p&Kgo zXN|wW_gF*3+N_$GzVt<8^I4$fr``B0w=lwPAjQ*do9Ex?@;AFTss49Vccz*H)w&Kw5Nh?EDEpJ*%|wc z-?A-;>V9vg^(st=#!+g9!bqryKVGvo%X4*mCdR!vYaTGuKrmQAvTizze)i8Juw?(^ z#5ae*UO8}^g`Q`1;R4Wd{%scy3`Oa&@bVlV-`mi?{H2Q%r{(Zbueit zb-gimjqRsqptIt$sP6yl;eWz#8KT_*o*4}Vbc*g|n^Vu0^T^&17Dcv&BuWWCl!PM` zYhRjz+6~@cgA7|)j7y#aPN`;r@LPON5kp%#9XF>rJ*H~1(>ke)30d7JN|HNkOKJ+- zADP5$z9B`6fEp4lywg9G;5Mw_-Yw|}>s$F2t}ANAT#823k!d}SA#Tp~sFsN3_{>>U zo@S6RZr8tvs~_RItP7{m40m^FkssYFvP5rQW7HSVWVw$dYo)P&7k0dfDwySsf37*< zR{oO+lWwYtljM@L3xzRqDYjbIjn;?yVGTy;s9X`!-P5k*^k#lBxSvaDlEJ1l*T5B2 zKS3{*ac$PU*r!;o+^7W%i)7oCQ$0VjKIv{bNtm~p-#44l{E2ZbAqBNvH50PdVo9zU zY~vh;Pre_CTRV0=;E{y+_oM#TzIQbg1SQVSMW<#%|KgThy;|YWJ-BXw`0mXIV6!)c z6fT!$|4nK3XSA?kYtf5*=)GT`D=DFJH8%qkRNV7Rd4TIGefD31c-z}>q;-(Cg6G(3 z?35{M^>$*UQJqLjbgUpyv420X*?C-XHR+bGd!Lrx$+iBQqOew{mJ+7&>{3y{F0sOA zKDqKBIXyasvlYXBw|d_&NDWz~hAo{8+-#Y9bG3dAc}BF@Smt}DhHY)JqTEEc<2;(O zpd|v>S1t>wpc#ZN=ShB70~jzuf#>4j507ML$J}ptY8|Vg z^UWLI-Fq~CA{D0kuFY?ml_8&N*-js36VQh=-B-jEUo8nttPP*+Ok`qiZk7PIoSnbk zxLuvqC+72LHR{u=Xy)Q3H@!a;-DsgD_cZKoFz+~M#H)4mU#~AAzD#b`>0&zX@XtAj+mc`k@?%_|WeRK+nnL(b$Io#~R34CVCd~(2kfrdy zJ$fuGIOTxM*eU@fO-}zoZQxg+JjT>MZ{L0zJ+OMC`?o>w)Te}ktTGUMC>L2<$V$VdUj%`CLB?Z_AZ z?S2(&x54Tv6ZPWt?g0S9i_~ zEQBBo2fE9G>c{b-S8xM?9AEeO=%omZ^z!e6W?HH&&)Kp+?%dI^E<^oEbeM7Ob3j#TdNfe34A&G>*jyo! zcI#<`NR2iHV5-DPW?#C=^hwlW=7D~b=jXPQwD+?n<=5!OfRi9>aUd;r`Ph@^`Z!l` z6XG?|?In!K1*W(8U;|>&!O=~tbmq*F{qT;0MjnI3IcF@!n?4DBwTZISj))&Zt6Hpx z)2;7z5L|thWwm^5=fE=kh+3&|cN^DoL zFlfDwCKy@Z(WgHokJ7VEYPp~CtO`za)5w-6P;rRL;ZxE6E_P&>6kmI{!i9S)7wCpa znY&K|WMW2Luvi4>%=y-(VV=9cBGC492iSQWjEdCi9Xu2t(*KTf(>=b%+8zHKigCOO z1g^2UMspCu52JnX`ynUz=-}*Z*8O8KoN@{mTvN=z?PYP9@K<@8Lu^?5>{K8O%LiRm z?`!VphFk5#%@wz5g_=f>Z!E4L=c8iFMdotdH6d>E<(wT{C+PX9aJiOs_QLqH&6{E& zx=Z}MUBb(o5>d*>3`7`a@XV|6cbS5j4___ur+?{3p%kqE_PCEW3A?=XLT z#qV!Ro9bIp&zis1E5EnzVIkoMQ-}uB-hKCSAM)I8O&3#^j|qm|RfR<4-n%gYZbuLC ztOB+iLQF@(9RC|+|F16=MEV4bF{vhK$NE!Y!ymMpUMFUMXk_PIJ=l!|yyP1a-$5;) zF;wnlu>_8sMZQVk)3pn|U)fzius2Y2i#DYEgF$&hX zJl^9+5`X$%@S*H{YuY5H$`D)8R&X*QqqS6ih_{z%Q_vwBR7#5hAi`nM zPL3vv_#YE9Gkltx#{?`(`c%qj!T5j&?AFWm^^~(Dt>vF|Uu+nOO6SU`pUgG9_%146 z@%g3CCJh{m1Tq6_Dq%k*1h$x(Uc^T3fQ&{c2@7uR&d6Ji(^XVza?Pa{z&3I5;i!4{0&C}7Fh=SRru8O%G0~8df;#O|tdAC|< zuf6ut80WoG+Wx2?74=^#wTq18s1Aw5AdpAJxXw{?s65?{61S;+M!cCKp9MP9)k>WX z_GWbo)~3~`oZ?|QFZDxPlB_thlUVkgI=!;ytt2Ambl{|NNeysEOzcPqZwxQ+K3uDm%40NM z+6a%cvU!9aACa7YD3nf1UNc+czF*7n>Rr~Fx-sVthp>%=O!rnESi*9&UnJK+RWQpY z73=UxnN4m4H(84>J9z&QEQ?WQ}Zpv3= zHZqh>mKk<(vm39o@Gg<&n`dkUTDT3Q<_a&SrxNYn?FbJM28TQS-pzEs9-E%xFz6Av z-CQ;jt#bIjB)1Y9v8OaA;4210xWSt7t8KPiL2nid%vfOi0^|TsKul`&XpRBjH zFE;_Gulvp+tq6a6(;NemcJzAwTal?EOclY=Vg;SAj zITO(5VB_VRC)h8->P0%0H>uM@VTCFh$MHx9WaxznQfbyd%RLKy`r2Oq-gMXXyW9mcB%pvZFo|w(h zIao`?BT&L>|#=Uj@zl^LQpctGj56DXKi_WK6!TwxvD zvh;flmv3(ptw`h%C0*315ny^!bI8#%@MclU7EpzjCR!QnZaWSKmBmeE~Q{qssA~zaDRQEX_2{3Y@BT z7qk8SbUo#RU*TOW#7_&lb)uki>-8}=F3?QYvLe8G(jT)5^p;_h%*E~0fl6wwLm@6& zm^iJq%s1NMWSEZ@{E^u)CKoldXMM37Trx(f7I%;)ez@xJ{k!@Ub{CEjY)YNdy0AirS z3-ks3vT#k&+USQ#BXLoiBC7YeR(zbPs9CQMF!Z{LFk_9+@$Q3YYRMZLIoh5AWasv%}G zk%##KUK*+`|Kuh6XJKHx6|xjCtA7&^ZulJNqG~ToZ+2^vh&REC*ocNFod$;>0$N zD)q?Jy;ElOTCp9mDwfUV2tW;K@%0gBzL)zil+q1$TU%1jUB2q<*2#eotudl z{8@kT_XRqq8!qRw#dt^`4@OUfbRleib$=c|iJ@O};TBUw=hueN;%+Fq&V(cN=>3iCO_ZLc ziCzw&pfC)Urq2@FPYu*U(QpK5Qf6rM@x{_sU<`f$PP6VXD;j^y`M0V^1L-S<5n1qW zAKh}QJ4OS1hJ=1Her=6Kmy+f7^?)Y=4pv{YTPnzwu0nzk@-Q5ENGTU8Su2(DZfh9D zLk}$CA)HkpM!^cJVL4?LgPHT z!6!hZ%L|-d>bh&lYPCH~@&MNgfq>yS zIL9g{(9$5qRNlzQFQ8%z?gvJ;QUrOF1$@k3e?GtZy?JzqNqkL3&~fvWO0Kco@HnB^Er)s zBKcw*qy&}F2GE*O`1kiJ^=?Qm2= zAWXja71@|-WxLmWrq%nf2SN|4dvqOnk(C+;FG zr8yOw`>I*HLct7yIrlns5Mkuf1Yvx!74!NQ^0#14w>gNKn<=$UCKL`>U`5q|Gw|RR z(GJ-;v(7jC*D~y~cUz>`i1L)wzx($plh~qqdNsBLQveSqSnih3`3cPdrQL(?yx=NmK7tZp^FSfB50S%W7Y=TGCl>dI16i^FV%eaRa<7N0nd*bAA^?PG zrr8;u&R6+3xU4OLpDmN6!VT7jWaz>z;%!?qlFYA#IH(wY(9Wurz|;4S8Idyz-9Cm& zT^t9Vb@)R5ybAxv$pDG3n;n8{$u&j_yV?ziYt^Qh7XLj>z#6d~wZq2?mg|c3R_+fL zK6AA1N0goxagyW zgg{^*ZXGlmhpVu#Jec1Umo(%+3!*+1aM3jL zVG376R)p#qMx2cB`xPwzF>3;(2@2F#c!f2g^9vL2k%wV>O^cP23;XY0WPdUQ!Y4YB zMLGXFwwC4#T^tF~0bma}X|3B1LRJXjzmU5-IPjn%N^%N1bU_ReeUcTLmz>{Hv4qL) zA8af#1j2tk0=&Nuf26o>3ZM-|cV*+6f|l4_(0VgNaA|KzYi#&>@V6S136Tb+$OlDc z$E}~laFA#{FH611fHC@V;hz}2i(oWAVEIiUl`fi6iRDS%qlV+TXgP>n!6|{tGB6EF-^C$NkiwKF_ zlFx?SO(K=!wZ89?rNS9TP=fA>v+DyCyUsaj2PgHO`73g~iqIJWtKbft(B?asjYY zkhCjNDITJvE{@i&wSh+3>oTdSg=u^6*1|U_T@b0t)Rh9V5*9v!g8Hn;s{h7~b&KWj zna<~uV84_O!7u%7@WrJW&Dm+&yAO`-{T};AKw-9|dw)?VZEb#%ep*NHoJgHOA5OMC znn#gHkIC%pEx-gg743$dc=^=BMo_^PA*`^&l~ObCy@*#=6=-jlBx10&5DEepv{A$L z6WdupeaGI$4d^E-i5TXihc5cG>vpoecThUOs6fH$@%St@w-XDP`Mv1i5B{4D&L)+` zk-Ssld*s=^v8|1LsmE0k{L>6yASg?$R-Jimnlb;}vGTb4=We7<$*(}w`{@FIe}&f* zQ4UB%LSqqNH`GM4-YU$9*waImQgsLfle2V!5GA}h7czYf12M#-UwJCL8TB$0o2$g6 zzA5v<)G_TBIq}%XLmMLDSsI?uo0;@b;rJBmr@ZOVRKds2zw~nu=2zJzaPF~gZf|fS z+w!p(@uHQ?ld$06-wPHTiuG9++vQfAZMgmmR)0zm*2tHviiwvAC}kyE-D1N#1;%zM zvE%iiAT{@_`MJ_cYgkgnxnCoj4b%+XD;7-Sg78SX{g0*HM3zRl9-=#X#OLzgW7mHu zC7m+$MC+dwR(7iR`bEamO;;2?0s7hIoAuz6eXq(6uM6)E=?+|X-Ss=(`=6NFQ;ddq z-yAD2J17+X)A`(_$bDnhYK0l9ZWw1o&F=9^xqLs*Q&b1x=lJ8o?IWhN+x$r z*RIM*ga5arWI$wa2{Y1}cuSbXz`%FErQg2*11l9c2NQ%OD**duB)UwxPS$l#W=HN( zlZD;zw%9-RP>0_Fr^B-exR#?8jA+~~=T>2vYYVTJ3R}1-4@nPCLz(m2K1l%kBo^xY z9&7a%8~D*~H$-3qb5sEe1YOC3jY>%F3LVh};vAY4M~jix`D}mR1^u|^s_*;Jrn|k( zboMf`0uMaQA#vaiKj07`EKu3KU@a0N7$VFz-pfZNdT){4!pXzm6vY9yD} zhZi{Mz&g@iB9iXm$Rpd*cOt0WnnG0=F@i`Vz(v4zkRK0GEzn}AWsi;tZd8-+==K#x1CY?rLGUaXa;BWqFNw7qPTITob_8=OuUkbP?zeF>XD)TBTCxb!}tM(2sk#+Z?NN&>bDl99eTe5m42jC>#QDHeWn}c&!()6O_)vi%4B5&AEFEy_?O}?)6Q3K3fWMCm#HA*Wv_vRtVxoe zjG}*OY|@TzZWL_&OGZK?10RMljuzWeND!2ezoh>*5(vU@>z(%-Zj@5+Bb=TQ9Ohf4U zhj}q511LMO3M4Hi6QY!?;tPjr2qqRi6l%>3#^y|*(bNlvLfEOB#7VOtFTUs%zCC3rdynNXrp;Rnx$;RRsxH$I9EWK&g$f zASz;iq<^KrUWBu_$%_$`Q(x`E6>O|sz_Z?LG~~gO%ZY%)zheKmW0xN;#0gk#T%U-+J2J*t$Ku4OxLnJn%}5{- zM8YyK3(DhzMRr4Y%m%jQ(SVRwHmQfGStb|m<)HZHWMEz*KHlJB0*y;p3 z=g?*P5d|%>aB72}v{zWa;{S&=ROIV=7(zu*v}Pw!coqU0m7?M4M(a} z{-Ds(8W!ivODd?TxX|=PV~<%#u2GaWI^|@?dM8PpU&{pz-+9<-f2TwKjd&x|dQ|V( zHwF<4CXNFx0K^QDO8EcAf5Cl%U}f!)$2?--USMmMTJXuSc%)!{N?`eG(6J=Yps1Pw z$2nHKurGl~bMf{a&x4tP>kWFy4cgbs(WeXpZ?QEn*L>Z z{B1wk@MM(#Wd$%${9{x^g-#O9r0xut_HC)SbQOU>gCb)^{4K3NupV+HYFhNxEAL<_ zZJlLa-?|Pz6^!(&A2lLNKABig^W&#Z?yt^X(r@_hPrUl!+WPisN{HHm0wn*nc{7dfba1=#A)s`sJV#}tTiD^=X`l~Y(M}BC>$0cFX436v8PIkaogO{>HBXzg zs_>u1Kdqp5>$&72nGPf3H-0TepR3W<64x_U(yK9F8+EmfUco{f@jow*g0`wwC~_zn z)ox@K&v{70)>;{f`8}r^G6CZ4g?H0r@{`V%>oTQxLKy;7`yY=Bj8uM8rFKfCk5tG) z+PQ(IhNT<e=y+q#Y&8-(mrtdMl+ot1T@20}I*nd}8!1dxpSw}Tyec^Lol zGtSCX&PoX^&*`w;Sj71T-!Qx5?3L1QYxDO)--P89Y*LJ;c zt?K4HZ@<}5@Xwa$ekr=P@9pS~g-93GejN`Y;zj#MvB)&AftEMASLM-NMj-IW>!-u3 zH*X5}L*}3(ll}jcH6k#-X0T+{S}IHJhX1dvs|<@{$=X=Z0R{$wI|;5K5ZoOG2^I(% zU?8{!cXxNlAc5czBn<8vAXtFlHUxLqkGs3Mx%=$bzq+bRy85ZEI_Ev-t;0CEd6{-? ztBj3X3VuCNQBE%#ZsM`sCf0S{K${EDq9aOc9NHT^nZ1-OBzK=ZU&kK2xO6L1CdK4P zRUdTZ(|U5x#&3^O067gnpz+l5%L|@ys2v}GboXU)S0`{41YjoutR3QMwmAj^y7S#J|PN0<(9)dXp9M{H_}@w6U#236rsAcBvJV} zQOQx>9F{l5>i5jjSpzv@>7+d^o;81z$Ge%s#-tMJn9K1nD5a>$mlPb#nG~trLo}*Y z&V-uaz#Y&(bHgFyaU_auKl!PHar86P8t=CcSdiEQiWCJyax2EjAv5E-iObmQVMo?& z-t`~-qi}HEVDBm$POjcvc7uom;v!*)Ew=NH1qak{toM%IQ;3}G9!3QI9$L16LqI4J zC9SlSMQ>b6;oe_ynx)syY56nLChQFB!SpijXhu4ZSaX^vxqvE=#uo7#`Q~U`QD{_U z%v!RnIu3fDY81{>QP(gBSJDt!TxyGzy-u@;3h!()JKjv^fX=DZj8SAs@)Ny`8jB@S znHyBAbwhoY`B`lFd1zFuPq!orHfQGQ2~Os!>Ph)=8^bM^pAq`J9d*wN8 zpK>{!72r~}*^8l8e^iu>^=9FEm1eg^Ck|OF23bs{F2`@x$<}b6gbrUNa)gN+xM#kX z{QB&L9Ywih(f$g^#-BSi+Ua@JLB(PP<&lbtTXDgP`hyBEk8DvW;d2c1Zo~etp3(gq zyl!hUlBIV9_)Wd?qnPt1EZN*V8b_?d!eXr17WV3p|JgTWCNIvfHZt(t8Wn% z!D2GCw9oEv?aPO~MwnF>VHn_g;+DTsx>4QH^IC0gMCtUJ)Ntf+7mvS?6TOfd8oJoR z*n<8b-dxaqnuCp|>)^6Lvd}3v?8#8Eo;zBf0ydOM7H)eO=L*tN(z}t)i!C)6Rdr&$ zW$@YLxm7v&+)TQ~WXxpgdG)IVhTGg6MVfY?B382eYeUKR1u2T#*?Qt1=@RuhwZ%Ns z4~G{8#LQ^*Q*ABJ01_S~t&492>LJ8v9V7k+f|`WfVXb z&JTCv{$kK|X`S81W4Bil>2$ZqUuTEajQ@6(q_Cy2Hlqy-owTyRcXU-4_4>^uBfG;& z58Uc_UlB0KPoihssC;^x7M(JJb=0oy_qCw)D6aFBcB17gRh5Z;?+%Ms1UDAet5gh9 z-GbX@(Q z-vVMO=2~kNAS$Bje7~$^UQ82ThXzTi?u-qlM1{=WNy{d30pqbA=vkyc1m7 z@{^O9&AxdPjru-u`tT4VltHUpC?)Skdz&fqP4O!_{NKAdqC`6-BHljouK>npoT{^> zCA&Wcr6uvUVTHztLXtj#j-opl5AFK&2h8(mub&x*g_}v<2Hv@pVAd5T60*q_i51Ak za5_HQ^fP9xhglZNOP~&afUiuGOZO>X)-ZryIqO`FP#%6@oSbL|1@Drq5Mi(hg}dce zy+vlm zg6~B5JtwdAbISM5`pdy0+Xw0CMPC|Pn-5Ba0ekd;!*PRn;-Vrrmkybtv9Y=_WcKWH zjoZA5?2s6&E7$}Ey08q|s|`=;MT|&v72>@3r;6}$fq~~i?207o@VfT|*U;LG(LQvG26dG$LU_`1n3)9W^ylu1vbXER`o-#300MAiX!<0t3eGZ4eFO8)7Gk z2Ei6U|F&3P&TfOQRq!UzaD9*0VX+Wzmrd%DVY2!S^&pg6{#|8Mbba~}WAMoLA*_zI zNFH_S4q387;SfO&qz~XG0>fvTeSmDdC{^#}Q!!gpKvS8`-1pr2Y4@{Xx@a9I2U-z< zqmRK$@B)LlSnw2~cSzw=Wt?#TwcbXC_9DY0u=o$ftOLmTGMe~jW@^L*-DiG89|zjadqRiB*lz)DdIeR~WP;wCAn@C46nrCH+lA%@K+7vPslB;&`*H(FPaDFn0DD@;k zrNvbO$wENeY}!nI#P1MXHHI1@dc%BLl_$W``}i6%vn+Q;v}3N6h$E?;=1i@kO&4RB zNyPEB{jZ&#ynX70Ja!W_q8|d@-4F9oPP!mU%Hk(5#OX1cd!7|4D^KT3wLSaO92k5V zFesGN%#4SBuD<}ZVQ4R!_Z18)j5UN&@ARuBCGdTO7BYqbpbgja;cA9i2y+QC?Drqu zZ=zcl6bnXSpJR)i0pD>_VRkH-F-d<*_s#uTw+KW2Mi3?2QYfAzXZTY!n6@{R3?<8? z6Ysrx(H+aN>Ja@dwfHF7wG{Q_Tqp4+@k_A~7ez(dbMVVkGI3r)i*3Hh#41U2|B9rItKdZX6d3&%6*mn9)OvFP&> zIkl+TpGDytmS_#bDExuphR!V1f7K?yc zr|zoZ*4_@7PeyTX6q+SB-eVwRbfhY0Q#c)=+ERIDGqAqJ1@A|J9`6u=&dFwaFd5$(7oTyU}Ifhx>mCd8<np z>64Of6df*1wS@@gpgdveqArQHoZRDAm^%Du=E)Ze{#zm{I_sba zRRKD5G=`JsHSNLMzgk0a-=N@0vqVf2^-#E3Z+OEV6hIE;hx(qbN|73-q5lw56cIvl zVas8@hL0ZKWeHklQwGA3huh?@!djpw2Mg-DnooI5@l%Y0CAfk$UbaLfm?`L0wsanP zS82R?M-hP@wq+$|KX8-$D^}OL+9dqDhDJ+X#YuIUr>y)r8341RyyiYh?-|t)JIeH#@oT}OJsYYJ~lA9hP0kC+6wyqhQMFLr2yW-Cr;jfd#+QMqKy#59ZU1TPq^UE!0OYt*yzroAcf& zDEJ;Kb4ZzioK+j}0_r=Cyn_J~IO*U@|fmfV?k z%0I;N3gzoixCrf?@O^Qesu5i4_GDAY?ze=sQ7JtkH#rm&Jf2H-46!_5y>)u73tXc! z4qov`ENb4Um6%&B)p+C@P4!TA?PR0{mz-O}kpR_;PExJJR&0_Fk*TG~*#EJO~ zzBXVB4N)P-}Z zGvA{~y!K@M;ZvpDs{6zn>c+dE%`R_4GO8y$Ea7|4S@0WZC-uh*;$M`^9}k^TFpm0Y zJ|3ga^&1vJpt09S3aKz^$+TN_Vtb-uXcm;mO*qWbxN)2=YAO1yWDz z1Eu zPsxK~p`=%6A2VJQO5{0o^*y5ydRdHW64I6xbj|{kOz;zpo)igtfq(CP*4qGt*r!Ns zq021gME@x{$MM!F@ZzC*NisvM3!&!f-0%xP!2HV zS9P@qk<1bFrh=o`!F;$6Zg(UqWA8j?!QvDd%{%-Q?M@J+l zzL&dZRs1G17CB*wtEnu+XpC$;X{QO;zkxhIATbK!CE|Hx6HjO0$!L*53uQA)Cfc0! znJPfiiZQpwI7^<|PB_((joCXQ7GiiaB$S-od__}gqfiQ=aI2fGSTfW8PAqB*{?$HM z&I52H;zrVAa#&J*qtW48)t0lZ$G0kd?FSIQrbG> z#@<=Aco~e(ZDJ9>EZTJHm1Lxoh!Wd?O>XhhN3gN~$8jOT3DF~b{fQKlX4~f`@L-Y! z>OKK0j7Tr_w}0RTVC$`oB8)^ zNB6CEJXkDPH?OTIR3=DYnrQ;9NUHxv3V}#`V6hlZsMYjRee!kx;?q|6=! zKQ*tMgG@Q@I045f;H4eV?@grTBIh@}&;dImA{?ZlQUWsQ#_GH48tueyiVXh6t*nu=vY$smy&Y0T{b&p&`n;Xma80AUP_K|U0aSDV3{!m23 z`mTd^2l#jfPn{(5J+>A-qANnpdt%rz@ZG5RF#clNL1{Rr*`x)Oo^&Y zSvNPgMiujrTLOkQ#_u3yS9a^%5fP>KC66~s$A3d|k9v(!70O$qyc{6qPGf#RO`uaP z93R0o$-SFcVp&^(dX~#1`B$EVjdXsM3u59p^JbCM%ZhMB*YBzd=%m$g=~_A1LUbb= zDFe63Q#?#h|Gf5X0)`(JpJAeed=rb$Kmqkv5K>UP&GQ`DvUV@R$hoqQMRdP$!G9@N zu$M>#()#4sR-ykQME+S$|3k*I{|o|pcwCBp85xmqa;(cEHDHL0OPNZtv^^n9+jnN} z7Wwmg{VhxHYam5wOq~ya^*F##gSV|(5lfC!lbWZhYL6je*bu(_!>ILJ!}D*7MtJ3T zSy1l9$>zAa8S?x@r3d{v|y9)kxPy~y&uCnXp7{$qV` z0kIGd^t-|Ts$q>Dwip5S3%<+r%z(0gKT{^^%#ZJ36xg_xb z9hTqp8ux`x!lZvc{5OI0KdGlED9WTQA!GU$-8Y41diOAjdwqg4%@a|EcYb(jI)`YqIcw Y45=7mEa7@bjPQ8LOM|5 Date: Tue, 27 Mar 2018 01:36:41 +0100 Subject: [PATCH 124/176] Give Scoped a default equals/hashCode implementation --- build.sbt | 4 + .../src/main/scala/sbt/Structure.scala | 19 ++- .../scala/sbt/BuildSettingsInstances.scala | 10 +- .../src/test/scala/sbt/ScopedSpec.scala | 145 ++++++++++++++++++ project/plugins.sbt | 1 + 5 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 main-settings/src/test/scala/sbt/ScopedSpec.scala diff --git a/build.sbt b/build.sbt index c5e115e31..eeb77984a 100644 --- a/build.sbt +++ b/build.sbt @@ -432,6 +432,10 @@ lazy val mainSettingsProj = (project in file("main-settings")) mimaSettings, mimaBinaryIssueFilters ++= Seq( exclude[DirectMissingMethodProblem]("sbt.Scope.display012StyleMasked"), + + // added a method to a sealed trait + exclude[InheritedNewAbstractMethodProblem]("sbt.Scoped.canEqual"), + exclude[InheritedNewAbstractMethodProblem]("sbt.ScopedTaskable.canEqual"), ), ) .configure( diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index 36955839d..9bb02bc02 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -17,7 +17,18 @@ import sbt.Def.{ Initialize, KeyedInitialize, ScopedKey, Setting, setting } import std.TaskExtra.{ task => mktask, _ } /** An abstraction on top of Settings for build configuration and task definition. */ -sealed trait Scoped { def scope: Scope; val key: AttributeKey[_] } +sealed trait Scoped extends Equals { + def scope: Scope + val key: AttributeKey[_] + + override def equals(that: Any) = + (this eq that.asInstanceOf[AnyRef]) || (that match { + case that: Scoped => scope == that.scope && key == that.key && canEqual(that) + case _ => false + }) + + override def hashCode() = (scope, key).## +} /** A common type for SettingKey and TaskKey so that both can be used as inputs to tasks.*/ sealed trait ScopedTaskable[T] extends Scoped { @@ -95,6 +106,8 @@ sealed abstract class SettingKey[T] final def withRank(rank: Int): SettingKey[T] = SettingKey(AttributeKey.copyWithRank(key, rank)) + + def canEqual(that: Any): Boolean = that.isInstanceOf[SettingKey[_]] } /** @@ -163,6 +176,8 @@ sealed abstract class TaskKey[T] final def withRank(rank: Int): TaskKey[T] = TaskKey(AttributeKey.copyWithRank(key, rank)) + + def canEqual(that: Any): Boolean = that.isInstanceOf[TaskKey[_]] } /** @@ -195,6 +210,8 @@ sealed trait InputKey[T] final def withRank(rank: Int): InputKey[T] = InputKey(AttributeKey.copyWithRank(key, rank)) + + def canEqual(that: Any): Boolean = that.isInstanceOf[InputKey[_]] } /** Methods and types related to constructing settings, including keys, scopes, and initializations. */ diff --git a/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala b/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala index 6ca193ccf..3618fb841 100644 --- a/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala +++ b/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala @@ -92,9 +92,13 @@ object BuildSettingsInstances { 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](_)) + final case class Label(value: String) + val genLabel: Gen[Label] = Gen.identifier map Label + implicit def arbLabel: Arbitrary[Label] = Arbitrary(genLabel) + + def genInputKey[A: Manifest]: Gen[InputKey[A]] = genLabel map (x => InputKey[A](x.value)) + def genSettingKey[A: Manifest]: Gen[SettingKey[A]] = genLabel map (x => SettingKey[A](x.value)) + def genTaskKey[A: Manifest]: Gen[TaskKey[A]] = genLabel map (x => TaskKey[A](x.value)) def withScope[K <: Scoped.ScopingSetting[K]](keyGen: Gen[K]): Arbitrary[K] = Arbitrary { Gen.frequency( diff --git a/main-settings/src/test/scala/sbt/ScopedSpec.scala b/main-settings/src/test/scala/sbt/ScopedSpec.scala new file mode 100644 index 000000000..5992403b0 --- /dev/null +++ b/main-settings/src/test/scala/sbt/ScopedSpec.scala @@ -0,0 +1,145 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt.test + +import org.scalacheck._, Prop._, util.Pretty + +import sbt.internal.util.AttributeKey +import sbt.util.NoJsonWriter +import sbt.{ InputTask, Scope, Task } +import sbt.{ InputKey, Scoped, SettingKey, TaskKey } + +import BuildSettingsInstances._ + +object ScopedSpec extends Properties("Scoped") { + val intManifest = manifest[Int] + val stringManifest = manifest[String] + + implicit val arbManifest: Arbitrary[Manifest[_]] = + Arbitrary(Gen.oneOf(intManifest, stringManifest)) + + property("setting keys are structurally equal") = { + forAll { (label: Label, manifest: Manifest[_], scope: Scope) => + val k1 = settingKey(label, manifest, scope) + val k2 = settingKey(label, manifest, scope) + expectEq(k1, k2) + } + } + + property("task keys are structurally equal") = { + forAll { (label: Label, manifest: Manifest[_], scope: Scope) => + val k1 = taskKey(label, manifest, scope) + val k2 = taskKey(label, manifest, scope) + expectEq(k1, k2) + } + } + + property("input keys are structurally equal") = { + forAll { (label: Label, manifest: Manifest[_], scope: Scope) => + val k1 = inputKey(label, manifest, scope) + val k2 = inputKey(label, manifest, scope) + expectEq(k1, k2) + } + } + + property("different key types are not equal") = { + forAll { (label: Label, manifest: Manifest[_], scope: Scope) => + val settingKey1 = settingKey(label, manifest, scope) + val taskKey1 = taskKey(label, manifest, scope) + val inputKey1 = inputKey(label, manifest, scope) + + all( + expectNe(settingKey1, taskKey1), + expectNe(settingKey1, inputKey1), + expectNe(taskKey1, inputKey1), + ) + } + } + + property("different key types, with the same manifest, are not equal") = { + forAll { (label: Label, scope: Scope) => + val prop1 = { + val manifest1 = manifest[Task[String]] + val attrKey = attributeKey(label, manifest1) + val k1 = SettingKey(attrKey) in scope + val k2 = TaskKey(attrKey) in scope + expectNeSameManifest(k1, k2) + } + + val prop2 = { + val manifest1 = manifest[InputTask[String]] + val attrKey = attributeKey(label, manifest1) + val k1 = SettingKey(attrKey) in scope + val k2 = InputKey(attrKey) in scope + expectNeSameManifest(k1, k2) + } + + all(prop1, prop2) + } + } + + /// + + def settingKey[A](label: Label, manifest: Manifest[A], scope: Scope): SettingKey[A] = { + val noJsonWriter = NoJsonWriter[A]() + SettingKey[A](label.value)(manifest, noJsonWriter) in scope + } + + def taskKey[A](label: Label, manifest: Manifest[A], s: Scope): TaskKey[A] = + TaskKey[A](label.value)(manifest) in s + + def inputKey[A](label: Label, manifest: Manifest[A], scope: Scope): InputKey[A] = + InputKey[A](label.value)(manifest) in scope + + def attributeKey[A](label: Label, manifest: Manifest[A]): AttributeKey[A] = { + val jsonWriter = NoJsonWriter[A]() + AttributeKey[A](label.value)(manifest, jsonWriter) + } + + /// + + def expectEq(k1: Scoped, k2: Scoped): Prop = + ?=(k1, k2) && ?=(k2, k1) map eqLabels(k1, k2) + + def expectNe(k1: Scoped, k2: Scoped): Prop = + !=(k1, k2) && !=(k2, k1) map eqLabels(k1, k2) + + def expectNeSameManifest(k1: Scoped, k2: Scoped) = { + all( + ?=(k1.key.manifest, k2.key.manifest), // sanity check the manifests are the same + expectNe(k1, k2), + ) + } + + def eqLabels(k1: Scoped, k2: Scoped): Prop.Result => Prop.Result = r => { + val eqLabel = k1.key.label == k2.key.label + val eqManifest = k1.key.manifest == k2.key.manifest + val eqScope = k1.scope == k2.scope + r.label(s"label equality: ${k1.key.label} == ${k2.key.label} : $eqLabel") + .label(s"manifest equality: ${k1.key.manifest} == ${k2.key.manifest} : $eqManifest") + .label(s"scope equality: ${k1.scope} == ${k2.scope} : $eqScope") + } + + def ?=[T](x: T, y: T)(implicit pp: T => Pretty): Prop = + if (x == y) proved + else + falsified :| { + val act = Pretty.pretty[T](x, Pretty.Params(0)) + val exp = Pretty.pretty[T](y, Pretty.Params(0)) + s"Expected $act to be equal to $exp" + } + + def !=[T](x: T, y: T)(implicit pp: T => Pretty): Prop = + if (x == y) falsified + else + proved :| { + val act = Pretty.pretty[T](x, Pretty.Params(0)) + val exp = Pretty.pretty[T](y, Pretty.Params(0)) + s"Expected $act to NOT be equal to $exp" + } +} diff --git a/project/plugins.sbt b/project/plugins.sbt index f2509a98e..0352032f5 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,7 @@ scalaVersion := "2.12.4" scalacOptions ++= Seq("-feature", "-language:postfixOps") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.2.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") From 03fc4ac68640cebdfcc8f7ca8f04efa0aea57e22 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Wed, 4 Apr 2018 21:01:05 +0200 Subject: [PATCH 125/176] Ensure unique project Id in composite projects --- main/src/main/scala/sbt/Project.scala | 21 +++++++++++ .../main/scala/sbt/internal/BuildDef.scala | 4 ++- .../sbt/internal/EvaluateConfigurations.scala | 6 ++-- .../project/sbt-composite-projects/a/a.sbt | 2 -- .../sbt-composite-projects/b/build.sbt | 2 -- .../project/sbt-composite-projects/build.sbt | 24 ++++++++----- .../sbt-composite-projects/changes/shadow.sbt | 36 ++++++++----------- .../sbt-composite-projects/js/build.sbt | 2 ++ .../sbt-composite-projects/{a => jvm}/A.scala | 0 .../project/sbt-composite-projects/jvm/a.sbt | 2 ++ .../project/sbt-composite-projects/test | 24 ++++++++----- 11 files changed, 78 insertions(+), 45 deletions(-) delete mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/a/a.sbt delete mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/b/build.sbt create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/js/build.sbt rename sbt/src/sbt-test/project/sbt-composite-projects/{a => jvm}/A.scala (100%) create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/jvm/a.sbt diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 2571b011e..1342cce77 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -125,6 +125,27 @@ trait CompositeProject { def componentProjects: Seq[Project] } +object CompositeProject { + + /** + * If two projects with the same id appear in `compositeProjects` and + * in `compositeProjects.map(_.componentProjects)`, the one in + * `compositeProjects` wins. + * This is necessary for backward compatibility with the idiom: + * lazy val foo = crossProject + * lazy val fooJVM = foo.jvm.setting + */ + def uniqueId(compositeProjects: Seq[Project]): Seq[Project] = { + for (p <- compositeProjects.flatMap(_.componentProjects)) yield { + compositeProjects.find(_.id == p.id) match { + case Some(overridingProject) => overridingProject + case None => p + } + } + + } +} + sealed trait Project extends ProjectDefinition[ProjectReference] with CompositeProject { def componentProjects: Seq[Project] = this :: Nil diff --git a/main/src/main/scala/sbt/internal/BuildDef.scala b/main/src/main/scala/sbt/internal/BuildDef.scala index 52a9059d9..03c03a40e 100644 --- a/main/src/main/scala/sbt/internal/BuildDef.scala +++ b/main/src/main/scala/sbt/internal/BuildDef.scala @@ -18,7 +18,9 @@ import sbt.internal.inc.ReflectUtilities trait BuildDef { def projectDefinitions(@deprecated("unused", "") baseDirectory: File): Seq[Project] = projects def projects: Seq[Project] = - ReflectUtilities.allVals[CompositeProject](this).values.toSeq.flatMap(_.componentProjects) + CompositeProject.uniqueId( + ReflectUtilities.allVals[CompositeProject](this).values.toSeq.flatMap(_.componentProjects)) + // TODO: Should we grab the build core settings here or in a plugin? def settings: Seq[Setting[_]] = Defaults.buildCore def buildLoaders: Seq[BuildLoader.Components] = Nil diff --git a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala index cfb599a20..c7e6781e9 100644 --- a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala +++ b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala @@ -151,11 +151,13 @@ private[sbt] object EvaluateConfigurations { val allGeneratedFiles = (definitions.generated ++ dslEntries.flatMap(_.generated)) loader => { - val projects = - definitions.values(loader).flatMap { + val projects = { + val compositeProjects = definitions.values(loader).flatMap { case p: CompositeProject => p.componentProjects.map(resolveBase(file.getParentFile, _)) case _ => Nil } + CompositeProject.uniqueId(compositeProjects) + } val (settingsRaw, manipulationsRaw) = dslEntries map (_.result apply loader) partition { case DslEntry.ProjectSettings(_) => true diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/a/a.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/a/a.sbt deleted file mode 100644 index 67966f6d2..000000000 --- a/sbt/src/sbt-test/project/sbt-composite-projects/a/a.sbt +++ /dev/null @@ -1,2 +0,0 @@ -val aa = taskKey[Unit]("A task in the 'a' project") -aa := println("Hello.") diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/b/build.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/b/build.sbt deleted file mode 100644 index 3c7dc5056..000000000 --- a/sbt/src/sbt-test/project/sbt-composite-projects/b/build.sbt +++ /dev/null @@ -1,2 +0,0 @@ -val h = taskKey[Unit]("A task in project 'b'") -h := println("Hello.") diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt index 7bd373aa4..de8f1c134 100644 --- a/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt +++ b/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt @@ -4,21 +4,29 @@ import sbt.CompositeProject lazy val check = taskKey[Unit]("check") // Based on sbt-file-projects test -lazy val cross = new CompositeProject +lazy val foo = new CompositeProject { - val p1 = Project.apply("a", new File("a")) - val p2 = Project.apply("b", new File("b")) - def componentProjects: Seq[Project] = Seq(p1, p2) + val jvm = Project.apply("jvm", new File("jvm")) + val js = Project.apply("js", new File("js")) + def componentProjects: Seq[Project] = Seq(jvm, js) } +lazy val fooJVM = foo.jvm + +lazy val bar = project + .dependsOn(foo.jvm) + val g = taskKey[Unit]("A task in the root project") g := println("Hello.") check := { - val verP1 = (version in cross.p1).?.value - assert (verP1 == Some("0.1.0-SNAPSHOT")) + val verJvm = (version in foo.jvm).?.value + assert (verJvm == Some("0.1.0-SNAPSHOT")) - val verP2 = (version in cross.p2).?.value - assert (verP2 == Some("0.1.0-SNAPSHOT")) + val verFooJvm = (version in fooJVM).?.value + assert (verFooJvm == Some("0.1.0-SNAPSHOT")) + + val verJs = (version in foo.js).?.value + assert (verJs == Some("0.1.0-SNAPSHOT")) } diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt index 9bf54ab9f..47334ba0f 100644 --- a/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt +++ b/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt @@ -3,38 +3,30 @@ import sbt.CompositeProject lazy val check = taskKey[Unit]("check") -lazy val a = (project in file("a")) - .settings( - version := "0.2.0" - ) - // Based on sbt-file-projects test -lazy val cross = new CompositeProject +lazy val foo = new CompositeProject { - val p1 = Project.apply("a", new File("a")) - val p2 = Project.apply("b", new File("b")) - def componentProjects: Seq[Project] = Seq(p1, p2) + val jvm = Project.apply("jvm", new File("jvm")) + val js = Project.apply("js", new File("js")) + def componentProjects: Seq[Project] = Seq(jvm, js) } -lazy val b = (project in file("b")) - .settings( - version := "0.2.0" - ) +lazy val fooJVM = foo.jvm.settings(version := "0.2.0") // this one needs to win + +lazy val bar = project + .dependsOn(foo.jvm) val g = taskKey[Unit]("A task in the root project") g := println("Hello.") check := { - val verP1 = (version in cross.p1).?.value - assert (verP1 == Some("0.2.0"))//Some("0.1.0-SNAPSHOT")) + val verJvm = (version in foo.jvm).?.value + assert (verJvm == Some("0.2.0")) - val verP2 = (version in cross.p2).?.value - assert (verP2 == Some("0.1.0-SNAPSHOT")) + val verFooJvm = (version in fooJVM).?.value + assert (verFooJvm == Some("0.2.0")) - val verA = (version in a).?.value - assert (verA == Some("0.2.0")) - - val verB = (version in b).?.value - assert (verA == Some("0.2.0")) + val verJs = (version in foo.js).?.value + assert (verJs == Some("0.1.0-SNAPSHOT")) } diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/js/build.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/js/build.sbt new file mode 100644 index 000000000..0ddd49e30 --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/js/build.sbt @@ -0,0 +1,2 @@ +val h = taskKey[Unit]("A task in project 'js'") +h := println("Hello.") diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/a/A.scala b/sbt/src/sbt-test/project/sbt-composite-projects/jvm/A.scala similarity index 100% rename from sbt/src/sbt-test/project/sbt-composite-projects/a/A.scala rename to sbt/src/sbt-test/project/sbt-composite-projects/jvm/A.scala diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/jvm/a.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/jvm/a.sbt new file mode 100644 index 000000000..9b73a55bf --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/jvm/a.sbt @@ -0,0 +1,2 @@ +val aa = taskKey[Unit]("A task in the 'jvm' project") +aa := println("Hello.") diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/test b/sbt/src/sbt-test/project/sbt-composite-projects/test index 868ecac69..5bb98d747 100644 --- a/sbt/src/sbt-test/project/sbt-composite-projects/test +++ b/sbt/src/sbt-test/project/sbt-composite-projects/test @@ -1,22 +1,30 @@ > g -> root/compile -> a/compile -> a/aa -> b/compile -> b/h +> jvm/compile +> jvm/aa +> js/compile +> js/h > c/compile +> bar/compile $ copy-file changes/basic.sbt basic.sbt > reload > g > root/compile -> a/compile -> a/aa -> b/compile -> b/h +> jvm/compile +> jvm/aa +> js/compile +> js/h > c/compile +> bar/compile > check $ copy-file changes/shadow.sbt build.sbt > reload +> jvm/compile +> jvm/aa +> js/compile +> js/h +> c/compile +> bar/compile > check From 68c005e4b58a968a3c0ead2b342076d3e8054684 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Sat, 7 Apr 2018 01:04:45 +0200 Subject: [PATCH 126/176] Ensure precedence of top level Projects over ComponentProjects --- main/src/main/scala/sbt/Project.scala | 28 +++++++++----- .../main/scala/sbt/internal/BuildDef.scala | 9 +++-- .../sbt/internal/EvaluateConfigurations.scala | 15 ++++++-- notes/1.2.0/introduce-CompositeProject.md | 9 +++++ .../project/sbt-composite-projects/build.sbt | 14 ++++--- .../sbt-composite-projects/changes/shadow.sbt | 10 +++-- .../changes/shadowLazy.sbt | 37 +++++++++++++++++++ .../project/sbt-composite-projects/test | 10 +++++ 8 files changed, 107 insertions(+), 25 deletions(-) create mode 100644 notes/1.2.0/introduce-CompositeProject.md create mode 100644 sbt/src/sbt-test/project/sbt-composite-projects/changes/shadowLazy.sbt diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 1342cce77..789cb709f 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -125,25 +125,35 @@ trait CompositeProject { def componentProjects: Seq[Project] } -object CompositeProject { +private[sbt] object CompositeProject { /** - * If two projects with the same id appear in `compositeProjects` and - * in `compositeProjects.map(_.componentProjects)`, the one in - * `compositeProjects` wins. - * This is necessary for backward compatibility with the idiom: + * Expand user defined `projects` with the component projects of `compositeProjects`. + * + * If two projects with the same id appear in the user defined `projects` and + * in `compositeProjects.componentProjects`, the one in `projects` wins. + * This is necessary for backward compatibility with the idioms: + * {{{ * lazy val foo = crossProject - * lazy val fooJVM = foo.jvm.setting + * lazy val fooJS = foo.js.settings(...) + * lazy val fooJVM = foo.jvm.settings(...) + * }}} + * and the rarer: + * {{{ + * lazy val fooJS = foo.js.settings(...) + * lazy val foo = crossProject + * lazy val fooJVM = foo.jvm.settings(...) + * }}} */ - def uniqueId(compositeProjects: Seq[Project]): Seq[Project] = { + def expand(projects: Seq[Project], compositeProjects: Seq[CompositeProject]): Seq[Project] = { for (p <- compositeProjects.flatMap(_.componentProjects)) yield { - compositeProjects.find(_.id == p.id) match { + projects.find(_.id == p.id) match { case Some(overridingProject) => overridingProject case None => p } } - } + } sealed trait Project extends ProjectDefinition[ProjectReference] with CompositeProject { diff --git a/main/src/main/scala/sbt/internal/BuildDef.scala b/main/src/main/scala/sbt/internal/BuildDef.scala index 03c03a40e..5a916acb5 100644 --- a/main/src/main/scala/sbt/internal/BuildDef.scala +++ b/main/src/main/scala/sbt/internal/BuildDef.scala @@ -17,10 +17,11 @@ import sbt.internal.inc.ReflectUtilities trait BuildDef { def projectDefinitions(@deprecated("unused", "") baseDirectory: File): Seq[Project] = projects - def projects: Seq[Project] = - CompositeProject.uniqueId( - ReflectUtilities.allVals[CompositeProject](this).values.toSeq.flatMap(_.componentProjects)) - + def projects: Seq[Project] = { + val projects = ReflectUtilities.allVals[Project](this).values.toSeq + val compositeProjects = ReflectUtilities.allVals[CompositeProject](this).values.toSeq + CompositeProject.expand(projects, compositeProjects) + } // TODO: Should we grab the build core settings here or in a plugin? def settings: Seq[Setting[_]] = Defaults.buildCore def buildLoaders: Seq[BuildLoader.Components] = Nil diff --git a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala index c7e6781e9..241b8c6b1 100644 --- a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala +++ b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala @@ -152,11 +152,18 @@ private[sbt] object EvaluateConfigurations { loader => { val projects = { - val compositeProjects = definitions.values(loader).flatMap { - case p: CompositeProject => p.componentProjects.map(resolveBase(file.getParentFile, _)) - case _ => Nil + + val projects = definitions.values(loader).collect { + case p: Project => p } - CompositeProject.uniqueId(compositeProjects) + + val compositeProjects = definitions.values(loader).collect { + case p: CompositeProject => p + } + + CompositeProject + .expand(projects, compositeProjects) + .map(resolveBase(file.getParentFile, _)) } val (settingsRaw, manipulationsRaw) = dslEntries map (_.result apply loader) partition { diff --git a/notes/1.2.0/introduce-CompositeProject.md b/notes/1.2.0/introduce-CompositeProject.md new file mode 100644 index 000000000..759d75a71 --- /dev/null +++ b/notes/1.2.0/introduce-CompositeProject.md @@ -0,0 +1,9 @@ + +[#3042]: https://github.com/sbt/sbt/issues/3042 +[#4056]: https://github.com/sbt/sbt/pull/4056 + +### Introduce CompositeProject + +### Improvements + +- Support for: `lazy val foo = someCompositeProject` (e.g.`CrossProject`) [#3042][]/[#4056][] diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt index de8f1c134..1537b2610 100644 --- a/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt +++ b/sbt/src/sbt-test/project/sbt-composite-projects/build.sbt @@ -6,11 +6,12 @@ lazy val check = taskKey[Unit]("check") // Based on sbt-file-projects test lazy val foo = new CompositeProject { - val jvm = Project.apply("jvm", new File("jvm")) - val js = Project.apply("js", new File("js")) + val jvm = Project.apply("jvm", new File("jvm")).settings(version := "0.1.0") // this one needs to win + val js = Project.apply("js", new File("js")).settings(version := "0.1.0") // this one needs to win def componentProjects: Seq[Project] = Seq(jvm, js) } +lazy val fooJS = foo.js lazy val fooJVM = foo.jvm lazy val bar = project @@ -22,11 +23,14 @@ g := println("Hello.") check := { val verJvm = (version in foo.jvm).?.value - assert (verJvm == Some("0.1.0-SNAPSHOT")) + assert (verJvm == Some("0.1.0")) val verFooJvm = (version in fooJVM).?.value - assert (verFooJvm == Some("0.1.0-SNAPSHOT")) + assert (verFooJvm == Some("0.1.0")) val verJs = (version in foo.js).?.value - assert (verJs == Some("0.1.0-SNAPSHOT")) + assert (verJs == Some("0.1.0")) + + val verFooJs = (version in fooJS).?.value + assert (verFooJs == Some("0.1.0")) } diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt index 47334ba0f..0713e7f4b 100644 --- a/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt +++ b/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadow.sbt @@ -6,11 +6,12 @@ lazy val check = taskKey[Unit]("check") // Based on sbt-file-projects test lazy val foo = new CompositeProject { - val jvm = Project.apply("jvm", new File("jvm")) - val js = Project.apply("js", new File("js")) + val jvm = Project.apply("jvm", new File("jvm")).settings(version := "0.1.0") + val js = Project.apply("js", new File("js")).settings(version := "0.1.0") // this one needs to win def componentProjects: Seq[Project] = Seq(jvm, js) } +lazy val fooJS = foo.js lazy val fooJVM = foo.jvm.settings(version := "0.2.0") // this one needs to win lazy val bar = project @@ -28,5 +29,8 @@ check := { assert (verFooJvm == Some("0.2.0")) val verJs = (version in foo.js).?.value - assert (verJs == Some("0.1.0-SNAPSHOT")) + assert (verJs == Some("0.1.0")) + + val verFooJs = (version in fooJS).?.value + assert (verFooJs == Some("0.1.0")) } diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadowLazy.sbt b/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadowLazy.sbt new file mode 100644 index 000000000..492fbd621 --- /dev/null +++ b/sbt/src/sbt-test/project/sbt-composite-projects/changes/shadowLazy.sbt @@ -0,0 +1,37 @@ +import sbt.internal.AddSettings +import sbt.CompositeProject + +lazy val check = taskKey[Unit]("check") + +lazy val fooJS = foo.js.settings(version := "0.2.1") // this one needs to win + +// Based on sbt-file-projects test +lazy val foo = new CompositeProject +{ + val jvm = Project.apply("jvm", new File("jvm")).settings(version := "0.1.0") + val js = Project.apply("js", new File("js")).settings(version := "0.1.0") + def componentProjects: Seq[Project] = Seq(jvm, js) +} + +lazy val fooJVM = foo.jvm.settings(version := "0.2.0") // this one needs to win + +lazy val bar = project + .dependsOn(foo.jvm) + +val g = taskKey[Unit]("A task in the root project") +g := println("Hello.") + + +check := { + val verJvm = (version in foo.jvm).?.value + assert (verJvm == Some("0.2.0")) + + val verFooJvm = (version in fooJVM).?.value + assert (verFooJvm == Some("0.2.0")) + + val verJs = (version in foo.js).?.value + assert (verJs == Some("0.2.1")) + + val verFooJs = (version in fooJS).?.value + assert (verFooJs == Some("0.2.1")) +} diff --git a/sbt/src/sbt-test/project/sbt-composite-projects/test b/sbt/src/sbt-test/project/sbt-composite-projects/test index 5bb98d747..34bc8097d 100644 --- a/sbt/src/sbt-test/project/sbt-composite-projects/test +++ b/sbt/src/sbt-test/project/sbt-composite-projects/test @@ -28,3 +28,13 @@ $ copy-file changes/shadow.sbt build.sbt > c/compile > bar/compile > check + +$ copy-file changes/shadowLazy.sbt build.sbt +> reload +> jvm/compile +> jvm/aa +> js/compile +> js/h +> c/compile +> bar/compile +> check \ No newline at end of file From 6cce4f6fd980d308b7ada79721c3c5d85ca86cb5 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Sat, 7 Apr 2018 01:26:40 +0200 Subject: [PATCH 127/176] Remove duplicate Projects --- main/src/main/scala/sbt/Project.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 789cb709f..5f867dd94 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -152,7 +152,7 @@ private[sbt] object CompositeProject { case None => p } } - } + }.distinct } From b0ad1a44c06a36c4697a922b6135c73351f5d9ee Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Sat, 7 Apr 2018 15:56:31 +0200 Subject: [PATCH 128/176] Remove projects parameter from CompositeProject.expand --- main/src/main/scala/sbt/Project.scala | 15 ++++++++------- main/src/main/scala/sbt/internal/BuildDef.scala | 7 ++----- .../sbt/internal/EvaluateConfigurations.scala | 10 +--------- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 5f867dd94..bac52a877 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -128,10 +128,10 @@ trait CompositeProject { private[sbt] object CompositeProject { /** - * Expand user defined `projects` with the component projects of `compositeProjects`. + * Expand user defined projects with the component projects of `compositeProjects`. * - * If two projects with the same id appear in the user defined `projects` and - * in `compositeProjects.componentProjects`, the one in `projects` wins. + * If two projects with the same id appear in the user defined projects and + * in `compositeProjects.componentProjects`, the user defined project wins. * This is necessary for backward compatibility with the idioms: * {{{ * lazy val foo = crossProject @@ -145,11 +145,12 @@ private[sbt] object CompositeProject { * lazy val fooJVM = foo.jvm.settings(...) * }}} */ - def expand(projects: Seq[Project], compositeProjects: Seq[CompositeProject]): Seq[Project] = { + def expand(compositeProjects: Seq[CompositeProject]): Seq[Project] = { + val userProjects = compositeProjects.collect { case p: Project => p } for (p <- compositeProjects.flatMap(_.componentProjects)) yield { - projects.find(_.id == p.id) match { - case Some(overridingProject) => overridingProject - case None => p + userProjects.find(_.id == p.id) match { + case Some(userProject) => userProject + case None => p } } }.distinct diff --git a/main/src/main/scala/sbt/internal/BuildDef.scala b/main/src/main/scala/sbt/internal/BuildDef.scala index 5a916acb5..d4a435ab5 100644 --- a/main/src/main/scala/sbt/internal/BuildDef.scala +++ b/main/src/main/scala/sbt/internal/BuildDef.scala @@ -17,11 +17,8 @@ import sbt.internal.inc.ReflectUtilities trait BuildDef { def projectDefinitions(@deprecated("unused", "") baseDirectory: File): Seq[Project] = projects - def projects: Seq[Project] = { - val projects = ReflectUtilities.allVals[Project](this).values.toSeq - val compositeProjects = ReflectUtilities.allVals[CompositeProject](this).values.toSeq - CompositeProject.expand(projects, compositeProjects) - } + def projects: Seq[Project] = + CompositeProject.expand(ReflectUtilities.allVals[CompositeProject](this).values.toSeq) // TODO: Should we grab the build core settings here or in a plugin? def settings: Seq[Setting[_]] = Defaults.buildCore def buildLoaders: Seq[BuildLoader.Components] = Nil diff --git a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala index 241b8c6b1..26e5348ff 100644 --- a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala +++ b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala @@ -152,18 +152,10 @@ private[sbt] object EvaluateConfigurations { loader => { val projects = { - - val projects = definitions.values(loader).collect { - case p: Project => p - } - val compositeProjects = definitions.values(loader).collect { case p: CompositeProject => p } - - CompositeProject - .expand(projects, compositeProjects) - .map(resolveBase(file.getParentFile, _)) + CompositeProject.expand(compositeProjects).map(resolveBase(file.getParentFile, _)) } val (settingsRaw, manipulationsRaw) = dslEntries map (_.result apply loader) partition { From 3e201cee4d830353f9a076e863d22cdc623f9ac8 Mon Sep 17 00:00:00 2001 From: Nafer Sanabria Date: Sun, 8 Apr 2018 10:51:54 -0500 Subject: [PATCH 129/176] Add lastGrep command --- main/src/main/scala/sbt/Main.scala | 12 +++++++++--- .../main/scala/sbt/internal/CommandStrings.scala | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 4f4ef57d6..c19d44c40 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -40,7 +40,6 @@ import sbt.internal.util.{ Types } import sbt.util.{ Level, Logger, Show } - import sbt.internal.util.complete.{ DefaultParsers, Parser } import sbt.internal.inc.ScalaInstance import sbt.compiler.EvalImports @@ -52,7 +51,6 @@ import xsbti.compile.CompilerCache import scala.annotation.tailrec import sbt.io.IO import sbt.io.syntax._ - import java.io.{ File, IOException } import java.net.URI import java.util.{ Locale, Properties } @@ -198,6 +196,7 @@ object BuiltinCommands { startServer, eval, last, + oldLastGrep, lastGrep, export, boot, @@ -452,8 +451,15 @@ object BuiltinCommands { s } + @deprecated("Use `lastGrep` instead.", "1.2.0") + def oldLastGrep: Command = + lastGrepCommand(OldLastGrepCommand, oldLastGrepBrief, oldLastGrepDetailed) + def lastGrep: Command = - Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) { + lastGrepCommand(LastGrepCommand, lastGrepBrief, lastGrepDetailed) + + private def lastGrepCommand(name: String, briefHelp: (String, String), detail: String): Command = + Command(name, briefHelp, detail)(lastGrepParser) { case (s, (pattern, Some(sks))) => val (str, _, display) = extractLast(s) Output.lastGrep(sks, str.streams(s), pattern, printLast)(display) diff --git a/main/src/main/scala/sbt/internal/CommandStrings.scala b/main/src/main/scala/sbt/internal/CommandStrings.scala index dc070a468..278985b64 100644 --- a/main/src/main/scala/sbt/internal/CommandStrings.scala +++ b/main/src/main/scala/sbt/internal/CommandStrings.scala @@ -57,10 +57,23 @@ $ShowCommand def pluginsDetailed = pluginsBrief // TODO: expand val LastCommand = "last" - val LastGrepCommand = "last-grep" + val OldLastGrepCommand = "last-grep" + val LastGrepCommand = "lastGrep" val ExportCommand = "export" val ExportStream = "export" + val oldLastGrepBrief = + (OldLastGrepCommand, "Shows lines from the last output for 'key' that match 'pattern'.") + val oldLastGrepDetailed = + s"""$OldLastGrepCommand + Displays lines from the logging of previous commands that match `pattern`. + +$OldLastGrepCommand [key] + Displays lines from logging associated with `key` that match `pattern`. The key typically refers to a task (for example, test:compile). The logging that is displayed is restricted to the logging for that particular task. + + is a regular expression interpreted by java.util.Pattern. Matching text is highlighted (when highlighting is supported and enabled). + See also '$LastCommand'.""" + val lastGrepBrief = (LastGrepCommand, "Shows lines from the last output for 'key' that match 'pattern'.") val lastGrepDetailed = From 190dc23f7063787e145b1cf1c061259829681504 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 11 Apr 2018 01:31:38 -0400 Subject: [PATCH 130/176] Fixes linter that detects missing .value Fixes #4079 #3216 introduced a linter that checks against missing `.value`, but the tree only checked for `Ident`. This doesn't work because in reality the symbols of build.sbt are transformed to `$somehash.npmInstallTask` where `somehash` is the wrapper object we create. Similarly for the built-in keys, they are presented as `sbt.Keys.compile`. With this change unused task will fail to load the build with the following message: ``` /sbt-4079/build.sbt:26: error: The key `compile` is not being invoked inside the task definition. Problem: Keys missing `.value` are not initialized and their dependency is not registered. Solution: Replace `compile` by `compile.value` or remove it if unused. compile ^ /sbt-4079/build.sbt:27: error: The key `npmInstallTask` is not being invoked inside the task definition. Problem: Keys missing `.value` are not initialized and their dependency is not registered. Solution: Replace `npmInstallTask` by `npmInstallTask.value` or remove it if unused. npmInstallTask ^ ``` --- .../main/scala/sbt/std/TaskLinterDSL.scala | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala index a328cee70..1c47216fa 100644 --- a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala +++ b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala @@ -27,6 +27,7 @@ abstract class BaseTaskLinterDSL extends LinterDSL { private val taskKeyType = typeOf[sbt.TaskKey[_]] private val settingKeyType = typeOf[sbt.SettingKey[_]] private val inputKeyType = typeOf[sbt.InputKey[_]] + private val initializeType = typeOf[sbt.Def.Initialize[_]] private val uncheckedWrappers = MutableSet.empty[Tree] var insideIf: Boolean = false var insideAnon: Boolean = false @@ -57,7 +58,7 @@ abstract class BaseTaskLinterDSL extends LinterDSL { } @inline def isKey(tpe: Type): Boolean = - tpe <:< taskKeyType || tpe <:< settingKeyType || tpe <:< inputKeyType + tpe <:< initializeType || tpe <:< taskKeyType || tpe <:< settingKeyType || tpe <:< inputKeyType def detectAndErrorOnKeyMissingValue(i: Ident): Unit = { if (isKey(i.tpe)) { @@ -66,6 +67,13 @@ abstract class BaseTaskLinterDSL extends LinterDSL { } else () } + def detectAndErrorOnKeyMissingValue(s: Select): Unit = { + if (isKey(s.tpe)) { + val keyName = s.name.decodedName.toString + ctx.error(s.pos, TaskLinterDSLFeedback.missingValueForKey(keyName)) + } else () + } + override def traverse(tree: ctx.universe.Tree): Unit = { tree match { case ap @ Apply(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) => @@ -118,11 +126,13 @@ abstract class BaseTaskLinterDSL extends LinterDSL { // TODO: Consider using unused names analysis to be able to report on more cases case ValDef(_, valName, _, rhs) if valName == termNames.WILDCARD => rhs match { - case i: Ident => detectAndErrorOnKeyMissingValue(i) - case _ => () + case i: Ident => detectAndErrorOnKeyMissingValue(i) + case s: Select => detectAndErrorOnKeyMissingValue(s) + case _ => () } - case i: Ident => detectAndErrorOnKeyMissingValue(i) - case _ => () + case i: Ident => detectAndErrorOnKeyMissingValue(i) + case s: Select => detectAndErrorOnKeyMissingValue(s) + case t => () } } traverseTrees(stmts) From f459b218c47a330354111c884e4f204cd9067d02 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 19 Apr 2018 09:47:02 +0100 Subject: [PATCH 131/176] Switch inThisBuild (+friends) to use varargs SettingsDefinition --- main/src/main/scala/sbt/Project.scala | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index bac52a877..8657f119f 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -32,7 +32,7 @@ import Keys.{ watch } import Scope.{ Global, ThisScope } -import Def.{ Flattened, Initialize, ScopedKey, Setting } +import Def.{ Flattened, Initialize, ScopedKey, Setting, SettingsDefinition } import sbt.internal.{ Load, BuildStructure, @@ -863,17 +863,19 @@ trait ProjectExtra { implicit def richTaskSessionVar[T](init: Initialize[Task[T]]): Project.RichTaskSessionVar[T] = new Project.RichTaskSessionVar(init) - def inThisBuild(ss: Seq[Setting[_]]): Seq[Setting[_]] = - inScope(ThisScope.copy(project = Select(ThisBuild)))(ss) + def inThisBuild(ss: SettingsDefinition*): Seq[Setting[_]] = + inScope(ThisScope.copy(project = Select(ThisBuild)))(ss flatMap (_.settings)) - def inConfig(conf: Configuration)(ss: Seq[Setting[_]]): Seq[Setting[_]] = - inScope(ThisScope.copy(config = Select(conf)))((configuration :== conf) +: ss) + def inConfig(conf: Configuration)(ss: SettingsDefinition*): Seq[Setting[_]] = + inScope(ThisScope.copy(config = Select(conf)))( + (configuration :== conf) +: (ss flatMap (_.settings)) + ) - def inTask(t: Scoped)(ss: Seq[Setting[_]]): Seq[Setting[_]] = - inScope(ThisScope.copy(task = Select(t.key)))(ss) + def inTask(t: Scoped)(ss: SettingsDefinition*): Seq[Setting[_]] = + inScope(ThisScope.copy(task = Select(t.key)))(ss flatMap (_.settings)) - def inScope(scope: Scope)(ss: Seq[Setting[_]]): Seq[Setting[_]] = - Project.transform(Scope.replaceThis(scope), ss) + def inScope(scope: Scope)(ss: SettingsDefinition*): Seq[Setting[_]] = + Project.transform(Scope.replaceThis(scope), ss flatMap (_.settings)) private[sbt] def inThisBuild[T](i: Initialize[T]): Initialize[T] = inScope(ThisScope.copy(project = Select(ThisBuild)), i) From ccf938c7861bcc0734f89a61b325a69277808de8 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 19 Apr 2018 09:47:58 +0100 Subject: [PATCH 132/176] Switch to varargs inThisBuild (+friends) --- main/src/main/scala/sbt/Defaults.scala | 315 +++++++++--------- main/src/main/scala/sbt/PluginCross.scala | 2 +- .../scala/sbt/internal/GlobalPlugin.scala | 13 +- main/src/main/scala/sbt/internal/Load.scala | 31 +- .../actions/cross-multiproject/build.sbt | 4 +- sbt/src/sbt-test/actions/previous/scopes.sbt | 4 +- .../cache-update/build.sbt | 4 +- .../cached-resolution-circular/multi.sbt | 4 +- .../cached-resolution-conflicts/multi.sbt | 4 +- .../cached-resolution-exclude/multi.sbt | 4 +- .../cached-resolution-interproj/multi.sbt | 4 +- .../make-pom-type/build.sbt | 4 +- .../publish-local/build.sbt | 4 +- .../publish-local/changes/RetrieveTest.sbt | 4 +- sbt/src/sbt-test/project/derived/build.sbt | 4 +- .../tests/fork-test-group-parallel/build.sbt | 4 +- 16 files changed, 199 insertions(+), 210 deletions(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index c8f1beecb..67dc6997e 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -134,9 +134,8 @@ object Defaults extends BuildCommon { def buildCore: Seq[Setting[_]] = thisBuildCore ++ globalCore def thisBuildCore: Seq[Setting[_]] = inScope(GlobalScope.copy(project = Select(ThisBuild)))( - Seq( - managedDirectory := baseDirectory.value / "lib_managed" - )) + managedDirectory := baseDirectory.value / "lib_managed" + ) private[sbt] lazy val globalCore: Seq[Setting[_]] = globalDefaults( defaultTestTasks(test) ++ defaultTestTasks(testOnly) ++ defaultTestTasks(testQuick) ++ Seq( excludeFilter :== HiddenFileFilter @@ -294,10 +293,9 @@ object Defaults extends BuildCommon { def defaultTestTasks(key: Scoped): Seq[Setting[_]] = inTask(key)( - Seq( - tags := Seq(Tags.Test -> 1), - logBuffered := true - )) + tags := Seq(Tags.Test -> 1), + logBuffered := true + ) // TODO: This should be on the new default settings for a project. def projectCore: Seq[Setting[_]] = Seq( @@ -715,21 +713,19 @@ object Defaults extends BuildCommon { lazy val ConfigGlobal: Scope = ConfigZero def testTaskOptions(key: Scoped): Seq[Setting[_]] = inTask(key)( - Seq( - testListeners := { - TestLogger.make(streams.value.log, - closeableTestLogger(streamsManager.value, - test in resolvedScoped.value.scope, - logBuffered.value)) +: - new TestStatusReporter(succeededFile(streams.in(test).value.cacheDirectory)) +: - testListeners.in(TaskZero).value - }, - testOptions := Tests.Listeners(testListeners.value) +: (testOptions in TaskZero).value, - testExecution := testExecutionTask(key).value - )) ++ inScope(GlobalScope)( - Seq( - derive(testGrouping := singleTestGroupDefault.value) - )) + testListeners := { + TestLogger.make(streams.value.log, + closeableTestLogger(streamsManager.value, + test in resolvedScoped.value.scope, + logBuffered.value)) +: + new TestStatusReporter(succeededFile(streams.in(test).value.cacheDirectory)) +: + testListeners.in(TaskZero).value + }, + testOptions := Tests.Listeners(testListeners.value) +: (testOptions in TaskZero).value, + testExecution := testExecutionTask(key).value + ) ++ inScope(GlobalScope)( + derive(testGrouping := singleTestGroupDefault.value) + ) private[this] def closeableTestLogger(manager: Streams, baseKey: Scoped, buffered: Boolean)( tdef: TestDefinition): TestLogger.PerTest = { @@ -985,7 +981,7 @@ object Defaults extends BuildCommon { )) lazy val packageConfig: Seq[Setting[_]] = - inTask(packageBin)(Seq( + inTask(packageBin)( packageOptions := { val n = name.value val ver = version.value @@ -997,14 +993,13 @@ object Defaults extends BuildCommon { Package.addImplManifestAttributes(n, ver, homepage.value, org, orgName) +: main.map(Package.MainClass.apply) ++: old } - )) ++ + ) ++ inTask(packageSrc)( - Seq( - packageOptions := Package.addSpecManifestAttributes( - name.value, - version.value, - organizationName.value) +: packageOptions.value - )) ++ + packageOptions := Package.addSpecManifestAttributes( + name.value, + version.value, + organizationName.value) +: packageOptions.value + ) ++ packageTaskSettings(packageBin, packageBinMappings) ++ packageTaskSettings(packageSrc, packageSrcMappings) ++ packageTaskSettings(packageDoc, packageDocMappings) ++ @@ -1081,14 +1076,13 @@ object Defaults extends BuildCommon { def packageTaskSettings(key: TaskKey[File], mappingsTask: Initialize[Task[Seq[(File, String)]]]) = inTask(key)( - Seq( - key in TaskZero := packageTask.value, - packageConfiguration := packageConfigurationTask.value, - mappings := mappingsTask.value, - packagedArtifact := (artifact.value -> key.value), - artifact := artifactSetting.value, - artifactPath := artifactPathSetting(artifact).value - )) + key in TaskZero := packageTask.value, + packageConfiguration := packageConfigurationTask.value, + mappings := mappingsTask.value, + packagedArtifact := (artifact.value -> key.value), + artifact := artifactSetting.value, + artifactPath := artifactPathSetting(artifact).value + ) def packageTask: Initialize[Task[File]] = Def.task { @@ -1288,49 +1282,48 @@ object Defaults extends BuildCommon { def docTaskSettings(key: TaskKey[File] = doc): Seq[Setting[_]] = inTask(key)( - Seq( - apiMappings ++= { - val dependencyCp = dependencyClasspath.value - val log = streams.value.log - if (autoAPIMappings.value) APIMappings.extract(dependencyCp, log).toMap - else Map.empty[File, URL] - }, - fileInputOptions := Seq("-doc-root-content", "-diagrams-dot-path"), - key in TaskZero := { - val s = streams.value - val cs: Compilers = compilers.value - val srcs = sources.value - val out = target.value - val sOpts = scalacOptions.value - val xapis = apiMappings.value - val hasScala = srcs.exists(_.name.endsWith(".scala")) - val hasJava = srcs.exists(_.name.endsWith(".java")) - val cp = data(dependencyClasspath.value).toList - val label = nameForSrc(configuration.value.name) - val fiOpts = fileInputOptions.value - val reporter = (compilerReporter in compile).value - (hasScala, hasJava) match { - case (true, _) => - val options = sOpts ++ Opts.doc.externalAPI(xapis) - val runDoc = Doc.scaladoc(label, s.cacheStoreFactory sub "scala", cs.scalac match { - case ac: AnalyzingCompiler => ac.onArgs(exported(s, "scaladoc")) - }, fiOpts) - runDoc(srcs, cp, out, options, maxErrors.value, s.log) - case (_, true) => - val javadoc = - sbt.inc.Doc.cachedJavadoc(label, s.cacheStoreFactory sub "java", cs.javaTools) - javadoc.run(srcs.toList, - cp, - out, - javacOptions.value.toList, - IncToolOptionsUtil.defaultIncToolOptions(), - s.log, - reporter) - case _ => () // do nothing - } - out + apiMappings ++= { + val dependencyCp = dependencyClasspath.value + val log = streams.value.log + if (autoAPIMappings.value) APIMappings.extract(dependencyCp, log).toMap + else Map.empty[File, URL] + }, + fileInputOptions := Seq("-doc-root-content", "-diagrams-dot-path"), + key in TaskZero := { + val s = streams.value + val cs: Compilers = compilers.value + val srcs = sources.value + val out = target.value + val sOpts = scalacOptions.value + val xapis = apiMappings.value + val hasScala = srcs.exists(_.name.endsWith(".scala")) + val hasJava = srcs.exists(_.name.endsWith(".java")) + val cp = data(dependencyClasspath.value).toList + val label = nameForSrc(configuration.value.name) + val fiOpts = fileInputOptions.value + val reporter = (compilerReporter in compile).value + (hasScala, hasJava) match { + case (true, _) => + val options = sOpts ++ Opts.doc.externalAPI(xapis) + val runDoc = Doc.scaladoc(label, s.cacheStoreFactory sub "scala", cs.scalac match { + case ac: AnalyzingCompiler => ac.onArgs(exported(s, "scaladoc")) + }, fiOpts) + runDoc(srcs, cp, out, options, maxErrors.value, s.log) + case (_, true) => + val javadoc = + sbt.inc.Doc.cachedJavadoc(label, s.cacheStoreFactory sub "java", cs.javaTools) + javadoc.run(srcs.toList, + cp, + out, + javacOptions.value.toList, + IncToolOptionsUtil.defaultIncToolOptions(), + s.log, + reporter) + case _ => () // do nothing } - )) + out + } + ) def mainBgRunTask = mainBgRunTaskForConfig(Select(Runtime)) def mainBgRunMainTask = mainBgRunMainTaskForConfig(Select(Runtime)) @@ -1630,15 +1623,14 @@ object Defaults extends BuildCommon { // build.sbt is treated a Scala source of metabuild, so to enable deprecation flag on build.sbt we set the option here. lazy val deprecationSettings: Seq[Setting[_]] = inConfig(Compile)( - Seq( - scalacOptions := { - val old = scalacOptions.value - val existing = old.toSet - val d = "-deprecation" - if (sbtPlugin.value && !existing(d)) d :: old.toList - else old - } - )) + scalacOptions := { + val old = scalacOptions.value + val existing = old.toSet + val d = "-deprecation" + if (sbtPlugin.value && !existing(d)) d :: old.toList + else old + } + ) } object Classpaths { import Keys._ @@ -2146,78 +2138,77 @@ object Classpaths { def sbtClassifiersTasks = sbtClassifiersGlobalDefaults ++ inTask(updateSbtClassifiers)( - Seq( - externalResolvers := { - val explicit = buildStructure.value - .units(thisProjectRef.value.build) - .unit - .plugins - .pluginData - .resolvers - explicit orElse bootRepositories(appConfiguration.value) getOrElse externalResolvers.value - }, - ivyConfiguration := InlineIvyConfiguration( - paths = ivyPaths.value, - resolvers = externalResolvers.value.toVector, - otherResolvers = Vector.empty, - moduleConfigurations = Vector.empty, - lock = Option(lock(appConfiguration.value)), - checksums = checksums.value.toVector, - managedChecksums = false, - resolutionCacheDir = Some(crossTarget.value / "resolution-cache"), - updateOptions = UpdateOptions(), - log = streams.value.log - ), - ivySbt := ivySbt0.value, - classifiersModule := classifiersModuleTask.value, - // Redefine scalaVersion and scalaBinaryVersion specifically for the dependency graph used for updateSbtClassifiers task. - // to fix https://github.com/sbt/sbt/issues/2686 - scalaVersion := appConfiguration.value.provider.scalaProvider.version, - scalaBinaryVersion := binaryScalaVersion(scalaVersion.value), - scalaModuleInfo := { - Some( - ScalaModuleInfo( - scalaVersion.value, - scalaBinaryVersion.value, - Vector(), - checkExplicit = false, - filterImplicit = false, - overrideScalaVersion = true).withScalaOrganization(scalaOrganization.value)) - }, - updateSbtClassifiers in TaskGlobal := (Def.task { - val lm = dependencyResolution.value - val s = streams.value - val is = ivySbt.value - val mod = classifiersModule.value - val c = updateConfiguration.value - val app = appConfiguration.value - val srcTypes = sourceArtifactTypes.value - val docTypes = docArtifactTypes.value - val log = s.log - val out = is.withIvy(log)(_.getSettings.getDefaultIvyUserDir) - val uwConfig = (unresolvedWarningConfiguration in update).value - withExcludes(out, mod.classifiers, lock(app)) { - excludes => - // val noExplicitCheck = ivy.map(_.withCheckExplicit(false)) - LibraryManagement.transitiveScratch( - lm, - "sbt", - GetClassifiersConfiguration( - mod, - excludes.toVector, - c.withArtifactFilter(c.artifactFilter.map(af => af.withInverted(!af.inverted))), - srcTypes.toVector, - docTypes.toVector - ), - uwConfig, - log - ) match { - case Left(_) => ??? - case Right(ur) => ur - } - } - } tag (Tags.Update, Tags.Network)).value - )) ++ Seq(bootIvyConfiguration := (ivyConfiguration in updateSbtClassifiers).value) + externalResolvers := { + val explicit = buildStructure.value + .units(thisProjectRef.value.build) + .unit + .plugins + .pluginData + .resolvers + explicit orElse bootRepositories(appConfiguration.value) getOrElse externalResolvers.value + }, + ivyConfiguration := InlineIvyConfiguration( + paths = ivyPaths.value, + resolvers = externalResolvers.value.toVector, + otherResolvers = Vector.empty, + moduleConfigurations = Vector.empty, + lock = Option(lock(appConfiguration.value)), + checksums = checksums.value.toVector, + managedChecksums = false, + resolutionCacheDir = Some(crossTarget.value / "resolution-cache"), + updateOptions = UpdateOptions(), + log = streams.value.log + ), + ivySbt := ivySbt0.value, + classifiersModule := classifiersModuleTask.value, + // Redefine scalaVersion and scalaBinaryVersion specifically for the dependency graph used for updateSbtClassifiers task. + // to fix https://github.com/sbt/sbt/issues/2686 + scalaVersion := appConfiguration.value.provider.scalaProvider.version, + scalaBinaryVersion := binaryScalaVersion(scalaVersion.value), + scalaModuleInfo := { + Some( + ScalaModuleInfo( + scalaVersion.value, + scalaBinaryVersion.value, + Vector(), + checkExplicit = false, + filterImplicit = false, + overrideScalaVersion = true).withScalaOrganization(scalaOrganization.value)) + }, + updateSbtClassifiers in TaskGlobal := (Def.task { + val lm = dependencyResolution.value + val s = streams.value + val is = ivySbt.value + val mod = classifiersModule.value + val c = updateConfiguration.value + val app = appConfiguration.value + val srcTypes = sourceArtifactTypes.value + val docTypes = docArtifactTypes.value + val log = s.log + val out = is.withIvy(log)(_.getSettings.getDefaultIvyUserDir) + val uwConfig = (unresolvedWarningConfiguration in update).value + withExcludes(out, mod.classifiers, lock(app)) { + excludes => + // val noExplicitCheck = ivy.map(_.withCheckExplicit(false)) + LibraryManagement.transitiveScratch( + lm, + "sbt", + GetClassifiersConfiguration( + mod, + excludes.toVector, + c.withArtifactFilter(c.artifactFilter.map(af => af.withInverted(!af.inverted))), + srcTypes.toVector, + docTypes.toVector + ), + uwConfig, + log + ) match { + case Left(_) => ??? + case Right(ur) => ur + } + } + } tag (Tags.Update, Tags.Network)).value + ) ++ Seq(bootIvyConfiguration := (ivyConfiguration in updateSbtClassifiers).value) def classifiersModuleTask: Initialize[Task[GetClassifiersModule]] = Def.task { @@ -3245,7 +3236,7 @@ trait BuildExtra extends BuildCommon with DefExtra { * This is useful for reducing test:compile time when not running test. */ def noTestCompletion(config: Configuration = Test): Setting[_] = - inConfig(config)(Seq(definedTests := detectTests.value)).head + inConfig(config)(definedTests := detectTests.value).head def filterKeys(ss: Seq[Setting[_]], transitive: Boolean = false)( f: ScopedKey[_] => Boolean): Seq[Setting[_]] = diff --git a/main/src/main/scala/sbt/PluginCross.scala b/main/src/main/scala/sbt/PluginCross.scala index 799c854f6..0da74dc7b 100644 --- a/main/src/main/scala/sbt/PluginCross.scala +++ b/main/src/main/scala/sbt/PluginCross.scala @@ -46,7 +46,7 @@ private[sbt] object PluginCross { val add = List(sbtVersion in GlobalScope in pluginCrossBuild :== version) ++ List(scalaVersion := scalaVersionSetting.value) ++ inScope(GlobalScope.copy(project = Select(currentRef)))( - Seq(scalaVersion := scalaVersionSetting.value) + scalaVersion := scalaVersionSetting.value ) val cleared = session.mergeSettings.filterNot(crossExclude) val newStructure = Load.reapply(cleared ++ add, structure) diff --git a/main/src/main/scala/sbt/internal/GlobalPlugin.scala b/main/src/main/scala/sbt/internal/GlobalPlugin.scala index 0424a325c..317e6fd0d 100644 --- a/main/src/main/scala/sbt/internal/GlobalPlugin.scala +++ b/main/src/main/scala/sbt/internal/GlobalPlugin.scala @@ -98,13 +98,12 @@ object GlobalPlugin { } } val globalPluginSettings = Project.inScope(Scope.GlobalScope in LocalRootProject)( - Seq( - organization := SbtArtifacts.Organization, - onLoadMessage := Keys.baseDirectory("Loading global plugins from " + _).value, - name := "global-plugin", - sbtPlugin := true, - version := "0.0" - )) + organization := SbtArtifacts.Organization, + onLoadMessage := Keys.baseDirectory("Loading global plugins from " + _).value, + name := "global-plugin", + sbtPlugin := true, + version := "0.0" + ) } final case class GlobalPluginData(projectID: ModuleID, dependencies: Seq[ModuleID], diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index da6c2c8c1..64819b906 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -1126,22 +1126,21 @@ private[sbt] object Load { /** These are the settings defined when loading a project "meta" build. */ val autoPluginSettings: Seq[Setting[_]] = inScope(GlobalScope in LocalRootProject)( - Seq( - sbtPlugin :== true, - pluginData := { - val prod = (exportedProducts in Configurations.Runtime).value - val cp = (fullClasspath in Configurations.Runtime).value - val opts = (scalacOptions in Configurations.Compile).value - PluginData( - removeEntries(cp, prod), - prod, - Some(fullResolvers.value.toVector), - Some(update.value), - opts - ) - }, - onLoadMessage := ("Loading project definition from " + baseDirectory.value) - )) + sbtPlugin :== true, + pluginData := { + val prod = (exportedProducts in Configurations.Runtime).value + val cp = (fullClasspath in Configurations.Runtime).value + val opts = (scalacOptions in Configurations.Compile).value + PluginData( + removeEntries(cp, prod), + prod, + Some(fullResolvers.value.toVector), + Some(update.value), + opts + ) + }, + onLoadMessage := ("Loading project definition from " + baseDirectory.value) + ) private[this] def removeEntries( cp: Seq[Attributed[File]], diff --git a/sbt/src/sbt-test/actions/cross-multiproject/build.sbt b/sbt/src/sbt-test/actions/cross-multiproject/build.sbt index d0eff709c..f7c0b12fc 100644 --- a/sbt/src/sbt-test/actions/cross-multiproject/build.sbt +++ b/sbt/src/sbt-test/actions/cross-multiproject/build.sbt @@ -1,6 +1,6 @@ -inThisBuild(List( +inThisBuild( crossScalaVersions := Seq("2.12.1", "2.11.8") -)) +) lazy val rootProj = (project in file(".")) .aggregate(libProj, fooPlugin) diff --git a/sbt/src/sbt-test/actions/previous/scopes.sbt b/sbt/src/sbt-test/actions/previous/scopes.sbt index 8062b1df1..a6998efe0 100644 --- a/sbt/src/sbt-test/actions/previous/scopes.sbt +++ b/sbt/src/sbt-test/actions/previous/scopes.sbt @@ -22,7 +22,7 @@ x in subA in Compile := { } -inConfig(Compile)(Seq( +inConfig(Compile)( y in subB := { // verify that the referenced key gets delegated val xty = (x in Test in y).previous getOrElse 0 // 13 @@ -31,7 +31,7 @@ inConfig(Compile)(Seq( println(s"xcy=$xcy, xty=$xty") xty * xcy } -)) +) def parser = { import complete.DefaultParsers._ diff --git a/sbt/src/sbt-test/dependency-management/cache-update/build.sbt b/sbt/src/sbt-test/dependency-management/cache-update/build.sbt index b0f3eec0b..146aca5b5 100644 --- a/sbt/src/sbt-test/dependency-management/cache-update/build.sbt +++ b/sbt/src/sbt-test/dependency-management/cache-update/build.sbt @@ -4,7 +4,7 @@ dependencyOverrides in ThisBuild += "com.github.nscala-time" %% "nscala-time" % lazy val root = (project in file(".")) .dependsOn(p1 % Compile) .settings( - inThisBuild(List( + inThisBuild( organizationName := "eed3si9n", organizationHomepage := Some(url("http://example.com/")), homepage := Some(url("https://github.com/example/example")), @@ -20,7 +20,7 @@ lazy val root = (project in file(".")) version := "0.3.1-SNAPSHOT", description := "An HTTP client for Scala with Async Http Client underneath.", licenses := Seq("Apache 2" -> new URL("http://www.apache.org/licenses/LICENSE-2.0.txt")), - )), + ), ivyPaths := IvyPaths( (baseDirectory in ThisBuild).value, Some((baseDirectory in LocalRootProject).value / "ivy-cache") diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-circular/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-circular/multi.sbt index b1180c1af..05a8ffae7 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-circular/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-circular/multi.sbt @@ -41,8 +41,8 @@ lazy val c = project. lazy val root = (project in file(".")). settings(commonSettings). - settings(inThisBuild(Seq( + settings(inThisBuild( organization := "org.example", version := "1.0-SNAPSHOT", updateOptions := updateOptions.value.withCachedResolution(true) - ))) + )) diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/multi.sbt index 61a573e18..aabebeae1 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/multi.sbt @@ -1,12 +1,12 @@ // https://github.com/sbt/sbt/issues/1710 // https://github.com/sbt/sbt/issues/1760 -inThisBuild(Seq( +inThisBuild( organization := "com.example", version := "0.1.0", scalaVersion := "2.10.4", updateOptions := updateOptions.value.withCachedResolution(true) -)) +) def commonSettings: Seq[Def.Setting[_]] = Seq( ivyPaths := IvyPaths((baseDirectory in ThisBuild).value, Some((baseDirectory in LocalRootProject).value / "ivy-cache")), diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt index 7bca9158b..0a3327fa1 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt @@ -27,7 +27,7 @@ lazy val b = project. lazy val root = (project in file(".")). aggregate(a, b). - settings(inThisBuild(Seq( + settings(inThisBuild( organization := "org.example", version := "1.0", updateOptions := updateOptions.value.withCachedResolution(true), @@ -45,4 +45,4 @@ lazy val root = (project in file(".")). sys.error("commons-io NOT found when it should NOT be excluded") } } - ))) + )) diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-interproj/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-interproj/multi.sbt index f359cfc76..c21995ecc 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-interproj/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-interproj/multi.sbt @@ -27,7 +27,7 @@ lazy val a = project. lazy val root = (project in file(".")). aggregate(a). - settings(inThisBuild(Seq( + settings(inThisBuild( organization := "org.example", version := "1.0", updateOptions := updateOptions.value.withCachedResolution(true), @@ -49,4 +49,4 @@ lazy val root = (project in file(".")). sys.error("junit NOT found when it should be included: " + atestcp.toString) } } - ))) + )) diff --git a/sbt/src/sbt-test/dependency-management/make-pom-type/build.sbt b/sbt/src/sbt-test/dependency-management/make-pom-type/build.sbt index 6d9005519..a7857d39d 100644 --- a/sbt/src/sbt-test/dependency-management/make-pom-type/build.sbt +++ b/sbt/src/sbt-test/dependency-management/make-pom-type/build.sbt @@ -2,12 +2,12 @@ lazy val p1 = (project in file("p1")). settings( checkTask(expectedMongo), libraryDependencies += "org.mongodb" %% "casbah" % "2.4.1" pomOnly(), - inThisBuild(List( + inThisBuild( organization := "org.example", version := "1.0", scalaVersion := "2.9.2", autoScalaLibrary := false - )) + ) ) lazy val p2 = (project in file("p2")). diff --git a/sbt/src/sbt-test/dependency-management/publish-local/build.sbt b/sbt/src/sbt-test/dependency-management/publish-local/build.sbt index 261aa6ac3..b8ace934d 100644 --- a/sbt/src/sbt-test/dependency-management/publish-local/build.sbt +++ b/sbt/src/sbt-test/dependency-management/publish-local/build.sbt @@ -1,12 +1,12 @@ lazy val root = (project in file(".")). dependsOn(sub). aggregate(sub). - settings(inThisBuild(List( + settings(inThisBuild( organization := "A", version := "1.0", ivyPaths := baseDirectory( dir => IvyPaths(dir, Some(dir / "ivy" / "cache")) ).value, externalResolvers := (baseDirectory map { base => Resolver.file("local", base / "ivy" / "local" asFile)(Resolver.ivyStylePatterns) :: Nil }).value - )), + ), mavenStyle, interProject, name := "Publish Test" diff --git a/sbt/src/sbt-test/dependency-management/publish-local/changes/RetrieveTest.sbt b/sbt/src/sbt-test/dependency-management/publish-local/changes/RetrieveTest.sbt index 795608334..ca033e3b8 100644 --- a/sbt/src/sbt-test/dependency-management/publish-local/changes/RetrieveTest.sbt +++ b/sbt/src/sbt-test/dependency-management/publish-local/changes/RetrieveTest.sbt @@ -1,10 +1,10 @@ lazy val root = (project in file(".")). - settings(inThisBuild(List( + settings(inThisBuild( organization := "A", version := "1.0", ivyPaths := baseDirectory( dir => IvyPaths(dir, Some(dir / "ivy" / "cache")) ).value, externalResolvers := (baseDirectory map { base => Resolver.file("local", base / "ivy" / "local" asFile)(Resolver.ivyStylePatterns) :: Nil }).value - )), + ), mavenStyle, name := "Retrieve Test", libraryDependencies := (publishMavenStyle { style => if(style) mavenStyleDependencies else ivyStyleDependencies }).value diff --git a/sbt/src/sbt-test/project/derived/build.sbt b/sbt/src/sbt-test/project/derived/build.sbt index 21cfc9207..1d1a5fd5b 100644 --- a/sbt/src/sbt-test/project/derived/build.sbt +++ b/sbt/src/sbt-test/project/derived/build.sbt @@ -23,7 +23,7 @@ globalDepE in Global := "globalE" // ---------------- Derived settings // verify that deriving is transitive -inScope(GlobalScope)(Seq( +inScope(GlobalScope)( Def.derive(customA := customB.value + "-a"), Def.derive(customB := thisProject.value.id + "-b"), // verify that a setting with multiple triggers still only gets added once @@ -36,7 +36,7 @@ inScope(GlobalScope)(Seq( // if customE were added in Global because of name, there would be an error // because description wouldn't be found Def.derive(customE := globalDepE.value + "-" + projectDepE.value) -)) +) // ---------------- Projects diff --git a/sbt/src/sbt-test/tests/fork-test-group-parallel/build.sbt b/sbt/src/sbt-test/tests/fork-test-group-parallel/build.sbt index 761d141ee..8f4ca2611 100644 --- a/sbt/src/sbt-test/tests/fork-test-group-parallel/build.sbt +++ b/sbt/src/sbt-test/tests/fork-test-group-parallel/build.sbt @@ -1,7 +1,7 @@ scalaVersion in ThisBuild := "2.11.8" concurrentRestrictions in Global := Seq(Tags.limitAll(4)) libraryDependencies += "org.specs2" %% "specs2-core" % "3.8.4" % Test -inConfig(Test)(Seq( +inConfig(Test)( testGrouping := { val home = javaHome.value val strategy = outputStrategy.value @@ -22,4 +22,4 @@ inConfig(Test)(Seq( ))} }, TaskKey[Unit]("test-failure") := test.failure.value -)) +) From 490f57c779e007436455d683ffaedaf235387185 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 4 Apr 2018 15:13:53 +0100 Subject: [PATCH 133/176] Upgrade to sbt-scalafmt 1.15 & scalafmt 1.4.0 --- build.sbt | 2 +- project/plugins.sbt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 61231fbd7..1c24cf775 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ def buildLevelSettings: Seq[Setting[_]] = resolvers += Resolver.mavenLocal, scalafmtOnCompile := true, scalafmtOnCompile in Sbt := false, - scalafmtVersion := "1.3.0", + scalafmtVersion := "1.4.0", )) def commonSettings: Seq[Setting[_]] = Def.settings( diff --git a/project/plugins.sbt b/project/plugins.sbt index 278929bd0..4ec42a3d6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,4 +6,5 @@ addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.5") addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.4.0") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.8.0") +addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15") addSbtPlugin("com.lightbend" % "sbt-whitesource" % "0.1.9") From cf1f65612a205f554c538f0d8542c1b845881213 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 3 Oct 2017 16:16:09 +0100 Subject: [PATCH 134/176] Don't align args or params by the opening parens --- .scalafmt.conf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.scalafmt.conf b/.scalafmt.conf index e4ab36511..2486945d0 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -8,3 +8,8 @@ docstrings = JavaDoc # This also seems more idiomatic to include whitespace in import x.{ yyy } spaces.inImportCurlyBraces = true + +# This is more idiomatic Scala. +# http://docs.scala-lang.org/style/indentation.html#methods-with-numerous-arguments +align.openParenCallSite = false +align.openParenDefnSite = false From aac916b1193e2b4b7f578ad64802b2b99ea21fa2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 4 Apr 2018 15:20:24 +0100 Subject: [PATCH 135/176] Improve code clarity with danglingParentheses = true --- .scalafmt.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.scalafmt.conf b/.scalafmt.conf index 2486945d0..a36334e44 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -13,3 +13,6 @@ spaces.inImportCurlyBraces = true # http://docs.scala-lang.org/style/indentation.html#methods-with-numerous-arguments align.openParenCallSite = false align.openParenDefnSite = false + +# For better code clarity +danglingParentheses = true From 8f4b8abb7bc80cf1a2836d20ab3b6cad35a979ac Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 24 Apr 2018 16:12:10 +0100 Subject: [PATCH 136/176] Run scalafmt & test:scalafmt --- .../internal/util/appmacro/ContextUtil.scala | 24 +- .../sbt/internal/util/appmacro/Convert.scala | 14 +- .../sbt/internal/util/appmacro/Instance.scala | 8 +- .../internal/util/appmacro/KListBuilder.scala | 22 +- .../internal/util/appmacro/MixedBuilder.scala | 5 +- .../internal/util/appmacro/TupleBuilder.scala | 5 +- .../util/appmacro/TupleNBuilder.scala | 20 +- .../main/scala/sbt/internal/util/AList.scala | 293 ++++-- .../scala/sbt/internal/util/Attributes.scala | 26 +- .../main/scala/sbt/internal/util/INode.scala | 6 +- .../scala/sbt/internal/util/Settings.scala | 14 +- .../scala/sbt/internal/util/LineReader.scala | 5 +- .../util/complete/JLineCompletion.scala | 3 +- .../sbt/internal/util/complete/Parser.scala | 16 +- .../sbt/internal/util/complete/Parsers.scala | 3 +- .../src/test/scala/DefaultParsersSpec.scala | 3 +- .../sbt/complete/ParserWithExamplesTest.scala | 9 +- main-actions/src/main/scala/sbt/Console.scala | 35 +- main-actions/src/main/scala/sbt/Doc.scala | 26 +- .../src/main/scala/sbt/DotGraph.scala | 46 +- .../src/main/scala/sbt/ForkTests.scala | 85 +- main-actions/src/main/scala/sbt/Package.scala | 26 +- .../src/main/scala/sbt/RawCompileLike.scala | 24 +- main-actions/src/main/scala/sbt/Sync.scala | 24 +- .../src/main/scala/sbt/TestResultLogger.scala | 61 +- main-actions/src/main/scala/sbt/Tests.scala | 171 ++-- .../src/main/scala/sbt/compiler/Eval.scala | 152 ++-- .../src/test/scala/sbt/CacheIvyTest.scala | 20 +- .../test/scala/sbt/compiler/EvalTest.scala | 12 +- .../main/scala/sbt/BasicCommandStrings.scala | 12 +- .../src/main/scala/sbt/BasicCommands.scala | 40 +- .../src/main/scala/sbt/BasicKeys.scala | 48 +- main-command/src/main/scala/sbt/Command.scala | 64 +- .../src/main/scala/sbt/MainControl.scala | 44 +- main-command/src/main/scala/sbt/State.scala | 5 +- main-command/src/main/scala/sbt/Watched.scala | 9 +- .../scala/sbt/internal/server/Server.scala | 11 +- .../sbt/internal/server/ServerHandler.scala | 12 +- main-settings/src/main/scala/sbt/Append.scala | 6 +- main-settings/src/main/scala/sbt/Def.scala | 38 +- .../src/main/scala/sbt/DelegateIndex.scala | 8 +- .../src/main/scala/sbt/InputTask.scala | 64 +- .../src/main/scala/sbt/Previous.scala | 14 +- main-settings/src/main/scala/sbt/Remove.scala | 6 +- main-settings/src/main/scala/sbt/Scope.scala | 89 +- .../src/main/scala/sbt/Structure.scala | 23 +- .../src/main/scala/sbt/std/InputWrapper.scala | 70 +- .../src/main/scala/sbt/std/KeyMacro.scala | 18 +- .../src/main/scala/sbt/std/SettingMacro.scala | 21 +- .../src/main/scala/sbt/std/TaskMacro.scala | 207 +++-- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 6 +- .../main/scala/sbt/BackgroundJobService.scala | 6 +- main/src/main/scala/sbt/BuildPaths.scala | 31 +- main/src/main/scala/sbt/Cross.scala | 18 +- main/src/main/scala/sbt/Defaults.scala | 852 +++++++++++------- main/src/main/scala/sbt/EvaluateTask.scala | 149 +-- main/src/main/scala/sbt/Extracted.scala | 11 +- main/src/main/scala/sbt/Main.scala | 98 +- main/src/main/scala/sbt/MainLoop.scala | 3 +- main/src/main/scala/sbt/Opts.scala | 6 +- main/src/main/scala/sbt/PluginCross.scala | 10 +- main/src/main/scala/sbt/Plugins.scala | 33 +- main/src/main/scala/sbt/Project.scala | 99 +- main/src/main/scala/sbt/RichURI.scala | 16 +- main/src/main/scala/sbt/ScopeFilter.scala | 76 +- main/src/main/scala/sbt/ScopedKeyData.scala | 8 +- main/src/main/scala/sbt/ScriptedPlugin.scala | 3 +- main/src/main/scala/sbt/SessionVar.scala | 6 +- main/src/main/scala/sbt/TemplateCommand.scala | 29 +- main/src/main/scala/sbt/internal/Act.scala | 219 +++-- .../main/scala/sbt/internal/Aggregation.scala | 15 +- .../sbt/internal/BuildDependencies.scala | 12 +- .../main/scala/sbt/internal/BuildLoader.scala | 132 +-- .../scala/sbt/internal/BuildStructure.scala | 109 ++- .../main/scala/sbt/internal/BuildUtil.scala | 15 +- .../scala/sbt/internal/CommandExchange.scala | 3 +- .../scala/sbt/internal/CommandStrings.scala | 6 +- .../scala/sbt/internal/ConsoleProject.scala | 3 +- .../DefaultBackgroundJobService.scala | 24 +- .../sbt/internal/EvaluateConfigurations.scala | 164 ++-- .../scala/sbt/internal/GlobalPlugin.scala | 60 +- .../sbt/internal/GroupedAutoPlugins.scala | 6 +- .../main/scala/sbt/internal/IvyConsole.scala | 18 +- .../main/scala/sbt/internal/KeyIndex.scala | 112 ++- main/src/main/scala/sbt/internal/Load.scala | 59 +- .../main/scala/sbt/internal/LogManager.scala | 15 +- .../scala/sbt/internal/PluginDiscovery.scala | 32 +- .../scala/sbt/internal/PluginManagement.scala | 36 +- .../scala/sbt/internal/PluginsDebug.scala | 72 +- .../sbt/internal/ProjectNavigation.scala | 3 +- .../src/main/scala/sbt/internal/Resolve.scala | 13 +- main/src/main/scala/sbt/internal/Script.scala | 23 +- .../scala/sbt/internal/SessionSettings.scala | 40 +- .../sbt/internal/SettingCompletions.scala | 65 +- .../scala/sbt/internal/SettingGraph.scala | 24 +- .../scala/sbt/internal/TaskSequential.scala | 549 ++++++----- .../main/scala/sbt/internal/TaskTimings.scala | 10 +- .../scala/sbt/internal/parser/SbtParser.scala | 49 +- .../sbt/internal/parser/SbtRefactorings.scala | 12 +- .../sbt/internal/server/Definition.scala | 3 +- .../server/LanguageServerProtocol.scala | 23 +- .../sbt/internal/server/NetworkChannel.scala | 23 +- .../sbt/internal/server/SettingQuery.scala | 28 +- main/src/test/scala/DefaultsTest.scala | 24 +- main/src/test/scala/Delegates.scala | 3 +- main/src/test/scala/ParseKey.scala | 18 +- main/src/test/scala/PluginCommandTest.scala | 50 +- main/src/test/scala/PluginsTest.scala | 6 +- .../test/scala/sbt/internal/TestBuild.scala | 74 +- .../internal/parser/CheckIfParsedSpec.scala | 4 +- .../internal/parser/CommentedXmlSpec.scala | 20 +- .../sbt/internal/parser/EmbeddedXmlSpec.scala | 20 +- .../scala/sbt/internal/parser/ErrorSpec.scala | 3 +- .../internal/parser/SessionSettingsSpec.scala | 5 +- .../parser/SplitExpressionsBehavior.scala | 3 +- .../sbt/internal/server/DefinitionTest.scala | 15 +- .../internal/server/SettingQueryTest.scala | 32 +- .../JsonRpcNotificationMessageFormats.scala | 9 +- .../codec/JsonRpcRequestMessageFormats.scala | 12 +- .../codec/JsonRpcResponseErrorFormats.scala | 12 +- .../codec/JsonRpcResponseMessageFormats.scala | 9 +- run/src/main/scala/sbt/Fork.scala | 8 +- run/src/main/scala/sbt/Run.scala | 17 +- run/src/main/scala/sbt/SelectMainClass.scala | 6 +- run/src/main/scala/sbt/TrapExit.scala | 18 +- sbt/src/test/scala/sbt/ServerSpec.scala | 27 +- .../sbt/scriptedtest/ScriptedTests.scala | 3 +- .../src/main/scala/sbt/Action.scala | 6 +- .../src/main/scala/sbt/std/Streams.scala | 24 +- .../src/main/scala/sbt/std/System.scala | 5 +- .../src/main/scala/sbt/std/TaskExtra.scala | 42 +- tasks-standard/src/test/scala/TaskGen.scala | 8 +- .../src/test/scala/TaskSerial.scala | 32 +- .../main/scala/sbt/CompletionService.scala | 10 +- .../scala/sbt/ConcurrentRestrictions.scala | 17 +- tasks/src/main/scala/sbt/Execute.scala | 39 +- tasks/src/main/scala/sbt/Incomplete.scala | 13 +- .../scala/sbt/JUnitXmlTestsListener.scala | 3 +- .../src/main/scala/sbt/TestFramework.scala | 109 ++- 139 files changed, 3740 insertions(+), 2370 deletions(-) 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 7e627317d..aeea0683e 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 @@ -29,8 +29,9 @@ object ContextUtil { * Given `myImplicitConversion(someValue).extensionMethod`, where `extensionMethod` is a macro that uses this * method, the result of this method is `f()`. */ - def selectMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] = { + def selectMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] = { import c.universe._ c.macroApplication match { case s @ Select(Apply(_, t :: Nil), _) => f(c.Expr[Any](t), s.pos) @@ -211,12 +212,14 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) { def changeOwner(tree: Tree, prev: Symbol, next: Symbol): Unit = new ChangeOwnerAndModuleClassTraverser( prev.asInstanceOf[global.Symbol], - next.asInstanceOf[global.Symbol]).traverse(tree.asInstanceOf[global.Tree]) + next.asInstanceOf[global.Symbol] + ).traverse(tree.asInstanceOf[global.Tree]) // Workaround copied from scala/async:can be removed once https://github.com/scala/scala/pull/3179 is merged. - private[this] class ChangeOwnerAndModuleClassTraverser(oldowner: global.Symbol, - newowner: global.Symbol) - extends global.ChangeOwnerTraverser(oldowner, newowner) { + private[this] class ChangeOwnerAndModuleClassTraverser( + oldowner: global.Symbol, + newowner: global.Symbol + ) extends global.ChangeOwnerTraverser(oldowner, newowner) { override def traverse(tree: global.Tree): Unit = { tree match { case _: global.DefTree => change(tree.symbol.moduleClass) @@ -248,7 +251,8 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) { * the type constructor `[x] List[x]`. */ def extractTC(tcp: AnyRef with Singleton, name: String)( - implicit it: ctx.TypeTag[tcp.type]): ctx.Type = { + implicit it: ctx.TypeTag[tcp.type] + ): ctx.Type = { val itTpe = it.tpe.asInstanceOf[global.Type] val m = itTpe.nonPrivateMember(global.newTypeName(name)) val tc = itTpe.memberInfo(m).asInstanceOf[ctx.universe.Type] @@ -262,8 +266,10 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) { * Typically, `f` is a `Select` or `Ident`. * The wrapper is replaced with the result of `subWrapper(, , )` */ - def transformWrappers(t: Tree, - subWrapper: (String, Type, Tree, Tree) => Converted[ctx.type]): Tree = { + def transformWrappers( + t: Tree, + subWrapper: (String, Type, Tree, Tree) => Converted[ctx.type] + ): Tree = { // the main tree transformer that replaces calls to InputWrapper.wrap(x) with // plain Idents that reference the actual input value object appTransformer extends Transformer { diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala index 2aae74f3b..36529ec6c 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala @@ -26,9 +26,10 @@ sealed trait Converted[C <: blackbox.Context with Singleton] { } object Converted { def NotApplicable[C <: blackbox.Context with Singleton] = new NotApplicable[C] - final case class Failure[C <: blackbox.Context with Singleton](position: C#Position, - message: String) - extends Converted[C] { + final case class Failure[C <: blackbox.Context with Singleton]( + position: C#Position, + message: String + ) extends Converted[C] { def isSuccess = false def transform(f: C#Tree => C#Tree): Converted[C] = new Failure(position, message) } @@ -36,9 +37,10 @@ object Converted { def isSuccess = false def transform(f: C#Tree => C#Tree): Converted[C] = this } - final case class Success[C <: blackbox.Context with Singleton](tree: C#Tree, - finalTransform: C#Tree => C#Tree) - extends Converted[C] { + final case class Success[C <: blackbox.Context with Singleton]( + tree: C#Tree, + finalTransform: C#Tree => C#Tree + ) extends Converted[C] { def isSuccess = true def transform(f: C#Tree => C#Tree): Converted[C] = Success(f(tree), finalTransform) } diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/Instance.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/Instance.scala index 33e614eab..a2d41f3e4 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/Instance.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/Instance.scala @@ -41,9 +41,11 @@ object Instance { final val MapName = "map" final val InstanceTCName = "M" - final class Input[U <: Universe with Singleton](val tpe: U#Type, - val expr: U#Tree, - val local: U#ValDef) + final class Input[U <: Universe with Singleton]( + val tpe: U#Type, + val expr: U#Tree, + val local: U#ValDef + ) trait Transform[C <: blackbox.Context with Singleton, N[_]] { def apply(in: C#Tree): C#Tree } diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala index 99a210a0c..148b772df 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala @@ -13,8 +13,9 @@ import macros._ /** A `TupleBuilder` that uses a KList as the tuple representation.*/ object KListBuilder extends TupleBuilder { - def make(c: blackbox.Context)(mt: c.Type, - inputs: Inputs[c.universe.type]): BuilderResult[c.type] = + def make( + c: blackbox.Context + )(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] { val ctx: c.type = c val util = ContextUtil[c.type](c) @@ -47,15 +48,20 @@ object KListBuilder extends TupleBuilder { case Nil => revBindings.reverse } - private[this] def makeKList(revInputs: Inputs[c.universe.type], - klist: Tree, - klistType: Type): Tree = + private[this] def makeKList( + revInputs: Inputs[c.universe.type], + klist: Tree, + klistType: Type + ): Tree = revInputs match { case in :: tail => val next = ApplyTree( - TypeApply(Ident(kcons), - TypeTree(in.tpe) :: TypeTree(klistType) :: TypeTree(mTC) :: Nil), - in.expr :: klist :: Nil) + TypeApply( + Ident(kcons), + TypeTree(in.tpe) :: TypeTree(klistType) :: TypeTree(mTC) :: Nil + ), + in.expr :: klist :: Nil + ) makeKList(tail, next, appliedType(kconsTC, in.tpe :: klistType :: mTC :: Nil)) case Nil => klist } diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala index 472a0446b..990bcb6e5 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala @@ -16,8 +16,9 @@ import macros._ * and `KList` for larger numbers of inputs. This builder cannot handle fewer than 2 inputs. */ object MixedBuilder extends TupleBuilder { - def make(c: blackbox.Context)(mt: c.Type, - inputs: Inputs[c.universe.type]): BuilderResult[c.type] = { + def make( + c: blackbox.Context + )(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = { val delegate = if (inputs.size > TupleNBuilder.MaxInputs) KListBuilder else TupleNBuilder delegate.make(c)(mt, inputs) } diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala index 0cd12ba44..1c67f9ed8 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala @@ -35,8 +35,9 @@ trait TupleBuilder { type Inputs[U <: Universe with Singleton] = List[Instance.Input[U]] /** Constructs a one-time use Builder for Context `c` and type constructor `tcType`. */ - def make(c: blackbox.Context)(tcType: c.Type, - inputs: Inputs[c.universe.type]): BuilderResult[c.type] + def make( + c: blackbox.Context + )(tcType: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] } trait BuilderResult[C <: blackbox.Context with Singleton] { diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala index fab552157..6cf541e41 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala @@ -22,8 +22,9 @@ object TupleNBuilder extends TupleBuilder { final val MaxInputs = 11 final val TupleMethodName = "tuple" - def make(c: blackbox.Context)(mt: c.Type, - inputs: Inputs[c.universe.type]): BuilderResult[c.type] = + def make( + c: blackbox.Context + )(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] { val util = ContextUtil[c.type](c) import c.universe._ @@ -34,8 +35,9 @@ object TupleNBuilder extends TupleBuilder { val ctx: c.type = c val representationC: PolyType = { val tcVariable: Symbol = newTCVariable(util.initialOwner) - val tupleTypeArgs = inputs.map(in => - internal.typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]) + val tupleTypeArgs = inputs.map( + in => internal.typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type] + ) val tuple = global.definitions.tupleType(tupleTypeArgs) internal.polyType(tcVariable :: Nil, tuple.asInstanceOf[Type]) } @@ -47,10 +49,12 @@ object TupleNBuilder extends TupleBuilder { } def extract(param: ValDef): List[ValDef] = bindTuple(param, Nil, inputs.map(_.local), 1) - def bindTuple(param: ValDef, - revBindings: List[ValDef], - params: List[ValDef], - i: Int): List[ValDef] = + def bindTuple( + param: ValDef, + revBindings: List[ValDef], + params: List[ValDef], + i: Int + ): List[ValDef] = params match { case (x @ ValDef(mods, name, tpt, _)) :: xs => val rhs = select(Ident(param.name), "_" + i.toString) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala index cc5343d2f..ba2379567 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala @@ -17,7 +17,9 @@ import Types._ */ trait AList[K[L[x]]] { def transform[M[_], N[_]](value: K[M], f: M ~> N): K[N] - def traverse[M[_], N[_], P[_]](value: K[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[K[P]] + def traverse[M[_], N[_], P[_]](value: K[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[K[P]] def foldr[M[_], A](value: K[M], f: (M[_], A) => A, init: A): A def toList[M[_]](value: K[M]): List[M[_]] = foldr[M, List[M[_]]](value, _ :: _, Nil) @@ -33,8 +35,11 @@ object AList { val empty: Empty = new Empty { def transform[M[_], N[_]](in: Unit, f: M ~> N) = () def foldr[M[_], T](in: Unit, f: (M[_], T) => T, init: T) = init - override def apply[M[_], C](in: Unit, f: Unit => C)(implicit app: Applicative[M]): M[C] = app.pure(f(())) - def traverse[M[_], N[_], P[_]](in: Unit, f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Unit] = np.pure(()) + override def apply[M[_], C](in: Unit, f: Unit => C)(implicit app: Applicative[M]): M[C] = + app.pure(f(())) + def traverse[M[_], N[_], P[_]](in: Unit, f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[Unit] = np.pure(()) } type SeqList[T] = AList[λ[L[x] => List[L[T]]]] @@ -42,9 +47,12 @@ object AList { /** AList for a homogeneous sequence. */ def seq[T]: SeqList[T] = new SeqList[T] { def transform[M[_], N[_]](s: List[M[T]], f: M ~> N) = s.map(f.fn[T]) - def foldr[M[_], A](s: List[M[T]], f: (M[_], A) => A, init: A): A = (init /: s.reverse)((t, m) => f(m, t)) + def foldr[M[_], A](s: List[M[T]], f: (M[_], A) => A, init: A): A = + (init /: s.reverse)((t, m) => f(m, t)) - override def apply[M[_], C](s: List[M[T]], f: List[T] => C)(implicit ap: Applicative[M]): M[C] = { + override def apply[M[_], C](s: List[M[T]], f: List[T] => C)( + implicit ap: Applicative[M] + ): M[C] = { def loop[V](in: List[M[T]], g: List[T] => V): M[V] = in match { case Nil => ap.pure(g(Nil)) @@ -55,15 +63,20 @@ object AList { loop(s, f) } - def traverse[M[_], N[_], P[_]](s: List[M[T]], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[List[P[T]]] = ??? + def traverse[M[_], N[_], P[_]](s: List[M[T]], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[List[P[T]]] = ??? } /** AList for the arbitrary arity data structure KList. */ def klist[KL[M[_]] <: KList.Aux[M, KL]]: AList[KL] = new AList[KL] { def transform[M[_], N[_]](k: KL[M], f: M ~> N) = k.transform(f) def foldr[M[_], T](k: KL[M], f: (M[_], T) => T, init: T): T = k.foldr(f, init) - override def apply[M[_], C](k: KL[M], f: KL[Id] => C)(implicit app: Applicative[M]): M[C] = k.apply(f)(app) - def traverse[M[_], N[_], P[_]](k: KL[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KL[P]] = k.traverse[N, P](f)(np) + override def apply[M[_], C](k: KL[M], f: KL[Id] => C)(implicit app: Applicative[M]): M[C] = + k.apply(f)(app) + def traverse[M[_], N[_], P[_]](k: KL[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[KL[P]] = k.traverse[N, P](f)(np) override def toList[M[_]](k: KL[M]) = k.toList } @@ -73,7 +86,9 @@ object AList { def single[A]: Single[A] = new Single[A] { def transform[M[_], N[_]](a: M[A], f: M ~> N) = f(a) def foldr[M[_], T](a: M[A], f: (M[_], T) => T, init: T): T = f(a, init) - def traverse[M[_], N[_], P[_]](a: M[A], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[P[A]] = f(a) + def traverse[M[_], N[_], P[_]](a: M[A], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[P[A]] = f(a) } type ASplit[K[L[x]], B[x]] = AList[λ[L[x] => K[(L ∙ B)#l]]] @@ -85,7 +100,9 @@ object AList { def transform[M[_], N[_]](value: Split[M], f: M ~> N): Split[N] = base.transform[(M ∙ B)#l, (N ∙ B)#l](value, nestCon[M, N, B](f)) - def traverse[M[_], N[_], P[_]](value: Split[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Split[P]] = { + def traverse[M[_], N[_], P[_]](value: Split[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[Split[P]] = { val g = nestCon[M, (N ∙ P)#l, B](f) base.traverse[(M ∙ B)#l, N, (P ∙ B)#l](value, g)(np) } @@ -101,7 +118,9 @@ object AList { type T2[M[_]] = (M[A], M[B]) def transform[M[_], N[_]](t: T2[M], f: M ~> N): T2[N] = (f(t._1), f(t._2)) def foldr[M[_], T](t: T2[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, init)) - def traverse[M[_], N[_], P[_]](t: T2[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T2[P]] = { + def traverse[M[_], N[_], P[_]](t: T2[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T2[P]] = { val g = (Tuple2.apply[P[A], P[B]] _).curried np.apply(np.map(g, f(t._1)), f(t._2)) } @@ -113,7 +132,9 @@ object AList { type T3[M[_]] = (M[A], M[B], M[C]) def transform[M[_], N[_]](t: T3[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3)) def foldr[M[_], T](t: T3[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, init))) - def traverse[M[_], N[_], P[_]](t: T3[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T3[P]] = { + def traverse[M[_], N[_], P[_]](t: T3[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T3[P]] = { val g = (Tuple3.apply[P[A], P[B], P[C]] _).curried np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)) } @@ -124,8 +145,11 @@ object AList { def tuple4[A, B, C, D]: T4List[A, B, C, D] = new T4List[A, B, C, D] { type T4[M[_]] = (M[A], M[B], M[C], M[D]) def transform[M[_], N[_]](t: T4[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4)) - def foldr[M[_], T](t: T4[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, init)))) - def traverse[M[_], N[_], P[_]](t: T4[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T4[P]] = { + def foldr[M[_], T](t: T4[M], f: (M[_], T) => T, init: T): T = + f(t._1, f(t._2, f(t._3, f(t._4, init)))) + def traverse[M[_], N[_], P[_]](t: T4[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T4[P]] = { val g = (Tuple4.apply[P[A], P[B], P[C], P[D]] _).curried np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)) } @@ -136,8 +160,11 @@ object AList { def tuple5[A, B, C, D, E]: T5List[A, B, C, D, E] = new T5List[A, B, C, D, E] { type T5[M[_]] = (M[A], M[B], M[C], M[D], M[E]) def transform[M[_], N[_]](t: T5[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5)) - def foldr[M[_], T](t: T5[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, init))))) - def traverse[M[_], N[_], P[_]](t: T5[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T5[P]] = { + def foldr[M[_], T](t: T5[M], f: (M[_], T) => T, init: T): T = + f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, init))))) + def traverse[M[_], N[_], P[_]](t: T5[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T5[P]] = { val g = (Tuple5.apply[P[A], P[B], P[C], P[D], P[E]] _).curried np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)) } @@ -147,71 +174,213 @@ object AList { type T6List[A, B, C, D, E, F] = AList[T6K[A, B, C, D, E, F]#l] def tuple6[A, B, C, D, E, F]: T6List[A, B, C, D, E, F] = new T6List[A, B, C, D, E, F] { type T6[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F]) - def transform[M[_], N[_]](t: T6[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6)) - def foldr[M[_], T](t: T6[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, init)))))) - def traverse[M[_], N[_], P[_]](t: T6[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T6[P]] = { + def transform[M[_], N[_]](t: T6[M], f: M ~> N) = + (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6)) + def foldr[M[_], T](t: T6[M], f: (M[_], T) => T, init: T): T = + f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, init)))))) + def traverse[M[_], N[_], P[_]](t: T6[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T6[P]] = { val g = (Tuple6.apply[P[A], P[B], P[C], P[D], P[E], P[F]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)) + np.apply( + np.apply( + np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), + f(t._5) + ), + f(t._6) + ) } } - sealed trait T7K[A, B, C, D, E, F, G] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G]) } + sealed trait T7K[A, B, C, D, E, F, G] { + type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G]) + } type T7List[A, B, C, D, E, F, G] = AList[T7K[A, B, C, D, E, F, G]#l] def tuple7[A, B, C, D, E, F, G]: T7List[A, B, C, D, E, F, G] = new T7List[A, B, C, D, E, F, G] { type T7[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G]) - def transform[M[_], N[_]](t: T7[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7)) - def foldr[M[_], T](t: T7[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, init))))))) - def traverse[M[_], N[_], P[_]](t: T7[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T7[P]] = { + def transform[M[_], N[_]](t: T7[M], f: M ~> N) = + (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7)) + def foldr[M[_], T](t: T7[M], f: (M[_], T) => T, init: T): T = + f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, init))))))) + def traverse[M[_], N[_], P[_]](t: T7[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T7[P]] = { val g = (Tuple7.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)) + np.apply( + np.apply( + np.apply( + np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), + f(t._5) + ), + f(t._6) + ), + f(t._7) + ) } } - sealed trait T8K[A, B, C, D, E, F, G, H] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H]) } + sealed trait T8K[A, B, C, D, E, F, G, H] { + type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H]) + } type T8List[A, B, C, D, E, F, G, H] = AList[T8K[A, B, C, D, E, F, G, H]#l] - def tuple8[A, B, C, D, E, F, G, H]: T8List[A, B, C, D, E, F, G, H] = new T8List[A, B, C, D, E, F, G, H] { - type T8[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H]) - def transform[M[_], N[_]](t: T8[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8)) - def foldr[M[_], T](t: T8[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, init)))))))) - def traverse[M[_], N[_], P[_]](t: T8[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T8[P]] = { - val g = (Tuple8.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)) + def tuple8[A, B, C, D, E, F, G, H]: T8List[A, B, C, D, E, F, G, H] = + new T8List[A, B, C, D, E, F, G, H] { + type T8[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H]) + def transform[M[_], N[_]](t: T8[M], f: M ~> N) = + (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8)) + def foldr[M[_], T](t: T8[M], f: (M[_], T) => T, init: T): T = + f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, init)))))))) + def traverse[M[_], N[_], P[_]](t: T8[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T8[P]] = { + val g = (Tuple8.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H]] _).curried + np.apply( + np.apply( + np.apply( + np.apply( + np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), + f(t._5) + ), + f(t._6) + ), + f(t._7) + ), + f(t._8) + ) + } } - } - sealed trait T9K[A, B, C, D, E, F, G, H, I] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I]) } + sealed trait T9K[A, B, C, D, E, F, G, H, I] { + type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I]) + } type T9List[A, B, C, D, E, F, G, H, I] = AList[T9K[A, B, C, D, E, F, G, H, I]#l] - def tuple9[A, B, C, D, E, F, G, H, I]: T9List[A, B, C, D, E, F, G, H, I] = new T9List[A, B, C, D, E, F, G, H, I] { - type T9[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I]) - def transform[M[_], N[_]](t: T9[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9)) - def foldr[M[_], T](t: T9[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, init))))))))) - def traverse[M[_], N[_], P[_]](t: T9[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T9[P]] = { - val g = (Tuple9.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)) + def tuple9[A, B, C, D, E, F, G, H, I]: T9List[A, B, C, D, E, F, G, H, I] = + new T9List[A, B, C, D, E, F, G, H, I] { + type T9[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I]) + def transform[M[_], N[_]](t: T9[M], f: M ~> N) = + (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9)) + def foldr[M[_], T](t: T9[M], f: (M[_], T) => T, init: T): T = + f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, init))))))))) + def traverse[M[_], N[_], P[_]](t: T9[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T9[P]] = { + val g = (Tuple9.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I]] _).curried + np.apply( + np.apply( + np.apply( + np.apply( + np.apply( + np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), + f(t._5) + ), + f(t._6) + ), + f(t._7) + ), + f(t._8) + ), + f(t._9) + ) + } } - } - sealed trait T10K[A, B, C, D, E, F, G, H, I, J] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J]) } + sealed trait T10K[A, B, C, D, E, F, G, H, I, J] { + type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J]) + } type T10List[A, B, C, D, E, F, G, H, I, J] = AList[T10K[A, B, C, D, E, F, G, H, I, J]#l] - def tuple10[A, B, C, D, E, F, G, H, I, J]: T10List[A, B, C, D, E, F, G, H, I, J] = new T10List[A, B, C, D, E, F, G, H, I, J] { - type T10[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J]) - def transform[M[_], N[_]](t: T10[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10)) - def foldr[M[_], T](t: T10[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, init)))))))))) - def traverse[M[_], N[_], P[_]](t: T10[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T10[P]] = { - val g = (Tuple10.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)) + def tuple10[A, B, C, D, E, F, G, H, I, J]: T10List[A, B, C, D, E, F, G, H, I, J] = + new T10List[A, B, C, D, E, F, G, H, I, J] { + type T10[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J]) + def transform[M[_], N[_]](t: T10[M], f: M ~> N) = + (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10)) + def foldr[M[_], T](t: T10[M], f: (M[_], T) => T, init: T): T = + f( + t._1, + f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, init))))))))) + ) + def traverse[M[_], N[_], P[_]](t: T10[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T10[P]] = { + val g = + (Tuple10.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J]] _).curried + np.apply( + np.apply( + np.apply( + np.apply( + np.apply( + np.apply( + np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), + f(t._5) + ), + f(t._6) + ), + f(t._7) + ), + f(t._8) + ), + f(t._9) + ), + f(t._10) + ) + } } - } - sealed trait T11K[A, B, C, D, E, F, G, H, I, J, K] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J], L[K]) } - type T11List[A, B, C, D, E, F, G, H, I, J, K] = AList[T11K[A, B, C, D, E, F, G, H, I, J, K]#l] - def tuple11[A, B, C, D, E, F, G, H, I, J, K]: T11List[A, B, C, D, E, F, G, H, I, J, K] = new T11List[A, B, C, D, E, F, G, H, I, J, K] { - type T11[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J], M[K]) - def transform[M[_], N[_]](t: T11[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10), f(t._11)) - def foldr[M[_], T](t: T11[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, f(t._11, init))))))))))) - def traverse[M[_], N[_], P[_]](t: T11[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T11[P]] = { - val g = (Tuple11.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J], P[K]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)), f(t._11)) - } + sealed trait T11K[A, B, C, D, E, F, G, H, I, J, K] { + type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J], L[K]) } + type T11List[A, B, C, D, E, F, G, H, I, J, K] = AList[T11K[A, B, C, D, E, F, G, H, I, J, K]#l] + def tuple11[A, B, C, D, E, F, G, H, I, J, K]: T11List[A, B, C, D, E, F, G, H, I, J, K] = + new T11List[A, B, C, D, E, F, G, H, I, J, K] { + type T11[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J], M[K]) + def transform[M[_], N[_]](t: T11[M], f: M ~> N) = + ( + f(t._1), + f(t._2), + f(t._3), + f(t._4), + f(t._5), + f(t._6), + f(t._7), + f(t._8), + f(t._9), + f(t._10), + f(t._11) + ) + def foldr[M[_], T](t: T11[M], f: (M[_], T) => T, init: T): T = + f( + t._1, + f( + t._2, + f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, f(t._11, init))))))))) + ) + ) + def traverse[M[_], N[_], P[_]](t: T11[M], f: M ~> (N ∙ P)#l)( + implicit np: Applicative[N] + ): N[T11[P]] = { + val g = (Tuple11 + .apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J], P[K]] _).curried + np.apply( + np.apply( + np.apply( + np.apply( + np.apply( + np.apply( + np.apply( + np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), + f(t._5) + ), + f(t._6) + ), + f(t._7) + ), + f(t._8) + ), + f(t._9) + ), + f(t._10) + ), + f(t._11) + ) + } + } } 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 3e4832f1e..b45eeb4a1 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 @@ -71,20 +71,26 @@ object AttributeKey { def apply[T: Manifest: OptJsonWriter](name: String, description: String): AttributeKey[T] = apply(name, description, Nil) - def apply[T: Manifest: OptJsonWriter](name: String, - description: String, - rank: Int): AttributeKey[T] = + def apply[T: Manifest: OptJsonWriter]( + name: String, + description: String, + rank: Int + ): AttributeKey[T] = apply(name, description, Nil, rank) - def apply[T: Manifest: OptJsonWriter](name: String, - description: String, - extend: Seq[AttributeKey[_]]): AttributeKey[T] = + def apply[T: Manifest: OptJsonWriter]( + name: String, + description: String, + extend: Seq[AttributeKey[_]] + ): AttributeKey[T] = apply(name, description, extend, Int.MaxValue) - def apply[T: Manifest: OptJsonWriter](name: String, - description: String, - extend: Seq[AttributeKey[_]], - rank: Int): AttributeKey[T] = + def apply[T: Manifest: OptJsonWriter]( + name: String, + description: String, + extend: Seq[AttributeKey[_]], + rank: Int + ): AttributeKey[T] = make(name, Some(description), extend, rank) private[sbt] def copyWithRank[T](a: AttributeKey[T], rank: Int): AttributeKey[T] = diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala index 939bd9576..2090e0139 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala @@ -170,8 +170,10 @@ abstract class EvaluateSettings[Scope] { } protected final def setValue(v: T): Unit = { - assert(state != Evaluated, - "Already evaluated (trying to set value to " + v + "): " + toString) + assert( + state != Evaluated, + "Already evaluated (trying to set value to " + v + "): " + toString + ) if (v == null) sys.error("Setting value cannot be null: " + keyString) value = v state = Evaluated diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index 910e1c089..c080fcbe3 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -357,7 +357,8 @@ trait Init[Scope] { keys.map(u => showUndefined(u, validKeys, delegates)).mkString("\n\n ", "\n\n ", "") new Uninitialized( keys, - prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ") + prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n " + ) } final class Compiled[T]( @@ -374,8 +375,9 @@ trait Init[Scope] { val locals = compiled flatMap { case (key, comp) => if (key.key.isLocal) Seq[Compiled[_]](comp) else Nil } - val ordered = Dag.topologicalSort(locals)(_.dependencies.flatMap(dep => - if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil)) + val ordered = Dag.topologicalSort(locals)( + _.dependencies.flatMap(dep => if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil) + ) def flatten( cmap: Map[ScopedKey[_], Flattened], key: ScopedKey[_], @@ -383,7 +385,8 @@ trait Init[Scope] { ): Flattened = new Flattened( key, - deps.flatMap(dep => if (dep.key.isLocal) cmap(dep).dependencies else dep :: Nil)) + deps.flatMap(dep => if (dep.key.isLocal) cmap(dep).dependencies else dep :: Nil) + ) val empty = Map.empty[ScopedKey[_], Flattened] @@ -415,7 +418,8 @@ trait Init[Scope] { * Intersects two scopes, returning the more specific one if they intersect, or None otherwise. */ private[sbt] def intersect(s1: Scope, s2: Scope)( - implicit delegates: Scope => Seq[Scope]): Option[Scope] = + implicit delegates: Scope => Seq[Scope] + ): Option[Scope] = if (delegates(s1).contains(s2)) Some(s1) // s1 is more specific else if (delegates(s2).contains(s1)) Some(s2) // s2 is more specific else None 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 406ee5e97..e67ff915c 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 @@ -49,8 +49,9 @@ abstract class JLine extends LineReader { private[this] def readLineDirect(prompt: String, mask: Option[Char]): Option[String] = if (handleCONT) - Signals.withHandler(() => resume(), signal = Signals.CONT)(() => - readLineDirectRaw(prompt, mask)) + Signals.withHandler(() => resume(), signal = Signals.CONT)( + () => readLineDirectRaw(prompt, mask) + ) else readLineDirectRaw(prompt, mask) 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 700dafa35..e0a44deb2 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 @@ -91,7 +91,8 @@ object JLineCompletion { def appendNonEmpty(set: Set[String], add: String) = if (add.trim.isEmpty) set else set + add def customCompletor( - f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean = + f: (String, Int) => (Seq[String], Seq[String]) + ): (ConsoleReader, Int) => Boolean = (reader, level) => { val success = complete(beforeCursor(reader), reader => f(reader, level), reader) reader.flush() diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index 94417e1c4..09bf9a8a3 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -275,8 +275,10 @@ object Parser extends ParserMain { revAcc: List[T] ): Parser[Seq[T]] = { assume(min >= 0, "Minimum must be greater than or equal to zero (was " + min + ")") - assume(max >= min, - "Minimum must be less than or equal to maximum (min: " + min + ", max: " + max + ")") + assume( + max >= min, + "Minimum must be less than or equal to maximum (min: " + min + ", max: " + max + ")" + ) def checkRepeated(invalidButOptional: => Parser[Seq[T]]): Parser[Seq[T]] = repeated match { @@ -836,10 +838,12 @@ private final class ParserWithExamples[T]( ) extends ValidParser[T] { def derive(c: Char) = - examples(delegate derive c, - exampleSource.withAddedPrefix(c.toString), - maxNumberOfExamples, - removeInvalidExamples) + examples( + delegate derive c, + exampleSource.withAddedPrefix(c.toString), + maxNumberOfExamples, + removeInvalidExamples + ) def result = delegate.result 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 1120ed173..ce444e229 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 @@ -44,7 +44,8 @@ trait Parsers { /** Parses a single hexadecimal digit (0-9, a-f, A-F). */ lazy val HexDigit = charClass(c => HexDigitSet(c.toUpper), "hex digit") examples HexDigitSet.map( - _.toString) + _.toString + ) /** Parses a single letter, according to Char.isLetter, into a Char. */ lazy val Letter = charClass(_.isLetter, "letter") diff --git a/internal/util-complete/src/test/scala/DefaultParsersSpec.scala b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala index 253a4216b..b62ff8566 100644 --- a/internal/util-complete/src/test/scala/DefaultParsersSpec.scala +++ b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala @@ -14,7 +14,8 @@ object DefaultParsersSpec extends Properties("DefaultParsers") { import DefaultParsers.{ ID, isIDChar, matches, validID } property("∀ s ∈ String: validID(s) == matches(ID, s)") = forAll( - (s: String) => validID(s) == matches(ID, s)) + (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)) diff --git a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala index e142e4a66..c2e7c314b 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala @@ -27,7 +27,8 @@ class ParserWithExamplesTest extends UnitSpec { Set( suggestion("blue"), suggestion("red") - )) + ) + ) parserWithExamples.completions(0) shouldEqual validCompletions } } @@ -38,7 +39,8 @@ class ParserWithExamplesTest extends UnitSpec { val derivedCompletions = Completions( Set( suggestion("lue") - )) + ) + ) parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions } } @@ -58,7 +60,8 @@ class ParserWithExamplesTest extends UnitSpec { Set( suggestion("lue"), suggestion("lock") - )) + ) + ) parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions } } diff --git a/main-actions/src/main/scala/sbt/Console.scala b/main-actions/src/main/scala/sbt/Console.scala index c2792d595..0e9d6e313 100644 --- a/main-actions/src/main/scala/sbt/Console.scala +++ b/main-actions/src/main/scala/sbt/Console.scala @@ -20,25 +20,30 @@ final class Console(compiler: AnalyzingCompiler) { def apply(classpath: Seq[File], log: Logger): Try[Unit] = apply(classpath, Nil, "", "", log) - def apply(classpath: Seq[File], - options: Seq[String], - initialCommands: String, - cleanupCommands: String, - log: Logger): Try[Unit] = + def apply( + classpath: Seq[File], + options: Seq[String], + initialCommands: String, + cleanupCommands: String, + log: Logger + ): Try[Unit] = apply(classpath, options, initialCommands, cleanupCommands)(None, Nil)(log) - def apply(classpath: Seq[File], - options: Seq[String], - loader: ClassLoader, - initialCommands: String, - cleanupCommands: String)(bindings: (String, Any)*)(implicit log: Logger): Try[Unit] = + def apply( + classpath: Seq[File], + options: Seq[String], + loader: ClassLoader, + initialCommands: String, + cleanupCommands: String + )(bindings: (String, Any)*)(implicit log: Logger): Try[Unit] = apply(classpath, options, initialCommands, cleanupCommands)(Some(loader), bindings) - def apply(classpath: Seq[File], - options: Seq[String], - initialCommands: String, - cleanupCommands: String)(loader: Option[ClassLoader], bindings: Seq[(String, Any)])( - implicit log: Logger): Try[Unit] = { + def apply( + classpath: Seq[File], + options: Seq[String], + initialCommands: String, + cleanupCommands: String + )(loader: Option[ClassLoader], bindings: Seq[(String, Any)])(implicit log: Logger): Try[Unit] = { def console0() = compiler.console(classpath, options, initialCommands, cleanupCommands, log)(loader, bindings) // TODO: Fix JLine diff --git a/main-actions/src/main/scala/sbt/Doc.scala b/main-actions/src/main/scala/sbt/Doc.scala index 12df12c9e..93eafb1e6 100644 --- a/main-actions/src/main/scala/sbt/Doc.scala +++ b/main-actions/src/main/scala/sbt/Doc.scala @@ -20,18 +20,24 @@ import sbt.internal.util.ManagedLogger object Doc { import RawCompileLike._ - def scaladoc(label: String, - cacheStoreFactory: CacheStoreFactory, - compiler: AnalyzingCompiler): Gen = + def scaladoc( + label: String, + cacheStoreFactory: CacheStoreFactory, + compiler: AnalyzingCompiler + ): Gen = scaladoc(label, cacheStoreFactory, compiler, Seq()) - def scaladoc(label: String, - cacheStoreFactory: CacheStoreFactory, - compiler: AnalyzingCompiler, - fileInputOptions: Seq[String]): Gen = - cached(cacheStoreFactory, - fileInputOptions, - prepare(label + " Scala API documentation", compiler.doc)) + def scaladoc( + label: String, + cacheStoreFactory: CacheStoreFactory, + compiler: AnalyzingCompiler, + fileInputOptions: Seq[String] + ): Gen = + cached( + cacheStoreFactory, + fileInputOptions, + prepare(label + " Scala API documentation", compiler.doc) + ) @deprecated("Going away", "1.1.1") def javadoc( diff --git a/main-actions/src/main/scala/sbt/DotGraph.scala b/main-actions/src/main/scala/sbt/DotGraph.scala index d0be921be..c3543770a 100644 --- a/main-actions/src/main/scala/sbt/DotGraph.scala +++ b/main-actions/src/main/scala/sbt/DotGraph.scala @@ -30,29 +30,37 @@ object DotGraph { val toString = packageOnly compose fToString(sourceRoots) apply(relations, outputDirectory, toString, toString) } - def apply(relations: Relations, - outputDir: File, - sourceToString: File => String, - externalToString: File => String): Unit = { + def apply( + relations: Relations, + outputDir: File, + sourceToString: File => String, + externalToString: File => String + ): Unit = { def file(name: String) = new File(outputDir, name) IO.createDirectory(outputDir) - generateGraph(file("int-class-deps"), - "dependencies", - relations.internalClassDep, - identity[String], - identity[String]) - generateGraph(file("binary-dependencies"), - "externalDependencies", - relations.libraryDep, - externalToString, - sourceToString) + generateGraph( + file("int-class-deps"), + "dependencies", + relations.internalClassDep, + identity[String], + identity[String] + ) + generateGraph( + file("binary-dependencies"), + "externalDependencies", + relations.libraryDep, + externalToString, + sourceToString + ) } - def generateGraph[K, V](file: File, - graphName: String, - relation: Relation[K, V], - keyToString: K => String, - valueToString: V => String): Unit = { + def generateGraph[K, V]( + file: File, + graphName: String, + relation: Relation[K, V], + keyToString: K => String, + valueToString: V => String + ): Unit = { import scala.collection.mutable.{ HashMap, HashSet } val mappedGraph = new HashMap[String, HashSet[String]] for ((key, values) <- relation.forwardMap; keyString = keyToString(key); value <- values) diff --git a/main-actions/src/main/scala/sbt/ForkTests.scala b/main-actions/src/main/scala/sbt/ForkTests.scala index c6eb96a1e..4299f54ac 100755 --- a/main-actions/src/main/scala/sbt/ForkTests.scala +++ b/main-actions/src/main/scala/sbt/ForkTests.scala @@ -20,13 +20,15 @@ import sbt.protocol.testing._ import sbt.internal.util.ConsoleAppender private[sbt] object ForkTests { - def apply(runners: Map[TestFramework, Runner], - tests: Vector[TestDefinition], - config: Execution, - classpath: Seq[File], - fork: ForkOptions, - log: Logger, - tag: Tag): Task[TestOutput] = { + def apply( + runners: Map[TestFramework, Runner], + tests: Vector[TestDefinition], + config: Execution, + classpath: Seq[File], + fork: ForkOptions, + log: Logger, + tag: Tag + ): Task[TestOutput] = { val opts = processOptions(config, tests, log) import std.TaskExtra._ @@ -43,12 +45,14 @@ private[sbt] object ForkTests { } } - private[this] def mainTestTask(runners: Map[TestFramework, Runner], - opts: ProcessedOptions, - classpath: Seq[File], - fork: ForkOptions, - log: Logger, - parallel: Boolean): Task[TestOutput] = + private[this] def mainTestTask( + runners: Map[TestFramework, Runner], + opts: ProcessedOptions, + classpath: Seq[File], + fork: ForkOptions, + log: Logger, + parallel: Boolean + ): Task[TestOutput] = std.TaskExtra.task { val server = new ServerSocket(0) val testListeners = opts.testListeners flatMap { @@ -68,7 +72,8 @@ private[sbt] object ForkTests { } catch { case e: java.net.SocketException => log.error( - "Could not accept connection from test agent: " + e.getClass + ": " + e.getMessage) + "Could not accept connection from test agent: " + e.getClass + ": " + e.getMessage + ) log.trace(e) server.close() return @@ -84,10 +89,13 @@ private[sbt] object ForkTests { val taskdefs = opts.tests.map( t => - new TaskDef(t.name, - forkFingerprint(t.fingerprint), - t.explicitlySpecified, - t.selectors)) + new TaskDef( + t.name, + forkFingerprint(t.fingerprint), + t.explicitlySpecified, + t.selectors + ) + ) os.writeObject(taskdefs.toArray) os.writeInt(runners.size) @@ -117,20 +125,27 @@ private[sbt] object ForkTests { val acceptorThread = new Thread(Acceptor) acceptorThread.start() - val fullCp = classpath ++: Seq(IO.classLocationFile[ForkMain], - IO.classLocationFile[Framework]) - val options = Seq("-classpath", - fullCp mkString File.pathSeparator, - classOf[ForkMain].getCanonicalName, - server.getLocalPort.toString) + val fullCp = classpath ++: Seq( + IO.classLocationFile[ForkMain], + IO.classLocationFile[Framework] + ) + val options = Seq( + "-classpath", + fullCp mkString File.pathSeparator, + classOf[ForkMain].getCanonicalName, + server.getLocalPort.toString + ) val ec = Fork.java(fork, options) val result = if (ec != 0) - TestOutput(TestResult.Error, - Map( - "Running java with options " + options - .mkString(" ") + " failed with exit code " + ec -> SuiteResult.Error), - Iterable.empty) + TestOutput( + TestResult.Error, + Map( + "Running java with options " + options + .mkString(" ") + " failed with exit code " + ec -> SuiteResult.Error + ), + Iterable.empty + ) else { // Need to wait acceptor thread to finish its business acceptorThread.join() @@ -151,11 +166,13 @@ private[sbt] object ForkTests { case _ => sys.error("Unknown fingerprint type: " + f.getClass) } } -private final class React(is: ObjectInputStream, - os: ObjectOutputStream, - log: Logger, - listeners: Seq[TestReportListener], - results: mutable.Map[String, SuiteResult]) { +private final class React( + is: ObjectInputStream, + os: ObjectOutputStream, + log: Logger, + listeners: Seq[TestReportListener], + results: mutable.Map[String, SuiteResult] +) { import ForkTags._ @annotation.tailrec def react(): Unit = is.readObject match { diff --git a/main-actions/src/main/scala/sbt/Package.scala b/main-actions/src/main/scala/sbt/Package.scala index 82ab1072f..8ecb1ccf7 100644 --- a/main-actions/src/main/scala/sbt/Package.scala +++ b/main-actions/src/main/scala/sbt/Package.scala @@ -49,9 +49,11 @@ object Package { } } - final class Configuration(val sources: Seq[(File, String)], - val jar: File, - val options: Seq[PackageOption]) + final class Configuration( + val sources: Seq[(File, String)], + val jar: File, + val options: Seq[PackageOption] + ) def apply(conf: Configuration, cacheStoreFactory: CacheStoreFactory, log: Logger): Unit = { val manifest = new Manifest val main = manifest.getMainAttributes @@ -66,8 +68,10 @@ object Package { setVersion(main) val cachedMakeJar = inputChanged(cacheStoreFactory make "inputs") { - (inChanged, - inputs: Map[File, String] :+: FilesInfo[ModifiedFileInfo] :+: Manifest :+: HNil) => + ( + inChanged, + inputs: Map[File, String] :+: FilesInfo[ModifiedFileInfo] :+: Manifest :+: HNil + ) => import exists.format val sources :+: _ :+: manifest :+: HNil = inputs inputChanged(cacheStoreFactory make "output") { (outChanged, jar: PlainFileInfo) => @@ -95,11 +99,13 @@ object Package { val attribVals = Seq(name, version, orgName) ManifestAttributes(attribKeys zip attribVals: _*) } - def addImplManifestAttributes(name: String, - version: String, - homepage: Option[java.net.URL], - org: String, - orgName: String): PackageOption = { + def addImplManifestAttributes( + name: String, + version: String, + homepage: Option[java.net.URL], + org: String, + orgName: String + ): PackageOption = { import Attributes.Name._ // The ones in Attributes.Name are deprecated saying: diff --git a/main-actions/src/main/scala/sbt/RawCompileLike.scala b/main-actions/src/main/scala/sbt/RawCompileLike.scala index 56fc1b551..cc4c0fe80 100644 --- a/main-actions/src/main/scala/sbt/RawCompileLike.scala +++ b/main-actions/src/main/scala/sbt/RawCompileLike.scala @@ -47,16 +47,20 @@ object RawCompileLike { def cached(cacheStoreFactory: CacheStoreFactory, doCompile: Gen): Gen = cached(cacheStoreFactory, Seq(), doCompile) - def cached(cacheStoreFactory: CacheStoreFactory, - fileInputOpts: Seq[String], - doCompile: Gen): Gen = + def cached( + cacheStoreFactory: CacheStoreFactory, + fileInputOpts: Seq[String], + doCompile: Gen + ): Gen = (sources, classpath, outputDirectory, options, maxErrors, log) => { type Inputs = FilesInfo[HashFileInfo] :+: FilesInfo[ModifiedFileInfo] :+: Seq[File] :+: File :+: Seq[ - String] :+: Int :+: HNil + String + ] :+: Int :+: HNil val inputs : Inputs = hash(sources.toSet ++ optionFiles(options, fileInputOpts)) :+: lastModified( - classpath.toSet) :+: classpath :+: outputDirectory :+: options :+: maxErrors :+: HNil + classpath.toSet + ) :+: classpath :+: outputDirectory :+: options :+: maxErrors :+: HNil val cachedComp = inputChanged(cacheStoreFactory make "inputs") { (inChanged, in: Inputs) => inputChanged(cacheStoreFactory make "output") { (outChanged, outputs: FilesInfo[PlainFileInfo]) => @@ -92,10 +96,12 @@ object RawCompileLike { compiler(sources, classpath, outputDirectory, options) } - def compile(label: String, - cacheStoreFactory: CacheStoreFactory, - instance: ScalaInstance, - cpOptions: ClasspathOptions): Gen = + def compile( + label: String, + cacheStoreFactory: CacheStoreFactory, + instance: ScalaInstance, + cpOptions: ClasspathOptions + ): Gen = cached(cacheStoreFactory, prepare(label + " sources", rawCompile(instance, cpOptions))) val nop: Gen = (_, _, _, _, _, _) => () diff --git a/main-actions/src/main/scala/sbt/Sync.scala b/main-actions/src/main/scala/sbt/Sync.scala index deec0ac34..663cc04fc 100644 --- a/main-actions/src/main/scala/sbt/Sync.scala +++ b/main-actions/src/main/scala/sbt/Sync.scala @@ -85,8 +85,10 @@ object Sync { sys.error("Duplicate mappings:" + dups.mkString) } - implicit def relationFormat[A, B](implicit af: JsonFormat[Map[A, Set[B]]], - bf: JsonFormat[Map[B, Set[A]]]): JsonFormat[Relation[A, B]] = + implicit def relationFormat[A, B]( + implicit af: JsonFormat[Map[A, Set[B]]], + bf: JsonFormat[Map[B, Set[A]]] + ): JsonFormat[Relation[A, B]] = new JsonFormat[Relation[A, B]] { def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Relation[A, B] = jsOpt match { @@ -109,15 +111,18 @@ object Sync { } - def writeInfo[F <: FileInfo](store: CacheStore, - relation: Relation[File, File], - info: Map[File, F])(implicit infoFormat: JsonFormat[F]): Unit = + def writeInfo[F <: FileInfo]( + store: CacheStore, + relation: Relation[File, File], + info: Map[File, F] + )(implicit infoFormat: JsonFormat[F]): Unit = store.write((relation, info)) type RelationInfo[F] = (Relation[File, File], Map[File, F]) - def readInfo[F <: FileInfo](store: CacheStore)( - implicit infoFormat: JsonFormat[F]): RelationInfo[F] = + def readInfo[F <: FileInfo]( + store: CacheStore + )(implicit infoFormat: JsonFormat[F]): RelationInfo[F] = try { readUncaught[F](store)(infoFormat) } catch { case _: IOException => (Relation.empty[File, File], Map.empty[File, F]) case _: ZipException => (Relation.empty[File, File], Map.empty[File, F]) @@ -128,7 +133,8 @@ object Sync { } } - private def readUncaught[F <: FileInfo](store: CacheStore)( - implicit infoFormat: JsonFormat[F]): RelationInfo[F] = + private def readUncaught[F <: FileInfo]( + store: CacheStore + )(implicit infoFormat: JsonFormat[F]): RelationInfo[F] = store.read(default = (Relation.empty[File, File], Map.empty[File, F])) } diff --git a/main-actions/src/main/scala/sbt/TestResultLogger.scala b/main-actions/src/main/scala/sbt/TestResultLogger.scala index 8248946b3..5f196f2b5 100644 --- a/main-actions/src/main/scala/sbt/TestResultLogger.scala +++ b/main-actions/src/main/scala/sbt/TestResultLogger.scala @@ -31,13 +31,17 @@ trait TestResultLogger { def run(log: Logger, results: Output, taskName: String): Unit /** Only allow invocation if certain criteria is met, else use another `TestResultLogger` (defaulting to nothing) . */ - final def onlyIf(f: (Output, String) => Boolean, - otherwise: TestResultLogger = TestResultLogger.Null) = + final def onlyIf( + f: (Output, String) => Boolean, + otherwise: TestResultLogger = TestResultLogger.Null + ) = TestResultLogger.choose(f, this, otherwise) /** Allow invocation unless a certain predicate passes, in which case use another `TestResultLogger` (defaulting to nothing) . */ - final def unless(f: (Output, String) => Boolean, - otherwise: TestResultLogger = TestResultLogger.Null) = + final def unless( + f: (Output, String) => Boolean, + otherwise: TestResultLogger = TestResultLogger.Null + ) = TestResultLogger.choose(f, otherwise, this) } @@ -69,8 +73,10 @@ object TestResultLogger { * @param f The `TestResultLogger` to choose if the predicate fails. */ def choose(cond: (Output, String) => Boolean, t: TestResultLogger, f: TestResultLogger) = - TestResultLogger((log, results, taskName) => - (if (cond(results, taskName)) t else f).run(log, results, taskName)) + TestResultLogger( + (log, results, taskName) => + (if (cond(results, taskName)) t else f).run(log, results, taskName) + ) /** Transforms the input to be completely silent when the subject module doesn't contain any tests. */ def silenceWhenNoTests(d: Defaults.Main) = @@ -127,35 +133,39 @@ object TestResultLogger { results.summaries.size > 1 || results.summaries.headOption.forall(_.summaryText.isEmpty) val printStandard = TestResultLogger((log, results, _) => { - val (skippedCount, - errorsCount, - passedCount, - failuresCount, - ignoredCount, - canceledCount, - pendingCount, + val ( + skippedCount, + errorsCount, + passedCount, + failuresCount, + ignoredCount, + canceledCount, + pendingCount, ) = results.events.foldLeft((0, 0, 0, 0, 0, 0, 0)) { 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, + ( + skippedAcc + testEvent.skippedCount, + errorAcc + testEvent.errorCount, + passedAcc + testEvent.passedCount, + failureAcc + testEvent.failureCount, + ignoredAcc + testEvent.ignoredCount, + canceledAcc + testEvent.canceledCount, + pendingAcc + testEvent.pendingCount, ) } val totalCount = failuresCount + errorsCount + skippedCount + passedCount val base = s"Total $totalCount, Failed $failuresCount, Errors $errorsCount, Passed $passedCount" - val otherCounts = Seq("Skipped" -> skippedCount, - "Ignored" -> ignoredCount, - "Canceled" -> canceledCount, - "Pending" -> pendingCount) + val otherCounts = Seq( + "Skipped" -> skippedCount, + "Ignored" -> ignoredCount, + "Canceled" -> canceledCount, + "Pending" -> pendingCount + ) val extra = otherCounts.filter(_._2 > 0).map { case (label, count) => s", $label $count" } val postfix = base + extra.mkString @@ -184,6 +194,7 @@ object TestResultLogger { }) val printNoTests = TestResultLogger( - (log, results, taskName) => log.info("No tests to run for " + taskName)) + (log, results, taskName) => log.info("No tests to run for " + taskName) + ) } } diff --git a/main-actions/src/main/scala/sbt/Tests.scala b/main-actions/src/main/scala/sbt/Tests.scala index 6885a0dc5..cc76b1412 100644 --- a/main-actions/src/main/scala/sbt/Tests.scala +++ b/main-actions/src/main/scala/sbt/Tests.scala @@ -44,9 +44,11 @@ object Tests { * @param events The result of each test group (suite) executed during this test run. * @param summaries Explicit summaries directly provided by test frameworks. This may be empty, in which case a default summary will be generated. */ - final case class Output(overall: TestResult, - events: Map[String, SuiteResult], - summaries: Iterable[Summary]) + final case class Output( + overall: TestResult, + events: Map[String, SuiteResult], + summaries: Iterable[Summary] + ) /** * Summarizes a test run. @@ -138,9 +140,11 @@ object Tests { val cleanup: Vector[ClassLoader => Unit], val testListeners: Vector[TestReportListener] ) - private[sbt] def processOptions(config: Execution, - discovered: Vector[TestDefinition], - log: Logger): ProcessedOptions = { + private[sbt] def processOptions( + config: Execution, + discovered: Vector[TestDefinition], + log: Logger + ): ProcessedOptions = { import collection.mutable.{ HashSet, ListBuffer } val testFilters = new ListBuffer[String => Boolean] var orderedFilters = Seq[String => Boolean]() @@ -168,7 +172,8 @@ object Tests { if (undefinedFrameworks.nonEmpty) log.warn( "Arguments defined for test frameworks that are not present:\n\t" + undefinedFrameworks - .mkString("\n\t")) + .mkString("\n\t") + ) def includeTest(test: TestDefinition) = !excludeTestsSet.contains(test.name) && testFilters.forall(filter => filter(test.name)) @@ -177,10 +182,12 @@ object Tests { if (orderedFilters.isEmpty) filtered0 else orderedFilters.flatMap(f => filtered0.filter(d => f(d.name))).toList.distinct val uniqueTests = distinctBy(tests)(_.name) - new ProcessedOptions(uniqueTests.toVector, - setup.toVector, - cleanup.toVector, - testListeners.toVector) + new ProcessedOptions( + uniqueTests.toVector, + setup.toVector, + cleanup.toVector, + testListeners.toVector + ) } private[this] def distinctBy[T, K](in: Seq[T])(f: T => K): Seq[T] = { @@ -188,33 +195,39 @@ object Tests { in.filter(t => seen.add(f(t))) } - def apply(frameworks: Map[TestFramework, Framework], - testLoader: ClassLoader, - runners: Map[TestFramework, Runner], - discovered: Vector[TestDefinition], - config: Execution, - log: ManagedLogger): Task[Output] = { + def apply( + frameworks: Map[TestFramework, Framework], + testLoader: ClassLoader, + runners: Map[TestFramework, Runner], + discovered: Vector[TestDefinition], + config: Execution, + log: ManagedLogger + ): Task[Output] = { val o = processOptions(config, discovered, log) - testTask(testLoader, - frameworks, - runners, - o.tests, - o.setup, - o.cleanup, - log, - o.testListeners, - config) + testTask( + testLoader, + frameworks, + runners, + o.tests, + o.setup, + o.cleanup, + log, + o.testListeners, + config + ) } - def testTask(loader: ClassLoader, - frameworks: Map[TestFramework, Framework], - runners: Map[TestFramework, Runner], - tests: Vector[TestDefinition], - userSetup: Iterable[ClassLoader => Unit], - userCleanup: Iterable[ClassLoader => Unit], - log: ManagedLogger, - testListeners: Vector[TestReportListener], - config: Execution): Task[Output] = { + def testTask( + loader: ClassLoader, + frameworks: Map[TestFramework, Framework], + runners: Map[TestFramework, Runner], + tests: Vector[TestDefinition], + userSetup: Iterable[ClassLoader => Unit], + userCleanup: Iterable[ClassLoader => Unit], + log: ManagedLogger, + testListeners: Vector[TestReportListener], + config: Execution + ): Task[Output] = { def fj(actions: Iterable[() => Unit]): Task[Unit] = nop.dependsOn(actions.toSeq.fork(_()): _*) def partApp(actions: Iterable[ClassLoader => Unit]) = actions.toSeq map { a => () => a(loader) @@ -239,31 +252,43 @@ object Tests { } type TestRunnable = (String, TestFunction) - private def createNestedRunnables(loader: ClassLoader, - testFun: TestFunction, - nestedTasks: Seq[TestTask]): Seq[(String, TestFunction)] = + private def createNestedRunnables( + loader: ClassLoader, + testFun: TestFunction, + nestedTasks: Seq[TestTask] + ): Seq[(String, TestFunction)] = nestedTasks.view.zipWithIndex map { case (nt, idx) => val testFunDef = testFun.taskDef - (testFunDef.fullyQualifiedName, - TestFramework.createTestFunction(loader, - new TaskDef(testFunDef.fullyQualifiedName + "-" + idx, - testFunDef.fingerprint, - testFunDef.explicitlySpecified, - testFunDef.selectors), - testFun.runner, - nt)) + ( + testFunDef.fullyQualifiedName, + TestFramework.createTestFunction( + loader, + new TaskDef( + testFunDef.fullyQualifiedName + "-" + idx, + testFunDef.fingerprint, + testFunDef.explicitlySpecified, + testFunDef.selectors + ), + testFun.runner, + nt + ) + ) } - def makeParallel(loader: ClassLoader, - runnables: Iterable[TestRunnable], - setupTasks: Task[Unit], - tags: Seq[(Tag, Int)]): Task[Map[String, SuiteResult]] = + def makeParallel( + loader: ClassLoader, + runnables: Iterable[TestRunnable], + setupTasks: Task[Unit], + tags: Seq[(Tag, Int)] + ): Task[Map[String, SuiteResult]] = toTasks(loader, runnables.toSeq, tags).dependsOn(setupTasks) - def toTasks(loader: ClassLoader, - runnables: Seq[TestRunnable], - tags: Seq[(Tag, Int)]): Task[Map[String, SuiteResult]] = { + def toTasks( + loader: ClassLoader, + runnables: Seq[TestRunnable], + tags: Seq[(Tag, Int)] + ): Task[Map[String, SuiteResult]] = { val tasks = runnables.map { case (name, test) => toTask(loader, name, test, tags) } tasks.join.map(_.foldLeft(Map.empty[String, SuiteResult]) { case (sum, e) => @@ -275,10 +300,12 @@ object Tests { }) } - def toTask(loader: ClassLoader, - name: String, - fun: TestFunction, - tags: Seq[(Tag, Int)]): Task[Map[String, SuiteResult]] = { + def toTask( + loader: ClassLoader, + name: String, + fun: TestFunction, + tags: Seq[(Tag, Int)] + ): Task[Map[String, SuiteResult]] = { val base = task { (name, fun.apply()) } val taggedBase = base.tagw(tags: _*).tag(fun.tags.map(ConcurrentRestrictions.Tag(_)): _*) taggedBase flatMap { @@ -310,8 +337,10 @@ object Tests { setupTasks: Task[Unit], ): Task[List[(String, SuiteResult)]] = { @tailrec - def processRunnable(runnableList: List[TestRunnable], - acc: List[(String, SuiteResult)]): List[(String, SuiteResult)] = + def processRunnable( + runnableList: List[TestRunnable], + acc: List[(String, SuiteResult)] + ): List[(String, SuiteResult)] = runnableList match { case hd :: rst => val testFun = hd._2 @@ -361,9 +390,11 @@ object Tests { ((TestResult.Passed: TestResult) /: results) { (acc, result) => if (severity(acc) < severity(result)) result else acc } - def discover(frameworks: Seq[Framework], - analysis: CompileAnalysis, - log: Logger): (Seq[TestDefinition], Set[String]) = + def discover( + frameworks: Seq[Framework], + analysis: CompileAnalysis, + log: Logger + ): (Seq[TestDefinition], Set[String]) = discover(frameworks flatMap TestFramework.getFingerprints, allDefs(analysis), log) def allDefs(analysis: CompileAnalysis) = analysis match { @@ -379,9 +410,11 @@ object Tests { all }.toSeq } - def discover(fingerprints: Seq[Fingerprint], - definitions: Seq[Definition], - log: Logger): (Seq[TestDefinition], Set[String]) = { + def discover( + fingerprints: Seq[Fingerprint], + definitions: Seq[Definition], + log: Logger + ): (Seq[TestDefinition], Set[String]) = { val subclasses = fingerprints collect { case sub: SubclassFingerprint => (sub.superclassName, sub.isModule, sub) }; @@ -392,9 +425,11 @@ object Tests { log.debug("Annotation fingerprints: " + annotations) def firsts[A, B, C](s: Seq[(A, B, C)]): Set[A] = s.map(_._1).toSet - def defined(in: Seq[(String, Boolean, Fingerprint)], - names: Set[String], - IsModule: Boolean): Seq[Fingerprint] = + def defined( + in: Seq[(String, Boolean, Fingerprint)], + names: Set[String], + IsModule: Boolean + ): Seq[Fingerprint] = in collect { case (name, IsModule, print) if names(name) => print } def toFingerprints(d: Discovered): Seq[Fingerprint] = diff --git a/main-actions/src/main/scala/sbt/compiler/Eval.scala b/main-actions/src/main/scala/sbt/compiler/Eval.scala index a9d97246a..a52a682ae 100644 --- a/main-actions/src/main/scala/sbt/compiler/Eval.scala +++ b/main-actions/src/main/scala/sbt/compiler/Eval.scala @@ -33,10 +33,12 @@ final class EvalImports(val strings: Seq[(String, Int)], val srcName: String) * the module from that class loader. `generated` contains the compiled classes and cache files related * to the expression. The name of the auto-generated module wrapping the expression is `enclosingModule`. */ -final class EvalResult(val tpe: String, - val getValue: ClassLoader => Any, - val generated: Seq[File], - val enclosingModule: String) +final class EvalResult( + val tpe: String, + val getValue: ClassLoader => Any, + val generated: Seq[File], + val enclosingModule: String +) /** * The result of evaluating a group of Scala definitions. The definitions are wrapped in an auto-generated, @@ -45,10 +47,12 @@ final class EvalResult(val tpe: String, * from the classpath that the definitions were compiled against. The list of vals with the requested types is `valNames`. * The values for these may be obtained by providing the parent class loader to `values` as is done with `loader`. */ -final class EvalDefinitions(val loader: ClassLoader => ClassLoader, - val generated: Seq[File], - val enclosingModule: String, - val valNames: Seq[String]) { +final class EvalDefinitions( + val loader: ClassLoader => ClassLoader, + val generated: Seq[File], + val enclosingModule: String, + val valNames: Seq[String] +) { def values(parent: ClassLoader): Seq[Any] = { val module = getModule(enclosingModule, loader(parent)) for (n <- valNames) yield module.getClass.getMethod(n).invoke(module) @@ -57,10 +61,12 @@ final class EvalDefinitions(val loader: ClassLoader => ClassLoader, final class EvalException(msg: String) extends RuntimeException(msg) // not thread safe, since it reuses a Global instance -final class Eval(optionsNoncp: Seq[String], - classpath: Seq[File], - mkReporter: Settings => Reporter, - backing: Option[File]) { +final class Eval( + optionsNoncp: Seq[String], + classpath: Seq[File], + mkReporter: Settings => Reporter, + backing: Option[File] +) { def this(mkReporter: Settings => Reporter, backing: Option[File]) = this(Nil, IO.classLocationFile[Product] :: Nil, mkReporter, backing) def this() = this(s => new ConsoleReporter(s), None) @@ -96,11 +102,13 @@ final class Eval(optionsNoncp: Seq[String], private[this] var toUnlinkLater = List[Symbol]() private[this] def unlink(sym: Symbol) = sym.owner.info.decls.unlink(sym) - def eval(expression: String, - imports: EvalImports = noImports, - tpeName: Option[String] = None, - srcName: String = "", - line: Int = DefaultStartLine): EvalResult = { + def eval( + expression: String, + imports: EvalImports = noImports, + tpeName: Option[String] = None, + srcName: String = "", + line: Int = DefaultStartLine + ): EvalResult = { val ev = new EvalType[String] { def makeUnit = mkUnit(srcName, line, expression) def unlink = true @@ -120,11 +128,13 @@ final class Eval(optionsNoncp: Seq[String], val value = (cl: ClassLoader) => getValue[Any](i.enclosingModule, i.loader(cl)) new EvalResult(i.extra, value, i.generated, i.enclosingModule) } - def evalDefinitions(definitions: Seq[(String, scala.Range)], - imports: EvalImports, - srcName: String, - file: Option[File], - valTypes: Seq[String]): EvalDefinitions = { + def evalDefinitions( + definitions: Seq[(String, scala.Range)], + imports: EvalImports, + srcName: String, + file: Option[File], + valTypes: Seq[String] + ): EvalDefinitions = { require(definitions.nonEmpty, "Definitions to evaluate cannot be empty.") val ev = new EvalType[Seq[String]] { lazy val (fullUnit, defUnits) = mkDefsUnit(srcName, definitions) @@ -151,20 +161,27 @@ final class Eval(optionsNoncp: Seq[String], new EvalDefinitions(i.loader, i.generated, i.enclosingModule, i.extra) } - private[this] def evalCommon[T](content: Seq[String], - imports: EvalImports, - tpeName: Option[String], - ev: EvalType[T]): EvalIntermediate[T] = { + private[this] def evalCommon[T]( + content: Seq[String], + imports: EvalImports, + tpeName: Option[String], + ev: EvalType[T] + ): EvalIntermediate[T] = { import Eval._ // TODO - We also encode the source of the setting into the hash to avoid conflicts where the exact SAME setting // is defined in multiple evaluated instances with a backing. This leads to issues with finding a previous // value on the classpath when compiling. val hash = Hash.toHex( - Hash(bytes( - stringSeqBytes(content) :: optBytes(backing)(fileExistsBytes) :: stringSeqBytes(options) :: - seqBytes(classpath)(fileModifiedBytes) :: stringSeqBytes(imports.strings.map(_._1)) :: optBytes( - tpeName)(bytes) :: - bytes(ev.extraHash) :: Nil))) + Hash( + bytes( + stringSeqBytes(content) :: optBytes(backing)(fileExistsBytes) :: stringSeqBytes(options) :: + seqBytes(classpath)(fileModifiedBytes) :: stringSeqBytes(imports.strings.map(_._1)) :: optBytes( + tpeName + )(bytes) :: + bytes(ev.extraHash) :: Nil + ) + ) + ) val moduleName = makeModuleName(hash) lazy val unit = { @@ -192,12 +209,14 @@ final class Eval(optionsNoncp: Seq[String], // location of the cached type or definition information private[this] def cacheFile(base: File, moduleName: String): File = new File(base, moduleName + ".cache") - private[this] def compileAndLoad[T](run: Run, - unit: CompilationUnit, - imports: EvalImports, - backing: Option[File], - moduleName: String, - ev: EvalType[T]): (T, ClassLoader => ClassLoader) = { + private[this] def compileAndLoad[T]( + run: Run, + unit: CompilationUnit, + imports: EvalImports, + backing: Option[File], + moduleName: String, + ev: EvalType[T] + ): (T, ClassLoader => ClassLoader) = { global.curRun = run run.currentUnit = unit val dir = outputDirectory(backing) @@ -242,18 +261,22 @@ final class Eval(optionsNoncp: Seq[String], parent => getValue[Any](moduleName, new URLClassLoader(Array(dir.toURI.toURL), parent)) //wrap tree in object objectName { def WrapValName = } - def augment(parser: global.syntaxAnalyzer.UnitParser, - imports: Seq[Tree], - tree: Tree, - tpt: Tree, - objectName: String): Tree = { + def augment( + parser: global.syntaxAnalyzer.UnitParser, + imports: Seq[Tree], + tree: Tree, + tpt: Tree, + objectName: String + ): Tree = { val method = DefDef(NoMods, newTermName(WrapValName), Nil, Nil, tpt, tree) syntheticModule(parser, imports, method :: Nil, objectName) } - private[this] def syntheticModule(parser: global.syntaxAnalyzer.UnitParser, - imports: Seq[Tree], - definitions: List[Tree], - objectName: String): Tree = { + private[this] def syntheticModule( + parser: global.syntaxAnalyzer.UnitParser, + imports: Seq[Tree], + definitions: List[Tree], + objectName: String + ): Tree = { val emptyTypeName = nme.EMPTY.toTypeName def emptyPkg = parser.atPos(0, 0, 0) { Ident(nme.EMPTY_PACKAGE_NAME) } def emptyInit = DefDef( @@ -262,8 +285,10 @@ final class Eval(optionsNoncp: Seq[String], Nil, List(Nil), TypeTree(), - Block(List(Apply(Select(Super(This(emptyTypeName), emptyTypeName), nme.CONSTRUCTOR), Nil)), - Literal(Constant(()))) + Block( + List(Apply(Select(Super(This(emptyTypeName), emptyTypeName), nme.CONSTRUCTOR), Nil)), + Literal(Constant(())) + ) ) def moduleBody = Template(List(gen.scalaAnyRefConstr), noSelfType, emptyInit :: definitions) @@ -301,10 +326,12 @@ final class Eval(optionsNoncp: Seq[String], private[this] def isTopLevelModule(s: Symbol): Boolean = s.hasFlag(reflect.internal.Flags.MODULE) && s.owner.isPackageClass - private[this] final class EvalIntermediate[T](val extra: T, - val loader: ClassLoader => ClassLoader, - val generated: Seq[File], - val enclosingModule: String) + private[this] final class EvalIntermediate[T]( + val extra: T, + val loader: ClassLoader => ClassLoader, + val generated: Seq[File], + val enclosingModule: String + ) private[this] def classExists(dir: File, name: String) = (new File(dir, name + ".class")).exists // TODO: use the code from Analyzer @@ -318,10 +345,12 @@ final class Eval(optionsNoncp: Seq[String], (s contains moduleName) } - private[this] class ParseErrorStrings(val base: String, - val extraBlank: String, - val missingBlank: String, - val extraSemi: String) + private[this] class ParseErrorStrings( + val base: String, + val extraBlank: String, + val missingBlank: String, + val extraSemi: String + ) private[this] def definitionErrorStrings = new ParseErrorStrings( base = "Error parsing definition.", extraBlank = " Ensure that there are no blank lines within a definition.", @@ -340,9 +369,11 @@ final class Eval(optionsNoncp: Seq[String], * Parses the provided compilation `unit` according to `f` and then performs checks on the final parser state * to catch errors that are common when the content is embedded in a blank-line-delimited format. */ - private[this] def parse[T](unit: CompilationUnit, - errors: ParseErrorStrings, - f: syntaxAnalyzer.UnitParser => T): (syntaxAnalyzer.UnitParser, T) = { + private[this] def parse[T]( + unit: CompilationUnit, + errors: ParseErrorStrings, + f: syntaxAnalyzer.UnitParser => T + ): (syntaxAnalyzer.UnitParser, T) = { val parser = new syntaxAnalyzer.UnitParser(unit) val tree = f(parser) @@ -443,7 +474,8 @@ final class Eval(optionsNoncp: Seq[String], */ private[this] def mkDefsUnit( srcName: String, - definitions: Seq[(String, scala.Range)]): (CompilationUnit, Seq[CompilationUnit]) = { + definitions: Seq[(String, scala.Range)] + ): (CompilationUnit, Seq[CompilationUnit]) = { def fragmentUnit(content: String, lineMap: Array[Int]) = new CompilationUnit(fragmentSourceFile(srcName, content, lineMap)) diff --git a/main-actions/src/test/scala/sbt/CacheIvyTest.scala b/main-actions/src/test/scala/sbt/CacheIvyTest.scala index 1827bc5db..73f145764 100644 --- a/main-actions/src/test/scala/sbt/CacheIvyTest.scala +++ b/main-actions/src/test/scala/sbt/CacheIvyTest.scala @@ -37,19 +37,21 @@ class CacheIvyTest extends Properties("CacheIvy") { content = converter.toJsonUnsafe(value) } - private def testCache[T: JsonFormat, U](f: (SingletonCache[T], CacheStore) => U)( - implicit cache: SingletonCache[T]): U = { + private def testCache[T: JsonFormat, U]( + f: (SingletonCache[T], CacheStore) => U + )(implicit cache: SingletonCache[T]): U = { val store = new InMemoryStore(Converter) f(cache, store) } - private def cachePreservesEquality[T: JsonFormat](m: T, - eq: (T, T) => Prop, - str: T => String): Prop = testCache[T, Prop] { - (cache, store) => - cache.write(store, m) - val out = cache.read(store) - eq(out, m) :| s"Expected: ${str(m)}" :| s"Got: ${str(out)}" + private def cachePreservesEquality[T: JsonFormat]( + m: T, + eq: (T, T) => Prop, + str: T => String + ): Prop = testCache[T, Prop] { (cache, store) => + cache.write(store, m) + val out = cache.read(store) + eq(out, m) :| s"Expected: ${str(m)}" :| s"Got: ${str(out)}" } implicit val arbConfigRef: Arbitrary[ConfigRef] = Arbitrary( diff --git a/main-actions/src/test/scala/sbt/compiler/EvalTest.scala b/main-actions/src/test/scala/sbt/compiler/EvalTest.scala index 10600a9a8..a5d6eb387 100644 --- a/main-actions/src/test/scala/sbt/compiler/EvalTest.scala +++ b/main-actions/src/test/scala/sbt/compiler/EvalTest.scala @@ -38,7 +38,8 @@ class EvalTest extends Properties("eval") { val line = math.abs(l) val src = "mismatch" throws(classOf[RuntimeException])( - eval.eval(i.toString, tpeName = Some(BooleanType), line = line, srcName = src)) && + eval.eval(i.toString, tpeName = Some(BooleanType), line = line, srcName = src) + ) && hasErrors(line + 1, src) } @@ -78,14 +79,17 @@ val p = { property("explicit import") = forAll(testImport("import math.abs" :: Nil)) property("wildcard import") = forAll(testImport("import math._" :: Nil)) property("comma-separated imports") = forAll( - testImport("import annotation._, math._, meta._" :: Nil)) + testImport("import annotation._, math._, meta._" :: Nil) + ) property("multiple imports") = forAll( - testImport("import annotation._" :: "import math._" :: "import meta._" :: Nil)) + testImport("import annotation._" :: "import math._" :: "import meta._" :: Nil) + ) private[this] def testImport(imports: Seq[String]): Int => Prop = i => value(eval.eval("abs(" + i + ")", new EvalImports(imports.zipWithIndex, "imp"))) == math.abs( - i) + i + ) private[this] def local(i: Int) = "{ class ETest(val i: Int); new ETest(" + i + ") }" val LocalType = "AnyRef{val i: Int}" diff --git a/main-command/src/main/scala/sbt/BasicCommandStrings.scala b/main-command/src/main/scala/sbt/BasicCommandStrings.scala index abc2bc9c1..6baee9509 100644 --- a/main-command/src/main/scala/sbt/BasicCommandStrings.scala +++ b/main-command/src/main/scala/sbt/BasicCommandStrings.scala @@ -21,8 +21,10 @@ object BasicCommandStrings { val TerminateAction: String = Exit def helpBrief = - (HelpCommand, - s"Displays this help message or prints detailed help on requested commands (run '$HelpCommand ').") + ( + HelpCommand, + s"Displays this help message or prints detailed help on requested commands (run '$HelpCommand ')." + ) def helpDetailed = s"""$HelpCommand Prints a help summary. @@ -131,8 +133,10 @@ $HelpCommand def Multi = ";" def MultiBrief = - (Multi + " (" + Multi + " )*", - "Runs the provided semicolon-separated commands.") + ( + Multi + " (" + Multi + " )*", + "Runs the provided semicolon-separated commands." + ) def MultiDetailed = Multi + " command1 " + Multi + """ command2 ... diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index cd095f0b5..e9d5b8a19 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -80,7 +80,8 @@ object BasicCommands { val h = (Help.empty /: s.definedCommands)( (a, b) => a ++ (try b.help(s) - catch { case NonFatal(_) => Help.empty })) + catch { case NonFatal(_) => Help.empty }) + ) val helpCommands = h.detail.keySet val spacedArg = singleArgument(helpCommands).? applyEffect(spacedArg)(runHelp(s, h)) @@ -95,7 +96,8 @@ object BasicCommands { def completionsCommand: Command = Command(CompletionsCommand, CompletionsBrief, CompletionsDetailed)(_ => completionsParser)( - runCompletions(_)(_)) + runCompletions(_)(_) + ) @deprecated("No longer public", "1.1.1") def completionsParser(state: State): Parser[String] = completionsParser @@ -118,8 +120,9 @@ object BasicCommands { def multiParser(s: State): Parser[List[String]] = { val nonSemi = token(charClass(_ != ';').+, hide = const(true)) val semi = token(';' ~> OptSpace) - val part = semi flatMap (_ => - matched((s.combinedParser & nonSemi) | nonSemi) <~ token(OptSpace)) + val part = semi flatMap ( + _ => matched((s.combinedParser & nonSemi) | nonSemi) <~ token(OptSpace) + ) (part map (_.trim)).+ map (_.toList) } @@ -135,16 +138,19 @@ object BasicCommands { matched(s.combinedParser | token(any, hide = const(true))) def ifLast: Command = - Command(IfLast, Help.more(IfLast, IfLastDetailed))(otherCommandParser)((s, arg) => - if (s.remainingCommands.isEmpty) arg :: s else s) + Command(IfLast, Help.more(IfLast, IfLastDetailed))(otherCommandParser)( + (s, arg) => if (s.remainingCommands.isEmpty) arg :: s else s + ) def append: Command = Command(AppendCommand, Help.more(AppendCommand, AppendLastDetailed))(otherCommandParser)( - (s, arg) => s.copy(remainingCommands = s.remainingCommands :+ Exec(arg, s.source))) + (s, arg) => s.copy(remainingCommands = s.remainingCommands :+ Exec(arg, s.source)) + ) def setOnFailure: Command = - Command(OnFailure, Help.more(OnFailure, OnFailureDetailed))(otherCommandParser)((s, arg) => - s.copy(onFailure = Some(Exec(arg, s.source)))) + Command(OnFailure, Help.more(OnFailure, OnFailureDetailed))(otherCommandParser)( + (s, arg) => s.copy(onFailure = Some(Exec(arg, s.source))) + ) private[sbt] def compatCommands = Seq( Command.command(Compat.ClearOnFailure) { s => @@ -154,7 +160,8 @@ object BasicCommands { Command.arb( s => token(Compat.OnFailure, hide = const(true)) - .flatMap(_ => otherCommandParser(s))) { (s, arg) => + .flatMap(_ => otherCommandParser(s)) + ) { (s, arg) => s.log.warn(Compat.OnFailureDeprecated) s.copy(onFailure = Some(Exec(arg, s.source))) }, @@ -167,8 +174,9 @@ object BasicCommands { def clearOnFailure: Command = Command.command(ClearOnFailure)(s => s.copy(onFailure = None)) def stashOnFailure: Command = - Command.command(StashOnFailure)(s => - s.copy(onFailure = None).update(OnFailureStack)(s.onFailure :: _.toList.flatten)) + Command.command(StashOnFailure)( + s => s.copy(onFailure = None).update(OnFailureStack)(s.onFailure :: _.toList.flatten) + ) def popOnFailure: Command = Command.command(PopOnFailure) { s => val stack = s.get(OnFailureStack).getOrElse(Nil) @@ -213,8 +221,9 @@ object BasicCommands { private[this] def className: Parser[String] = { val base = StringBasic & not('-' ~> any.*, "Class name cannot start with '-'.") def single(s: String) = Completions.single(Completion.displayOnly(s)) - val compl = TokenCompletions.fixed((seen, _) => - if (seen.startsWith("-")) Completions.nil else single("")) + val compl = TokenCompletions.fixed( + (seen, _) => if (seen.startsWith("-")) Completions.nil else single("") + ) token(base, compl) } @@ -402,7 +411,8 @@ object BasicCommands { } def delegateToAlias(name: String, orElse: Parser[() => State])( - state: State): Parser[() => State] = + state: State + ): Parser[() => State] = aliases(state, (nme, _) => nme == name).headOption match { case None => orElse case Some((n, v)) => aliasBody(n, v)(state) diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index 5ceeec21b..d1fc63f7a 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -18,11 +18,13 @@ object BasicKeys { val historyPath = AttributeKey[Option[File]]( "history", "The location where command line history is persisted.", - 40) + 40 + ) val shellPrompt = AttributeKey[State => String]( "shell-prompt", "The function that constructs the command prompt from the current build state.", - 10000) + 10000 + ) val watch = AttributeKey[Watched]("watch", "Continuous execution configuration.", 1000) val serverPort = AttributeKey[Int]("server-port", "The port number used by server command.", 10000) @@ -31,25 +33,32 @@ object BasicKeys { AttributeKey[String]("serverHost", "The host used by server command.", 10000) val serverAuthentication = - AttributeKey[Set[ServerAuthentication]]("serverAuthentication", - "Method of authenticating server command.", - 10000) + AttributeKey[Set[ServerAuthentication]]( + "serverAuthentication", + "Method of authenticating server command.", + 10000 + ) val serverConnectionType = - AttributeKey[ConnectionType]("serverConnectionType", - "The wire protocol for the server command.", - 10000) + AttributeKey[ConnectionType]( + "serverConnectionType", + "The wire protocol for the server command.", + 10000 + ) val fullServerHandlers = - AttributeKey[Seq[ServerHandler]]("fullServerHandlers", - "Combines default server handlers and user-defined handlers.", - 10000) + AttributeKey[Seq[ServerHandler]]( + "fullServerHandlers", + "Combines default server handlers and user-defined handlers.", + 10000 + ) val autoStartServer = AttributeKey[Boolean]( "autoStartServer", "If true, the sbt server will startup automatically during interactive sessions.", - 10000) + 10000 + ) // Unlike other BasicKeys, this is not used directly as a setting key, // and severLog / logLevel is used instead. @@ -62,23 +71,28 @@ object BasicKeys { private[sbt] val interactive = AttributeKey[Boolean]( "interactive", "True if commands are currently being entered from an interactive environment.", - 10) + 10 + ) private[sbt] val classLoaderCache = AttributeKey[ClassLoaderCache]( "class-loader-cache", "Caches class loaders based on the classpath entries and last modified times.", - 10) + 10 + ) private[sbt] val OnFailureStack = AttributeKey[List[Option[Exec]]]( "on-failure-stack", "Stack that remembers on-failure handlers.", - 10) + 10 + ) private[sbt] val explicitGlobalLogLevels = AttributeKey[Boolean]( "explicit-global-log-levels", "True if the global logging levels were explicitly set by the user.", - 10) + 10 + ) private[sbt] val templateResolverInfos = AttributeKey[Seq[TemplateResolverInfo]]( "templateResolverInfos", "List of template resolver infos.", - 1000) + 1000 + ) } case class TemplateResolverInfo(module: ModuleID, implementationClass: String) diff --git a/main-command/src/main/scala/sbt/Command.scala b/main-command/src/main/scala/sbt/Command.scala index 6ee8e0e44..49cda7fec 100644 --- a/main-command/src/main/scala/sbt/Command.scala +++ b/main-command/src/main/scala/sbt/Command.scala @@ -67,18 +67,21 @@ object Command { new SimpleCommand(name, help, parser, AttributeMap.empty) def make(name: String, briefHelp: (String, String), detail: String)( - parser: State => Parser[() => State]): Command = + parser: State => Parser[() => State] + ): Command = make(name, Help(name, briefHelp, detail))(parser) // General command construction /** Construct a command with the given name, parser and effect. */ - def apply[T](name: String, help: Help = Help.empty)(parser: State => Parser[T])( - effect: (State, T) => State): Command = + def apply[T](name: String, help: Help = Help.empty)( + parser: State => Parser[T] + )(effect: (State, T) => State): Command = make(name, help)(applyEffect(parser)(effect)) def apply[T](name: String, briefHelp: (String, String), detail: String)( - parser: State => Parser[T])(effect: (State, T) => State): Command = + parser: State => Parser[T] + )(effect: (State, T) => State): Command = apply(name, Help(name, briefHelp, detail))(parser)(effect) // No-argument command construction @@ -97,18 +100,21 @@ object Command { make(name, help)(state => token(trimmed(spacedAny(name)) map apply1(f, state))) def single(name: String, briefHelp: (String, String), detail: String)( - f: (State, String) => State): Command = + f: (State, String) => State + ): Command = single(name, Help(name, briefHelp, detail))(f) // Multi-argument command construction /** Construct a multi-argument command with the given name, tab completion display and effect. */ def args(name: String, display: String, help: Help = Help.empty)( - f: (State, Seq[String]) => State): Command = + f: (State, Seq[String]) => State + ): Command = make(name, help)(state => spaceDelimited(display) map apply1(f, state)) def args(name: String, briefHelp: (String, String), detail: String, display: String)( - f: (State, Seq[String]) => State): Command = + f: (State, Seq[String]) => State + ): Command = args(name, display, Help(name, briefHelp, detail))(f) // create ArbitraryCommand @@ -120,7 +126,8 @@ object Command { customHelp(parser, const(help)) def arb[T](parser: State => Parser[T], help: Help = Help.empty)( - effect: (State, T) => State): Command = + effect: (State, T) => State + ): Command = custom(applyEffect(parser)(effect), help) // misc Command object utilities @@ -129,8 +136,9 @@ object Command { def applyEffect[T](p: Parser[T])(f: T => State): Parser[() => State] = p map (t => () => f(t)) - def applyEffect[T](parser: State => Parser[T])( - effect: (State, T) => State): State => Parser[() => State] = + def applyEffect[T]( + parser: State => Parser[T] + )(effect: (State, T) => State): State => Parser[() => State] = s => applyEffect(parser(s))(t => effect(s, t)) def combine(cmds: Seq[Command]): State => Parser[() => State] = { @@ -140,7 +148,8 @@ object Command { } private[this] def separateCommands( - cmds: Seq[Command]): (Seq[SimpleCommand], Seq[ArbitraryCommand]) = + cmds: Seq[Command] + ): (Seq[SimpleCommand], Seq[ArbitraryCommand]) = Util.separate(cmds) { case s: SimpleCommand => Left(s); case a: ArbitraryCommand => Right(a) } private[this] def apply1[A, B, C](f: (A, B) => C, a: A): B => () => C = b => () => f(a, b) @@ -155,13 +164,16 @@ object Command { } def simpleParser( - commandMap: Map[String, State => Parser[() => State]]): State => Parser[() => State] = + commandMap: Map[String, State => Parser[() => State]] + ): State => Parser[() => State] = state => - token(OpOrID examples commandMap.keys.toSet) flatMap (id => - (commandMap get id) match { - case None => failure(invalidValue("command", commandMap.keys)(id)) - case Some(c) => c(state) - }) + token(OpOrID examples commandMap.keys.toSet) flatMap ( + id => + (commandMap get id) match { + case None => failure(invalidValue("command", commandMap.keys)(id)) + case Some(c) => c(state) + } + ) def process(command: String, state: State): State = { val parser = combine(state.definedCommands) @@ -181,10 +193,12 @@ object Command { if (suggested.isEmpty) "" else suggested.mkString(" (similar: ", ", ", ")") } - def suggestions(a: String, - bs: Seq[String], - maxDistance: Int = 3, - maxSuggestions: Int = 3): Seq[String] = + def suggestions( + a: String, + bs: Seq[String], + maxDistance: Int = 3, + maxSuggestions: Int = 3 + ): Seq[String] = bs map (b => (b, distance(a, b))) filter (_._2 <= maxDistance) sortBy (_._2) take (maxSuggestions) map (_._1) def distance(a: String, b: String): Int = @@ -233,9 +247,11 @@ object Help { def apply(briefHelp: Seq[(String, String)], detailedHelp: Map[String, String]): Help = apply(briefHelp, detailedHelp, Set.empty[String]) - def apply(briefHelp: Seq[(String, String)], - detailedHelp: Map[String, String], - more: Set[String]): Help = + def apply( + briefHelp: Seq[(String, String)], + detailedHelp: Map[String, String], + more: Set[String] + ): Help = new Help0(briefHelp, detailedHelp, more) def more(name: String, detailedHelp: String): Help = diff --git a/main-command/src/main/scala/sbt/MainControl.scala b/main-command/src/main/scala/sbt/MainControl.scala index e728dd765..e24c657e2 100644 --- a/main-command/src/main/scala/sbt/MainControl.scala +++ b/main-command/src/main/scala/sbt/MainControl.scala @@ -12,21 +12,23 @@ import java.io.File final case class Exit(code: Int) extends xsbti.Exit { require(code >= 0) } -final case class Reboot(scalaVersion: String, - argsList: Seq[String], - app: xsbti.ApplicationID, - baseDirectory: File) - extends xsbti.Reboot { +final case class Reboot( + scalaVersion: String, + argsList: Seq[String], + app: xsbti.ApplicationID, + baseDirectory: File +) extends xsbti.Reboot { def arguments = argsList.toArray } -final case class ApplicationID(groupID: String, - name: String, - version: String, - mainClass: String, - components: Seq[String], - crossVersionedValue: xsbti.CrossValue, - extra: Seq[File]) - extends xsbti.ApplicationID { +final case class ApplicationID( + groupID: String, + name: String, + version: String, + mainClass: String, + components: Seq[String], + crossVersionedValue: xsbti.CrossValue, + extra: Seq[File] +) extends xsbti.ApplicationID { def mainComponents = components.toArray def classpathExtra = extra.toArray def crossVersioned = crossVersionedValue != xsbti.CrossValue.Disabled @@ -35,11 +37,13 @@ object ApplicationID { def apply(delegate: xsbti.ApplicationID, newVersion: String): ApplicationID = apply(delegate).copy(version = newVersion) def apply(delegate: xsbti.ApplicationID): ApplicationID = - ApplicationID(delegate.groupID, - delegate.name, - delegate.version, - delegate.mainClass, - delegate.mainComponents, - delegate.crossVersionedValue, - delegate.classpathExtra) + ApplicationID( + delegate.groupID, + delegate.name, + delegate.version, + delegate.mainClass, + delegate.mainComponents, + delegate.crossVersionedValue, + delegate.classpathExtra + ) } diff --git a/main-command/src/main/scala/sbt/State.scala b/main-command/src/main/scala/sbt/State.scala index a3e178dc6..e65e58a03 100644 --- a/main-command/src/main/scala/sbt/State.scala +++ b/main-command/src/main/scala/sbt/State.scala @@ -287,8 +287,9 @@ object State { def fail = { import BasicCommandStrings.Compat.{ FailureWall => CompatFailureWall } val remaining = - s.remainingCommands.dropWhile(c => - c.commandLine != FailureWall && c.commandLine != CompatFailureWall) + s.remainingCommands.dropWhile( + c => c.commandLine != FailureWall && c.commandLine != CompatFailureWall + ) if (remaining.isEmpty) applyOnFailure(s, Nil, exit(ok = false)) else diff --git a/main-command/src/main/scala/sbt/Watched.scala b/main-command/src/main/scala/sbt/Watched.scala index fad1b6a8d..6ff60a135 100644 --- a/main-command/src/main/scala/sbt/Watched.scala +++ b/main-command/src/main/scala/sbt/Watched.scala @@ -113,7 +113,8 @@ object Watched { } catch { case e: Exception => s.log.error( - "Error occurred obtaining files to watch. Terminating continuous execution...") + "Error occurred obtaining files to watch. Terminating continuous execution..." + ) s.handleError(e) (false, watchState) } @@ -133,8 +134,10 @@ object Watched { AttributeKey[WatchState]("watch state", "Internal: tracks state for continuous execution.") val ContinuousWatchService = - AttributeKey[WatchService]("watch service", - "Internal: tracks watch service for continuous execution.") + AttributeKey[WatchService]( + "watch service", + "Internal: tracks watch service for continuous execution." + ) val Configuration = AttributeKey[Watched]("watched-configuration", "Configures continuous execution.") 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 95ef32c95..c3d130470 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -40,9 +40,11 @@ private[sbt] object Server { with TokenFileFormats object JsonProtocol extends JsonProtocol - def start(connection: ServerConnection, - onIncomingSocket: (Socket, ServerInstance) => Unit, - log: Logger): ServerInstance = + def start( + connection: ServerConnection, + onIncomingSocket: (Socket, ServerInstance) => Unit, + log: Logger + ): ServerInstance = new ServerInstance { self => import connection._ val running = new AtomicBoolean(false) @@ -67,7 +69,8 @@ private[sbt] object Server { "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}") + s"Current path: ${path}" + ) tryClient(new UnixDomainSocket(path)) prepareSocketfile() addServerError(new UnixDomainServerSocket(path)) diff --git a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala index 47b4f4fbf..61969a466 100644 --- a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala +++ b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala @@ -34,14 +34,18 @@ object ServerHandler { }) } -final class ServerIntent(val onRequest: PartialFunction[JsonRpcRequestMessage, Unit], - val onNotification: PartialFunction[JsonRpcNotificationMessage, Unit]) { +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 = + def apply( + onRequest: PartialFunction[JsonRpcRequestMessage, Unit], + onNotification: PartialFunction[JsonRpcNotificationMessage, Unit] + ): ServerIntent = new ServerIntent(onRequest, onNotification) def request(onRequest: PartialFunction[JsonRpcRequestMessage, Unit]): ServerIntent = diff --git a/main-settings/src/main/scala/sbt/Append.scala b/main-settings/src/main/scala/sbt/Append.scala index c9116de97..fb08b5e33 100644 --- a/main-settings/src/main/scala/sbt/Append.scala +++ b/main-settings/src/main/scala/sbt/Append.scala @@ -18,12 +18,14 @@ import sbt.io.{ AllPassFilter, NothingFilter } object Append { @implicitNotFound( - msg = "No implicit for Append.Value[${A}, ${B}] found,\n so ${B} cannot be appended to ${A}") + msg = "No implicit for Append.Value[${A}, ${B}] found,\n so ${B} cannot be appended to ${A}" + ) trait Value[A, B] { def appendValue(a: A, b: B): A } @implicitNotFound( - msg = "No implicit for Append.Values[${A}, ${B}] found,\n so ${B} cannot be appended to ${A}") + msg = "No implicit for Append.Values[${A}, ${B}] found,\n so ${B} cannot be appended to ${A}" + ) trait Values[A, -B] { def appendValues(a: A, b: B): A } diff --git a/main-settings/src/main/scala/sbt/Def.scala b/main-settings/src/main/scala/sbt/Def.scala index d03b4ba10..0fd53e281 100644 --- a/main-settings/src/main/scala/sbt/Def.scala +++ b/main-settings/src/main/scala/sbt/Def.scala @@ -27,11 +27,13 @@ object Def extends Init[Scope] with TaskMacroExtra { val resolvedScoped = SettingKey[ScopedKey[_]]( "resolved-scoped", "The ScopedKey for the referencing setting or task.", - KeyRanks.DSetting) + KeyRanks.DSetting + ) private[sbt] val taskDefinitionKey = AttributeKey[ScopedKey[_]]( "task-definition-key", "Internal: used to map a task back to its ScopedKey.", - Invisible) + Invisible + ) lazy val showFullKey: Show[ScopedKey[_]] = showFullKey(None) @@ -56,7 +58,8 @@ object Def extends Init[Scope] with TaskMacroExtra { key.scope, withColor(key.key.label, keyNameColor), ref => displayRelative2(current, ref) - )) + ) + ) @deprecated("Use showBuildRelativeKey2 which doesn't take the unused multi param", "1.1.1") def showBuildRelativeKey( @@ -76,7 +79,8 @@ object Def extends Init[Scope] with TaskMacroExtra { key.scope, withColor(key.key.label, keyNameColor), ref => displayBuildRelative(currentBuild, ref) - )) + ) + ) /** * Returns a String expression for the given [[Reference]] (BuildRef, [[ProjectRef]], etc) @@ -96,9 +100,11 @@ object Def extends Init[Scope] with TaskMacroExtra { * Constructs the String of a given [[Reference]] relative to current. * Note that this no longer takes "multi" parameter, and omits the subproject id at all times. */ - private[sbt] def displayRelative(current: ProjectRef, - project: Reference, - trailingSlash: Boolean): String = { + private[sbt] def displayRelative( + current: ProjectRef, + project: Reference, + trailingSlash: Boolean + ): String = { val trailing = if (trailingSlash) " /" else "" project match { case BuildRef(current.build) => "ThisBuild" + trailing @@ -145,11 +151,14 @@ object Def extends Init[Scope] with TaskMacroExtra { else None) orElse s.dependencies .find(k => k.scope != ThisScope) - .map(k => - s"Scope cannot be defined for dependency ${k.key.label} of ${definedSettingString(s)}") + .map( + k => + s"Scope cannot be defined for dependency ${k.key.label} of ${definedSettingString(s)}" + ) override def intersect(s1: Scope, s2: Scope)( - implicit delegates: Scope => Seq[Scope]): Option[Scope] = + implicit delegates: Scope => Seq[Scope] + ): Option[Scope] = if (s2 == GlobalScope) Some(s1) // s1 is more specific else if (s1 == GlobalScope) Some(s2) // s2 is more specific else super.intersect(s1, s2) @@ -230,7 +239,8 @@ object Def extends Init[Scope] with TaskMacroExtra { 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 + sys.error("Dummy task '" + name + "' did not get converted to a full task.") + ) named name base.copy(info = base.info.set(isDummyTask, true)) } @@ -240,13 +250,15 @@ object Def extends Init[Scope] with TaskMacroExtra { 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) + 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.") + "Streams manager, which provides streams for different contexts." + ) } // these need to be mixed into the sbt package object diff --git a/main-settings/src/main/scala/sbt/DelegateIndex.scala b/main-settings/src/main/scala/sbt/DelegateIndex.scala index a5a6f64c7..4915a7886 100644 --- a/main-settings/src/main/scala/sbt/DelegateIndex.scala +++ b/main-settings/src/main/scala/sbt/DelegateIndex.scala @@ -26,6 +26,8 @@ private final class DelegateIndex0(refs: Map[ProjectRef, ProjectDelegates]) exte case None => Select(conf) :: Zero :: Nil } } -private final class ProjectDelegates(val ref: ProjectRef, - val refs: Seq[ScopeAxis[ResolvedReference]], - val confs: Map[ConfigKey, Seq[ScopeAxis[ConfigKey]]]) +private final class ProjectDelegates( + val ref: ProjectRef, + val refs: Seq[ScopeAxis[ResolvedReference]], + val confs: Map[ConfigKey, Seq[ScopeAxis[ConfigKey]]] +) diff --git a/main-settings/src/main/scala/sbt/InputTask.scala b/main-settings/src/main/scala/sbt/InputTask.scala index 2a722de45..c10315d0c 100644 --- a/main-settings/src/main/scala/sbt/InputTask.scala +++ b/main-settings/src/main/scala/sbt/InputTask.scala @@ -22,13 +22,15 @@ final class InputTask[T] private (val parser: State => Parser[Task[T]]) { new InputTask[T](s => Parser(parser(s))(in)) def fullInput(in: String): InputTask[T] = - new InputTask[T](s => - Parser.parse(in, parser(s)) match { - case Right(v) => Parser.success(v) - case Left(msg) => - val indented = msg.lines.map(" " + _).mkString("\n") - Parser.failure(s"Invalid programmatic input:\n$indented") - }) + new InputTask[T]( + s => + Parser.parse(in, parser(s)) match { + case Right(v) => Parser.success(v) + case Left(msg) => + val indented = msg.lines.map(" " + _).mkString("\n") + Parser.failure(s"Invalid programmatic input:\n$indented") + } + ) } object InputTask { @@ -38,14 +40,18 @@ object InputTask { import std.FullInstance._ def toTask(in: String): Initialize[Task[T]] = flatten( - (Def.stateKey zipWith i)((sTask, it) => - sTask map (s => - Parser.parse(in, it.parser(s)) match { - case Right(t) => Def.value(t) - case Left(msg) => - val indented = msg.lines.map(" " + _).mkString("\n") - sys.error(s"Invalid programmatic input:\n$indented") - })) + (Def.stateKey zipWith i)( + (sTask, it) => + sTask map ( + s => + Parser.parse(in, it.parser(s)) match { + case Right(t) => Def.value(t) + case Left(msg) => + val indented = msg.lines.map(" " + _).mkString("\n") + sys.error(s"Invalid programmatic input:\n$indented") + } + ) + ) ) } @@ -67,12 +73,14 @@ object InputTask { def free[I, T](p: State => Parser[I])(c: I => Task[T]): InputTask[T] = free(s => p(s) map c) - def separate[I, T](p: State => Parser[I])( - action: Initialize[I => Task[T]]): Initialize[InputTask[T]] = + def separate[I, T]( + p: State => Parser[I] + )(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] = separate(Def value p)(action) - def separate[I, T](p: Initialize[State => Parser[I]])( - action: Initialize[I => Task[T]]): Initialize[InputTask[T]] = + def separate[I, T]( + p: Initialize[State => Parser[I]] + )(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] = p.zipWith(action)((parser, act) => free(parser)(act)) /** Constructs an InputTask that accepts no user input. */ @@ -86,8 +94,9 @@ object InputTask { * a) a Parser constructed using other Settings, but not Tasks * b) a dynamically constructed Task that uses Settings, Tasks, and the result of parsing. */ - def createDyn[I, T](p: Initialize[State => Parser[I]])( - action: Initialize[Task[I => Initialize[Task[T]]]]): Initialize[InputTask[T]] = + def createDyn[I, T]( + p: Initialize[State => Parser[I]] + )(action: Initialize[Task[I => Initialize[Task[T]]]]): Initialize[InputTask[T]] = separate(p)(std.FullInstance.flattenFun[I, T](action)) /** A dummy parser that consumes no input and produces nothing useful (unit).*/ @@ -103,8 +112,9 @@ object InputTask { i(Types.const) @deprecated("Use another InputTask constructor or the `Def.inputTask` macro.", "0.13.0") - def apply[I, T](p: Initialize[State => Parser[I]])( - action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] = { + def apply[I, T]( + p: Initialize[State => Parser[I]] + )(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] = { val dummyKey = localKey[Task[I]] val (marker, dummy) = dummyTask[I] val it = action(TaskKey(dummyKey)) mapConstant subResultForDummy(dummyKey, dummy) @@ -141,9 +151,11 @@ object InputTask { (key, t) } - private[this] def subForDummy[I, T](marker: AttributeKey[Option[I]], - value: I, - task: Task[T]): Task[T] = { + private[this] def subForDummy[I, T]( + marker: AttributeKey[Option[I]], + value: I, + task: Task[T] + ): Task[T] = { val seen = new java.util.IdentityHashMap[Task[_], Task[_]] lazy val f: Task ~> Task = new (Task ~> Task) { def apply[A](t: Task[A]): Task[A] = { diff --git a/main-settings/src/main/scala/sbt/Previous.scala b/main-settings/src/main/scala/sbt/Previous.scala index 62bfd705f..a23afbcbf 100644 --- a/main-settings/src/main/scala/sbt/Previous.scala +++ b/main-settings/src/main/scala/sbt/Previous.scala @@ -53,11 +53,13 @@ object Previous { private[sbt] val references = SettingKey[References]( "previous-references", "Collects all static references to previous values of tasks.", - KeyRanks.Invisible) + KeyRanks.Invisible + ) private[sbt] val cache = TaskKey[Previous]( "previous-cache", "Caches previous values of tasks read from disk for the duration of a task execution.", - KeyRanks.Invisible) + KeyRanks.Invisible + ) /** Records references to previous task value. This should be completely populated after settings finish loading. */ private[sbt] final class References { @@ -72,9 +74,11 @@ object Previous { } /** Persists values of tasks t where there is some task referencing it via t.previous. */ - private[sbt] def complete(referenced: References, - results: RMap[Task, Result], - streams: Streams): Unit = { + private[sbt] def complete( + referenced: References, + results: RMap[Task, Result], + streams: Streams + ): Unit = { val map = referenced.getReferences def impl[T](key: ScopedKey[_], result: T): Unit = for (i <- map.get(key.asInstanceOf[ScopedTaskKey[T]])) { diff --git a/main-settings/src/main/scala/sbt/Remove.scala b/main-settings/src/main/scala/sbt/Remove.scala index ad2a4a0fe..60c395050 100644 --- a/main-settings/src/main/scala/sbt/Remove.scala +++ b/main-settings/src/main/scala/sbt/Remove.scala @@ -11,12 +11,14 @@ import scala.annotation.implicitNotFound object Remove { @implicitNotFound( - msg = "No implicit for Remove.Value[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}") + msg = "No implicit for Remove.Value[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}" + ) trait Value[A, B] extends Any { def removeValue(a: A, b: B): A } @implicitNotFound( - msg = "No implicit for Remove.Values[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}") + msg = "No implicit for Remove.Values[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}" + ) trait Values[A, -B] extends Any { def removeValues(a: A, b: B): A } diff --git a/main-settings/src/main/scala/sbt/Scope.scala b/main-settings/src/main/scala/sbt/Scope.scala index 09eafc92b..4e8747d10 100644 --- a/main-settings/src/main/scala/sbt/Scope.scala +++ b/main-settings/src/main/scala/sbt/Scope.scala @@ -13,10 +13,12 @@ import sbt.internal.util.{ AttributeKey, AttributeMap, Dag } import sbt.io.IO -final case class Scope(project: ScopeAxis[Reference], - config: ScopeAxis[ConfigKey], - task: ScopeAxis[AttributeKey[_]], - extra: ScopeAxis[AttributeMap]) { +final case class Scope( + project: ScopeAxis[Reference], + config: ScopeAxis[ConfigKey], + task: ScopeAxis[AttributeKey[_]], + extra: ScopeAxis[AttributeMap] +) { def in(project: Reference, config: ConfigKey): Scope = copy(project = Select(project), config = Select(config)) def in(config: ConfigKey, task: AttributeKey[_]): Scope = @@ -106,17 +108,21 @@ object Scope { else IO.directoryURI(current resolve uri) - def resolveReference(current: URI, - rootProject: URI => String, - ref: Reference): ResolvedReference = + def resolveReference( + current: URI, + rootProject: URI => String, + ref: Reference + ): ResolvedReference = ref match { case br: BuildReference => resolveBuildRef(current, br) case pr: ProjectReference => resolveProjectRef(current, rootProject, pr) } - def resolveProjectRef(current: URI, - rootProject: URI => String, - ref: ProjectReference): ProjectRef = + def resolveProjectRef( + current: URI, + rootProject: URI => String, + ref: ProjectReference + ): ProjectRef = ref match { case LocalRootProject => ProjectRef(current, rootProject(current)) case LocalProject(id) => ProjectRef(current, id) @@ -164,10 +170,12 @@ object Scope { def displayMasked(scope: Scope, sep: String, mask: ScopeMask, showZeroConfig: Boolean): String = displayMasked(scope, sep, showProject, mask, showZeroConfig) - def displayMasked(scope: Scope, - sep: String, - showProject: Reference => String, - mask: ScopeMask): String = + def displayMasked( + scope: Scope, + sep: String, + showProject: Reference => String, + mask: ScopeMask + ): String = displayMasked(scope, sep, showProject, mask, false) /** @@ -177,11 +185,13 @@ object Scope { * Technically speaking an unspecified configuration axis defaults to * the scope delegation (first configuration defining the key, then Zero). */ - def displayMasked(scope: Scope, - sep: String, - showProject: Reference => String, - mask: ScopeMask, - showZeroConfig: Boolean): String = { + def displayMasked( + scope: Scope, + sep: String, + showProject: Reference => String, + mask: ScopeMask, + showZeroConfig: Boolean + ): String = { import scope.{ project, config, task, extra } val zeroConfig = if (showZeroConfig) "Zero /" else "" val configPrefix = config.foldStrict(display, zeroConfig, "./") @@ -190,11 +200,13 @@ object Scope { val postfix = if (extras.isEmpty) "" else extras.mkString("(", ", ", ")") if (scope == GlobalScope) "Global / " + sep + postfix else - mask.concatShow(appendSpace(projectPrefix(project, showProject)), - appendSpace(configPrefix), - appendSpace(taskPrefix), - sep, - postfix) + mask.concatShow( + appendSpace(projectPrefix(project, showProject)), + appendSpace(configPrefix), + appendSpace(taskPrefix), + sep, + postfix + ) } private[sbt] def appendSpace(s: String): String = @@ -207,12 +219,16 @@ object Scope { (!mask.task || a.task == b.task) && (!mask.extra || a.extra == b.extra) - def projectPrefix(project: ScopeAxis[Reference], - show: Reference => String = showProject): String = + def projectPrefix( + project: ScopeAxis[Reference], + show: Reference => String = showProject + ): String = project.foldStrict(show, "Zero /", "./") - def projectPrefix012Style(project: ScopeAxis[Reference], - show: Reference => String = showProject): String = + def projectPrefix012Style( + project: ScopeAxis[Reference], + show: Reference => String = showProject + ): String = project.foldStrict(show, "*/", "./") def showProject = (ref: Reference) => Reference.display(ref) + " /" @@ -332,27 +348,32 @@ object Scope { } private[this] def delegateIndex(ref: ProjectRef, confs: Seq[ConfigKey])( projectInherit: ProjectRef => Seq[ProjectRef], - configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey]): ProjectDelegates = { + configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey] + ): ProjectDelegates = { val refDelegates = withRawBuilds(linearize(Select(ref), false)(projectInherit)) val configs = confs map { c => axisDelegates(configInherit, ref, c) } new ProjectDelegates(ref, refDelegates, configs.toMap) } - def axisDelegates[T](direct: (ResolvedReference, T) => Seq[T], - ref: ResolvedReference, - init: T): (T, Seq[ScopeAxis[T]]) = + def axisDelegates[T]( + direct: (ResolvedReference, T) => Seq[T], + ref: ResolvedReference, + init: T + ): (T, Seq[ScopeAxis[T]]) = (init, linearize(Select(init))(direct(ref, _))) def linearize[T](axis: ScopeAxis[T], appendZero: Boolean = true)( - inherit: T => Seq[T]): Seq[ScopeAxis[T]] = + inherit: T => Seq[T] + ): Seq[ScopeAxis[T]] = axis match { case Select(x) => topologicalSort[T](x, appendZero)(inherit) case Zero | This => if (appendZero) Zero :: Nil else Nil } def topologicalSort[T](node: T, appendZero: Boolean)( - dependencies: T => Seq[T]): Seq[ScopeAxis[T]] = { + dependencies: T => Seq[T] + ): Seq[ScopeAxis[T]] = { val o = Dag.topologicalSortUnchecked(node)(dependencies).map(Select.apply) if (appendZero) o ::: Zero :: Nil else o diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index a855a6014..4830cfc54 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -337,8 +337,10 @@ object Scoped { def transform(f: S => S, source: SourcePosition): Setting[Task[S]] = set(scopedKey(_ map f), source) - @deprecated("No longer needed with new task syntax and SettingKey inheriting from Initialize.", - "0.13.2") + @deprecated( + "No longer needed with new task syntax and SettingKey inheriting from Initialize.", + "0.13.2" + ) def task: SettingKey[Task[S]] = scopedSetting(scope, key) def toSettingKey: SettingKey[Task[S]] = scopedSetting(scope, key) @@ -401,8 +403,9 @@ object Scoped { def dependsOn(tasks: AnyInitTask*): Initialize[InputTask[S]] = { import TupleSyntax._ - (i, Initialize.joinAny[Task](tasks))((thisTask, deps) => - thisTask.mapTask(_.dependsOn(deps: _*))) + (i, Initialize.joinAny[Task](tasks))( + (thisTask, deps) => thisTask.mapTask(_.dependsOn(deps: _*)) + ) } } @@ -429,23 +432,27 @@ 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") + "0.13.0" + ) 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") + "0.13.0" + ) 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") + "0.13.0" + ) 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") + "0.13.0" + ) def mapFailure[T](f: Incomplete => T): Initialize[R[T]] = onTask(_.result map (f compose failM)) } diff --git a/main-settings/src/main/scala/sbt/std/InputWrapper.scala b/main-settings/src/main/scala/sbt/std/InputWrapper.scala index be34721ae..c1cc5dbac 100644 --- a/main-settings/src/main/scala/sbt/std/InputWrapper.scala +++ b/main-settings/src/main/scala/sbt/std/InputWrapper.scala @@ -31,27 +31,33 @@ object InputWrapper { private[std] final val WrapPreviousName = "wrapPrevious_\u2603\u2603" @compileTimeOnly( - "`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.") + "`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task." + ) 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.") + "`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting." + ) 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.") + "`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task." + ) 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.") + "`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask." + ) 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.") + "`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask." + ) 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.") + "`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](@deprecated("unused", "") in: Any): T = implDetailError private[this] def implDetailError = @@ -161,8 +167,9 @@ object InputWrapper { } /** Translates .previous(format) to Previous.runtime()(format).value*/ - def previousMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - format: c.Expr[sjsonnew.JsonFormat[T]]): c.Expr[Option[T]] = { + def previousMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(format: c.Expr[sjsonnew.JsonFormat[T]]): c.Expr[Option[T]] = { import c.universe._ c.macroApplication match { case a @ Apply(Select(Apply(_, t :: Nil), _), _) => @@ -182,35 +189,42 @@ object InputWrapper { sealed abstract class MacroTaskValue[T] { @compileTimeOnly( - "`taskValue` can only be used within a setting macro, such as :=, +=, ++=, or Def.setting.") + "`taskValue` can only be used within a setting macro, such as :=, +=, ++=, or Def.setting." + ) def taskValue: Task[T] = macro InputWrapper.taskValueMacroImpl[T] } sealed abstract class MacroValue[T] { @compileTimeOnly( - "`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") + "`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting." + ) def value: T = macro InputWrapper.valueMacroImpl[T] } sealed abstract class ParserInput[T] { @compileTimeOnly( - "`parsed` can only be used within an input task macro, such as := or Def.inputTask.") + "`parsed` can only be used within an input task macro, such as := or Def.inputTask." + ) def parsed: T = macro ParserInput.parsedMacroImpl[T] } sealed abstract class InputEvaluated[T] { @compileTimeOnly( - "`evaluated` can only be used within an input task macro, such as := or Def.inputTask.") + "`evaluated` can only be used within an input task macro, such as := or Def.inputTask." + ) def evaluated: T = macro InputWrapper.valueMacroImpl[T] @compileTimeOnly( - "`inputTaskValue` can only be used within an input task macro, such as := or Def.inputTask.") + "`inputTaskValue` can only be used within an input task macro, such as := or Def.inputTask." + ) def inputTaskValue: InputTask[T] = macro InputWrapper.inputTaskValueMacroImpl[T] } sealed abstract class ParserInputTask[T] { @compileTimeOnly( - "`parsed` can only be used within an input task macro, such as := or Def.inputTask.") + "`parsed` can only be used within an input task macro, such as := or Def.inputTask." + ) def parsed: Task[T] = macro ParserInput.parsedInputMacroImpl[T] } sealed abstract class MacroPrevious[T] { @compileTimeOnly( - "`previous` can only be used within a task macro, such as :=, +=, ++=, or Def.task.") + "`previous` can only be used within a task macro, such as :=, +=, ++=, or Def.task." + ) def previous(implicit format: sjsonnew.JsonFormat[T]): Option[T] = macro InputWrapper.previousMacroImpl[T] } @@ -224,24 +238,29 @@ object ParserInput { private[std] val WrapInitName = "initParser_\u2603\u2603" @compileTimeOnly( - "`parsed` can only be used within an input task macro, such as := or Def.inputTask.") + "`parsed` can only be used within an input task macro, such as := or Def.inputTask." + ) 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.") + "`parsed` can only be used within an input task macro, such as := or Def.inputTask." + ) 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], - pos: c.Position): c.Expr[T] = + private[std] def wrap[T: c.WeakTypeTag]( + c: blackbox.Context + )(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = InputWrapper.wrapImpl[T, ParserInput.type](c, ParserInput, WrapName)(ts, pos) - private[std] def wrapInit[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], - pos: c.Position): c.Expr[T] = + private[std] def wrapInit[T: c.WeakTypeTag]( + c: blackbox.Context + )(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = InputWrapper.wrapImpl[T, ParserInput.type](c, ParserInput, WrapInitName)(ts, pos) - private[std] def inputParser[T: c.WeakTypeTag](c: blackbox.Context)( - t: c.Expr[InputTask[T]]): c.Expr[State => Parser[Task[T]]] = + private[std] def inputParser[T: c.WeakTypeTag]( + c: blackbox.Context + )(t: c.Expr[InputTask[T]]): c.Expr[State => Parser[Task[T]]] = c.universe.reify(t.splice.parser) def parsedInputMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Task[T]] = @@ -261,8 +280,9 @@ object ParserInput { wrap[Task[T]](c)(inputParser(c)(e), pos) } - private def wrapInitInputTask[T: c.WeakTypeTag](c: blackbox.Context)(tree: c.Tree, - pos: c.Position) = { + private def wrapInitInputTask[T: c.WeakTypeTag]( + c: blackbox.Context + )(tree: c.Tree, pos: c.Position) = { val e = c.Expr[Initialize[InputTask[T]]](tree) wrapInit[Task[T]](c)(c.universe.reify { Def.toIParser(e.splice) }, pos) } diff --git a/main-settings/src/main/scala/sbt/std/KeyMacro.scala b/main-settings/src/main/scala/sbt/std/KeyMacro.scala index 697963ab0..4a57fbb2d 100644 --- a/main-settings/src/main/scala/sbt/std/KeyMacro.scala +++ b/main-settings/src/main/scala/sbt/std/KeyMacro.scala @@ -14,18 +14,21 @@ import scala.reflect.macros._ import sbt.util.OptJsonWriter private[sbt] object KeyMacro { - def settingKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)( - description: c.Expr[String]): c.Expr[SettingKey[T]] = + def settingKeyImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(description: c.Expr[String]): c.Expr[SettingKey[T]] = keyImpl2[T, SettingKey[T]](c) { (name, mf, ojw) => c.universe.reify { SettingKey[T](name.splice, description.splice)(mf.splice, ojw.splice) } } - def taskKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)( - description: c.Expr[String]): c.Expr[TaskKey[T]] = + def taskKeyImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(description: c.Expr[String]): c.Expr[TaskKey[T]] = keyImpl[T, TaskKey[T]](c) { (name, mf) => c.universe.reify { TaskKey[T](name.splice, description.splice)(mf.splice) } } - def inputKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)( - description: c.Expr[String]): c.Expr[InputKey[T]] = + def inputKeyImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(description: c.Expr[String]): c.Expr[InputKey[T]] = keyImpl[T, InputKey[T]](c) { (name, mf) => c.universe.reify { InputKey[T](name.splice, description.splice)(mf.splice) } } @@ -45,7 +48,8 @@ private[sbt] object KeyMacro { val enclosingValName = definingValName( c, methodName => - s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""") + s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""" + ) c.Expr[String](Literal(Constant(enclosingValName))) } diff --git a/main-settings/src/main/scala/sbt/std/SettingMacro.scala b/main-settings/src/main/scala/sbt/std/SettingMacro.scala index 23b9e51e0..d80b85242 100644 --- a/main-settings/src/main/scala/sbt/std/SettingMacro.scala +++ b/main-settings/src/main/scala/sbt/std/SettingMacro.scala @@ -46,11 +46,13 @@ object InitializeConvert extends Convert { Converted.Success(t) } - private def failTask[C <: blackbox.Context with Singleton](c: C)( - pos: c.Position): Converted[c.type] = + private def failTask[C <: blackbox.Context with Singleton]( + c: C + )(pos: c.Position): Converted[c.type] = Converted.Failure(pos, "A setting cannot depend on a task") - private def failPrevious[C <: blackbox.Context with Singleton](c: C)( - pos: c.Position): Converted[c.type] = + private def failPrevious[C <: blackbox.Context with Singleton]( + c: C + )(pos: c.Position): Converted[c.type] = Converted.Failure(pos, "A setting cannot depend on a task's previous value.") } @@ -59,11 +61,14 @@ object SettingMacro { def settingMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[Initialize[T]] = Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder, EmptyLinter)( Left(t), - Instance.idTransform[c.type]) + Instance.idTransform[c.type] + ) - def settingDynMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - t: c.Expr[Initialize[T]]): c.Expr[Initialize[T]] = + def settingDynMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(t: c.Expr[Initialize[T]]): c.Expr[Initialize[T]] = Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder, EmptyLinter)( Right(t), - Instance.idTransform[c.type]) + Instance.idTransform[c.type] + ) } diff --git a/main-settings/src/main/scala/sbt/std/TaskMacro.scala b/main-settings/src/main/scala/sbt/std/TaskMacro.scala index 562a685f5..e77658925 100644 --- a/main-settings/src/main/scala/sbt/std/TaskMacro.scala +++ b/main-settings/src/main/scala/sbt/std/TaskMacro.scala @@ -56,9 +56,11 @@ object FullInstance extends Instance.Composed[Initialize, Task](InitializeInstance, TaskInstance) with MonadInstance { type SS = sbt.internal.util.Settings[Scope] - val settingsData = TaskKey[SS]("settings-data", - "Provides access to the project data for the build.", - KeyRanks.DTask) + val settingsData = TaskKey[SS]( + "settings-data", + "Provides access to the project data for the build.", + KeyRanks.DTask + ) def flatten[T](in: Initialize[Task[Initialize[Task[T]]]]): Initialize[Task[T]] = { import TupleSyntax._ @@ -98,29 +100,35 @@ object TaskMacro { import LinterDSL.{ Empty => EmptyLinter } - def taskMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - t: c.Expr[T]): c.Expr[Initialize[Task[T]]] = + def taskMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(t: c.Expr[T]): c.Expr[Initialize[Task[T]]] = Instance.contImpl[T, Id](c, FullInstance, FullConvert, MixedBuilder, TaskLinterDSL)( Left(t), - Instance.idTransform[c.type]) + Instance.idTransform[c.type] + ) - def taskDynMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[Task[T]]] = + def taskDynMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[Task[T]]] = Instance.contImpl[T, Id](c, FullInstance, FullConvert, MixedBuilder, TaskDynLinterDSL)( Right(t), - Instance.idTransform[c.type]) + Instance.idTransform[c.type] + ) /** Implementation of := macro for settings. */ - def settingAssignMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - v: c.Expr[T]): c.Expr[Setting[T]] = { + def settingAssignMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(v: c.Expr[T]): c.Expr[Setting[T]] = { val init = SettingMacro.settingMacroImpl[T](c)(v) val assign = transformMacroImpl(c)(init.tree)(AssignInitName) c.Expr[Setting[T]](assign) } /** Implementation of := macro for tasks. */ - def taskAssignMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - v: c.Expr[T]): c.Expr[Setting[Task[T]]] = { + def taskAssignMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(v: c.Expr[T]): c.Expr[Setting[Task[T]]] = { val init = taskMacroImpl[T](c)(v) val assign = transformMacroImpl(c)(init.tree)(AssignInitName) c.Expr[Setting[Task[T]]](assign) @@ -134,14 +142,16 @@ object TaskMacro { ): c.Expr[Setting[T]] = ContextUtil.selectMacroImpl[Setting[T]](c)((_, pos) => c.abort(pos, assignMigration)) - def fakeSettingAppend1Position[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)( - @deprecated("unused", "") v: c.Expr[Initialize[V]])( + 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]])( + 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)) @@ -151,14 +161,16 @@ object TaskMacro { ): 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]]])( + 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]]])( + 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)) @@ -166,56 +178,66 @@ object TaskMacro { // 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]]] = + def itaskAssignPosition[T: c.WeakTypeTag]( + c: blackbox.Context + )(app: c.Expr[Initialize[Task[T]]]): c.Expr[Setting[Task[T]]] = settingAssignPosition(c)(app) - def taskAssignPositionT[T: c.WeakTypeTag](c: blackbox.Context)( - app: c.Expr[Task[T]]): c.Expr[Setting[Task[T]]] = + def taskAssignPositionT[T: c.WeakTypeTag]( + c: blackbox.Context + )(app: c.Expr[Task[T]]): c.Expr[Setting[Task[T]]] = itaskAssignPosition(c)(c.universe.reify { Def.valueStrict(app.splice) }) - def taskAssignPositionPure[T: c.WeakTypeTag](c: blackbox.Context)( - app: c.Expr[T]): c.Expr[Setting[Task[T]]] = + def taskAssignPositionPure[T: c.WeakTypeTag]( + c: blackbox.Context + )(app: c.Expr[T]): c.Expr[Setting[Task[T]]] = taskAssignPositionT(c)(c.universe.reify { TaskExtra.constant(app.splice) }) - def taskTransformPosition[S: c.WeakTypeTag](c: blackbox.Context)( - f: c.Expr[S => S]): c.Expr[Setting[Task[S]]] = + def taskTransformPosition[S: c.WeakTypeTag]( + c: blackbox.Context + )(f: c.Expr[S => S]): c.Expr[Setting[Task[S]]] = c.Expr[Setting[Task[S]]](transformMacroImpl(c)(f.tree)(TransformInitName)) - def settingTransformPosition[S: c.WeakTypeTag](c: blackbox.Context)( - f: c.Expr[S => S]): c.Expr[Setting[S]] = + def settingTransformPosition[S: c.WeakTypeTag]( + c: blackbox.Context + )(f: c.Expr[S => S]): c.Expr[Setting[S]] = c.Expr[Setting[S]](transformMacroImpl(c)(f.tree)(TransformInitName)) - def itaskTransformPosition[S: c.WeakTypeTag](c: blackbox.Context)( - f: c.Expr[S => S]): c.Expr[Setting[S]] = + def itaskTransformPosition[S: c.WeakTypeTag]( + c: blackbox.Context + )(f: c.Expr[S => S]): c.Expr[Setting[S]] = c.Expr[Setting[S]](transformMacroImpl(c)(f.tree)(TransformInitName)) def settingAssignPure[T: c.WeakTypeTag](c: blackbox.Context)(app: c.Expr[T]): c.Expr[Setting[T]] = settingAssignPosition(c)(c.universe.reify { Def.valueStrict(app.splice) }) - def settingAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)( - app: c.Expr[Initialize[T]]): c.Expr[Setting[T]] = + def settingAssignPosition[T: c.WeakTypeTag]( + c: blackbox.Context + )(app: c.Expr[Initialize[T]]): c.Expr[Setting[T]] = c.Expr[Setting[T]](transformMacroImpl(c)(app.tree)(AssignInitName)) /** Implementation of := macro for tasks. */ - def inputTaskAssignMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - v: c.Expr[T]): c.Expr[Setting[InputTask[T]]] = { + def inputTaskAssignMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(v: c.Expr[T]): c.Expr[Setting[InputTask[T]]] = { val init = inputTaskMacroImpl[T](c)(v) val assign = transformMacroImpl(c)(init.tree)(AssignInitName) c.Expr[Setting[InputTask[T]]](assign) } /** Implementation of += macro for tasks. */ - def taskAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])( - a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[Task[T]]] = { + def taskAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag]( + c: blackbox.Context + )(v: c.Expr[U])(a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[Task[T]]] = { val init = taskMacroImpl[U](c)(v) val append = appendMacroImpl(c)(init.tree, a.tree)(Append1InitName) c.Expr[Setting[Task[T]]](append) } /** Implementation of += macro for settings. */ - def settingAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])( - a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[T]] = { + def settingAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag]( + c: blackbox.Context + )(v: c.Expr[U])(a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[T]] = { import c.universe._ val ttpe = c.weakTypeOf[T] val typeArgs = ttpe.typeArgs @@ -228,7 +250,8 @@ object TaskMacro { 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) + Select(v.tree, TermName("taskValue").encodedName) :: Nil + ) c.Expr[Setting[T]](tree) case x => ContextUtil.unexpectedTree(x) } @@ -240,55 +263,62 @@ object TaskMacro { } /** Implementation of ++= macro for tasks. */ - def taskAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])( - a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[Task[T]]] = { + def taskAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag]( + c: blackbox.Context + )(vs: c.Expr[U])(a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[Task[T]]] = { val init = taskMacroImpl[U](c)(vs) val append = appendMacroImpl(c)(init.tree, a.tree)(AppendNInitName) c.Expr[Setting[Task[T]]](append) } /** Implementation of ++= macro for settings. */ - def settingAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])( - a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[T]] = { + def settingAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag]( + c: blackbox.Context + )(vs: c.Expr[U])(a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[T]] = { val init = SettingMacro.settingMacroImpl[U](c)(vs) val append = appendMacroImpl(c)(init.tree, a.tree)(AppendNInitName) c.Expr[Setting[T]](append) } /** Implementation of -= macro for tasks. */ - def taskRemove1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])( - r: c.Expr[Remove.Value[T, U]]): c.Expr[Setting[Task[T]]] = { + def taskRemove1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag]( + c: blackbox.Context + )(v: c.Expr[U])(r: c.Expr[Remove.Value[T, U]]): c.Expr[Setting[Task[T]]] = { val init = taskMacroImpl[U](c)(v) val remove = removeMacroImpl(c)(init.tree, r.tree)(Remove1InitName) c.Expr[Setting[Task[T]]](remove) } /** Implementation of -= macro for settings. */ - def settingRemove1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])( - r: c.Expr[Remove.Value[T, U]]): c.Expr[Setting[T]] = { + def settingRemove1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag]( + c: blackbox.Context + )(v: c.Expr[U])(r: c.Expr[Remove.Value[T, U]]): c.Expr[Setting[T]] = { val init = SettingMacro.settingMacroImpl[U](c)(v) val remove = removeMacroImpl(c)(init.tree, r.tree)(Remove1InitName) c.Expr[Setting[T]](remove) } /** Implementation of --= macro for tasks. */ - def taskRemoveNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])( - r: c.Expr[Remove.Values[T, U]]): c.Expr[Setting[Task[T]]] = { + def taskRemoveNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag]( + c: blackbox.Context + )(vs: c.Expr[U])(r: c.Expr[Remove.Values[T, U]]): c.Expr[Setting[Task[T]]] = { val init = taskMacroImpl[U](c)(vs) val remove = removeMacroImpl(c)(init.tree, r.tree)(RemoveNInitName) c.Expr[Setting[Task[T]]](remove) } /** Implementation of --= macro for settings. */ - def settingRemoveNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])( - r: c.Expr[Remove.Values[T, U]]): c.Expr[Setting[T]] = { + def settingRemoveNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag]( + c: blackbox.Context + )(vs: c.Expr[U])(r: c.Expr[Remove.Values[T, U]]): c.Expr[Setting[T]] = { val init = SettingMacro.settingMacroImpl[U](c)(vs) val remove = removeMacroImpl(c)(init.tree, r.tree)(RemoveNInitName) c.Expr[Setting[T]](remove) } - private[this] def appendMacroImpl(c: blackbox.Context)(init: c.Tree, append: c.Tree)( - newName: String): c.Tree = { + private[this] def appendMacroImpl( + c: blackbox.Context + )(init: c.Tree, append: c.Tree)(newName: String): c.Tree = { import c.universe._ c.macroApplication match { case Apply(Apply(TypeApply(Select(preT, _), targs), _), _) => @@ -303,8 +333,9 @@ object TaskMacro { } } - private[this] def removeMacroImpl(c: blackbox.Context)(init: c.Tree, remove: c.Tree)( - newName: String): c.Tree = { + private[this] def removeMacroImpl( + c: blackbox.Context + )(init: c.Tree, remove: c.Tree)(newName: String): c.Tree = { import c.universe._ c.macroApplication match { case Apply(Apply(TypeApply(Select(preT, _), targs), _), _) => @@ -328,8 +359,10 @@ object TaskMacro { case Apply(Select(prefix, _), _) => prefix case x => ContextUtil.unexpectedTree(x) } - Apply.apply(Select(target, TermName(newName).encodedName), - init :: sourcePosition(c).tree :: Nil) + Apply.apply( + Select(target, TermName(newName).encodedName), + init :: sourcePosition(c).tree :: Nil + ) } private[this] def sourcePosition(c: blackbox.Context): c.Expr[SourcePosition] = { @@ -347,7 +380,8 @@ object TaskMacro { private[this] def settingSource(c: blackbox.Context, path: String, name: String): String = { @tailrec def inEmptyPackage(s: c.Symbol): Boolean = s != c.universe.NoSymbol && ( s.owner == c.mirror.EmptyPackage || s.owner == c.mirror.EmptyPackageClass || inEmptyPackage( - s.owner) + s.owner + ) ) c.internal.enclosingOwner match { case ec if !ec.isStatic => name @@ -361,16 +395,19 @@ object TaskMacro { c.Expr[T](Literal(Constant(t))) } - def inputTaskMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] = + def inputTaskMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] = inputTaskMacro0[T](c)(t) - def inputTaskDynMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)( - t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] = + def inputTaskDynMacroImpl[T: c.WeakTypeTag]( + c: blackbox.Context + )(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] = inputTaskDynMacro0[T](c)(t) - private[this] def inputTaskMacro0[T: c.WeakTypeTag](c: blackbox.Context)( - t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] = + private[this] def inputTaskMacro0[T: c.WeakTypeTag]( + c: blackbox.Context + )(t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] = iInitializeMacro(c)(t) { et => val pt = iParserMacro(c)(et) { pt => iTaskMacro(c)(pt) @@ -379,8 +416,8 @@ object TaskMacro { } private[this] def iInitializeMacro[M[_], T](c: blackbox.Context)(t: c.Expr[T])( - f: c.Expr[T] => c.Expr[M[T]])(implicit tt: c.WeakTypeTag[T], - mt: c.WeakTypeTag[M[T]]): c.Expr[Initialize[M[T]]] = { + f: c.Expr[T] => c.Expr[M[T]] + )(implicit tt: c.WeakTypeTag[T], mt: c.WeakTypeTag[M[T]]): c.Expr[Initialize[M[T]]] = { val inner: Transform[c.type, M] = new Transform[c.type, M] { def apply(in: c.Tree): c.Tree = f(c.Expr[T](in)).tree } @@ -388,7 +425,8 @@ object TaskMacro { Instance .contImpl[T, M](c, InitializeInstance, InputInitConvert, MixedBuilder, EmptyLinter)( Left(cond), - inner) + inner + ) } private[this] def conditionInputTaskTree(c: blackbox.Context)(t: c.Tree): c.Tree = { @@ -424,25 +462,29 @@ object TaskMacro { } private[this] def iParserMacro[M[_], T](c: blackbox.Context)(t: c.Expr[T])( - f: c.Expr[T] => c.Expr[M[T]])(implicit tt: c.WeakTypeTag[T], - mt: c.WeakTypeTag[M[T]]): c.Expr[State => Parser[M[T]]] = { + f: c.Expr[T] => c.Expr[M[T]] + )(implicit tt: c.WeakTypeTag[T], mt: c.WeakTypeTag[M[T]]): c.Expr[State => Parser[M[T]]] = { val inner: Transform[c.type, M] = new Transform[c.type, M] { def apply(in: c.Tree): c.Tree = f(c.Expr[T](in)).tree } Instance.contImpl[T, M](c, ParserInstance, ParserConvert, MixedBuilder, LinterDSL.Empty)( Left(t), - inner) + inner + ) } - private[this] def iTaskMacro[T: c.WeakTypeTag](c: blackbox.Context)( - t: c.Expr[T]): c.Expr[Task[T]] = + private[this] def iTaskMacro[T: c.WeakTypeTag]( + c: blackbox.Context + )(t: c.Expr[T]): c.Expr[Task[T]] = Instance .contImpl[T, Id](c, TaskInstance, TaskConvert, MixedBuilder, EmptyLinter)( Left(t), - Instance.idTransform) + Instance.idTransform + ) - private[this] def inputTaskDynMacro0[T: c.WeakTypeTag](c: blackbox.Context)( - t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] = { + private[this] def inputTaskDynMacro0[T: c.WeakTypeTag]( + c: blackbox.Context + )(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] = { import c.universe.{ Apply => ApplyTree, _ } import internal.decorators._ @@ -467,7 +509,8 @@ object TaskMacro { if (result.isDefined) { c.error( qual.pos, - "Implementation restriction: a dynamic InputTask can only have a single input parser.") + "Implementation restriction: a dynamic InputTask can only have a single input parser." + ) EmptyTree } else { qual.foreach(checkQual) @@ -526,11 +569,13 @@ object PlainTaskMacro { def taskImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[Task[T]] = Instance.contImpl[T, Id](c, TaskInstance, TaskConvert, MixedBuilder, OnlyTaskLinterDSL)( Left(t), - Instance.idTransform[c.type]) + Instance.idTransform[c.type] + ) def taskDyn[T](t: Task[T]): Task[T] = macro taskDynImpl[T] def taskDynImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[Task[T]]): c.Expr[Task[T]] = Instance.contImpl[T, Id](c, TaskInstance, TaskConvert, MixedBuilder, OnlyTaskDynLinterDSL)( Right(t), - Instance.idTransform[c.type]) + Instance.idTransform[c.type] + ) } diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index b3b77c71d..45d61b6de 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -85,8 +85,10 @@ object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { } property("Reference? / ConfigKey? / key == key in ThisScope.copy(..)") = { - forAll((r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: Key) => - expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k)) + 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(..)") = { diff --git a/main/src/main/scala/sbt/BackgroundJobService.scala b/main/src/main/scala/sbt/BackgroundJobService.scala index 5af16058e..d06288b22 100644 --- a/main/src/main/scala/sbt/BackgroundJobService.scala +++ b/main/src/main/scala/sbt/BackgroundJobService.scala @@ -23,7 +23,8 @@ abstract class BackgroundJobService extends Closeable { * then you could process.destroy() for example. */ def runInBackground(spawningTask: ScopedKey[_], state: State)( - start: (Logger, File) => Unit): JobHandle + start: (Logger, File) => Unit + ): JobHandle /** Same as shutown. */ def close(): Unit @@ -51,7 +52,8 @@ object BackgroundJobService { { val stringIdParser: Parser[Seq[String]] = Space ~> token( NotSpace examples handles.map(_.id.toString).toSet, - description = "").+ + description = "" + ).+ stringIdParser.map { strings => strings.map(Integer.parseInt(_)).flatMap(id => handles.find(_.id == id)) } diff --git a/main/src/main/scala/sbt/BuildPaths.scala b/main/src/main/scala/sbt/BuildPaths.scala index b83fe2311..b74bfde44 100644 --- a/main/src/main/scala/sbt/BuildPaths.scala +++ b/main/src/main/scala/sbt/BuildPaths.scala @@ -17,19 +17,25 @@ object BuildPaths { val globalBaseDirectory = AttributeKey[File]( "global-base-directory", "The base directory for global sbt configuration and staging.", - DSetting) - val globalPluginsDirectory = AttributeKey[File]("global-plugins-directory", - "The base directory for global sbt plugins.", - DSetting) - val globalSettingsDirectory = AttributeKey[File]("global-settings-directory", - "The base directory for global sbt settings.", - DSetting) + DSetting + ) + val globalPluginsDirectory = AttributeKey[File]( + "global-plugins-directory", + "The base directory for global sbt plugins.", + DSetting + ) + val globalSettingsDirectory = AttributeKey[File]( + "global-settings-directory", + "The base directory for global sbt settings.", + DSetting + ) val stagingDirectory = AttributeKey[File]("staging-directory", "The directory for staging remote projects.", DSetting) val dependencyBaseDirectory = AttributeKey[File]( "dependency-base-directory", "The base directory for caching dependency resolution.", - DSetting) + DSetting + ) val globalZincDirectory = AttributeKey[File]("global-zinc-directory", "The base directory for Zinc internals.", DSetting) @@ -56,7 +62,8 @@ object BuildPaths { def getGlobalPluginsDirectory(state: State, globalBase: File): File = fileSetting(globalPluginsDirectory, GlobalPluginsProperty, defaultGlobalPlugins(globalBase))( - state) + state + ) def getGlobalSettingsDirectory(state: State, globalBase: File): File = fileSetting(globalSettingsDirectory, GlobalSettingsProperty, globalBase)(state) @@ -70,11 +77,13 @@ object BuildPaths { fileSetting(globalZincDirectory, GlobalZincProperty, defaultGlobalZinc(globalBase))(state) private[this] def fileSetting(stateKey: AttributeKey[File], property: String, default: File)( - state: State): File = + state: State + ): File = getFileSetting(stateKey, property, default)(state) def getFileSetting(stateKey: AttributeKey[File], property: String, default: => File)( - state: State): File = + state: State + ): File = state get stateKey orElse getFileProperty(property) getOrElse default def getFileProperty(name: String): Option[File] = Option(System.getProperty(name)) flatMap { diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index dcff5d449..c1257db5a 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -150,7 +150,8 @@ object Cross { "configuration. This could result in subprojects cross building against Scala versions that they are " + "not compatible with. Try issuing cross building command with tasks instead, since sbt will be able " + "to ensure that cross building is only done using configured project and Scala version combinations " + - "that are configured.") + "that are configured." + ) state.log.debug("Scala versions configuration is:") projCrossVersions.foreach { case (project, versions) => state.log.debug(s"$project: $versions") @@ -174,12 +175,14 @@ object Cross { case (version, projects) if aggCommand.contains(" ") => // If the command contains a space, then the `all` command won't work because it doesn't support issuing // commands with spaces, so revert to running the command on each project one at a time - s"$SwitchCommand $verbose $version" :: projects.map(project => - s"$project/$aggCommand") + s"$SwitchCommand $verbose $version" :: projects + .map(project => s"$project/$aggCommand") case (version, projects) => // First switch scala version, then use the all command to run the command on each project concurrently - Seq(s"$SwitchCommand $verbose $version", - projects.map(_ + "/" + aggCommand).mkString("all ", " ", "")) + Seq( + s"$SwitchCommand $verbose $version", + projects.map(_ + "/" + aggCommand).mkString("all ", " ", "") + ) } } @@ -188,8 +191,9 @@ object Cross { } def crossRestoreSession: Command = - Command.arb(_ => crossRestoreSessionParser, crossRestoreSessionHelp)((s, _) => - crossRestoreSessionImpl(s)) + Command.arb(_ => crossRestoreSessionParser, crossRestoreSessionHelp)( + (s, _) => crossRestoreSessionImpl(s) + ) 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 67dc6997e..10c561540 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -139,7 +139,8 @@ object Defaults extends BuildCommon { private[sbt] lazy val globalCore: Seq[Setting[_]] = globalDefaults( defaultTestTasks(test) ++ defaultTestTasks(testOnly) ++ defaultTestTasks(testQuick) ++ Seq( excludeFilter :== HiddenFileFilter - ) ++ globalIvyCore ++ globalJvmCore) ++ globalSbtCore + ) ++ globalIvyCore ++ globalJvmCore + ) ++ globalSbtCore private[sbt] lazy val globalJvmCore: Seq[Setting[_]] = Seq( @@ -167,7 +168,8 @@ object Defaults extends BuildCommon { artifactClassifier in packageDoc :== Some(DocClassifier), includeFilter :== NothingFilter, includeFilter in unmanagedSources :== ("*.java" | "*.scala") && new SimpleFileFilter( - _.isFile), + _.isFile + ), includeFilter in unmanagedJars :== "*.jar" | "*.so" | "*.dll" | "*.jnilib" | "*.zip", includeFilter in unmanagedResources :== AllPassFilter, bgList := { bgJobService.value.jobs }, @@ -247,8 +249,10 @@ object Defaults extends BuildCommon { () => { IO.delete(dir); IO.createDirectory(dir) } }, - Previous.cache := new Previous(Def.streamsManagerKey.value, - Previous.references.value.getReferences), + Previous.cache := new Previous( + Def.streamsManagerKey.value, + Previous.references.value.getReferences + ), Previous.references :== new Previous.References, concurrentRestrictions := defaultRestrictions.value, parallelExecution :== true, @@ -289,7 +293,8 @@ object Defaults extends BuildCommon { ++ Vector(ServerHandler.fallback)) }, insideCI :== sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI"), - )) + ) + ) def defaultTestTasks(key: Scoped): Seq[Setting[_]] = inTask(key)( @@ -323,18 +328,24 @@ object Defaults extends BuildCommon { scalaSource := sourceDirectory.value / "scala", javaSource := sourceDirectory.value / "java", unmanagedSourceDirectories := { - makeCrossSources(scalaSource.value, - javaSource.value, - scalaBinaryVersion.value, - crossPaths.value) ++ - makePluginCrossSources(sbtPlugin.value, - scalaSource.value, - (sbtBinaryVersion in pluginCrossBuild).value, - crossPaths.value) + makeCrossSources( + scalaSource.value, + javaSource.value, + scalaBinaryVersion.value, + crossPaths.value + ) ++ + makePluginCrossSources( + sbtPlugin.value, + scalaSource.value, + (sbtBinaryVersion in pluginCrossBuild).value, + crossPaths.value + ) }, - unmanagedSources := collectFiles(unmanagedSourceDirectories, - includeFilter in unmanagedSources, - excludeFilter in unmanagedSources).value, + unmanagedSources := collectFiles( + unmanagedSourceDirectories, + includeFilter in unmanagedSources, + excludeFilter in unmanagedSources + ).value, watchSources in ConfigGlobal ++= { val baseDir = baseDirectory.value val bases = unmanagedSourceDirectories.value @@ -361,9 +372,11 @@ object Defaults extends BuildCommon { resourceDirectories := Classpaths .concatSettings(unmanagedResourceDirectories, managedResourceDirectories) .value, - unmanagedResources := collectFiles(unmanagedResourceDirectories, - includeFilter in unmanagedResources, - excludeFilter in unmanagedResources).value, + unmanagedResources := collectFiles( + unmanagedResourceDirectories, + includeFilter in unmanagedResources, + excludeFilter in unmanagedResources + ).value, watchSources in ConfigGlobal ++= { val bases = unmanagedResourceDirectories.value val include = (includeFilter in unmanagedResources).value @@ -395,19 +408,24 @@ object Defaults extends BuildCommon { def compileBase = inTask(console)(compilersSetting :: Nil) ++ compileBaseGlobal ++ Seq( incOptions := incOptions.value .withClassfileManagerType( - Option(TransactionalManagerType - .of(crossTarget.value / "classes.bak", sbt.util.Logger.Null): ClassFileManagerType).toOptional + Option( + TransactionalManagerType + .of(crossTarget.value / "classes.bak", sbt.util.Logger.Null): ClassFileManagerType + ).toOptional ), scalaInstance := scalaInstanceTask.value, crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled), sbtBinaryVersion in pluginCrossBuild := binarySbtVersion( - (sbtVersion in pluginCrossBuild).value), + (sbtVersion in pluginCrossBuild).value + ), crossSbtVersions := Vector((sbtVersion in pluginCrossBuild).value), - crossTarget := makeCrossTarget(target.value, - scalaBinaryVersion.value, - (sbtBinaryVersion in pluginCrossBuild).value, - sbtPlugin.value, - crossPaths.value), + crossTarget := makeCrossTarget( + target.value, + scalaBinaryVersion.value, + (sbtBinaryVersion in pluginCrossBuild).value, + sbtPlugin.value, + crossPaths.value + ), clean := { val _ = clean.value IvyActions.cleanCachedResolutionCache(ivyModule.value, streams.value.log) @@ -427,7 +445,8 @@ object Defaults extends BuildCommon { derive(crossScalaVersions := Seq(scalaVersion.value)), derive(compilersSetting), derive(scalaBinaryVersion := binaryScalaVersion(scalaVersion.value)) - )) + ) + ) def makeCrossSources( scalaSrcDir: File, @@ -441,10 +460,12 @@ object Defaults extends BuildCommon { Seq(scalaSrcDir, javaSrcDir) } - def makePluginCrossSources(isPlugin: Boolean, - scalaSrcDir: File, - sbtBinaryV: String, - cross: Boolean): Seq[File] = { + def makePluginCrossSources( + isPlugin: Boolean, + scalaSrcDir: File, + sbtBinaryV: String, + cross: Boolean + ): Seq[File] = { if (cross && isPlugin) Vector(scalaSrcDir.getParentFile / s"${scalaSrcDir.name}-sbt-$sbtBinaryV") else Vector() @@ -473,10 +494,12 @@ object Defaults extends BuildCommon { scalaJarsTarget = zincDir, log = streams.value.log ) - val compilers = ZincUtil.compilers(instance = scalaInstance.value, - classpathOptions = classpathOptions.value, - javaHome = javaHome.value, - scalac) + val compilers = ZincUtil.compilers( + instance = scalaInstance.value, + classpathOptions = classpathOptions.value, + javaHome = javaHome.value, + scalac + ) val classLoaderCache = state.value.classLoaderCache if (java.lang.Boolean.getBoolean("sbt.disable.interface.classloader.cache")) compilers else { @@ -494,7 +517,8 @@ object Defaults extends BuildCommon { globalDefaults(enableBinaryCompileAnalysis := true) lazy val configTasks: Seq[Setting[_]] = docTaskSettings(doc) ++ inTask(compile)( - compileInputsSettings) ++ configGlobal ++ defaultCompileSettings ++ compileAnalysisSettings ++ Seq( + compileInputsSettings + ) ++ configGlobal ++ defaultCompileSettings ++ compileAnalysisSettings ++ Seq( compile := compileTask.value, manipulateBytecode := compileIncremental.value, compileIncremental := (compileIncrementalTask tag (Tags.Compile, Tags.CPU)).value, @@ -532,7 +556,8 @@ object Defaults extends BuildCommon { initialCommands :== "", cleanupCommands :== "", asciiGraphWidth :== 40 - )) + ) + ) lazy val projectTasks: Seq[Setting[_]] = Seq( cleanFiles := cleanFilesTask.value, @@ -659,14 +684,17 @@ object Defaults extends BuildCommon { testOptions :== Nil, testResultLogger :== TestResultLogger.Default, testFilter in testOnly :== (selectedFilter _) - )) + ) + ) lazy val testTasks : Seq[Setting[_]] = testTaskOptions(test) ++ testTaskOptions(testOnly) ++ testTaskOptions( - testQuick) ++ testDefaults ++ Seq( + testQuick + ) ++ testDefaults ++ Seq( testLoader := TestFramework.createTestLoader( data(fullClasspath.value), scalaInstance.value, - IO.createUniqueDirectory(taskTemporaryDirectory.value)), + IO.createUniqueDirectory(taskTemporaryDirectory.value) + ), loadedTestFrameworks := { val loader = testLoader.value val log = streams.value.log @@ -714,10 +742,14 @@ object Defaults extends BuildCommon { def testTaskOptions(key: Scoped): Seq[Setting[_]] = inTask(key)( testListeners := { - TestLogger.make(streams.value.log, - closeableTestLogger(streamsManager.value, - test in resolvedScoped.value.scope, - logBuffered.value)) +: + TestLogger.make( + streams.value.log, + closeableTestLogger( + streamsManager.value, + test in resolvedScoped.value.scope, + logBuffered.value + ) + ) +: new TestStatusReporter(succeededFile(streams.in(test).value.cacheDirectory)) +: testListeners.in(TaskZero).value }, @@ -728,7 +760,8 @@ object Defaults extends BuildCommon { ) private[this] def closeableTestLogger(manager: Streams, baseKey: Scoped, buffered: Boolean)( - tdef: TestDefinition): TestLogger.PerTest = { + tdef: TestDefinition + ): TestLogger.PerTest = { val scope = baseKey.scope val extra = scope.extra match { case Select(x) => x; case _ => AttributeMap.empty } val key = ScopedKey(scope.copy(extra = Select(testExtra(extra, tdef))), baseKey.key) @@ -769,9 +802,11 @@ object Defaults extends BuildCommon { def testExecutionTask(task: Scoped): Initialize[Task[Tests.Execution]] = Def.task { - new Tests.Execution((testOptions in task).value, - (parallelExecution in task).value, - (tags in task).value) + new Tests.Execution( + (testOptions in task).value, + (parallelExecution in task).value, + (tags in task).value + ) } def testQuickFilter: Initialize[Task[Seq[String] => Seq[String => Boolean]]] = @@ -849,9 +884,11 @@ object Defaults extends BuildCommon { } } - def createTestRunners(frameworks: Map[TestFramework, Framework], - loader: ClassLoader, - config: Tests.Execution): Map[TestFramework, Runner] = { + def createTestRunners( + frameworks: Map[TestFramework, Framework], + loader: ClassLoader, + config: Tests.Execution + ): Map[TestFramework, Runner] = { import Tests.Argument val opts = config.options.toList frameworks.map { @@ -873,14 +910,16 @@ object Defaults extends BuildCommon { config: Tests.Execution, cp: Classpath, ): Initialize[Task[Tests.Output]] = { - allTestGroupsTask(s, - frameworks, - loader, - groups, - config, - cp, - forkedParallelExecution = false, - javaOptions = Nil) + allTestGroupsTask( + s, + frameworks, + loader, + groups, + config, + cp, + forkedParallelExecution = false, + javaOptions = Nil + ) } private[sbt] def allTestGroupsTask( @@ -890,25 +929,30 @@ object Defaults extends BuildCommon { groups: Seq[Tests.Group], config: Tests.Execution, cp: Classpath, - forkedParallelExecution: Boolean): Initialize[Task[Tests.Output]] = { - allTestGroupsTask(s, - frameworks, - loader, - groups, - config, - cp, - forkedParallelExecution, - javaOptions = Nil) + forkedParallelExecution: Boolean + ): Initialize[Task[Tests.Output]] = { + allTestGroupsTask( + s, + frameworks, + loader, + groups, + config, + cp, + forkedParallelExecution, + javaOptions = Nil + ) } - private[sbt] def allTestGroupsTask(s: TaskStreams, - frameworks: Map[TestFramework, Framework], - loader: ClassLoader, - groups: Seq[Tests.Group], - config: Tests.Execution, - cp: Classpath, - forkedParallelExecution: Boolean, - javaOptions: Seq[String]): 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, + forkedParallelExecution: Boolean, + javaOptions: Seq[String] + ): Initialize[Task[Tests.Output]] = { val runners = createTestRunners(frameworks, loader, config) val groupTasks = groups map { case Tests.Group(_, tests, runPolicy) => @@ -917,13 +961,15 @@ object Defaults extends BuildCommon { s.log.debug(s"javaOptions: ${opts.runJVMOptions}") val forkedConfig = config.copy(parallel = config.parallel && forkedParallelExecution) s.log.debug(s"Forking tests - parallelism = ${forkedConfig.parallel}") - ForkTests(runners, - tests.toVector, - forkedConfig, - cp.files, - opts, - s.log, - Tags.ForkedTestGroup) + ForkTests( + runners, + tests.toVector, + forkedConfig, + cp.files, + opts, + s.log, + Tags.ForkedTestGroup + ) case Tests.InProcess => if (javaOptions.nonEmpty) { s.log.warn("javaOptions will be ignored, fork is set to false") @@ -978,7 +1024,8 @@ object Defaults extends BuildCommon { Seq( packageOptions :== Nil, artifactName :== (Artifact.artifactName _) - )) + ) + ) lazy val packageConfig: Seq[Setting[_]] = inTask(packageBin)( @@ -998,7 +1045,8 @@ object Defaults extends BuildCommon { packageOptions := Package.addSpecManifestAttributes( name.value, version.value, - organizationName.value) +: packageOptions.value + organizationName.value + ) +: packageOptions.value ) ++ packageTaskSettings(packageBin, packageBinMappings) ++ packageTaskSettings(packageSrc, packageSrcMappings) ++ @@ -1022,26 +1070,34 @@ object Defaults extends BuildCommon { (srcs --- sdirs --- base) pair (relativeTo(sdirs) | relativeTo(base) | flat) } def resourceMappings = relativeMappings(unmanagedResources, unmanagedResourceDirectories) - def relativeMappings(files: ScopedTaskable[Seq[File]], - dirs: ScopedTaskable[Seq[File]]): Initialize[Task[Seq[(File, String)]]] = + def relativeMappings( + files: ScopedTaskable[Seq[File]], + dirs: ScopedTaskable[Seq[File]] + ): Initialize[Task[Seq[(File, String)]]] = Def.task { val rs = files.toTask.value val rdirs = dirs.toTask.value (rs --- rdirs) pair (relativeTo(rdirs) | flat) } - def collectFiles(dirs: ScopedTaskable[Seq[File]], - filter: ScopedTaskable[FileFilter], - excludes: ScopedTaskable[FileFilter]): Initialize[Task[Seq[File]]] = + def collectFiles( + dirs: ScopedTaskable[Seq[File]], + filter: ScopedTaskable[FileFilter], + excludes: ScopedTaskable[FileFilter] + ): Initialize[Task[Seq[File]]] = Def.task { dirs.toTask.value.descendantsExcept(filter.toTask.value, excludes.toTask.value).get } def artifactPathSetting(art: SettingKey[Artifact]): Initialize[File] = Def.setting { val f = artifactName.value - (crossTarget.value / f(ScalaVersion((scalaVersion in artifactName).value, - (scalaBinaryVersion in artifactName).value), - projectID.value, - art.value)).asFile + (crossTarget.value / f( + ScalaVersion( + (scalaVersion in artifactName).value, + (scalaBinaryVersion in artifactName).value + ), + projectID.value, + art.value + )).asFile } def artifactSetting: Initialize[Artifact] = @@ -1066,9 +1122,11 @@ object Defaults extends BuildCommon { } @deprecated("The configuration(s) should not be decided based on the classifier.", "1.0.0") - def artifactConfigurations(base: Artifact, - scope: Configuration, - classifier: Option[String]): Iterable[Configuration] = + def artifactConfigurations( + base: Artifact, + scope: Configuration, + classifier: Option[String] + ): Iterable[Configuration] = classifier match { case Some(c) => Artifact.classifierConf(c) :: Nil case None => scope :: Nil @@ -1107,7 +1165,8 @@ object Defaults extends BuildCommon { classes match { case multiple if multiple.size > 1 => logger.warn( - "Multiple main classes detected. Run 'show discoveredMainClasses' to see the list") + "Multiple main classes detected. Run 'show discoveredMainClasses' to see the list" + ) case _ => } pickMainClass(classes) @@ -1126,7 +1185,8 @@ object Defaults extends BuildCommon { case xs if xs.isEmpty => () case xs => sys.error( - s"cleanKeepFiles contains directory/file that are not directly under cleanFiles: $xs") + s"cleanKeepFiles contains directory/file that are not directly under cleanFiles: $xs" + ) } val toClean = (dirItems filterNot { preserveSet(_) }) ++ fs toClean @@ -1138,8 +1198,9 @@ object Defaults extends BuildCommon { copyClasspath: Initialize[Boolean], scalaRun: Initialize[Task[ScalaRun]] ): Initialize[InputTask[JobHandle]] = { - val parser = Defaults.loadForParser(discoveredMainClasses)((s, names) => - Defaults.runMainParser(s, names getOrElse Nil)) + val parser = Defaults.loadForParser(discoveredMainClasses)( + (s, names) => Defaults.runMainParser(s, names getOrElse Nil) + ) Def.inputTask { val service = bgJobService.value val (mainClass, args) = parser.parsed @@ -1232,12 +1293,16 @@ object Defaults extends BuildCommon { } else { if (options.nonEmpty) { val mask = ScopeMask(project = false) - val showJavaOptions = Scope.displayMasked((javaOptions in resolvedScope).scopedKey.scope, - (javaOptions in resolvedScope).key.label, - mask) - val showFork = Scope.displayMasked((fork in resolvedScope).scopedKey.scope, - (fork in resolvedScope).key.label, - mask) + val showJavaOptions = Scope.displayMasked( + (javaOptions in resolvedScope).scopedKey.scope, + (javaOptions in resolvedScope).key.label, + mask + ) + val showFork = Scope.displayMasked( + (fork in resolvedScope).scopedKey.scope, + (fork in resolvedScope).key.label, + mask + ) s.log.warn(s"$showJavaOptions will be ignored, $showFork is set to false") } new Run(si, trap, tmp) @@ -1312,13 +1377,15 @@ object Defaults extends BuildCommon { case (_, true) => val javadoc = sbt.inc.Doc.cachedJavadoc(label, s.cacheStoreFactory sub "java", cs.javaTools) - javadoc.run(srcs.toList, - cp, - out, - javacOptions.value.toList, - IncToolOptionsUtil.defaultIncToolOptions(), - s.log, - reporter) + javadoc.run( + srcs.toList, + cp, + out, + javacOptions.value.toList, + IncToolOptionsUtil.defaultIncToolOptions(), + s.log, + reporter + ) case _ => () // do nothing } out @@ -1514,15 +1581,17 @@ object Defaults extends BuildCommon { val max = maxErrors.value val spms = sourcePositionMappers.value val problems = - analysis.infos.allInfos.values.flatMap(i => - i.getReportedProblems ++ i.getUnreportedProblems) + analysis.infos.allInfos.values + .flatMap(i => i.getReportedProblems ++ i.getUnreportedProblems) val reporter = new ManagedLoggedReporter(max, streams.value.log, foldMappers(spms)) problems.foreach(p => reporter.log(p)) } def sbtPluginExtra(m: ModuleID, sbtV: String, scalaV: String): ModuleID = - m.extra(PomExtraDependencyAttributes.SbtVersionKey -> sbtV, - PomExtraDependencyAttributes.ScalaVersionKey -> scalaV) + m.extra( + PomExtraDependencyAttributes.SbtVersionKey -> sbtV, + PomExtraDependencyAttributes.ScalaVersionKey -> scalaV + ) .withCrossVersion(Disabled()) def discoverSbtPluginNames: Initialize[Task[PluginDiscovery.DiscoveredNames]] = Def.taskDyn { @@ -1636,8 +1705,10 @@ object Classpaths { import Keys._ import Defaults._ - def concatDistinct[T](a: ScopedTaskable[Seq[T]], - b: ScopedTaskable[Seq[T]]): Initialize[Task[Seq[T]]] = Def.task { + def concatDistinct[T]( + a: ScopedTaskable[Seq[T]], + b: ScopedTaskable[Seq[T]] + ): Initialize[Task[Seq[T]]] = Def.task { (a.toTask.value ++ b.toTask.value).distinct } def concat[T](a: ScopedTaskable[Seq[T]], b: ScopedTaskable[Seq[T]]): Initialize[Task[Seq[T]]] = @@ -1648,10 +1719,12 @@ object Classpaths { lazy val configSettings: Seq[Setting[_]] = classpaths ++ Seq( products := makeProducts.value, productDirectories := classDirectory.value :: Nil, - classpathConfiguration := findClasspathConfig(internalConfigurationMap.value, - configuration.value, - classpathConfiguration.?.value, - update.value) + classpathConfiguration := findClasspathConfig( + internalConfigurationMap.value, + configuration.value, + classpathConfiguration.?.value, + update.value + ) ) private[this] def classpaths: Seq[Setting[_]] = Seq( @@ -1660,9 +1733,11 @@ object Classpaths { fullClasspath := concatDistinct(exportedProducts, dependencyClasspath).value, internalDependencyClasspath := internalDependencies.value, unmanagedClasspath := unmanagedDependencies.value, - managedClasspath := managedJars(classpathConfiguration.value, - classpathTypes.value, - update.value), + managedClasspath := managedJars( + classpathConfiguration.value, + classpathTypes.value, + update.value + ), exportedProducts := trackedExportedProducts(TrackLevel.TrackAlways).value, exportedProductsIfMissing := trackedExportedProducts(TrackLevel.TrackIfMissing).value, exportedProductsNoTracking := trackedExportedProducts(TrackLevel.NoTracking).value, @@ -1672,10 +1747,12 @@ object Classpaths { internalDependencyAsJars := internalDependencyJarsTask.value, dependencyClasspathAsJars := concat(internalDependencyAsJars, externalDependencyClasspath).value, fullClasspathAsJars := concatDistinct(exportedProductJars, dependencyClasspathAsJars).value, - unmanagedJars := findUnmanagedJars(configuration.value, - unmanagedBase.value, - includeFilter in unmanagedJars value, - excludeFilter in unmanagedJars value) + unmanagedJars := findUnmanagedJars( + configuration.value, + unmanagedBase.value, + includeFilter in unmanagedJars value, + excludeFilter in unmanagedJars value + ) ).map(exportClasspath) private[this] def exportClasspath(s: Setting[Task[Classpath]]): Setting[Task[Classpath]] = @@ -1692,15 +1769,18 @@ object Classpaths { for (task <- defaultPackageKeys; conf <- Seq(Compile, Test)) yield (task in conf) lazy val defaultArtifactTasks: Seq[TaskKey[File]] = makePom +: defaultPackages - def findClasspathConfig(map: Configuration => Configuration, - thisConfig: Configuration, - delegated: Option[Configuration], - report: UpdateReport): Configuration = { + def findClasspathConfig( + map: Configuration => Configuration, + thisConfig: Configuration, + delegated: Option[Configuration], + report: UpdateReport + ): Configuration = { val defined = report.allConfigurations.toSet val search = map(thisConfig) +: (delegated.toList ++ Seq(Compile, Configurations.Default)) def notFound = sys.error( - "Configuration to use for managed classpath must be explicitly defined when default configurations are not present.") + "Configuration to use for managed classpath must be explicitly defined when default configurations are not present." + ) search find { c => defined contains ConfigRef(c.name) } getOrElse notFound @@ -1729,7 +1809,8 @@ object Classpaths { publishMavenStyle :== true, publishArtifact :== true, publishArtifact in Test :== false - )) + ) + ) val jvmPublishSettings: Seq[Setting[_]] = Seq( artifacts := artifactDefs(defaultArtifactTasks).value, @@ -1799,7 +1880,8 @@ object Classpaths { CrossVersion(scalaVersion, binVersion)(base).withCrossVersion(Disabled()) }, shellPrompt := shellPromptFromState - )) + ) + ) val ivyBaseSettings: Seq[Setting[_]] = baseGlobalDefaults ++ sbtClassifiersTasks ++ Seq( conflictWarning := conflictWarning.value.copy(label = Reference.display(thisProjectRef.value)), @@ -1822,10 +1904,12 @@ object Classpaths { developers.value.toVector ), overrideBuildResolvers := appConfiguration(isOverrideRepositories).value, - externalResolvers := ((externalResolvers.?.value, - resolvers.value, - appResolvers.value, - useJCenter.value) match { + externalResolvers := (( + externalResolvers.?.value, + resolvers.value, + appResolvers.value, + useJCenter.value + ) match { case (Some(delegated), Seq(), _, _) => delegated case (_, rs, Some(ars), _) => ars ++ rs case (_, rs, _, uj) => Resolver.combineDefaultResolvers(rs.toVector, uj, mavenCentral = true) @@ -1877,7 +1961,8 @@ object Classpaths { checkExplicit = true, overrideScalaVersion = true ).withScalaOrganization(scalaOrganization.value) - .withScalaArtifacts(scalaArtifacts.value.toVector)) + .withScalaArtifacts(scalaArtifacts.value.toVector) + ) } )).value, artifactPath in makePom := artifactPathSetting(artifact in makePom).value, @@ -1903,7 +1988,8 @@ object Classpaths { .withRetrieveDirectory(managedDirectory.value) .withOutputPattern(retrievePattern.value) .withSync(retrieveManagedSync.value) - .withConfigurationsToRetrieve(configurationsToRetrieve.value map { _.toVector })) + .withConfigurationsToRetrieve(configurationsToRetrieve.value map { _.toVector }) + ) else None }, dependencyResolution := IvyDependencyResolution(ivyConfiguration.value), @@ -1997,7 +2083,8 @@ object Classpaths { }, dependencyPositions := dependencyPositionsTask.value, unresolvedWarningConfiguration in update := UnresolvedWarningConfiguration( - dependencyPositions.value), + dependencyPositions.value + ), update := (updateTask tag (Tags.Update, Tags.Network)).value, update := { val report = update.value @@ -2066,7 +2153,8 @@ object Classpaths { autoScalaLibrary.value && scalaHome.value.isEmpty && managedScalaInstance.value, sbtPlugin.value, scalaOrganization.value, - scalaVersion.value), + scalaVersion.value + ), // Override the default to handle mixing in the sbtPlugin + scala dependencies. allDependencies := { val base = projectDependencies.value ++ libraryDependencies.value @@ -2091,7 +2179,8 @@ object Classpaths { val resset = ress.toSet for ((name, r) <- resset groupBy (_.name) if r.size > 1) { log.warn( - "Multiple resolvers having different access mechanism configured with same name '" + name + "'. To avoid conflict, Remove duplicate project resolvers (`resolvers`) or rename publishing resolver (`publishTo`).") + "Multiple resolvers having different access mechanism configured with same name '" + name + "'. To avoid conflict, Remove duplicate project resolvers (`resolvers`) or rename publishing resolver (`publishTo`)." + ) } } @@ -2107,9 +2196,11 @@ object Classpaths { def pluginProjectID: Initialize[ModuleID] = Def.setting { if (sbtPlugin.value) - sbtPluginExtra(projectID.value, - (sbtBinaryVersion in pluginCrossBuild).value, - (scalaBinaryVersion in pluginCrossBuild).value) + sbtPluginExtra( + projectID.value, + (sbtBinaryVersion in pluginCrossBuild).value, + (scalaBinaryVersion in pluginCrossBuild).value + ) else projectID.value } private[sbt] def ivySbt0: Initialize[Task[IvySbt]] = @@ -2134,7 +2225,8 @@ object Classpaths { Defaults.globalDefaults( Seq( transitiveClassifiers in updateSbtClassifiers ~= (_.filter(_ != DocClassifier)) - )) + ) + ) def sbtClassifiersTasks = sbtClassifiersGlobalDefaults ++ inTask(updateSbtClassifiers)( @@ -2173,7 +2265,9 @@ object Classpaths { Vector(), checkExplicit = false, filterImplicit = false, - overrideScalaVersion = true).withScalaOrganization(scalaOrganization.value)) + overrideScalaVersion = true + ).withScalaOrganization(scalaOrganization.value) + ) }, updateSbtClassifiers in TaskGlobal := (Def.task { val lm = dependencyResolution.value @@ -2262,7 +2356,8 @@ object Classpaths { } def withExcludes(out: File, classifiers: Seq[String], lock: xsbti.GlobalLock)( - f: Map[ModuleID, Vector[ConfigRef]] => UpdateReport): UpdateReport = { + f: Map[ModuleID, Vector[ConfigRef]] => UpdateReport + ): UpdateReport = { import sbt.librarymanagement.LibraryManagementCodec._ import sbt.util.FileBasedStore implicit val isoString: sjsonnew.IsoString[JValue] = @@ -2281,15 +2376,15 @@ object Classpaths { val excludes = store .read[Map[ModuleID, Vector[ConfigRef]]]( - default = Map.empty[ModuleID, Vector[ConfigRef]]) + default = Map.empty[ModuleID, Vector[ConfigRef]] + ) val report = f(excludes) val allExcludes: Map[ModuleID, Vector[ConfigRef]] = excludes ++ IvyActions .extractExcludes(report) .mapValues(cs => cs.map(c => ConfigRef(c)).toVector) store.write(allExcludes) - IvyActions.addExcluded(report, - classifiers.toVector, - allExcludes.mapValues(_.map(_.name).toSet)) + IvyActions + .addExcluded(report, classifiers.toVector, allExcludes.mapValues(_.map(_.name).toSet)) } } ) @@ -2474,42 +2569,50 @@ object Classpaths { def getPublishTo(repo: Option[Resolver]): Resolver = repo getOrElse sys.error("Repository for publishing is not specified.") - def publishConfig(publishMavenStyle: Boolean, - deliverIvyPattern: String, - status: String, - configurations: Vector[ConfigRef], - artifacts: Vector[(Artifact, File)], - checksums: Vector[String], - resolverName: String = "local", - logging: UpdateLogging = UpdateLogging.DownloadOnly, - overwrite: Boolean = false) = - PublishConfiguration(publishMavenStyle, - deliverIvyPattern, - status, - configurations, - resolverName, - artifacts, - checksums, - logging, - overwrite) + def publishConfig( + publishMavenStyle: Boolean, + deliverIvyPattern: String, + status: String, + configurations: Vector[ConfigRef], + artifacts: Vector[(Artifact, File)], + checksums: Vector[String], + resolverName: String = "local", + logging: UpdateLogging = UpdateLogging.DownloadOnly, + overwrite: Boolean = false + ) = + PublishConfiguration( + publishMavenStyle, + deliverIvyPattern, + status, + configurations, + resolverName, + artifacts, + checksums, + logging, + overwrite + ) - def makeIvyXmlConfig(publishMavenStyle: Boolean, - deliverIvyPattern: String, - status: String, - configurations: Vector[ConfigRef], - checksums: Vector[String], - logging: sbt.librarymanagement.UpdateLogging = UpdateLogging.DownloadOnly, - overwrite: Boolean = false, - optResolverName: Option[String] = None) = - PublishConfiguration(publishMavenStyle, - Some(deliverIvyPattern), - Some(status), - Some(configurations), - optResolverName, - Vector.empty, - checksums, - Some(logging), - overwrite) + def makeIvyXmlConfig( + publishMavenStyle: Boolean, + deliverIvyPattern: String, + status: String, + configurations: Vector[ConfigRef], + checksums: Vector[String], + logging: sbt.librarymanagement.UpdateLogging = UpdateLogging.DownloadOnly, + overwrite: Boolean = false, + optResolverName: Option[String] = None + ) = + PublishConfiguration( + publishMavenStyle, + Some(deliverIvyPattern), + Some(status), + Some(configurations), + optResolverName, + Vector.empty, + checksums, + Some(logging), + overwrite + ) def deliverPattern(outputPath: File): String = (outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath @@ -2528,14 +2631,18 @@ object Classpaths { private[sbt] def depMap: Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] = Def.taskDyn { - depMap(buildDependencies.value classpathTransitiveRefs thisProjectRef.value, - settingsData.value, - streams.value.log) + depMap( + buildDependencies.value classpathTransitiveRefs thisProjectRef.value, + settingsData.value, + streams.value.log + ) } - private[sbt] def depMap(projects: Seq[ProjectRef], - data: Settings[Scope], - log: Logger): Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] = + private[sbt] def depMap( + projects: Seq[ProjectRef], + data: Settings[Scope], + log: Logger + ): Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] = Def.value { projects.flatMap(ivyModule in _ get data).join.map { mod => mod map { _.dependencyMapping(log) } toMap; @@ -2580,14 +2687,16 @@ object Classpaths { .put(configuration.key, config) } private[this] def trackedExportedProductsImplTask( - track: TrackLevel): Initialize[Task[Seq[(File, CompileAnalysis)]]] = + track: TrackLevel + ): Initialize[Task[Seq[(File, CompileAnalysis)]]] = Def.taskDyn { val useJars = exportJars.value if (useJars) trackedJarProductsImplTask(track) else trackedNonJarProductsImplTask(track) } private[this] def trackedNonJarProductsImplTask( - track: TrackLevel): Initialize[Task[Seq[(File, CompileAnalysis)]]] = + track: TrackLevel + ): Initialize[Task[Seq[(File, CompileAnalysis)]]] = Def.taskDyn { val dirs = productDirectories.value def containsClassFile(fs: List[File]): Boolean = @@ -2609,15 +2718,18 @@ object Classpaths { Def.task { val analysisOpt = previousCompile.value.analysis.toOption dirs map { x => - (x, - if (analysisOpt.isDefined) analysisOpt.get - else Analysis.empty) + ( + x, + if (analysisOpt.isDefined) analysisOpt.get + else Analysis.empty + ) } } } } private[this] def trackedJarProductsImplTask( - track: TrackLevel): Initialize[Task[Seq[(File, CompileAnalysis)]]] = + track: TrackLevel + ): Initialize[Task[Seq[(File, CompileAnalysis)]]] = Def.taskDyn { val jar = (artifactPath in packageBin).value TrackLevel.intersection(track, exportToInternal.value) match { @@ -2633,9 +2745,11 @@ object Classpaths { Def.task { val analysisOpt = previousCompile.value.analysis.toOption Seq(jar) map { x => - (x, - if (analysisOpt.isDefined) analysisOpt.get - else Analysis.empty) + ( + x, + if (analysisOpt.isDefined) analysisOpt.get + else Analysis.empty + ) } } } @@ -2646,28 +2760,34 @@ object Classpaths { def internalDependencies: Initialize[Task[Classpath]] = Def.taskDyn { - internalDependenciesImplTask(thisProjectRef.value, - classpathConfiguration.value, - configuration.value, - settingsData.value, - buildDependencies.value, - trackInternalDependencies.value) + internalDependenciesImplTask( + thisProjectRef.value, + classpathConfiguration.value, + configuration.value, + settingsData.value, + buildDependencies.value, + trackInternalDependencies.value + ) } def internalDependencyJarsTask: Initialize[Task[Classpath]] = Def.taskDyn { - internalDependencyJarsImplTask(thisProjectRef.value, - classpathConfiguration.value, - configuration.value, - settingsData.value, - buildDependencies.value, - trackInternalDependencies.value) + internalDependencyJarsImplTask( + thisProjectRef.value, + classpathConfiguration.value, + configuration.value, + settingsData.value, + buildDependencies.value, + trackInternalDependencies.value + ) } def unmanagedDependencies: Initialize[Task[Classpath]] = Def.taskDyn { - unmanagedDependencies0(thisProjectRef.value, - configuration.value, - settingsData.value, - buildDependencies.value) + unmanagedDependencies0( + thisProjectRef.value, + configuration.value, + settingsData.value, + buildDependencies.value + ) } def mkIvyConfiguration: Initialize[Task[IvyConfiguration]] = Def.task { @@ -2688,10 +2808,12 @@ object Classpaths { import java.util.LinkedHashSet import collection.JavaConverters._ - def interSort(projectRef: ProjectRef, - conf: Configuration, - data: Settings[Scope], - deps: BuildDependencies): Seq[(ProjectRef, String)] = { + def interSort( + projectRef: ProjectRef, + conf: Configuration, + data: Settings[Scope], + deps: BuildDependencies + ): Seq[(ProjectRef, String)] = { val visited = (new LinkedHashSet[(ProjectRef, String)]).asScala def visit(p: ProjectRef, c: Configuration): Unit = { val applicableConfigs = allConfigs(c) @@ -2725,10 +2847,12 @@ object Classpaths { case (projectRef, configName) => (projectRef, ConfigRef(configName)) } - private[sbt] def unmanagedDependencies0(projectRef: ProjectRef, - conf: Configuration, - data: Settings[Scope], - deps: BuildDependencies): Initialize[Task[Classpath]] = + private[sbt] def unmanagedDependencies0( + projectRef: ProjectRef, + conf: Configuration, + data: Settings[Scope], + deps: BuildDependencies + ): Initialize[Task[Classpath]] = Def.value { interDependencies( projectRef, @@ -2741,19 +2865,23 @@ object Classpaths { (dep, conf, data, _) => unmanagedLibs(dep, conf, data), ) } - private[sbt] def internalDependenciesImplTask(projectRef: ProjectRef, - conf: Configuration, - self: Configuration, - data: Settings[Scope], - deps: BuildDependencies, - track: TrackLevel): Initialize[Task[Classpath]] = + private[sbt] def internalDependenciesImplTask( + projectRef: ProjectRef, + conf: Configuration, + self: Configuration, + data: Settings[Scope], + deps: BuildDependencies, + track: TrackLevel + ): Initialize[Task[Classpath]] = Def.value { interDependencies(projectRef, deps, conf, self, data, track, false, productsTask) } - private[sbt] def internalDependencyJarsImplTask(projectRef: ProjectRef, - conf: Configuration, - self: Configuration, - data: Settings[Scope], - deps: BuildDependencies, - track: TrackLevel): Initialize[Task[Classpath]] = + private[sbt] def internalDependencyJarsImplTask( + projectRef: ProjectRef, + conf: Configuration, + self: Configuration, + data: Settings[Scope], + deps: BuildDependencies, + track: TrackLevel + ): Initialize[Task[Classpath]] = Def.value { interDependencies(projectRef, deps, conf, self, data, track, false, jarProductsTask) } @@ -2765,7 +2893,8 @@ object Classpaths { data: Settings[Scope], track: TrackLevel, includeSelf: Boolean, - f: (ProjectRef, String, Settings[Scope], TrackLevel) => Task[Classpath]): Task[Classpath] = { + f: (ProjectRef, String, Settings[Scope], TrackLevel) => Task[Classpath] + ): Task[Classpath] = { val visited = interSort(projectRef, conf, data, deps) val tasks = (new LinkedHashSet[Task[Classpath]]).asScala for ((dep, c) <- visited) @@ -2775,23 +2904,28 @@ object Classpaths { (tasks.toSeq.join).map(_.flatten.distinct) } - def mapped(confString: Option[String], - masterConfs: Seq[String], - depConfs: Seq[String], - default: String, - defaultMapping: String): String => Seq[String] = { + def mapped( + confString: Option[String], + masterConfs: Seq[String], + depConfs: Seq[String], + default: String, + defaultMapping: String + ): String => Seq[String] = { lazy val defaultMap = parseMapping(defaultMapping, masterConfs, depConfs, _ :: Nil) parseMapping(confString getOrElse default, masterConfs, depConfs, defaultMap) } - def parseMapping(confString: String, - masterConfs: Seq[String], - depConfs: Seq[String], - default: String => Seq[String]): String => Seq[String] = + def parseMapping( + confString: String, + masterConfs: Seq[String], + depConfs: Seq[String], + default: String => Seq[String] + ): String => Seq[String] = union(confString.split(";") map parseSingleMapping(masterConfs, depConfs, default)) def parseSingleMapping( masterConfs: Seq[String], depConfs: Seq[String], - default: String => Seq[String])(confString: String): String => Seq[String] = { + default: String => Seq[String] + )(confString: String): String => Seq[String] = { val ms: Seq[(String, Seq[String])] = trim(confString.split("->", 2)) match { case x :: Nil => for (a <- parseList(x, masterConfs)) yield (a, default(a)) @@ -2826,19 +2960,23 @@ object Classpaths { ivyConfigurations in p get data getOrElse Nil def confOpt(configurations: Seq[Configuration], conf: String): Option[Configuration] = configurations.find(_.name == conf) - private[sbt] def productsTask(dep: ResolvedReference, - conf: String, - data: Settings[Scope], - track: TrackLevel): Task[Classpath] = + private[sbt] def productsTask( + dep: ResolvedReference, + conf: String, + data: Settings[Scope], + track: TrackLevel + ): Task[Classpath] = track match { case TrackLevel.NoTracking => getClasspath(exportedProductsNoTracking, dep, conf, data) case TrackLevel.TrackIfMissing => getClasspath(exportedProductsIfMissing, dep, conf, data) case TrackLevel.TrackAlways => getClasspath(exportedProducts, dep, conf, data) } - private[sbt] def jarProductsTask(dep: ResolvedReference, - conf: String, - data: Settings[Scope], - track: TrackLevel): Task[Classpath] = + private[sbt] def jarProductsTask( + dep: ResolvedReference, + conf: String, + data: Settings[Scope], + track: TrackLevel + ): Task[Classpath] = track match { case TrackLevel.NoTracking => getClasspath(exportedProductJarsNoTracking, dep, conf, data) case TrackLevel.TrackIfMissing => getClasspath(exportedProductJarsIfMissing, dep, conf, data) @@ -2848,10 +2986,12 @@ object Classpaths { 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] = + 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 = @@ -2866,10 +3006,12 @@ object Classpaths { def modifyForPlugin(plugin: Boolean, dep: ModuleID): ModuleID = if (plugin) dep.withConfigurations(Some(Provided.name)) else dep - def autoLibraryDependency(auto: Boolean, - plugin: Boolean, - org: String, - version: String): Seq[ModuleID] = + def autoLibraryDependency( + auto: Boolean, + plugin: Boolean, + org: String, + version: String + ): Seq[ModuleID] = if (auto) modifyForPlugin(plugin, ModuleID(org, ScalaArtifacts.LibraryID, version)) :: Nil else @@ -2899,10 +3041,12 @@ object Classpaths { } .distinct - def findUnmanagedJars(config: Configuration, - base: File, - filter: FileFilter, - excl: FileFilter): Classpath = + def findUnmanagedJars( + config: Configuration, + base: File, + filter: FileFilter, + excl: FileFilter + ): Classpath = (base * (filter -- excl) +++ (base / config.name).descendantsExcept(filter, excl)).classpath def autoPlugins(report: UpdateReport, internalPluginClasspath: Seq[File]): Seq[String] = { @@ -2916,12 +3060,14 @@ object Classpaths { val ref = thisProjectRef.value val data = settingsData.value val deps = buildDependencies.value - internalDependenciesImplTask(ref, - CompilerPlugin, - CompilerPlugin, - data, - deps, - TrackLevel.TrackAlways) + internalDependenciesImplTask( + ref, + CompilerPlugin, + CompilerPlugin, + data, + deps, + TrackLevel.TrackAlways + ) } lazy val compilerPluginConfig = Seq( @@ -2934,7 +3080,8 @@ object Classpaths { ) def substituteScalaFiles(scalaOrg: String, report: UpdateReport)( - scalaJars: String => Seq[File]): UpdateReport = + scalaJars: String => Seq[File] + ): UpdateReport = report.substitute { (configuration, module, arts) => if (module.organization == scalaOrg) { val jarName = module.name + ".jar" @@ -2986,11 +3133,13 @@ object Classpaths { repo match { case m: xsbti.MavenRepository => MavenRepository(m.id, m.url.toString) case i: xsbti.IvyRepository => - val patterns = Patterns(Vector(i.ivyPattern), - Vector(i.artifactPattern), - mavenCompatible(i), - descriptorOptional(i), - skipConsistencyCheck(i)) + val patterns = Patterns( + Vector(i.ivyPattern), + Vector(i.artifactPattern), + mavenCompatible(i), + descriptorOptional(i), + skipConsistencyCheck(i) + ) i.url.getProtocol match { case "file" => // This hackery is to deal suitably with UNC paths on Windows. Once we can assume Java7, Paths should save us from this. @@ -3009,7 +3158,8 @@ object Classpaths { case Predefined.SonatypeOSSSnapshots => Resolver.sonatypeRepo("snapshots") case unknown => sys.error( - "Unknown predefined resolver '" + unknown + "'. This resolver may only be supported in newer sbt versions.") + "Unknown predefined resolver '" + unknown + "'. This resolver may only be supported in newer sbt versions." + ) } } } @@ -3048,15 +3198,18 @@ trait BuildExtra extends BuildCommon with DefExtra { libraryDependencies += sbtPluginExtra( ModuleID("org.scala-sbt", "sbt-maven-resolver", sbtVersion.value), sbtBinaryVersion.value, - scalaBinaryVersion.value) + scalaBinaryVersion.value + ) /** * Adds `dependency` as an sbt plugin for the specific sbt version `sbtVersion` and Scala version `scalaVersion`. * Typically, use the default values for these versions instead of specifying them explicitly. */ - def addSbtPlugin(dependency: ModuleID, - sbtVersion: String, - scalaVersion: String): Setting[Seq[ModuleID]] = + def addSbtPlugin( + dependency: ModuleID, + sbtVersion: String, + scalaVersion: String + ): Setting[Seq[ModuleID]] = libraryDependencies += sbtPluginExtra(dependency, sbtVersion, scalaVersion) /** @@ -3095,8 +3248,10 @@ trait BuildExtra extends BuildCommon with DefExtra { } /** Constructs a setting that declares a new artifact `artifact` that is generated by `taskDef`. */ - def addArtifact(artifact: Initialize[Artifact], - taskDef: Initialize[Task[File]]): SettingsDefinition = { + def addArtifact( + artifact: Initialize[Artifact], + taskDef: Initialize[Task[File]] + ): SettingsDefinition = { val artLocal = SettingKey.local[Artifact] val taskLocal = TaskKey.local[File] val art = artifacts := artLocal.value +: artifacts.value @@ -3104,22 +3259,30 @@ trait BuildExtra extends BuildCommon with DefExtra { Seq(artLocal := artifact.value, taskLocal := taskDef.value, art, pkgd) } - def externalIvySettings(file: Initialize[File] = inBase("ivysettings.xml"), - addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = + def externalIvySettings( + file: Initialize[File] = inBase("ivysettings.xml"), + addMultiResolver: Boolean = true + ): Setting[Task[IvyConfiguration]] = externalIvySettingsURI(file(_.toURI), addMultiResolver) - def externalIvySettingsURL(url: URL, - addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = + def externalIvySettingsURL( + url: URL, + addMultiResolver: Boolean = true + ): Setting[Task[IvyConfiguration]] = externalIvySettingsURI(Def.value(url.toURI), addMultiResolver) - def externalIvySettingsURI(uri: Initialize[URI], - addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = { + def externalIvySettingsURI( + uri: Initialize[URI], + addMultiResolver: Boolean = true + ): Setting[Task[IvyConfiguration]] = { val other = Def.task { - (baseDirectory.value, - appConfiguration.value, - projectResolver.value, - updateOptions.value, - streams.value) + ( + baseDirectory.value, + appConfiguration.value, + projectResolver.value, + updateOptions.value, + streams.value + ) } ivyConfiguration := ((uri zipWith other) { case (u, otherTask) => @@ -3141,13 +3304,16 @@ trait BuildExtra extends BuildCommon with DefExtra { baseDirectory.value / name } - def externalIvyFile(file: Initialize[File] = inBase("ivy.xml"), - iScala: Initialize[Option[ScalaModuleInfo]] = scalaModuleInfo) - : Setting[Task[ModuleSettings]] = - moduleSettings := IvyFileConfiguration(ivyValidate.value, - iScala.value, - file.value, - managedScalaInstance.value) + def externalIvyFile( + file: Initialize[File] = inBase("ivy.xml"), + iScala: Initialize[Option[ScalaModuleInfo]] = scalaModuleInfo + ): Setting[Task[ModuleSettings]] = + moduleSettings := IvyFileConfiguration( + ivyValidate.value, + iScala.value, + file.value, + managedScalaInstance.value + ) def externalPom( file: Initialize[File] = inBase("pom.xml"), @@ -3160,9 +3326,11 @@ trait BuildExtra extends BuildCommon with DefExtra { managedScalaInstance.value, ) - def runInputTask(config: Configuration, - mainClass: String, - baseArguments: String*): Initialize[InputTask[Unit]] = + def runInputTask( + config: Configuration, + mainClass: String, + baseArguments: String* + ): Initialize[InputTask[Unit]] = Def.inputTask { import Def._ val r = (runner in (config, run)).value @@ -3171,9 +3339,11 @@ trait BuildExtra extends BuildCommon with DefExtra { r.run(mainClass, data(cp), baseArguments ++ args, streams.value.log).get } - def runTask(config: Configuration, - mainClass: String, - arguments: String*): Initialize[Task[Unit]] = + def runTask( + config: Configuration, + mainClass: String, + arguments: String* + ): Initialize[Task[Unit]] = Def.task { val cp = (fullClasspath in config).value val r = (runner in (config, run)).value @@ -3183,10 +3353,12 @@ trait BuildExtra extends BuildCommon with DefExtra { // public API /** Returns a vector of settings that create custom run input task. */ - def fullRunInputTask(scoped: InputKey[Unit], - config: Configuration, - mainClass: String, - baseArguments: String*): Vector[Setting[_]] = { + def fullRunInputTask( + scoped: InputKey[Unit], + config: Configuration, + mainClass: String, + baseArguments: String* + ): Vector[Setting[_]] = { // 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 @@ -3210,10 +3382,12 @@ trait BuildExtra extends BuildCommon with DefExtra { // public API /** Returns a vector of settings that create custom run task. */ - def fullRunTask(scoped: TaskKey[Unit], - config: Configuration, - mainClass: String, - arguments: String*): Vector[Setting[_]] = + def fullRunTask( + scoped: TaskKey[Unit], + config: Configuration, + mainClass: String, + arguments: String* + ): Vector[Setting[_]] = Vector( scoped := ((initScoped(scoped.scopedKey, runnerInit) .zipWith(Def.task { ((fullClasspath in config).value, streams.value) })) { @@ -3239,7 +3413,8 @@ trait BuildExtra extends BuildCommon with DefExtra { inConfig(config)(definedTests := detectTests.value).head def filterKeys(ss: Seq[Setting[_]], transitive: Boolean = false)( - f: ScopedKey[_] => Boolean): Seq[Setting[_]] = + f: ScopedKey[_] => Boolean + ): Seq[Setting[_]] = ss filter (s => f(s.key) && (!transitive || s.dependencies.forall(f))) } @@ -3295,24 +3470,29 @@ trait BuildCommon { SessionVar.get(SessionVar.resolveContext(task.scopedKey, context.scope, s), s) def loadFromContext[T](task: TaskKey[T], context: ScopedKey[_], s: State)( - implicit f: JsonFormat[T]): Option[T] = + implicit f: JsonFormat[T] + ): Option[T] = SessionVar.load(SessionVar.resolveContext(task.scopedKey, context.scope, s), s) // intended for use in constructing InputTasks - def loadForParser[P, T](task: TaskKey[T])(f: (State, Option[T]) => Parser[P])( - implicit format: JsonFormat[T]): Initialize[State => Parser[P]] = + def loadForParser[P, T](task: TaskKey[T])( + f: (State, Option[T]) => Parser[P] + )(implicit format: JsonFormat[T]): Initialize[State => Parser[P]] = loadForParserI(task)(Def value f)(format) - def loadForParserI[P, T](task: TaskKey[T])(init: Initialize[(State, Option[T]) => Parser[P]])( - implicit format: JsonFormat[T]): Initialize[State => Parser[P]] = + def loadForParserI[P, T](task: TaskKey[T])( + init: Initialize[(State, Option[T]) => Parser[P]] + )(implicit format: JsonFormat[T]): Initialize[State => Parser[P]] = Def.setting { (s: State) => init.value(s, loadFromContext(task, resolvedScoped.value, s)(format)) } - def getForParser[P, T](task: TaskKey[T])( - init: (State, Option[T]) => Parser[P]): Initialize[State => Parser[P]] = + def getForParser[P, T]( + task: TaskKey[T] + )(init: (State, Option[T]) => Parser[P]): Initialize[State => Parser[P]] = getForParserI(task)(Def value init) - def getForParserI[P, T](task: TaskKey[T])( - init: Initialize[(State, Option[T]) => Parser[P]]): Initialize[State => Parser[P]] = + def getForParserI[P, T]( + task: TaskKey[T] + )(init: Initialize[(State, Option[T]) => Parser[P]]): Initialize[State => Parser[P]] = Def.setting { (s: State) => init.value(s, getFromContext(task, resolvedScoped.value, s)) } diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index fed8b4acb..479530f15 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -172,9 +172,11 @@ object EvaluateTask { val SystemProcessors = Runtime.getRuntime.availableProcessors - def extractedTaskConfig(extracted: Extracted, - structure: BuildStructure, - state: State): EvaluateTaskConfig = { + def extractedTaskConfig( + extracted: Extracted, + structure: BuildStructure, + state: State + ): EvaluateTaskConfig = { val rs = restrictions(extracted, structure) val canceller = cancelStrategy(extracted, structure, state) val progress = executeProgress(extracted, structure, state) @@ -193,10 +195,12 @@ object EvaluateTask { } def restrictions(extracted: Extracted, structure: BuildStructure): Seq[Tags.Rule] = - getSetting(Keys.concurrentRestrictions, - defaultRestrictions(extracted, structure), - extracted, - structure) + getSetting( + Keys.concurrentRestrictions, + defaultRestrictions(extracted, structure), + extracted, + structure + ) def maxWorkers(extracted: Extracted, structure: BuildStructure): Int = if (getSetting(Keys.parallelExecution, true, extracted, structure)) @@ -207,22 +211,27 @@ object EvaluateTask { def cancelable(extracted: Extracted, structure: BuildStructure): Boolean = getSetting(Keys.cancelable, false, extracted, structure) - def cancelStrategy(extracted: Extracted, - structure: BuildStructure, - state: State): TaskCancellationStrategy = + def cancelStrategy( + extracted: Extracted, + structure: BuildStructure, + state: State + ): TaskCancellationStrategy = getSetting(Keys.taskCancelStrategy, { (_: State) => TaskCancellationStrategy.Null }, extracted, structure)(state) - private[sbt] def executeProgress(extracted: Extracted, - structure: BuildStructure, - state: State): ExecuteProgress[Task] = { + private[sbt] def executeProgress( + extracted: Extracted, + structure: BuildStructure, + state: State + ): ExecuteProgress[Task] = { import Types.const val maker: State => Keys.TaskProgress = getSetting( Keys.executeProgress, const(new Keys.TaskProgress(defaultProgress)), extracted, - structure) + structure + ) maker(state).progress } // TODO - Should this pull from Global or from the project itself? @@ -230,15 +239,19 @@ object EvaluateTask { getSetting(Keys.forcegc in Global, GCUtil.defaultForceGarbageCollection, extracted, structure) // TODO - Should this pull from Global or from the project itself? private[sbt] def minForcegcInterval(extracted: Extracted, structure: BuildStructure): Duration = - getSetting(Keys.minForcegcInterval in Global, - GCUtil.defaultMinForcegcInterval, - extracted, - structure) + getSetting( + Keys.minForcegcInterval in Global, + GCUtil.defaultMinForcegcInterval, + extracted, + structure + ) - def getSetting[T](key: SettingKey[T], - default: T, - extracted: Extracted, - structure: BuildStructure): T = + def getSetting[T]( + key: SettingKey[T], + default: T, + extracted: Extracted, + structure: BuildStructure + ): T = key in extracted.currentRef get structure.data getOrElse default def injectSettings: Seq[Setting[_]] = Seq( @@ -258,7 +271,8 @@ object EvaluateTask { val evaluated = apply(pluginDef, ScopedKey(pluginKey.scope, pluginKey.key), state, root, config) val (newS, result) = evaluated getOrElse sys.error( - "Plugin data does not exist for plugin definition at " + pluginDef.root) + "Plugin data does not exist for plugin definition at " + pluginDef.root + ) Project.runUnloadHooks(newS) // discard states processResult2(result) } @@ -268,26 +282,32 @@ object EvaluateTask { * If the task is not defined, None is returned. The provided task key is resolved against the current project `ref`. * Task execution is configured according to settings defined in the loaded project. */ - def apply[T](structure: BuildStructure, - taskKey: ScopedKey[Task[T]], - state: State, - ref: ProjectRef): Option[(State, Result[T])] = - apply[T](structure, - taskKey, - state, - ref, - extractedTaskConfig(Project.extract(state), structure, state)) + def apply[T]( + structure: BuildStructure, + taskKey: ScopedKey[Task[T]], + state: State, + ref: ProjectRef + ): Option[(State, Result[T])] = + apply[T]( + structure, + taskKey, + state, + ref, + extractedTaskConfig(Project.extract(state), structure, state) + ) /** * Evaluates `taskKey` and returns the new State and the result of the task wrapped in Some. * If the task is not defined, None is returned. The provided task key is resolved against the current project `ref`. * `config` configures concurrency and canceling of task execution. */ - def apply[T](structure: BuildStructure, - taskKey: ScopedKey[Task[T]], - state: State, - ref: ProjectRef, - config: EvaluateTaskConfig): Option[(State, Result[T])] = { + def apply[T]( + structure: BuildStructure, + taskKey: ScopedKey[Task[T]], + state: State, + ref: ProjectRef, + config: EvaluateTaskConfig + ): Option[(State, Result[T])] = { withStreams(structure, state) { str => for ((task, toNode) <- getTask(structure, taskKey, state, str, ref)) yield runTask(task, state, str, structure.index.triggers, config)(toNode) @@ -335,34 +355,41 @@ object EvaluateTask { try { f(str) } finally { str.close() } } - def getTask[T](structure: BuildStructure, - taskKey: ScopedKey[Task[T]], - state: State, - streams: Streams, - ref: ProjectRef): Option[(Task[T], NodeView[Task])] = { + def getTask[T]( + structure: BuildStructure, + taskKey: ScopedKey[Task[T]], + state: State, + streams: Streams, + ref: ProjectRef + ): Option[(Task[T], NodeView[Task])] = { val thisScope = Load.projectScope(ref) val resolvedScope = Scope.replaceThis(thisScope)(taskKey.scope) for (t <- structure.data.get(resolvedScope, taskKey.key)) yield (t, nodeView(state, streams, taskKey :: Nil)) } - def nodeView[HL <: HList](state: State, - streams: Streams, - roots: Seq[ScopedKey[_]], - dummies: DummyTaskMap = DummyTaskMap(Nil)): NodeView[Task] = + def nodeView[HL <: HList]( + state: State, + streams: Streams, + roots: Seq[ScopedKey[_]], + dummies: DummyTaskMap = DummyTaskMap(Nil) + ): NodeView[Task] = Transform( - (dummyRoots, roots) :: (Def.dummyStreamsManager, streams) :: (dummyState, state) :: dummies) + (dummyRoots, roots) :: (Def.dummyStreamsManager, streams) :: (dummyState, state) :: dummies + ) def runTask[T]( root: Task[T], state: State, streams: Streams, triggers: Triggers[Task], - config: EvaluateTaskConfig)(implicit taskToNode: NodeView[Task]): (State, Result[T]) = { + config: EvaluateTaskConfig + )(implicit taskToNode: NodeView[Task]): (State, Result[T]) = { import ConcurrentRestrictions.{ completionService, tagged, tagsKey } val log = state.log log.debug( - s"Running task... Cancel: ${config.cancelStrategy}, check cycles: ${config.checkCycles}, forcegc: ${config.forceGarbageCollection}") + s"Running task... Cancel: ${config.cancelStrategy}, check cycles: ${config.checkCycles}, forcegc: ${config.forceGarbageCollection}" + ) val tags = tagged[Task[_]](_.info get tagsKey getOrElse Map.empty, Tags.predicate(config.restrictions)) val (service, shutdownThreads) = @@ -383,9 +410,11 @@ object EvaluateTask { case _ => true } def run() = { - val x = new Execute[Task](Execute.config(config.checkCycles, overwriteNode), - triggers, - config.progressReporter)(taskToNode) + val x = new Execute[Task]( + Execute.config(config.checkCycles, overwriteNode), + triggers, + config.progressReporter + )(taskToNode) val (newState, result) = try { val results = x.runKeep(root)(service) @@ -410,15 +439,19 @@ object EvaluateTask { finally strat.onTaskEngineFinish(cancelState) } - private[this] def storeValuesForPrevious(results: RMap[Task, Result], - state: State, - streams: Streams): Unit = + private[this] def storeValuesForPrevious( + results: RMap[Task, Result], + state: State, + streams: Streams + ): Unit = for (referenced <- Previous.references in Global get Project.structure(state).data) Previous.complete(referenced, results, streams) - def applyResults[T](results: RMap[Task, Result], - state: State, - root: Task[T]): (State, Result[T]) = + def applyResults[T]( + results: RMap[Task, Result], + state: State, + root: Task[T] + ): (State, Result[T]) = (stateTransform(results)(state), results(root)) def stateTransform(results: RMap[Task, Result]): State => State = Function.chain( diff --git a/main/src/main/scala/sbt/Extracted.scala b/main/src/main/scala/sbt/Extracted.scala index c35b6021d..20c2115a1 100644 --- a/main/src/main/scala/sbt/Extracted.scala +++ b/main/src/main/scala/sbt/Extracted.scala @@ -16,9 +16,11 @@ import sbt.util.Show import std.Transform.DummyTaskMap import sbt.EvaluateTask.extractedTaskConfig -final case class Extracted(structure: BuildStructure, - session: SessionSettings, - currentRef: ProjectRef)(implicit val showKey: Show[ScopedKey[_]]) { +final case class Extracted( + structure: BuildStructure, + session: SessionSettings, + currentRef: ProjectRef +)(implicit val showKey: Show[ScopedKey[_]]) { def rootProject = structure.rootProject lazy val currentUnit = structure units currentRef.build lazy val currentProject = currentUnit defined currentRef.project @@ -123,7 +125,8 @@ final case class Extracted(structure: BuildStructure, @deprecated( "This discards session settings. Migrate to appendWithSession or appendWithoutSession.", - "1.2.0") + "1.2.0" + ) def append(settings: Seq[Setting[_]], state: State): State = appendWithoutSession(settings, state) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index c19d44c40..ac1ad03be 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -69,7 +69,8 @@ final class xMain extends xsbti.AppMain { val state = StandardMain.initialState( configuration, Seq(defaults, early), - runEarly(DefaultsCommand) :: runEarly(InitCommand) :: BootCommand :: Nil) + runEarly(DefaultsCommand) :: runEarly(InitCommand) :: BootCommand :: Nil + ) StandardMain.runManaged(state) } } @@ -119,13 +120,17 @@ object StandardMain { ConsoleOut.systemOutOverwrite(ConsoleOut.overwriteContaining("Resolving ")) def initialGlobalLogging: GlobalLogging = - GlobalLogging.initial(MainAppender.globalDefault(console), - File.createTempFile("sbt", ".log"), - console) + GlobalLogging.initial( + MainAppender.globalDefault(console), + File.createTempFile("sbt", ".log"), + console + ) - def initialState(configuration: xsbti.AppConfiguration, - initialDefinitions: Seq[Command], - preCommands: Seq[String]): State = { + def initialState( + configuration: xsbti.AppConfiguration, + initialDefinitions: Seq[Command], + preCommands: Seq[String] + ): State = { // This is to workaround https://github.com/sbt/io/issues/110 sys.props.put("jna.nosys", "true") @@ -277,21 +282,27 @@ object BuiltinCommands { catch { case _: Exception => None } def settingsCommand: Command = - showSettingLike(SettingsCommand, - settingsPreamble, - KeyRanks.MainSettingCutoff, - key => !isTask(key.manifest)) + showSettingLike( + SettingsCommand, + settingsPreamble, + KeyRanks.MainSettingCutoff, + key => !isTask(key.manifest) + ) def tasks: Command = - showSettingLike(TasksCommand, - tasksPreamble, - KeyRanks.MainTaskCutoff, - key => isTask(key.manifest)) + showSettingLike( + TasksCommand, + tasksPreamble, + KeyRanks.MainTaskCutoff, + key => isTask(key.manifest) + ) - def showSettingLike(command: String, - preamble: String, - cutoff: Int, - keep: AttributeKey[_] => Boolean): Command = + def showSettingLike( + command: String, + preamble: String, + cutoff: Int, + keep: AttributeKey[_] => Boolean + ): Command = Command(command, settingsBrief(command), settingsDetailed(command))(showSettingParser(keep)) { case (s: State, (verbosity: Int, selected: Option[String])) => if (selected.isEmpty) System.out.println(preamble) @@ -302,8 +313,9 @@ object BuiltinCommands { if (prominentOnly) System.out.println(moreAvailableMessage(command, selected.isDefined)) s } - def showSettingParser(keepKeys: AttributeKey[_] => Boolean)( - s: State): Parser[(Int, Option[String])] = + def showSettingParser( + keepKeys: AttributeKey[_] => Boolean + )(s: State): Parser[(Int, Option[String])] = verbosityParser ~ selectedParser(s, keepKeys).? def selectedParser(s: State, keepKeys: AttributeKey[_] => Boolean): Parser[String] = singleArgument(allTaskAndSettingKeys(s).filter(keepKeys).map(_.label).toSet) @@ -338,16 +350,19 @@ object BuiltinCommands { def sortByRank(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] = keys.sortBy(_.rank) def withDescription(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] = keys.filter(_.description.isDefined) - def isTask(mf: Manifest[_])(implicit taskMF: Manifest[Task[_]], - inputMF: Manifest[InputTask[_]]): Boolean = + def isTask( + mf: Manifest[_] + )(implicit taskMF: Manifest[Task[_]], inputMF: Manifest[InputTask[_]]): Boolean = mf.runtimeClass == taskMF.runtimeClass || mf.runtimeClass == inputMF.runtimeClass def topNRanked(n: Int) = (keys: Seq[AttributeKey[_]]) => sortByRank(keys).take(n) def highPass(rankCutoff: Int) = (keys: Seq[AttributeKey[_]]) => sortByRank(keys).takeWhile(_.rank <= rankCutoff) - def tasksHelp(s: State, - filter: Seq[AttributeKey[_]] => Seq[AttributeKey[_]], - arg: Option[String]): String = { + def tasksHelp( + s: State, + filter: Seq[AttributeKey[_]] => Seq[AttributeKey[_]], + arg: Option[String] + ): String = { val commandAndDescription = taskDetail(filter(allTaskAndSettingKeys(s))) arg match { case Some(selected) => detail(selected, commandAndDescription.toMap) @@ -618,8 +633,9 @@ object BuiltinCommands { } def projects: Command = - Command(ProjectsCommand, (ProjectsCommand, projectsBrief), projectsDetailed)(s => - projectsParser(s).?) { + Command(ProjectsCommand, (ProjectsCommand, projectsBrief), projectsDetailed)( + s => projectsParser(s).? + ) { case (s, Some(modifyBuilds)) => transformExtraBuilds(s, modifyBuilds) case (s, None) => showProjects(s); s } @@ -658,7 +674,8 @@ object BuiltinCommands { @tailrec private[this] def doLoadFailed(s: State, loadArg: String): State = { val result = (SimpleReader.readLine( - "Project loading failed: (r)etry, (q)uit, (l)ast, or (i)gnore? ") getOrElse Quit) + "Project loading failed: (r)etry, (q)uit, (l)ast, or (i)gnore? " + ) getOrElse Quit) .toLowerCase(Locale.ENGLISH) def matches(s: String) = !result.isEmpty && (s startsWith result) def retry = loadProjectCommand(LoadProject, loadArg) :: s.clearGlobalLog @@ -684,8 +701,9 @@ object BuiltinCommands { Nil def loadProject: Command = - Command(LoadProject, LoadProjectBrief, LoadProjectDetailed)(loadProjectParser)((s, arg) => - loadProjectCommands(arg) ::: s) + Command(LoadProject, LoadProjectBrief, LoadProjectDetailed)(loadProjectParser)( + (s, arg) => loadProjectCommands(arg) ::: s + ) private[this] def loadProjectParser: State => Parser[String] = _ => matched(Project.loadActionParser) @@ -707,11 +725,13 @@ object BuiltinCommands { Option(buildProperties.getProperty("sbt.version")) } else None - sbtVersionOpt.foreach(version => - if (version != app.id.version()) { - state.log.warn(s"""sbt version mismatch, current: ${app.id - .version()}, in build.properties: "$version", use 'reboot' to use the new value.""") - }) + sbtVersionOpt.foreach( + version => + if (version != app.id.version()) { + state.log.warn(s"""sbt version mismatch, current: ${app.id + .version()}, in build.properties: "$version", use 'reboot' to use the new value.""") + } + ) } def doLoadProject(s0: State, action: LoadAction.Value): State = { @@ -758,8 +778,10 @@ object BuiltinCommands { exchange publishEventMessage ConsolePromptEvent(s0) val exec: Exec = exchange.blockUntilNextExec val newState = s1 - .copy(onFailure = Some(Exec(Shell, None)), - remainingCommands = exec +: Exec(Shell, None) +: s1.remainingCommands) + .copy( + onFailure = Some(Exec(Shell, None)), + remainingCommands = exec +: Exec(Shell, None) +: s1.remainingCommands + ) .setInteractive(true) exchange publishEventMessage ConsoleUnpromptEvent(exec.source) if (exec.commandLine.trim.isEmpty) newState diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index e2c1fe0f2..3b007ced4 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -67,7 +67,8 @@ object MainLoop { throw new xsbti.FullReload(e.arguments.toArray, false) case NonFatal(e) => System.err.println( - "sbt appears to be exiting abnormally.\n The log file for this session is at " + logBacking.file) + "sbt appears to be exiting abnormally.\n The log file for this session is at " + logBacking.file + ) deleteLastLog(logBacking) throw e } diff --git a/main/src/main/scala/sbt/Opts.scala b/main/src/main/scala/sbt/Opts.scala index 2d6ac2c6c..e788b027e 100644 --- a/main/src/main/scala/sbt/Opts.scala +++ b/main/src/main/scala/sbt/Opts.scala @@ -45,9 +45,11 @@ object Opts { val sonatypeSnapshots = Resolver.sonatypeRepo("snapshots") val sonatypeStaging = MavenRepository( "sonatype-staging", - "https://oss.sonatype.org/service/local/staging/deploy/maven2") + "https://oss.sonatype.org/service/local/staging/deploy/maven2" + ) val mavenLocalFile = Resolver.file("Local Repository", userHome / ".m2" / "repository" asFile)( - Resolver.defaultPatterns) + Resolver.defaultPatterns + ) val sbtSnapshots = Resolver.bintrayRepo("sbt", "maven-snapshots") val sbtIvySnapshots = Resolver.bintrayIvyRepo("sbt", "ivy-snapshots") } diff --git a/main/src/main/scala/sbt/PluginCross.scala b/main/src/main/scala/sbt/PluginCross.scala index 0da74dc7b..3e3fd4bba 100644 --- a/main/src/main/scala/sbt/PluginCross.scala +++ b/main/src/main/scala/sbt/PluginCross.scala @@ -25,7 +25,8 @@ private[sbt] object PluginCross { lazy val pluginSwitch: Command = { def switchParser(state: State): Parser[(String, String)] = { lazy val switchArgs = token(NotSpace.examples()) ~ (token( - Space ~> matched(state.combinedParser)) ?? "") + Space ~> matched(state.combinedParser) + ) ?? "") lazy val nextSpaced = spacedFirst(PluginSwitchCommand) token(PluginSwitchCommand ~ OptSpace) flatMap { _ => switchArgs & nextSpaced @@ -58,8 +59,11 @@ private[sbt] object PluginCross { def crossParser(state: State): Parser[String] = token(PluginCrossCommand <~ OptSpace) flatMap { _ => token( - matched(state.combinedParser & - spacedFirst(PluginCrossCommand))) + matched( + state.combinedParser & + spacedFirst(PluginCrossCommand) + ) + ) } def crossVersions(state: State): List[String] = { val x = Project.extract(state) diff --git a/main/src/main/scala/sbt/Plugins.scala b/main/src/main/scala/sbt/Plugins.scala index a0aa1ee2a..7df9f0561 100644 --- a/main/src/main/scala/sbt/Plugins.scala +++ b/main/src/main/scala/sbt/Plugins.scala @@ -202,10 +202,12 @@ object Plugins extends PluginsFunctions { _.head subsetOf knowledge0 }) log.debug( - s"deducing auto plugins based on known facts ${knowledge0.toString} and clauses ${clauses.toString}") + s"deducing auto plugins based on known facts ${knowledge0.toString} and clauses ${clauses.toString}" + ) Logic.reduce( clauses, - (flattenConvert(requestedPlugins) ++ convertAll(alwaysEnabled)).toSet) match { + (flattenConvert(requestedPlugins) ++ convertAll(alwaysEnabled)).toSet + ) match { case Left(problem) => throw AutoPluginException(problem) case Right(results) => log.debug(s" :: deduced result: ${results}") @@ -234,9 +236,11 @@ object Plugins extends PluginsFunctions { private[sbt] def topologicalSort(ns: List[AutoPlugin]): List[AutoPlugin] = { @tailrec - def doSort(found0: List[AutoPlugin], - notFound0: List[AutoPlugin], - limit0: Int): List[AutoPlugin] = { + def doSort( + found0: List[AutoPlugin], + notFound0: List[AutoPlugin], + limit0: Int + ): List[AutoPlugin] = { if (limit0 < 0) throw AutoPluginException(s"Failed to sort ${ns} topologically") else if (notFound0.isEmpty) found0 else { @@ -252,11 +256,9 @@ object Plugins extends PluginsFunctions { private[sbt] def translateMessage(e: LogicException) = e match { case ic: InitialContradictions => - s"Contradiction in selected plugins. These plugins were both included and excluded: ${literalsString( - ic.literals.toSeq)}" + s"Contradiction in selected plugins. These plugins were both included and excluded: ${literalsString(ic.literals.toSeq)}" case io: InitialOverlap => - s"Cannot directly enable plugins. Plugins are enabled when their required plugins are satisfied. The directly selected plugins were: ${literalsString( - io.literals.toSeq)}" + s"Cannot directly enable plugins. Plugins are enabled when their required plugins are satisfied. The directly selected plugins were: ${literalsString(io.literals.toSeq)}" case cn: CyclicNegation => s"Cycles in plugin requirements cannot involve excludes. The problematic cycle is: ${literalsString(cn.cycle)}" } @@ -273,9 +275,11 @@ object Plugins extends PluginsFunctions { throw AutoPluginException(message) } - private[this] def exclusionConflictError(requested: Plugins, - selected: Seq[AutoPlugin], - conflicting: Seq[AutoPlugin]): Unit = { + private[this] def exclusionConflictError( + requested: Plugins, + selected: Seq[AutoPlugin], + conflicting: Seq[AutoPlugin] + ): Unit = { def listConflicts(ns: Seq[AutoPlugin]) = (ns map { c => val reasons = (if (flatten(requested) contains c) List("requested") @@ -427,8 +431,9 @@ ${listConflicts(conflicting)}""") val pluginClazz = ap.getClass existsAutoImportVal(pluginClazz) .orElse( - catching(classOf[ClassNotFoundException]).opt( - Class.forName(s"${pluginClazz.getName}$autoImport$$", false, loader))) + catching(classOf[ClassNotFoundException]) + .opt(Class.forName(s"${pluginClazz.getName}$autoImport$$", false, loader)) + ) .isDefined } diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 8657f119f..e78a47f14 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -482,7 +482,8 @@ object Project extends ProjectExtra { val newState = unloaded.copy(attributes = newAttrs) // TODO: Fix this onLoad( - updateCurrent(newState) /*LogManager.setGlobalLogLevels(updateCurrent(newState), structure.data)*/ ) + updateCurrent(newState) /*LogManager.setGlobalLogLevels(updateCurrent(newState), structure.data)*/ + ) } def orIdentity[T](opt: Option[T => T]): T => T = opt getOrElse idFun @@ -517,8 +518,10 @@ object Project extends ProjectExtra { 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) + val newDefinedCommands = commandDefs ++ BasicCommands.removeTagged( + s.definedCommands, + projectCommand + ) val newAttrs = s.attributes .setCond(Watched.Configuration, watched) @@ -555,7 +558,8 @@ object Project extends ProjectExtra { } } private[this] def overlappingTargets( - targets: Seq[(ProjectRef, File)]): Map[File, Seq[ProjectRef]] = + targets: Seq[(ProjectRef, File)] + ): Map[File, Seq[ProjectRef]] = targets.groupBy(_._2).filter(_._2.size > 1).mapValues(_.map(_._1)) private[this] def allTargets(data: Settings[Scope]): Seq[(ProjectRef, File)] = { @@ -588,15 +592,18 @@ object Project extends ProjectExtra { def delegates(structure: BuildStructure, scope: Scope, key: AttributeKey[_]): Seq[ScopedKey[_]] = structure.delegates(scope).map(d => ScopedKey(d, key)) - def scopedKeyData(structure: BuildStructure, - scope: Scope, - key: AttributeKey[_]): Option[ScopedKeyData[_]] = + def scopedKeyData( + structure: BuildStructure, + scope: Scope, + key: AttributeKey[_] + ): Option[ScopedKeyData[_]] = structure.data.get(scope, key) map { v => ScopedKeyData(ScopedKey(scope, key), v) } def details(structure: BuildStructure, actual: Boolean, scope: Scope, key: AttributeKey[_])( - implicit display: Show[ScopedKey[_]]): String = { + implicit display: Show[ScopedKey[_]] + ): String = { val scoped = ScopedKey(scope, key) val data = scopedKeyData(structure, scope, key) map { _.description } getOrElse { @@ -637,20 +644,24 @@ object Project extends ProjectExtra { val reverse = reverseDependencies(cMap, scoped) val derivedReverse = reverse.filter(r => derivedDependencies(r).contains(definingScoped)).toSet - def printDepScopes(baseLabel: String, - derivedLabel: String, - scopes: Iterable[ScopedKey[_]], - derived: Set[ScopedKey[_]]): String = { + def printDepScopes( + baseLabel: String, + derivedLabel: String, + scopes: Iterable[ScopedKey[_]], + derived: Set[ScopedKey[_]] + ): String = { val label = s"$baseLabel${if (derived.isEmpty) "" else s" (D=$derivedLabel)"}" val prefix: ScopedKey[_] => String = if (derived.isEmpty) const("") else sk => if (derived(sk)) "D " else " " printScopes(label, scopes, prefix = prefix) } - def printScopes(label: String, - scopes: Iterable[ScopedKey[_]], - max: Int = Int.MaxValue, - prefix: ScopedKey[_] => String = const("")) = + def printScopes( + label: String, + scopes: Iterable[ScopedKey[_]], + max: Int = Int.MaxValue, + prefix: ScopedKey[_] => String = const("") + ) = if (scopes.isEmpty) "" else { val (limited, more) = @@ -668,23 +679,27 @@ object Project extends ProjectExtra { printScopes("Related", related, 10) } def settingGraph(structure: BuildStructure, basedir: File, scoped: ScopedKey[_])( - implicit display: Show[ScopedKey[_]]): SettingGraph = + implicit display: Show[ScopedKey[_]] + ): SettingGraph = SettingGraph(structure, basedir, scoped, 0) def graphSettings(structure: BuildStructure, basedir: File)( - implicit display: Show[ScopedKey[_]]): Unit = { + implicit display: Show[ScopedKey[_]] + ): Unit = { def graph(actual: Boolean, name: String) = graphSettings(structure, actual, name, new File(basedir, name + ".dot")) graph(true, "actual_dependencies") graph(false, "declared_dependencies") } def graphSettings(structure: BuildStructure, actual: Boolean, graphName: String, file: File)( - implicit display: Show[ScopedKey[_]]): Unit = { + implicit display: Show[ScopedKey[_]] + ): Unit = { val rel = relation(structure, actual) val keyToString = display.show _ DotGraph.generateGraph(file, graphName, rel, keyToString, keyToString) } def relation(structure: BuildStructure, actual: Boolean)( - implicit display: Show[ScopedKey[_]]): Relation[ScopedKey[_], ScopedKey[_]] = + implicit display: Show[ScopedKey[_]] + ): Relation[ScopedKey[_], ScopedKey[_]] = relation(structure.settings, actual)(structure.delegates, structure.scopeLocal, display) private[sbt] def relation(settings: Seq[Def.Setting[_]], actual: Boolean)( @@ -698,7 +713,8 @@ object Project extends ProjectExtra { } def showDefinitions(key: AttributeKey[_], defs: Seq[Scope])( - implicit display: Show[ScopedKey[_]]): String = + implicit display: Show[ScopedKey[_]] + ): String = showKeys(defs.map(scope => ScopedKey(scope, key))) def showUses(defs: Seq[ScopedKey[_]])(implicit display: Show[ScopedKey[_]]): String = @@ -708,17 +724,21 @@ object Project extends ProjectExtra { s.map(display.show).sorted.mkString("\n\t", "\n\t", "\n\n") def definitions(structure: BuildStructure, actual: Boolean, key: AttributeKey[_])( - implicit display: Show[ScopedKey[_]]): Seq[Scope] = + implicit display: Show[ScopedKey[_]] + ): Seq[Scope] = relation(structure, actual)(display)._1s.toSeq flatMap { sk => if (sk.key == key) sk.scope :: Nil else Nil } def usedBy(structure: BuildStructure, actual: Boolean, key: AttributeKey[_])( - implicit display: Show[ScopedKey[_]]): Seq[ScopedKey[_]] = + implicit display: Show[ScopedKey[_]] + ): Seq[ScopedKey[_]] = relation(structure, actual)(display).all.toSeq flatMap { case (a, b) => if (b.key == key) List[ScopedKey[_]](a) else Nil } - def reverseDependencies(cMap: Map[ScopedKey[_], Flattened], - scoped: ScopedKey[_]): Iterable[ScopedKey[_]] = + def reverseDependencies( + cMap: Map[ScopedKey[_], Flattened], + scoped: ScopedKey[_] + ): Iterable[ScopedKey[_]] = for ((key, compiled) <- cMap; dep <- compiled.dependencies if dep == scoped) yield key def setAll(extracted: Extracted, settings: Seq[Def.Setting[_]]): SessionSettings = @@ -726,7 +746,8 @@ object Project extends ProjectExtra { val ExtraBuilds = AttributeKey[List[URI]]( "extra-builds", - "Extra build URIs to load in addition to the ones defined by the project.") + "Extra build URIs to load in addition to the ones defined by the project." + ) def extraBuilds(s: State): List[URI] = getOrNil(s, ExtraBuilds) def getOrNil[T](s: State, key: AttributeKey[List[T]]): List[T] = s get key getOrElse Nil def setExtraBuilds(s: State, extra: List[URI]): State = s.put(ExtraBuilds, extra) @@ -812,15 +833,20 @@ object Project extends ProjectExtra { import TupleSyntax._ (Keys.resolvedScoped, i)( (scoped, task) => - tx(task, - (state, value) => - persistAndSet(resolveContext(key, scoped.scope, state), state, value)(f))) + tx( + task, + (state, value) => + persistAndSet(resolveContext(key, scoped.scope, state), state, value)(f) + ) + ) } def keepAs(key: TaskKey[S]): Def.Initialize[Task[S]] = { import TupleSyntax._ - (i, Keys.resolvedScoped)((t, scoped) => - tx(t, (state, value) => set(resolveContext(key, scoped.scope, state), state, value))) + (i, Keys.resolvedScoped)( + (t, scoped) => + tx(t, (state, value) => set(resolveContext(key, scoped.scope, state), state, value)) + ) } } @@ -831,7 +857,8 @@ object Project extends ProjectExtra { val enclosingValName = std.KeyMacro.definingValName( c, methodName => - s"""$methodName must be directly assigned to a val, such as `val x = $methodName`. Alternatively, you can use `sbt.Project.apply`""") + s"""$methodName must be directly assigned to a val, such as `val x = $methodName`. Alternatively, you can use `sbt.Project.apply`""" + ) val name = c.Expr[String](Literal(Constant(enclosingValName))) reify { Project(name.splice, new File(name.splice)) } } @@ -840,8 +867,9 @@ object Project extends ProjectExtra { private[sbt] trait GeneratedRootProject trait ProjectExtra { - implicit def configDependencyConstructor[T](p: T)( - implicit ev: T => ProjectReference): Constructor = + implicit def configDependencyConstructor[T]( + p: T + )(implicit ev: T => ProjectReference): Constructor = new Constructor(p) implicit def classpathDependency[T]( @@ -854,7 +882,8 @@ trait ProjectExtra { new Scoped.RichInitializeTask(init) implicit def richInitializeInputTask[T]( - init: Initialize[InputTask[T]]): Scoped.RichInitializeInputTask[T] = + init: Initialize[InputTask[T]] + ): Scoped.RichInitializeInputTask[T] = new Scoped.RichInitializeInputTask(init) implicit def richInitialize[T](i: Initialize[T]): Scoped.RichInitialize[T] = diff --git a/main/src/main/scala/sbt/RichURI.scala b/main/src/main/scala/sbt/RichURI.scala index 78a06372d..2e54ca65d 100644 --- a/main/src/main/scala/sbt/RichURI.scala +++ b/main/src/main/scala/sbt/RichURI.scala @@ -17,13 +17,15 @@ class RichURI(uri: URI) { * Note that this method simply passes the individual components of this URI to the URI constructor * that accepts each component individually. It is thus limited by the implementation restrictions of the relevant methods. */ - def copy(scheme: String = uri.getScheme, - userInfo: String = uri.getUserInfo, - host: String = uri.getHost, - port: Int = uri.getPort, - path: String = uri.getPath, - query: String = uri.getQuery, - fragment: String = uri.getFragment) = + def copy( + scheme: String = uri.getScheme, + userInfo: String = uri.getUserInfo, + host: String = uri.getHost, + port: Int = uri.getPort, + path: String = uri.getPath, + query: String = uri.getQuery, + fragment: String = uri.getFragment + ) = new URI(scheme, userInfo, host, port, path, query, fragment) /** Returns `true` if the fragment of the URI is defined. */ diff --git a/main/src/main/scala/sbt/ScopeFilter.scala b/main/src/main/scala/sbt/ScopeFilter.scala index 4ce8aca12..fea70d23e 100644 --- a/main/src/main/scala/sbt/ScopeFilter.scala +++ b/main/src/main/scala/sbt/ScopeFilter.scala @@ -30,9 +30,11 @@ object ScopeFilter { * If a task filter is not supplied, global is selected. * Generally, always specify the project axis. */ - def apply(projects: ProjectFilter = inProjects(ThisProject), - configurations: ConfigurationFilter = zeroAxis, - tasks: TaskFilter = zeroAxis): ScopeFilter = + def apply( + projects: ProjectFilter = inProjects(ThisProject), + configurations: ConfigurationFilter = zeroAxis, + tasks: TaskFilter = zeroAxis + ): ScopeFilter = new ScopeFilter { private[sbt] def apply(data: Data): Scope => Boolean = { val pf = projects(data) @@ -116,27 +118,35 @@ object ScopeFilter { * Selects Scopes that have a project axis that is aggregated by `ref`, transitively if `transitive` is true. * If `includeRoot` is true, Scopes with `ref` itself as the project axis value are also selected. */ - def inAggregates(ref: ProjectReference, - transitive: Boolean = true, - includeRoot: Boolean = true): ProjectFilter = - byDeps(ref, - transitive = transitive, - includeRoot = includeRoot, - aggregate = true, - classpath = false) + def inAggregates( + ref: ProjectReference, + transitive: Boolean = true, + includeRoot: Boolean = true + ): ProjectFilter = + byDeps( + ref, + transitive = transitive, + includeRoot = includeRoot, + aggregate = true, + classpath = false + ) /** * Selects Scopes that have a project axis that is a dependency of `ref`, transitively if `transitive` is true. * If `includeRoot` is true, Scopes with `ref` itself as the project axis value are also selected. */ - def inDependencies(ref: ProjectReference, - transitive: Boolean = true, - includeRoot: Boolean = true): ProjectFilter = - byDeps(ref, - transitive = transitive, - includeRoot = includeRoot, - aggregate = false, - classpath = true) + def inDependencies( + ref: ProjectReference, + transitive: Boolean = true, + includeRoot: Boolean = true + ): ProjectFilter = + byDeps( + ref, + transitive = transitive, + includeRoot = includeRoot, + aggregate = false, + classpath = true + ) /** Selects Scopes that have a project axis with one of the provided values.*/ def inProjects(projects: ProjectReference*): ProjectFilter = @@ -172,9 +182,11 @@ object ScopeFilter { * Information provided to Scope filters. These provide project relationships, * project reference resolution, and the list of all static Scopes. */ - private final class Data(val units: Map[URI, LoadedBuildUnit], - val resolve: ProjectReference => ProjectRef, - val allScopes: Set[Scope]) + private final class Data( + val units: Map[URI, LoadedBuildUnit], + val resolve: ProjectReference => ProjectRef, + val allScopes: Set[Scope] + ) /** Constructs a Data instance from the list of static scopes and the project relationships.*/ private[this] val getData: Initialize[Data] = @@ -195,20 +207,24 @@ object ScopeFilter { new Data(build.units, resolve, scopes) } - private[this] def getDependencies(structure: Map[URI, LoadedBuildUnit], - classpath: Boolean, - aggregate: Boolean): ProjectRef => Seq[ProjectRef] = + private[this] def getDependencies( + structure: Map[URI, LoadedBuildUnit], + classpath: Boolean, + aggregate: Boolean + ): ProjectRef => Seq[ProjectRef] = ref => Project.getProject(ref, structure).toList flatMap { p => (if (classpath) p.dependencies.map(_.project) else Nil) ++ (if (aggregate) p.aggregate else Nil) } - private[this] def byDeps(ref: ProjectReference, - transitive: Boolean, - includeRoot: Boolean, - aggregate: Boolean, - classpath: Boolean): ProjectFilter = + private[this] def byDeps( + ref: ProjectReference, + transitive: Boolean, + includeRoot: Boolean, + aggregate: Boolean, + classpath: Boolean + ): ProjectFilter = inResolvedProjects { data => val resolvedRef = data.resolve(ref) val direct = getDependencies(data.units, classpath = classpath, aggregate = aggregate) diff --git a/main/src/main/scala/sbt/ScopedKeyData.scala b/main/src/main/scala/sbt/ScopedKeyData.scala index 3574f455d..abfabb2f4 100644 --- a/main/src/main/scala/sbt/ScopedKeyData.scala +++ b/main/src/main/scala/sbt/ScopedKeyData.scala @@ -16,9 +16,11 @@ final case class ScopedKeyData[A](scoped: ScopedKey[A], value: Any) { def typeName: String = fold(fmtMf("Task[%s]"), fmtMf("InputTask[%s]"), key.manifest.toString) def settingValue: Option[Any] = fold(const(None), const(None), Some(value)) def description: String = - fold(fmtMf("Task: %s"), - fmtMf("Input task: %s"), - "Setting: %s = %s" format (key.manifest.toString, value.toString)) + fold( + fmtMf("Task: %s"), + fmtMf("Input task: %s"), + "Setting: %s = %s" format (key.manifest.toString, value.toString) + ) def fold[T](targ: OptManifest[_] => T, itarg: OptManifest[_] => T, s: => T): T = key.manifest.runtimeClass match { case TaskClass => targ(key.manifest.typeArguments.head) diff --git a/main/src/main/scala/sbt/ScriptedPlugin.scala b/main/src/main/scala/sbt/ScriptedPlugin.scala index 6e2544426..f1dfd3434 100644 --- a/main/src/main/scala/sbt/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/ScriptedPlugin.scala @@ -39,7 +39,8 @@ object ScriptedPlugin extends AutoPlugin { 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.") + "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") diff --git a/main/src/main/scala/sbt/SessionVar.scala b/main/src/main/scala/sbt/SessionVar.scala index 58ec38b15..da5e41213 100644 --- a/main/src/main/scala/sbt/SessionVar.scala +++ b/main/src/main/scala/sbt/SessionVar.scala @@ -28,7 +28,8 @@ object SessionVar { def emptyMap = Map(IMap.empty) def persistAndSet[T](key: ScopedKey[Task[T]], state: State, value: T)( - implicit f: JsonFormat[T]): State = { + implicit f: JsonFormat[T] + ): State = { persist(key, state, value)(f) set(key, state, value) } @@ -70,7 +71,8 @@ object SessionVar { get(key, state) orElse read(key, state)(f) def loadAndSet[T](key: ScopedKey[Task[T]], state: State, setIfUnset: Boolean = true)( - implicit f: JsonFormat[T]): (State, Option[T]) = + implicit f: JsonFormat[T] + ): (State, Option[T]) = get(key, state) match { case s: Some[T] => (state, s) case None => diff --git a/main/src/main/scala/sbt/TemplateCommand.scala b/main/src/main/scala/sbt/TemplateCommand.scala index ae1cafa33..a7759ed91 100644 --- a/main/src/main/scala/sbt/TemplateCommand.scala +++ b/main/src/main/scala/sbt/TemplateCommand.scala @@ -22,7 +22,8 @@ import BasicCommandStrings._, BasicKeys._ private[sbt] object TemplateCommandUtil { def templateCommand: Command = Command(TemplateCommand, templateBrief, templateDetailed)(_ => templateCommandParser)( - runTemplate) + runTemplate + ) private def templateCommandParser: Parser[Seq[String]] = (token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF) map (_ => Nil)) @@ -35,10 +36,12 @@ private[sbt] object TemplateCommandUtil { val templateStage = stagingDirectory / "new" // This moves the target directory to a staging directory // https://github.com/sbt/sbt/issues/2835 - val state = extracted0.appendWithSession(Seq( - Keys.target := templateStage - ), - s0) + val state = extracted0.appendWithSession( + Seq( + Keys.target := templateStage + ), + s0 + ) val infos = (state get templateResolverInfos getOrElse Nil).toList val log = state.globalLogging.full val extracted = (Project extract state) @@ -74,18 +77,22 @@ private[sbt] object TemplateCommandUtil { case None => System.err.println("Template not found for: " + arguments.mkString(" ")) } - private def tryTemplate(info: TemplateResolverInfo, - arguments: List[String], - loader: ClassLoader): Boolean = { + private def tryTemplate( + info: TemplateResolverInfo, + arguments: List[String], + loader: ClassLoader + ): Boolean = { val resultObj = call(info.implementationClass, "isDefined", loader)( classOf[Array[String]] )(arguments.toArray) resultObj.asInstanceOf[Boolean] } - private def runTemplate(info: TemplateResolverInfo, - arguments: List[String], - loader: ClassLoader): Unit = { + private def runTemplate( + info: TemplateResolverInfo, + arguments: List[String], + loader: ClassLoader + ): Unit = { call(info.implementationClass, "run", loader)(classOf[Array[String]])(arguments.toArray) () } diff --git a/main/src/main/scala/sbt/internal/Act.scala b/main/src/main/scala/sbt/internal/Act.scala index 9349f2b64..953598933 100644 --- a/main/src/main/scala/sbt/internal/Act.scala +++ b/main/src/main/scala/sbt/internal/Act.scala @@ -32,44 +32,56 @@ object Act { token(OptSpace ~> '/' <~ OptSpace).examples("/").map(_ => ()) // this does not take aggregation into account - def scopedKey(index: KeyIndex, - current: ProjectRef, - defaultConfigs: Option[ResolvedReference] => Seq[String], - keyMap: Map[String, AttributeKey[_]], - data: Settings[Scope]): Parser[ScopedKey[_]] = + def scopedKey( + index: KeyIndex, + current: ProjectRef, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]], + data: Settings[Scope] + ): Parser[ScopedKey[_]] = scopedKeySelected(index, current, defaultConfigs, keyMap, data).map(_.key) // the index should be an aggregated index for proper tab completion - def scopedKeyAggregated(current: ProjectRef, - defaultConfigs: Option[ResolvedReference] => Seq[String], - structure: BuildStructure): KeysParser = - for (selected <- scopedKeySelected(structure.index.aggregateKeyIndex, - current, - defaultConfigs, - structure.index.keyMap, - structure.data)) + def scopedKeyAggregated( + current: ProjectRef, + defaultConfigs: Option[ResolvedReference] => Seq[String], + structure: BuildStructure + ): KeysParser = + for (selected <- scopedKeySelected( + structure.index.aggregateKeyIndex, + current, + defaultConfigs, + structure.index.keyMap, + structure.data + )) yield Aggregation.aggregate(selected.key, selected.mask, structure.extra) - def scopedKeySelected(index: KeyIndex, - current: ProjectRef, - defaultConfigs: Option[ResolvedReference] => Seq[String], - keyMap: Map[String, AttributeKey[_]], - data: Settings[Scope]): Parser[ParsedKey] = + def scopedKeySelected( + index: KeyIndex, + current: ProjectRef, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]], + data: Settings[Scope] + ): Parser[ParsedKey] = scopedKeyFull(index, current, defaultConfigs, keyMap) flatMap { choices => select(choices, data)(showRelativeKey2(current)) } - def scopedKeyFull(index: KeyIndex, - current: ProjectRef, - defaultConfigs: Option[ResolvedReference] => Seq[String], - keyMap: Map[String, AttributeKey[_]]): Parser[Seq[Parser[ParsedKey]]] = { + def scopedKeyFull( + index: KeyIndex, + current: ProjectRef, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]] + ): Parser[Seq[Parser[ParsedKey]]] = { def fullKey = for { rawProject <- optProjectRef(index, current) proj = resolveProject(rawProject, current) - confAmb <- configIdent(index configs proj, - index configIdents proj, - index.fromConfigIdent(proj)) + confAmb <- configIdent( + index configs proj, + index configIdents proj, + index.fromConfigIdent(proj) + ) partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false) } yield taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask) @@ -78,12 +90,14 @@ object Act { for { g <- globalIdent } yield - taskKeyExtra(index, - defaultConfigs, - keyMap, - None, - ParsedZero, - ScopeMask(true, true, false, false)) + taskKeyExtra( + index, + defaultConfigs, + keyMap, + None, + ParsedZero, + ScopeMask(true, true, false, false) + ) globalKey | fullKey } @@ -109,17 +123,21 @@ object Act { new ParsedKey(makeScopedKey(proj, conf, task, extra, key), mask) } - def makeScopedKey(proj: Option[ResolvedReference], - conf: Option[String], - task: Option[AttributeKey[_]], - extra: ScopeAxis[AttributeMap], - key: AttributeKey[_]): ScopedKey[_] = + def makeScopedKey( + proj: Option[ResolvedReference], + conf: Option[String], + task: Option[AttributeKey[_]], + extra: ScopeAxis[AttributeMap], + key: AttributeKey[_] + ): ScopedKey[_] = ScopedKey( Scope(toAxis(proj, Zero), toAxis(conf map ConfigKey.apply, Zero), toAxis(task, Zero), extra), - key) + key + ) def select(allKeys: Seq[Parser[ParsedKey]], data: Settings[Scope])( - implicit show: Show[ScopedKey[_]]): Parser[ParsedKey] = + implicit show: Show[ScopedKey[_]] + ): Parser[ParsedKey] = seq(allKeys) flatMap { ss => val default = ss.headOption match { case None => noValidKeys @@ -128,7 +146,8 @@ object Act { selectFromValid(ss filter isValid(data), default) } def selectFromValid(ss: Seq[ParsedKey], default: Parser[ParsedKey])( - implicit show: Show[ScopedKey[_]]): Parser[ParsedKey] = + implicit show: Show[ScopedKey[_]] + ): Parser[ParsedKey] = selectByTask(selectByConfig(ss)) match { case Seq() => default case Seq(single) => success(single) @@ -179,9 +198,11 @@ object Act { } // New configuration parser that's able to parse configuration ident trailed by slash. - private[sbt] def configIdent(confs: Set[String], - idents: Set[String], - fromIdent: String => String): Parser[ParsedAxis[String]] = { + private[sbt] def configIdent( + confs: Set[String], + idents: Set[String], + fromIdent: String => String + ): Parser[ParsedAxis[String]] = { val oldSep: Parser[Char] = ':' val sep: Parser[Unit] = spacedSlash !!! "Expected '/'" token( @@ -195,14 +216,17 @@ object Act { ) ?? Omitted } - def configs(explicit: ParsedAxis[String], - defaultConfigs: Option[ResolvedReference] => Seq[String], - proj: Option[ResolvedReference], - index: KeyIndex): Seq[Option[String]] = + def configs( + explicit: ParsedAxis[String], + defaultConfigs: Option[ResolvedReference] => Seq[String], + proj: Option[ResolvedReference], + index: KeyIndex + ): Seq[Option[String]] = explicit match { case Omitted => None +: defaultConfigurations(proj, index, defaultConfigs).flatMap( - nonEmptyConfig(index, proj)) + nonEmptyConfig(index, proj) + ) case ParsedZero | ParsedGlobal => None :: Nil case pv: ParsedValue[x] => Some(pv.value) :: Nil } @@ -214,15 +238,19 @@ object Act { ): Seq[String] = if (index exists proj) defaultConfigs(proj) else Nil - def nonEmptyConfig(index: KeyIndex, - proj: Option[ResolvedReference]): String => Seq[Option[String]] = + def nonEmptyConfig( + index: KeyIndex, + proj: Option[ResolvedReference] + ): String => Seq[Option[String]] = config => if (index.isEmpty(proj, Some(config))) Nil else Some(config) :: Nil - def key(index: KeyIndex, - proj: Option[ResolvedReference], - conf: Option[String], - task: Option[AttributeKey[_]], - keyMap: Map[String, AttributeKey[_]]): Parser[AttributeKey[_]] = { + def key( + index: KeyIndex, + proj: Option[ResolvedReference], + conf: Option[String], + task: Option[AttributeKey[_]], + keyMap: Map[String, AttributeKey[_]] + ): Parser[AttributeKey[_]] = { def dropHyphenated(keys: Set[String]): Set[String] = keys.filterNot(Util.hasHyphen) def keyParser(keys: Set[String]): Parser[AttributeKey[_]] = token(ID !!! "Expected key" examples dropHyphenated(keys)) flatMap { keyString => @@ -240,9 +268,11 @@ object Act { keyParser(keys) } - def getKey[T](keyMap: Map[String, AttributeKey[_]], - keyString: String, - f: AttributeKey[_] => T): Parser[T] = + def getKey[T]( + keyMap: Map[String, AttributeKey[_]], + keyString: String, + f: AttributeKey[_] => T + ): Parser[T] = keyMap.get(keyString) match { case Some(k) => success(f(k)) case None => failure(Command.invalidValue("key", keyMap.keys)(keyString)) @@ -250,8 +280,10 @@ object Act { val spacedComma = token(OptSpace ~ ',' ~ OptSpace) - def extraAxis(knownKeys: Map[String, AttributeKey[_]], - knownValues: IMap[AttributeKey, Set]): Parser[ScopeAxis[AttributeMap]] = { + def extraAxis( + knownKeys: Map[String, AttributeKey[_]], + knownValues: IMap[AttributeKey, Set] + ): Parser[ScopeAxis[AttributeMap]] = { val extrasP = extrasParser(knownKeys, knownValues) val extras = token('(', hide = _ == 1 && knownValues.isEmpty) ~> extrasP <~ token(')') optionalAxis(extras, Zero) @@ -271,7 +303,8 @@ object Act { (token( value(keyP) | ZeroString ^^^ ParsedZero - | ZeroIdent ^^^ ParsedZero) <~ (token("::".id) | spacedSlash)) ?? Omitted + | ZeroIdent ^^^ ParsedZero + ) <~ (token("::".id) | spacedSlash)) ?? Omitted } def resolveTask(task: ParsedAxis[AttributeKey[_]]): Option[AttributeKey[_]] = @@ -283,8 +316,10 @@ object Act { def filterStrings(base: Parser[String], valid: Set[String], label: String): Parser[String] = base.filter(valid, Command.invalidValue(label, valid)) - def extrasParser(knownKeys: Map[String, AttributeKey[_]], - knownValues: IMap[AttributeKey, Set]): Parser[AttributeMap] = { + def extrasParser( + knownKeys: Map[String, AttributeKey[_]], + knownValues: IMap[AttributeKey, Set] + ): Parser[AttributeMap] = { val validKeys = knownKeys.filter { case (_, key) => knownValues get key exists (_.nonEmpty) } if (validKeys.isEmpty) failure("No valid extra keys.") @@ -292,8 +327,10 @@ object Act { rep1sep(extraParser(validKeys, knownValues), spacedComma) map AttributeMap.apply } - def extraParser(knownKeys: Map[String, AttributeKey[_]], - knownValues: IMap[AttributeKey, Set]): Parser[AttributeEntry[_]] = { + def extraParser( + knownKeys: Map[String, AttributeKey[_]], + knownValues: IMap[AttributeKey, Set] + ): Parser[AttributeEntry[_]] = { val keyp = knownIDParser(knownKeys, "Not a valid extra key") <~ token(':' ~ OptSpace) keyp flatMap { case key: AttributeKey[t] => @@ -321,12 +358,15 @@ object Act { value(resolvedReference(index, currentBuild, trailing)) } - private[sbt] def resolvedReferenceIdent(index: KeyIndex, - currentBuild: URI, - trailing: Parser[_]): Parser[ResolvedReference] = { + private[sbt] def resolvedReferenceIdent( + index: KeyIndex, + currentBuild: URI, + trailing: Parser[_] + ): Parser[ResolvedReference] = { def projectID(uri: URI) = token( - DQuoteChar ~> examplesStrict(ID, index projects uri, "project ID") <~ DQuoteChar <~ OptSpace <~ ")" <~ trailing) + DQuoteChar ~> examplesStrict(ID, index projects uri, "project ID") <~ DQuoteChar <~ OptSpace <~ ")" <~ trailing + ) def projectRef(uri: URI) = projectID(uri) map { id => ProjectRef(uri, id) } @@ -336,15 +376,18 @@ object Act { val buildRef = token( "ProjectRef(" ~> OptSpace ~> "uri(" ~> OptSpace ~> DQuoteChar ~> - resolvedURI <~ DQuoteChar <~ OptSpace <~ ")" <~ spacedComma) + resolvedURI <~ DQuoteChar <~ OptSpace <~ ")" <~ spacedComma + ) buildRef flatMap { uri => projectRef(uri) } } - def resolvedReference(index: KeyIndex, - currentBuild: URI, - trailing: Parser[_]): Parser[ResolvedReference] = { + def resolvedReference( + index: KeyIndex, + currentBuild: URI, + trailing: Parser[_] + ): Parser[ResolvedReference] = { def projectID(uri: URI) = token(examplesStrict(ID, index projects uri, "project ID") <~ trailing) def projectRef(uri: URI) = projectID(uri) map { id => @@ -363,8 +406,10 @@ object Act { def optProjectRef(index: KeyIndex, current: ProjectRef): Parser[ParsedAxis[ResolvedReference]] = projectRef(index, current.build) ?? Omitted - def resolveProject(parsed: ParsedAxis[ResolvedReference], - current: ProjectRef): Option[ResolvedReference] = + def resolveProject( + parsed: ParsedAxis[ResolvedReference], + current: ProjectRef + ): Option[ResolvedReference] = parsed match { case Omitted => Some(current) case ParsedZero => None @@ -412,11 +457,13 @@ object Act { def scopedKeyParser(extracted: Extracted): Parser[ScopedKey[_]] = scopedKeyParser(extracted.structure, extracted.currentRef) def scopedKeyParser(structure: BuildStructure, currentRef: ProjectRef): Parser[ScopedKey[_]] = - scopedKey(structure.index.keyIndex, - currentRef, - structure.extra.configurationsForAxis, - structure.index.keyMap, - structure.data) + scopedKey( + structure.index.keyIndex, + currentRef, + structure.extra.configurationsForAxis, + structure.index.keyMap, + structure.data + ) type KeysParser = Parser[Seq[ScopedKey[T]] forSome { type T }] def aggregatedKeyParser(state: State): KeysParser = aggregatedKeyParser(Project extract state) @@ -435,17 +482,21 @@ object Act { KeyValue(key, value) } } - private[this] def anyKeyValues(structure: BuildStructure, - keys: Seq[ScopedKey[_]]): Seq[KeyValue[_]] = + private[this] def anyKeyValues( + structure: BuildStructure, + keys: Seq[ScopedKey[_]] + ): Seq[KeyValue[_]] = keys.flatMap { key => getValue(structure.data, key.scope, key.key) map { value => KeyValue(key, value) } } - private[this] def getValue[T](data: Settings[Scope], - scope: Scope, - key: AttributeKey[T]): Option[T] = + private[this] def getValue[T]( + data: Settings[Scope], + scope: Scope, + key: AttributeKey[T] + ): Option[T] = if (java.lang.Boolean.getBoolean("sbt.cli.nodelegation")) data.getDirect(scope, key) else data.get(scope, key) diff --git a/main/src/main/scala/sbt/internal/Aggregation.scala b/main/src/main/scala/sbt/internal/Aggregation.scala index ea523df53..c6a2c9828 100644 --- a/main/src/main/scala/sbt/internal/Aggregation.scala +++ b/main/src/main/scala/sbt/internal/Aggregation.scala @@ -102,10 +102,12 @@ object Aggregation { Complete(start, stop, result, newS) } - def runTasks[HL <: HList, T](s: State, - ts: Values[Task[T]], - extra: DummyTaskMap, - show: ShowConfig)(implicit display: Show[ScopedKey[_]]): State = { + def runTasks[HL <: HList, T]( + s: State, + ts: Values[Task[T]], + extra: DummyTaskMap, + show: ShowConfig + )(implicit display: Show[ScopedKey[_]]): State = { val complete = timedRun[T](s, ts, extra) showRun(complete, show) complete.results match { @@ -226,8 +228,9 @@ object Aggregation { reverse: Boolean ): Seq[ProjectRef] = { val resRef = proj.map(p => extra.projectRefFor(extra.resolveRef(p))) - resRef.toList.flatMap(ref => - if (reverse) extra.aggregates.reverse(ref) else extra.aggregates.forward(ref)) + resRef.toList.flatMap( + ref => if (reverse) extra.aggregates.reverse(ref) else extra.aggregates.forward(ref) + ) } def aggregate[T, Proj]( diff --git a/main/src/main/scala/sbt/internal/BuildDependencies.scala b/main/src/main/scala/sbt/internal/BuildDependencies.scala index 6b6709610..437018cf7 100644 --- a/main/src/main/scala/sbt/internal/BuildDependencies.scala +++ b/main/src/main/scala/sbt/internal/BuildDependencies.scala @@ -12,8 +12,10 @@ import sbt.internal.util.Types.idFun import sbt.internal.util.Dag import BuildDependencies._ -final class BuildDependencies private (val classpath: DependencyMap[ClasspathDep[ProjectRef]], - val aggregate: DependencyMap[ProjectRef]) { +final class BuildDependencies private ( + val classpath: DependencyMap[ClasspathDep[ProjectRef]], + val aggregate: DependencyMap[ProjectRef] +) { def classpathRefs(ref: ProjectRef): Seq[ProjectRef] = classpath(ref) map getID def classpathTransitiveRefs(ref: ProjectRef): Seq[ProjectRef] = classpathTransitive(ref) @@ -27,8 +29,10 @@ final class BuildDependencies private (val classpath: DependencyMap[ClasspathDep new BuildDependencies(classpath, aggregate.updated(ref, deps ++ aggregate.getOrElse(ref, Nil))) } object BuildDependencies { - def apply(classpath: DependencyMap[ClasspathDep[ProjectRef]], - aggregate: DependencyMap[ProjectRef]): BuildDependencies = + def apply( + classpath: DependencyMap[ClasspathDep[ProjectRef]], + aggregate: DependencyMap[ProjectRef] + ): BuildDependencies = new BuildDependencies(classpath, aggregate) type DependencyMap[D] = Map[ProjectRef, Seq[D]] diff --git a/main/src/main/scala/sbt/internal/BuildLoader.scala b/main/src/main/scala/sbt/internal/BuildLoader.scala index 11d024337..bdc697b68 100644 --- a/main/src/main/scala/sbt/internal/BuildLoader.scala +++ b/main/src/main/scala/sbt/internal/BuildLoader.scala @@ -16,20 +16,24 @@ import sbt.internal.util.Types.{ const, idFun } import sbt.util.Logger import sbt.librarymanagement.ModuleID -final class MultiHandler[S, T](builtIn: S => Option[T], - root: Option[S => Option[T]], - nonRoots: List[(URI, S => Option[T])], - getURI: S => URI, - log: S => Logger) { +final class MultiHandler[S, T]( + builtIn: S => Option[T], + root: Option[S => Option[T]], + nonRoots: List[(URI, S => Option[T])], + getURI: S => URI, + log: S => Logger +) { def applyFun: S => Option[T] = apply def apply(info: S): Option[T] = (baseLoader(info), applyNonRoots(info)) match { case (None, Nil) => None case (None, xs @ (_, nr) :: ignored) => if (ignored.nonEmpty) - warn("Using first of multiple matching non-root build resolvers for " + getURI(info), - log(info), - xs) + warn( + "Using first of multiple matching non-root build resolvers for " + getURI(info), + log(info), + xs + ) Some(nr) case (Some(b), xs) => if (xs.nonEmpty) @@ -72,28 +76,34 @@ object BuildLoader { type Loader = LoadInfo => Option[() => BuildUnit] type TransformAll = PartBuild => PartBuild - final class Components(val resolver: Resolver, - val builder: Builder, - val transformer: Transformer, - val full: Loader, - val transformAll: TransformAll) { + final class Components( + val resolver: Resolver, + val builder: Builder, + val transformer: Transformer, + val full: Loader, + val transformAll: TransformAll + ) { def |(cs: Components): Components = - new Components(resolver | cs.resolver, - builder | cs.builder, - seq(transformer, cs.transformer), - full | cs.full, - transformAll andThen cs.transformAll) + new Components( + resolver | cs.resolver, + builder | cs.builder, + seq(transformer, cs.transformer), + full | cs.full, + transformAll andThen cs.transformAll + ) } def transform(t: Transformer): Components = components(transformer = t) def resolve(r: Resolver): Components = components(resolver = r) def build(b: Builder): Components = components(builder = b) def full(f: Loader): Components = components(full = f) def transformAll(t: TransformAll) = components(transformAll = t) - def components(resolver: Resolver = const(None), - builder: Builder = const(None), - transformer: Transformer = _.unit, - full: Loader = const(None), - transformAll: TransformAll = idFun) = + def components( + resolver: Resolver = const(None), + builder: Builder = const(None), + transformer: Transformer = _.unit, + full: Loader = const(None), + transformAll: TransformAll = idFun + ) = new Components(resolver, builder, transformer, full, transformAll) def seq(a: Transformer, b: Transformer): Transformer = info => b(info.setUnit(a(info))) @@ -103,47 +113,55 @@ object BuildLoader { def config: LoadBuildConfiguration def state: State } - final class ResolveInfo(val uri: URI, - val staging: File, - val config: LoadBuildConfiguration, - val state: State) - extends Info - final class BuildInfo(val uri: URI, - val base: File, - val config: LoadBuildConfiguration, - val state: State) - extends Info - final class TransformInfo(val uri: URI, - val base: File, - val unit: BuildUnit, - val config: LoadBuildConfiguration, - val state: State) - extends Info { + final class ResolveInfo( + val uri: URI, + val staging: File, + val config: LoadBuildConfiguration, + val state: State + ) extends Info + final class BuildInfo( + val uri: URI, + val base: File, + val config: LoadBuildConfiguration, + val state: State + ) extends Info + final class TransformInfo( + val uri: URI, + val base: File, + val unit: BuildUnit, + val config: LoadBuildConfiguration, + val state: State + ) extends Info { def setUnit(newUnit: BuildUnit): TransformInfo = new TransformInfo(uri, base, newUnit, config, state) } - final class LoadInfo(val uri: URI, - val staging: File, - val config: LoadBuildConfiguration, - val state: State, - val components: Components) - extends Info + final class LoadInfo( + val uri: URI, + val staging: File, + val config: LoadBuildConfiguration, + val state: State, + val components: Components + ) extends Info - def apply(base: Components, - fail: URI => Nothing, - s: State, - config: LoadBuildConfiguration): BuildLoader = { + def apply( + base: Components, + fail: URI => Nothing, + s: State, + config: LoadBuildConfiguration + ): BuildLoader = { def makeMulti[S <: Info, T](base: S => Option[T]) = new MultiHandler[S, T](base, None, Nil, _.uri, _.config.log) - new BuildLoader(fail, - s, - config, - makeMulti(base.resolver), - makeMulti(base.builder), - base.transformer, - makeMulti(base.full), - base.transformAll) + new BuildLoader( + fail, + s, + config, + makeMulti(base.resolver), + makeMulti(base.builder), + base.transformer, + makeMulti(base.full), + base.transformAll + ) } def componentLoader: Loader = (info: LoadInfo) => { diff --git a/main/src/main/scala/sbt/internal/BuildStructure.scala b/main/src/main/scala/sbt/internal/BuildStructure.scala index 65b5eea6a..04db31933 100644 --- a/main/src/main/scala/sbt/internal/BuildStructure.scala +++ b/main/src/main/scala/sbt/internal/BuildStructure.scala @@ -20,14 +20,16 @@ import sbt.internal.util.Attributed.data import sbt.util.Logger import sjsonnew.shaded.scalajson.ast.unsafe.JValue -final class BuildStructure(val units: Map[URI, LoadedBuildUnit], - val root: URI, - val settings: Seq[Setting[_]], - val data: Settings[Scope], - val index: StructureIndex, - val streams: State => Streams, - val delegates: Scope => Seq[Scope], - val scopeLocal: ScopeLocal) { +final class BuildStructure( + val units: Map[URI, LoadedBuildUnit], + val root: URI, + val settings: Seq[Setting[_]], + val data: Settings[Scope], + val index: StructureIndex, + val streams: State => Streams, + val delegates: Scope => Seq[Scope], + val scopeLocal: ScopeLocal +) { val rootProject: URI => String = Load getRootProject units def allProjects: Seq[ResolvedProject] = units.values.flatMap(_.defined.values).toSeq def allProjects(build: URI): Seq[ResolvedProject] = @@ -59,11 +61,12 @@ final class StructureIndex( * @param rootProjects The list of project IDs for the projects considered roots of this build. * The first root project is used as the default in several situations where a project is not otherwise selected. */ -final class LoadedBuildUnit(val unit: BuildUnit, - val defined: Map[String, ResolvedProject], - val rootProjects: Seq[String], - val buildSettings: Seq[Setting[_]]) - extends BuildUnitBase { +final class LoadedBuildUnit( + val unit: BuildUnit, + val defined: Map[String, ResolvedProject], + val rootProjects: Seq[String], + val buildSettings: Seq[Setting[_]] +) extends BuildUnitBase { /** * The project to use as the default when one is not otherwise selected. @@ -72,7 +75,8 @@ final class LoadedBuildUnit(val unit: BuildUnit, val root = rootProjects match { case Nil => throw new java.lang.AssertionError( - "assertion failed: No root projects defined for build unit " + unit) + "assertion failed: No root projects defined for build unit " + unit + ) case Seq(root, _*) => root } @@ -157,8 +161,10 @@ case class DetectedAutoPlugin(name: String, value: AutoPlugin, hasAutoImport: Bo * @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]) { +final class DetectedPlugins( + val autoPlugins: Seq[DetectedAutoPlugin], + val builds: DetectedModules[BuildDef] +) { /** * Sequence of import expressions for the build definition. @@ -201,10 +207,12 @@ final class DetectedPlugins(val autoPlugins: Seq[DetectedAutoPlugin], * @param loader The class loader for the build definition project, notably excluding classes used for .sbt files. * @param detected Auto-detected modules in the build definition. */ -final class LoadedPlugins(val base: File, - val pluginData: PluginData, - val loader: ClassLoader, - val detected: DetectedPlugins) { +final class LoadedPlugins( + val base: File, + val pluginData: PluginData, + val loader: ClassLoader, + val detected: DetectedPlugins +) { def fullClasspath: Seq[Attributed[File]] = pluginData.classpath def classpath = data(fullClasspath) } @@ -215,10 +223,12 @@ final class LoadedPlugins(val base: File, * @param localBase The working location of the build on the filesystem. * For local URIs, this is the same as `uri`, but for remote URIs, this is the local copy or workspace allocated for the build. */ -final class BuildUnit(val uri: URI, - val localBase: File, - val definitions: LoadedDefinitions, - val plugins: LoadedPlugins) { +final class BuildUnit( + val uri: URI, + val localBase: File, + val definitions: LoadedDefinitions, + val plugins: LoadedPlugins +) { override def toString = if (uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase + ")") } @@ -234,11 +244,12 @@ final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit]) { } final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit]) sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] } -final class PartBuildUnit(val unit: BuildUnit, - val defined: Map[String, Project], - val rootProjects: Seq[String], - val buildSettings: Seq[Setting[_]]) - extends BuildUnitBase { +final class PartBuildUnit( + val unit: BuildUnit, + val defined: Map[String, Project], + val rootProjects: Seq[String], + val buildSettings: Seq[Setting[_]] +) extends BuildUnitBase { def resolve(f: Project => ResolvedProject): LoadedBuildUnit = new LoadedBuildUnit(unit, defined mapValues f toMap, rootProjects, buildSettings) def resolveRefs(f: ProjectReference => ProjectRef): LoadedBuildUnit = resolve(_ resolve f) @@ -251,29 +262,37 @@ object BuildStreams { final val BuildUnitPath = "$build" final val StreamsDirectory = "streams" - def mkStreams(units: Map[URI, LoadedBuildUnit], - root: URI, - data: Settings[Scope]): State => Streams = s => { + def mkStreams( + units: Map[URI, LoadedBuildUnit], + root: URI, + data: Settings[Scope] + ): State => Streams = s => { implicit val isoString: sjsonnew.IsoString[JValue] = - sjsonnew.IsoString.iso(sjsonnew.support.scalajson.unsafe.CompactPrinter.apply, - sjsonnew.support.scalajson.unsafe.Parser.parseUnsafe) + sjsonnew.IsoString.iso( + sjsonnew.support.scalajson.unsafe.CompactPrinter.apply, + sjsonnew.support.scalajson.unsafe.Parser.parseUnsafe + ) (s get Keys.stateStreams) getOrElse { - std.Streams(path(units, root, data), - displayFull, - LogManager.construct(data, s), - sjsonnew.support.scalajson.unsafe.Converter) + std.Streams( + path(units, root, data), + displayFull, + LogManager.construct(data, s), + sjsonnew.support.scalajson.unsafe.Converter + ) } } def path(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope])( - scoped: ScopedKey[_]): File = + scoped: ScopedKey[_] + ): File = resolvePath(projectPath(units, root, scoped, data), nonProjectPath(scoped)) def resolvePath(base: File, components: Seq[String]): File = (base /: components)((b, p) => new File(b, p)) def pathComponent[T](axis: ScopeAxis[T], scoped: ScopedKey[_], label: String)( - show: T => String): String = + show: T => String + ): String = axis match { case Zero => GlobalPath case This => @@ -292,10 +311,12 @@ object BuildStreams { a.entries.toSeq.sortBy(_.key.label).map { case AttributeEntry(key, value) => key.label + "=" + value.toString } mkString (" ") - def projectPath(units: Map[URI, LoadedBuildUnit], - root: URI, - scoped: ScopedKey[_], - data: Settings[Scope]): File = + def projectPath( + units: Map[URI, LoadedBuildUnit], + root: URI, + scoped: ScopedKey[_], + data: Settings[Scope] + ): File = scoped.scope.project match { case Zero => refTarget(GlobalScope, units(root).localBase, data) / GlobalPath case Select(br @ BuildRef(uri)) => refTarget(br, units(uri).localBase, data) / BuildUnitPath diff --git a/main/src/main/scala/sbt/internal/BuildUtil.scala b/main/src/main/scala/sbt/internal/BuildUtil.scala index 0d1ac38e5..a62511a30 100644 --- a/main/src/main/scala/sbt/internal/BuildUtil.scala +++ b/main/src/main/scala/sbt/internal/BuildUtil.scala @@ -48,10 +48,12 @@ final class BuildUtil[Proj]( refOpt => configurations(projectForAxis(refOpt)).map(_.name) } object BuildUtil { - def apply(root: URI, - units: Map[URI, LoadedBuildUnit], - keyIndex: KeyIndex, - data: Settings[Scope]): BuildUtil[ResolvedProject] = { + def apply( + root: URI, + units: Map[URI, LoadedBuildUnit], + keyIndex: KeyIndex, + data: Settings[Scope] + ): BuildUtil[ResolvedProject] = { val getp = (build: URI, project: String) => Load.getProject(units, build, project) val configs = (_: ResolvedProject).configurations.map(c => ConfigKey(c.name)) val aggregates = aggregationRelation(units) @@ -72,8 +74,9 @@ object BuildUtil { def checkCycles(units: Map[URI, LoadedBuildUnit]): Unit = { def getRef(pref: ProjectRef) = units(pref.build).defined(pref.project) - def deps(proj: ResolvedProject)( - base: ResolvedProject => Seq[ProjectRef]): Seq[ResolvedProject] = + def deps( + proj: ResolvedProject + )(base: ResolvedProject => Seq[ProjectRef]): Seq[ResolvedProject] = Dag.topologicalSort(proj)(p => base(p) map getRef) // check for cycles for ((_, lbu) <- units; proj <- lbu.defined.values) { diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 2a4e1c105..9474a83b6 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -147,7 +147,8 @@ private[sbt] final class CommandExchange { 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.") + "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) diff --git a/main/src/main/scala/sbt/internal/CommandStrings.scala b/main/src/main/scala/sbt/internal/CommandStrings.scala index 278985b64..1239cc034 100644 --- a/main/src/main/scala/sbt/internal/CommandStrings.scala +++ b/main/src/main/scala/sbt/internal/CommandStrings.scala @@ -115,8 +115,10 @@ $LastCommand val InspectCommand = "inspect" val inspectBrief = - (s"$InspectCommand [tree|uses|definitions|actual] ", - "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.") + ( + s"$InspectCommand [tree|uses|definitions|actual] ", + "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies." + ) val inspectDetailed = s""" |$InspectCommand | diff --git a/main/src/main/scala/sbt/internal/ConsoleProject.scala b/main/src/main/scala/sbt/internal/ConsoleProject.scala index f4b554c51..bb1279d8f 100644 --- a/main/src/main/scala/sbt/internal/ConsoleProject.scala +++ b/main/src/main/scala/sbt/internal/ConsoleProject.scala @@ -15,7 +15,8 @@ import xsbti.compile.ClasspathOptionsUtil object ConsoleProject { def apply(state: State, extra: String, cleanupCommands: String = "", options: Seq[String] = Nil)( - implicit log: Logger): Unit = { + implicit log: Logger + ): Unit = { val extracted = Project extract state val cpImports = new Imports(extracted, state) val bindings = ("currentState" -> state) :: ("extracted" -> extracted) :: ("cpHelpers" -> cpImports) :: Nil diff --git a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala index a4dc0641a..873ac2fdb 100644 --- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala +++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala @@ -113,9 +113,11 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe protected def makeContext(id: Long, spawningTask: ScopedKey[_], state: State): ManagedLogger - def doRunInBackground(spawningTask: ScopedKey[_], - state: State, - start: (Logger, File) => BackgroundJob): JobHandle = { + def doRunInBackground( + spawningTask: ScopedKey[_], + state: State, + start: (Logger, File) => BackgroundJob + ): JobHandle = { val id = nextId.getAndIncrement() val logger = makeContext(id, spawningTask, state) val workingDir = serviceTempDir / s"job-$id" @@ -132,7 +134,8 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe } override def runInBackground(spawningTask: ScopedKey[_], state: State)( - start: (Logger, File) => Unit): JobHandle = { + start: (Logger, File) => Unit + ): JobHandle = { pool.run(this, spawningTask, state)(start) } @@ -155,7 +158,8 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe case _: DeadHandle @unchecked => () // nothing to stop or wait for case other => sys.error( - s"BackgroundJobHandle does not originate with the current BackgroundJobService: $other") + s"BackgroundJobHandle does not originate with the current BackgroundJobService: $other" + ) } private def withHandleTry(job: JobHandle)(f: ThreadJobHandle => Try[Unit]): Try[Unit] = @@ -163,8 +167,11 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe case handle: ThreadJobHandle @unchecked => f(handle) case _: DeadHandle @unchecked => Try(()) // nothing to stop or wait for case other => - Try(sys.error( - s"BackgroundJobHandle does not originate with the current BackgroundJobService: $other")) + Try( + sys.error( + s"BackgroundJobHandle does not originate with the current BackgroundJobService: $other" + ) + ) } override def stop(job: JobHandle): Unit = @@ -363,7 +370,8 @@ private[sbt] class BackgroundThreadPool extends java.io.Closeable { } def run(manager: AbstractBackgroundJobService, spawningTask: ScopedKey[_], state: State)( - work: (Logger, File) => Unit): JobHandle = { + work: (Logger, File) => Unit + ): JobHandle = { def start(logger: Logger, workingDir: File): BackgroundJob = { val runnable = new BackgroundRunnable(spawningTask.key.label, { () => work(logger, workingDir) diff --git a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala index 26e5348ff..347e23b86 100644 --- a/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala +++ b/main/src/main/scala/sbt/internal/EvaluateConfigurations.scala @@ -44,9 +44,11 @@ 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 ") @@ -72,9 +74,11 @@ private[sbt] object EvaluateConfigurations { * * 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) /** @@ -83,14 +87,17 @@ 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 = { + 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)) + addOffsetToRange(offset, settingsAndDefinitions) + ) new ParsedFile(allImports, definitions, settings) } @@ -104,11 +111,13 @@ 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[_]]] = { + 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 @@ -124,11 +133,13 @@ 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] = { + 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 @@ -170,12 +181,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 + ) } } @@ -208,19 +221,23 @@ 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) } @@ -249,11 +266,13 @@ 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 @@ -265,7 +284,8 @@ private[sbt] object EvaluateConfigurations { */ private[sbt] def splitExpressions( file: File, - lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)]) = { + 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. @@ -273,7 +293,8 @@ private[sbt] object EvaluateConfigurations { } private[this] def splitSettingsDefinitions( - lines: Seq[(String, LineRange)]): (Seq[(String, LineRange)], Seq[(String, LineRange)]) = + lines: Seq[(String, LineRange)] + ): (Seq[(String, LineRange)], Seq[(String, LineRange)]) = lines partition { case (line, _) => isDefinition(line) } private[this] def isDefinition(line: String): Boolean = { @@ -282,34 +303,41 @@ private[sbt] object EvaluateConfigurations { } private[this] def extractedValTypes: Seq[String] = - Seq(classOf[CompositeProject], - classOf[InputKey[_]], - classOf[TaskKey[_]], - classOf[SettingKey[_]]) - .map(_.getName) + Seq( + classOf[CompositeProject], + 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 = { + 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) + eval.evalDefinitions( + convertedRanges, + new EvalImports(imports, name), + name, + file, + extractedValTypes + ) } } object Index { 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[_]]])) - }) + 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[_]]] } @@ -326,8 +354,9 @@ 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[_]] = { + 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) @@ -336,7 +365,8 @@ object Index { 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: ", ", ", "")) + 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[_]]] diff --git a/main/src/main/scala/sbt/internal/GlobalPlugin.scala b/main/src/main/scala/sbt/internal/GlobalPlugin.scala index 317e6fd0d..92f237b7d 100644 --- a/main/src/main/scala/sbt/internal/GlobalPlugin.scala +++ b/main/src/main/scala/sbt/internal/GlobalPlugin.scala @@ -41,8 +41,10 @@ object GlobalPlugin { injectInternalClasspath(Runtime, gp.internalClasspath), injectInternalClasspath(Compile, gp.internalClasspath) ) - private[this] def injectInternalClasspath(config: Configuration, - cp: Seq[Attributed[File]]): Setting[_] = + private[this] def injectInternalClasspath( + config: Configuration, + cp: Seq[Attributed[File]] + ): Setting[_] = internalDependencyClasspath in config ~= { prev => (prev ++ cp).distinct } @@ -50,8 +52,10 @@ object GlobalPlugin { def build(base: File, s: State, config: LoadBuildConfiguration): (BuildStructure, State) = { val newInject = config.injectSettings.copy(global = config.injectSettings.global ++ globalPluginSettings) - val globalConfig = config.copy(injectSettings = newInject, - pluginManagement = config.pluginManagement.forGlobalPlugin) + val globalConfig = config.copy( + injectSettings = newInject, + pluginManagement = config.pluginManagement.forGlobalPlugin + ) val (eval, structure) = Load(base, s, globalConfig) val session = Load.initialSession(structure, eval) (structure, Project.setProject(session, structure, s)) @@ -73,22 +77,26 @@ object GlobalPlugin { // If we reference it directly (if it's an executionRoot) then it forces an update, which is not what we want. val updateReport = Def.taskDyn { Def.task { update.value } }.value - GlobalPluginData(projectID.value, - projectDependencies.value, - depMap, - resolvers.value.toVector, - (fullClasspath in Runtime).value, - (prods ++ intcp).distinct)(updateReport) + GlobalPluginData( + projectID.value, + projectDependencies.value, + depMap, + resolvers.value.toVector, + (fullClasspath in Runtime).value, + (prods ++ intcp).distinct + )(updateReport) } val resolvedTaskInit = taskInit mapReferenced Project.mapScope(Scope replaceThis p) val task = resolvedTaskInit evaluate data val roots = resolvedTaskInit.dependencies evaluate(state, structure, task, roots) } - def evaluate[T](state: State, - structure: BuildStructure, - t: Task[T], - roots: Seq[ScopedKey[_]]): (State, T) = { + def evaluate[T]( + state: State, + structure: BuildStructure, + t: Task[T], + roots: Seq[ScopedKey[_]] + ): (State, T) = { import EvaluateTask._ withStreams(structure, state) { str => val nv = nodeView(state, str, roots) @@ -105,13 +113,17 @@ object GlobalPlugin { version := "0.0" ) } -final case class GlobalPluginData(projectID: ModuleID, - dependencies: Seq[ModuleID], - descriptors: Map[ModuleRevisionId, ModuleDescriptor], - resolvers: Vector[Resolver], - fullClasspath: Classpath, - internalClasspath: Classpath)(val updateReport: UpdateReport) -final case class GlobalPlugin(data: GlobalPluginData, - structure: BuildStructure, - inject: Seq[Setting[_]], - base: File) +final case class GlobalPluginData( + projectID: ModuleID, + dependencies: Seq[ModuleID], + descriptors: Map[ModuleRevisionId, ModuleDescriptor], + resolvers: Vector[Resolver], + fullClasspath: Classpath, + internalClasspath: Classpath +)(val updateReport: UpdateReport) +final case class GlobalPlugin( + data: GlobalPluginData, + structure: BuildStructure, + inject: Seq[Setting[_]], + base: File +) diff --git a/main/src/main/scala/sbt/internal/GroupedAutoPlugins.scala b/main/src/main/scala/sbt/internal/GroupedAutoPlugins.scala index c43b5a50b..06564204f 100644 --- a/main/src/main/scala/sbt/internal/GroupedAutoPlugins.scala +++ b/main/src/main/scala/sbt/internal/GroupedAutoPlugins.scala @@ -11,8 +11,10 @@ package internal import Def.Setting import java.net.URI -private[sbt] final class GroupedAutoPlugins(val all: Seq[AutoPlugin], - val byBuild: Map[URI, Seq[AutoPlugin]]) { +private[sbt] final class GroupedAutoPlugins( + val all: Seq[AutoPlugin], + val byBuild: Map[URI, Seq[AutoPlugin]] +) { def globalSettings: Seq[Setting[_]] = all.flatMap(_.globalSettings) def buildSettings(uri: URI): Seq[Setting[_]] = byBuild.getOrElse(uri, Nil).flatMap(_.buildSettings) diff --git a/main/src/main/scala/sbt/internal/IvyConsole.scala b/main/src/main/scala/sbt/internal/IvyConsole.scala index 9cce3a74f..a2cd16b1d 100644 --- a/main/src/main/scala/sbt/internal/IvyConsole.scala +++ b/main/src/main/scala/sbt/internal/IvyConsole.scala @@ -50,19 +50,23 @@ object IvyConsole { logLevel in Global := Level.Warn, showSuccess in Global := false ) - val append = Load.transformSettings(Load.projectScope(currentRef), - currentRef.build, - rootProject, - depSettings) + val append = Load.transformSettings( + Load.projectScope(currentRef), + currentRef.build, + rootProject, + depSettings + ) val newStructure = Load.reapply(session.original ++ append, structure) val newState = state.copy(remainingCommands = Exec(Keys.consoleQuick.key.label, None) :: Nil) Project.setProject(session, newStructure, newState) } - final case class Dependencies(managed: Seq[ModuleID], - resolvers: Seq[Resolver], - unmanaged: Seq[File]) + final case class Dependencies( + managed: Seq[ModuleID], + resolvers: Seq[Resolver], + unmanaged: Seq[File] + ) def parseDependencies(args: Seq[String], log: Logger): Dependencies = (Dependencies(Nil, Nil, Nil) /: args)(parseArgument(log)) def parseArgument(log: Logger)(acc: Dependencies, arg: String): Dependencies = diff --git a/main/src/main/scala/sbt/internal/KeyIndex.scala b/main/src/main/scala/sbt/internal/KeyIndex.scala index 78183984e..fc6ef184c 100644 --- a/main/src/main/scala/sbt/internal/KeyIndex.scala +++ b/main/src/main/scala/sbt/internal/KeyIndex.scala @@ -17,19 +17,25 @@ import sbt.librarymanagement.Configuration object KeyIndex { def empty: ExtendableKeyIndex = new KeyIndex0(emptyBuildIndex) - def apply(known: Iterable[ScopedKey[_]], - projects: Map[URI, Set[String]], - configurations: Map[String, Seq[Configuration]]): ExtendableKeyIndex = + def apply( + known: Iterable[ScopedKey[_]], + projects: Map[URI, Set[String]], + configurations: Map[String, Seq[Configuration]] + ): ExtendableKeyIndex = (base(projects, configurations) /: known) { _ add _ } - def aggregate(known: Iterable[ScopedKey[_]], - extra: BuildUtil[_], - projects: Map[URI, Set[String]], - configurations: Map[String, Seq[Configuration]]): ExtendableKeyIndex = + def aggregate( + known: Iterable[ScopedKey[_]], + extra: BuildUtil[_], + projects: Map[URI, Set[String]], + configurations: Map[String, Seq[Configuration]] + ): ExtendableKeyIndex = (base(projects, configurations) /: known) { (index, key) => index.addAggregated(key, extra) } - private[this] def base(projects: Map[URI, Set[String]], - configurations: Map[String, Seq[Configuration]]): ExtendableKeyIndex = { + private[this] def base( + projects: Map[URI, Set[String]], + configurations: Map[String, Seq[Configuration]] + ): ExtendableKeyIndex = { val data = for { (uri, ids) <- projects } yield { @@ -78,23 +84,29 @@ trait KeyIndex { // TODO, optimize def isEmpty(proj: Option[ResolvedReference], conf: Option[String]): Boolean = keys(proj, conf).isEmpty - def isEmpty(proj: Option[ResolvedReference], - conf: Option[String], - task: Option[AttributeKey[_]]): Boolean = keys(proj, conf, task).isEmpty + def isEmpty( + proj: Option[ResolvedReference], + conf: Option[String], + task: Option[AttributeKey[_]] + ): Boolean = keys(proj, conf, task).isEmpty def buildURIs: Set[URI] def projects(uri: URI): Set[String] def exists(project: Option[ResolvedReference]): Boolean def configs(proj: Option[ResolvedReference]): Set[String] def tasks(proj: Option[ResolvedReference], conf: Option[String]): Set[AttributeKey[_]] - def tasks(proj: Option[ResolvedReference], - conf: Option[String], - key: String): Set[AttributeKey[_]] + def tasks( + proj: Option[ResolvedReference], + conf: Option[String], + key: String + ): Set[AttributeKey[_]] def keys(proj: Option[ResolvedReference]): Set[String] def keys(proj: Option[ResolvedReference], conf: Option[String]): Set[String] - def keys(proj: Option[ResolvedReference], - conf: Option[String], - task: Option[AttributeKey[_]]): Set[String] + def keys( + proj: Option[ResolvedReference], + conf: Option[String], + task: Option[AttributeKey[_]] + ): Set[String] private[sbt] def configIdents(project: Option[ResolvedReference]): Set[String] private[sbt] def fromConfigIdent(proj: Option[ResolvedReference])(configIdent: String): String } @@ -116,11 +128,15 @@ private[sbt] final class AKeyIndex(val data: Relation[Option[AttributeKey[_]], S * data contains the mapping between a configuration and keys. * identData contains the mapping between a configuration and its identifier. */ -private[sbt] final class ConfigIndex(val data: Map[Option[String], AKeyIndex], - val identData: Map[String, String]) { - def add(config: Option[String], - task: Option[AttributeKey[_]], - key: AttributeKey[_]): ConfigIndex = { +private[sbt] final class ConfigIndex( + val data: Map[Option[String], AKeyIndex], + val identData: Map[String, String] +) { + def add( + config: Option[String], + task: Option[AttributeKey[_]], + key: AttributeKey[_] + ): ConfigIndex = { new ConfigIndex(data updated (config, keyIndex(config).add(task, key)), this.identData) } @@ -141,20 +157,24 @@ private[sbt] final class ConfigIndex(val data: Map[Option[String], AKeyIndex], configIdentsInverse.getOrElse(ident, Scope.unguessConfigIdent(ident)) } private[sbt] final class ProjectIndex(val data: Map[Option[String], ConfigIndex]) { - def add(id: Option[String], - config: Option[String], - task: Option[AttributeKey[_]], - key: AttributeKey[_]): ProjectIndex = + def add( + id: Option[String], + config: Option[String], + task: Option[AttributeKey[_]], + key: AttributeKey[_] + ): ProjectIndex = new ProjectIndex(data updated (id, confIndex(id).add(config, task, key))) def confIndex(id: Option[String]): ConfigIndex = getOr(data, id, emptyConfigIndex) def projects: Set[String] = keySet(data) } private[sbt] final class BuildIndex(val data: Map[Option[URI], ProjectIndex]) { - def add(build: Option[URI], - project: Option[String], - config: Option[String], - task: Option[AttributeKey[_]], - key: AttributeKey[_]): BuildIndex = + def add( + build: Option[URI], + project: Option[String], + config: Option[String], + task: Option[AttributeKey[_]], + key: AttributeKey[_] + ): BuildIndex = new BuildIndex(data updated (build, projectIndex(build).add(project, config, task, key))) def projectIndex(build: Option[URI]): ProjectIndex = getOr(data, build, emptyProjectIndex) def builds: Set[URI] = keySet(data) @@ -176,18 +196,22 @@ private[sbt] final class KeyIndex0(val data: BuildIndex) extends ExtendableKeyIn def tasks(proj: Option[ResolvedReference], conf: Option[String]): Set[AttributeKey[_]] = keyIndex(proj, conf).tasks - def tasks(proj: Option[ResolvedReference], - conf: Option[String], - key: String): Set[AttributeKey[_]] = keyIndex(proj, conf).tasks(key) + def tasks( + proj: Option[ResolvedReference], + conf: Option[String], + key: String + ): Set[AttributeKey[_]] = keyIndex(proj, conf).tasks(key) def keys(proj: Option[ResolvedReference]): Set[String] = (Set.empty[String] /: optConfigs(proj)) { (s, c) => s ++ keys(proj, c) } def keys(proj: Option[ResolvedReference], conf: Option[String]): Set[String] = keyIndex(proj, conf).allKeys - def keys(proj: Option[ResolvedReference], - conf: Option[String], - task: Option[AttributeKey[_]]): Set[String] = keyIndex(proj, conf).keys(task) + def keys( + proj: Option[ResolvedReference], + conf: Option[String], + task: Option[AttributeKey[_]] + ): Set[String] = keyIndex(proj, conf).keys(task) def keyIndex(proj: Option[ResolvedReference], conf: Option[String]): AKeyIndex = confIndex(proj).keyIndex(conf) @@ -217,10 +241,12 @@ private[sbt] final class KeyIndex0(val data: BuildIndex) extends ExtendableKeyIn val (build, project) = parts(scoped.scope.project.toOption) add1(build, project, scoped.scope.config, scoped.scope.task, scoped.key) } - private[this] def add1(uri: Option[URI], - id: Option[String], - config: ScopeAxis[ConfigKey], - task: ScopeAxis[AttributeKey[_]], - key: AttributeKey[_]): ExtendableKeyIndex = + private[this] def add1( + uri: Option[URI], + id: Option[String], + config: ScopeAxis[ConfigKey], + task: ScopeAxis[AttributeKey[_]], + key: AttributeKey[_] + ): ExtendableKeyIndex = new KeyIndex0(data.add(uri, id, config.toOption.map(_.name), task.toOption, key)) } diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 64819b906..6d620ec7c 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -309,8 +309,9 @@ private[sbt] object Load { case _ => None } ) - ss.map(s => - s mapConstant setResolved(s.key) mapReferenced mapSpecial(s.key) mapInit setDefining) + ss.map( + s => s mapConstant setResolved(s.key) mapReferenced mapSpecial(s.key) mapInit setDefining + ) } def setDefinitionKey[T](tk: Task[T], key: ScopedKey[_]): Task[T] = @@ -559,8 +560,10 @@ private[sbt] object Load { def checkProjectBase(buildBase: File, projectBase: File): Unit = { checkDirectory(projectBase) - assert(buildBase == projectBase || IO.relativize(buildBase, projectBase).isDefined, - s"Directory $projectBase is not contained in build root $buildBase") + assert( + buildBase == projectBase || IO.relativize(buildBase, projectBase).isDefined, + s"Directory $projectBase is not contained in build root $buildBase" + ) } def checkBuildBase(base: File) = checkDirectory(base) @@ -581,8 +584,10 @@ private[sbt] object Load { } } - def checkAll(referenced: Map[URI, List[ProjectReference]], - builds: Map[URI, PartBuildUnit]): Unit = { + def checkAll( + referenced: Map[URI, List[ProjectReference]], + builds: Map[URI, PartBuildUnit] + ): Unit = { val rootProject = getRootProject(builds) for ((uri, refs) <- referenced; ref <- refs) { val ProjectRef(refURI, refID) = Scope.resolveProjectRef(uri, rootProject, ref) @@ -718,12 +723,15 @@ private[sbt] object Load { // here on, so the autogenerated build aggregated can be removed from this code. ( I think) // We may actually want to move it back here and have different flags in loadTransitive... val hasRoot = loadedProjectsRaw.projects.exists(_.base == normBase) || defsScala.exists( - _.rootProject.isDefined) + _.rootProject.isDefined + ) val (loadedProjects, defaultBuildIfNone, keepClassFiles) = if (hasRoot) - (loadedProjectsRaw.projects, - BuildDef.defaultEmpty, - loadedProjectsRaw.generatedConfigClassFiles) + ( + loadedProjectsRaw.projects, + BuildDef.defaultEmpty, + loadedProjectsRaw.generatedConfigClassFiles + ) else { val existingIDs = loadedProjectsRaw.projects.map(_.id) val refs = existingIDs.map(id => ProjectRef(uri, id)) @@ -732,9 +740,11 @@ private[sbt] object Load { val defaultProjects = timed("Load.loadUnit: defaultProjects", log) { loadProjects(projectsFromBuild(b, normBase), false) } - (defaultProjects.projects ++ loadedProjectsRaw.projects, - b, - defaultProjects.generatedConfigClassFiles ++ loadedProjectsRaw.generatedConfigClassFiles) + ( + defaultProjects.projects ++ loadedProjectsRaw.projects, + b, + defaultProjects.generatedConfigClassFiles ++ loadedProjectsRaw.generatedConfigClassFiles + ) } // Now we clean stale class files. // TODO - this may cause issues with multiple sbt clients, but that should be deprecated pending sbt-server anyway @@ -907,7 +917,8 @@ private[sbt] object Load { discover(AddSettings.defaultSbtFiles, buildBase) match { case DiscoveredProjects(Some(root), discovered, files, generated) => log.debug( - s"[Loading] Found root project ${root.id} w/ remaining ${discovered.map(_.id).mkString(",")}") + s"[Loading] Found root project ${root.id} w/ remaining ${discovered.map(_.id).mkString(",")}" + ) val (finalRoot, projectLevelExtra) = timed(s"Load.loadTransitive: finalizeProject($root)", log) { finalizeProject(root, files, true) @@ -957,18 +968,22 @@ private[sbt] object Load { } val result = root +: (acc ++ otherProjects.projects) log.debug( - s"[Loading] Done in ${buildBase}, returning: ${result.map(_.id).mkString("(", ", ", ")")}") + s"[Loading] Done in ${buildBase}, returning: ${result.map(_.id).mkString("(", ", ", ")")}" + ) LoadedProjects(result, generated ++ otherGenerated ++ generatedConfigClassFiles) } case Nil => log.debug( - s"[Loading] Done in ${buildBase}, returning: ${acc.map(_.id).mkString("(", ", ", ")")}") + s"[Loading] Done in ${buildBase}, returning: ${acc.map(_.id).mkString("(", ", ", ")")}" + ) LoadedProjects(acc, generatedConfigClassFiles) } } - private[this] def translateAutoPluginException(e: AutoPluginException, - project: Project): AutoPluginException = + private[this] def translateAutoPluginException( + e: AutoPluginException, + project: Project + ): AutoPluginException = e.withPrefix(s"Error determining plugins for project '${project.id}' in ${project.base}:\n") /** @@ -1157,7 +1172,8 @@ private[sbt] object Load { injectSettings = config.injectSettings.copy( global = autoPluginSettings ++ config.injectSettings.global, project = config.pluginManagement.inject ++ config.injectSettings.project - )) + ) + ) def activateGlobalPlugin(config: LoadBuildConfiguration): LoadBuildConfiguration = config.globalPlugin match { @@ -1266,8 +1282,9 @@ private[sbt] object Load { def initialSession(structure: BuildStructure, rootEval: () => Eval, s: State): SessionSettings = { val session = s get Keys.sessionSettings val currentProject = session map (_.currentProject) getOrElse Map.empty - val currentBuild = session map (_.currentBuild) filter (uri => - structure.units.keys exists (uri ==)) getOrElse structure.root + val currentBuild = session map (_.currentBuild) filter ( + uri => structure.units.keys exists (uri ==) + ) getOrElse structure.root new SessionSettings( currentBuild, projectMap(structure, currentProject), diff --git a/main/src/main/scala/sbt/internal/LogManager.scala b/main/src/main/scala/sbt/internal/LogManager.scala index 85e293dab..982b6a45c 100644 --- a/main/src/main/scala/sbt/internal/LogManager.scala +++ b/main/src/main/scala/sbt/internal/LogManager.scala @@ -111,11 +111,13 @@ object LogManager { } // to change from global being the default to overriding, switch the order of state.get and data.get - def getOr[T](key: AttributeKey[T], - data: Settings[Scope], - scope: Scope, - state: State, - default: T): T = + def getOr[T]( + key: AttributeKey[T], + data: Settings[Scope], + scope: Scope, + state: State, + default: T + ): T = data.get(scope, key) orElse state.get(key) getOrElse default // This is the main function that is used to generate the logger for tasks. @@ -205,7 +207,8 @@ object LogManager { val consoleOpt = consoleLocally(state, console) LogExchange.bindLoggerAppenders( loggerName, - (consoleOpt.toList map { _ -> screenLevel }) ::: (relay -> backingLevel) :: Nil) + (consoleOpt.toList map { _ -> screenLevel }) ::: (relay -> backingLevel) :: Nil + ) log } diff --git a/main/src/main/scala/sbt/internal/PluginDiscovery.scala b/main/src/main/scala/sbt/internal/PluginDiscovery.scala index cc99453e3..5fe657639 100644 --- a/main/src/main/scala/sbt/internal/PluginDiscovery.scala +++ b/main/src/main/scala/sbt/internal/PluginDiscovery.scala @@ -94,10 +94,12 @@ object PluginDiscovery { * Discovers the names of top-level modules listed in resources named `resourceName` as per [[binaryModuleNames]] or * available as analyzed source and extending from any of `subclasses` as per [[sourceModuleNames]]. */ - def binarySourceModuleNames(classpath: Seq[Attributed[File]], - loader: ClassLoader, - resourceName: String, - subclasses: String*): Seq[String] = + def binarySourceModuleNames( + classpath: Seq[Attributed[File]], + loader: ClassLoader, + resourceName: String, + subclasses: String* + ): Seq[String] = ( binaryModuleNames(data(classpath), loader, resourceName) ++ (analyzed(classpath) flatMap (a => sourceModuleNames(a, subclasses: _*))) @@ -120,9 +122,11 @@ object PluginDiscovery { * `classpath` and `loader` are both required to ensure that `loader` * doesn't bring in any resources outside of the intended `classpath`, such as from parent loaders. */ - def binaryModuleNames(classpath: Seq[File], - loader: ClassLoader, - resourceName: String): Seq[String] = { + def binaryModuleNames( + classpath: Seq[File], + loader: ClassLoader, + resourceName: String + ): Seq[String] = { import collection.JavaConverters._ loader.getResources(resourceName).asScala.toSeq.filter(onClasspath(classpath)) flatMap { u => IO.readLinesURL(u).map(_.trim).filter(!_.isEmpty) @@ -136,7 +140,8 @@ object PluginDiscovery { private[sbt] def binarySourceModules[T]( data: PluginData, loader: ClassLoader, - resourceName: String)(implicit classTag: reflect.ClassTag[T]): DetectedModules[T] = { + resourceName: String + )(implicit classTag: reflect.ClassTag[T]): DetectedModules[T] = { val classpath = data.classpath val namesAndValues = if (classpath.isEmpty) Nil @@ -148,9 +153,11 @@ object PluginDiscovery { new DetectedModules(namesAndValues) } - private[this] def loadModules[T: reflect.ClassTag](data: PluginData, - names: Seq[String], - loader: ClassLoader): Seq[(String, T)] = + private[this] def loadModules[T: reflect.ClassTag]( + data: PluginData, + names: Seq[String], + loader: ClassLoader + ): Seq[(String, T)] = try ModuleUtilities.getCheckedObjects[T](names, loader) catch { case e: ExceptionInInitializerError => @@ -170,7 +177,8 @@ object PluginDiscovery { if (evictedStrings.isEmpty) "" else "\nNote that conflicts were resolved for some dependencies:\n\t" + evictedStrings.mkString( - "\n\t") + "\n\t" + ) throw new IncompatiblePluginsException(msgBase + msgExtra, t) } } diff --git a/main/src/main/scala/sbt/internal/PluginManagement.scala b/main/src/main/scala/sbt/internal/PluginManagement.scala index e35964040..386648c35 100644 --- a/main/src/main/scala/sbt/internal/PluginManagement.scala +++ b/main/src/main/scala/sbt/internal/PluginManagement.scala @@ -15,17 +15,21 @@ import sbt.librarymanagement.ModuleID import java.net.{ URI, URL, URLClassLoader } -final case class PluginManagement(overrides: Set[ModuleID], - applyOverrides: Set[ModuleID], - loader: PluginClassLoader, - initialLoader: ClassLoader, - context: Context) { +final case class PluginManagement( + overrides: Set[ModuleID], + applyOverrides: Set[ModuleID], + loader: PluginClassLoader, + initialLoader: ClassLoader, + context: Context +) { def shift: PluginManagement = - PluginManagement(Set.empty, - overrides, - new PluginClassLoader(initialLoader), - initialLoader, - context) + PluginManagement( + Set.empty, + overrides, + new PluginClassLoader(initialLoader), + initialLoader, + context + ) def addOverrides(os: Set[ModuleID]): PluginManagement = copy(overrides = overrides ++ os) @@ -49,11 +53,13 @@ object PluginManagement { val emptyContext: Context = Context(false, 0) def apply(initialLoader: ClassLoader): PluginManagement = - PluginManagement(Set.empty, - Set.empty, - new PluginClassLoader(initialLoader), - initialLoader, - emptyContext) + PluginManagement( + Set.empty, + Set.empty, + new PluginClassLoader(initialLoader), + initialLoader, + emptyContext + ) def extractOverrides(classpath: Classpath): Set[ModuleID] = classpath flatMap { _.metadata get Keys.moduleID.key map keepOverrideInfo } toSet; diff --git a/main/src/main/scala/sbt/internal/PluginsDebug.scala b/main/src/main/scala/sbt/internal/PluginsDebug.scala index f908a29fe..6462475f4 100644 --- a/main/src/main/scala/sbt/internal/PluginsDebug.scala +++ b/main/src/main/scala/sbt/internal/PluginsDebug.scala @@ -48,8 +48,10 @@ private[sbt] class PluginsDebug( activePrefix + debugDeactivated(notFoundKey, deactivated) } - private[this] def debugDeactivated(notFoundKey: String, - deactivated: Seq[EnableDeactivated]): String = { + private[this] def debugDeactivated( + notFoundKey: String, + deactivated: Seq[EnableDeactivated] + ): String = { val (impossible, possible) = Util.separate(deactivated) { case pi: PluginImpossible => Left(pi) case pr: PluginRequirements => Right(pr) @@ -154,11 +156,13 @@ private[sbt] object PluginsDebug { val perBuild: Map[URI, Set[AutoPlugin]] = structure.units.mapValues(unit => availableAutoPlugins(unit).toSet) val pluginsThisBuild = perBuild.getOrElse(currentRef.build, Set.empty).toList - lazy val context = Context(currentProject.plugins, - currentProject.autoPlugins, - Plugins.deducer(pluginsThisBuild), - pluginsThisBuild, - s.log) + lazy val context = Context( + currentProject.plugins, + currentProject.autoPlugins, + Plugins.deducer(pluginsThisBuild), + pluginsThisBuild, + s.log + ) lazy val debug = PluginsDebug(context.available) if (!pluginsThisBuild.contains(plugin)) { val availableInBuilds: List[URI] = perBuild.toList.filter(_._2(plugin)).map(_._1) @@ -222,10 +226,11 @@ private[sbt] object PluginsDebug { sealed abstract class EnableDeactivated extends PluginEnable /** Describes a [[plugin]] that cannot be activated in a [[context]] due to [[contradictions]] in requirements. */ - final case class PluginImpossible(plugin: AutoPlugin, - context: Context, - contradictions: Set[AutoPlugin]) - extends EnableDeactivated + final case class PluginImpossible( + plugin: AutoPlugin, + context: Context, + contradictions: Set[AutoPlugin] + ) extends EnableDeactivated /** * Describes the requirements for activating [[plugin]] in [[context]]. @@ -256,9 +261,11 @@ private[sbt] object PluginsDebug { * affecting the other plugin. If empty, a direct exclusion is required. * @param newlySelected If false, this plugin was selected in the original context. */ - final case class DeactivatePlugin(plugin: AutoPlugin, - removeOneOf: Set[AutoPlugin], - newlySelected: Boolean) + final case class DeactivatePlugin( + plugin: AutoPlugin, + removeOneOf: Set[AutoPlugin], + newlySelected: Boolean + ) /** Determines how to enable [[AutoPlugin]] in [[Context]]. */ def pluginEnable(context: Context, plugin: AutoPlugin): PluginEnable = @@ -344,13 +351,15 @@ private[sbt] object PluginsDebug { DeactivatePlugin(d, removeToDeactivate, newlySelected) } - PluginRequirements(plugin, - context, - blockingExcludes, - addToExistingPlugins, - extraPlugins, - willRemove, - deactivate) + PluginRequirements( + plugin, + context, + blockingExcludes, + addToExistingPlugins, + extraPlugins, + willRemove, + deactivate + ) } } @@ -376,13 +385,15 @@ private[sbt] object PluginsDebug { /** String representation of [[PluginEnable]], intended for end users. */ def explainPluginEnable(ps: PluginEnable): String = ps match { - case PluginRequirements(plugin, - _, - blockingExcludes, - enablingPlugins, - extraEnabledPlugins, - toBeRemoved, - deactivate) => + case PluginRequirements( + plugin, + _, + blockingExcludes, + enablingPlugins, + extraEnabledPlugins, + toBeRemoved, + deactivate + ) => def indent(str: String) = if (str.isEmpty) "" else s"\t$str" def note(str: String) = if (str.isEmpty) "" else s"Note: $str" val parts = @@ -490,8 +501,9 @@ private[sbt] object PluginsDebug { s"$s1 $s2 $s3" } - private[this] def pluginImpossibleN(plugin: AutoPlugin)( - contradictions: List[AutoPlugin]): String = { + private[this] def pluginImpossibleN( + plugin: AutoPlugin + )(contradictions: List[AutoPlugin]): String = { val s1 = s"There is no way to enable plugin ${plugin.label}." val s2 = s"It (or its dependencies) requires these plugins to be both present and absent:" val s3 = s"Please report the problem to the plugin's author." diff --git a/main/src/main/scala/sbt/internal/ProjectNavigation.scala b/main/src/main/scala/sbt/internal/ProjectNavigation.scala index 305967d06..b7f04ffd1 100644 --- a/main/src/main/scala/sbt/internal/ProjectNavigation.scala +++ b/main/src/main/scala/sbt/internal/ProjectNavigation.scala @@ -49,7 +49,8 @@ final class ProjectNavigation(s: State) { setProject(uri, to) else fail( - s"Invalid project name '$to' in build $uri (type 'projects' to list available projects).") + s"Invalid project name '$to' in build $uri (type 'projects' to list available projects)." + ) def changeBuild(newBuild: URI): State = if (structure.units contains newBuild) diff --git a/main/src/main/scala/sbt/internal/Resolve.scala b/main/src/main/scala/sbt/internal/Resolve.scala index 9797bbab8..7e3fe0676 100644 --- a/main/src/main/scala/sbt/internal/Resolve.scala +++ b/main/src/main/scala/sbt/internal/Resolve.scala @@ -11,10 +11,12 @@ package internal import sbt.internal.util.AttributeKey object Resolve { - def apply(index: BuildUtil[_], - current: ScopeAxis[Reference], - key: AttributeKey[_], - mask: ScopeMask): Scope => Scope = { + def apply( + index: BuildUtil[_], + current: ScopeAxis[Reference], + key: AttributeKey[_], + mask: ScopeMask + ): Scope => Scope = { val rs = resolveProject(current, mask) _ :: resolveExtra(mask) _ :: @@ -39,7 +41,8 @@ object Resolve { else scope.copy(extra = Zero) def resolveConfig[P](index: BuildUtil[P], key: AttributeKey[_], mask: ScopeMask)( - scope: Scope): Scope = + scope: Scope + ): Scope = if (mask.config) scope else { diff --git a/main/src/main/scala/sbt/internal/Script.scala b/main/src/main/scala/sbt/internal/Script.scala index addf7d4ed..2192785a6 100644 --- a/main/src/main/scala/sbt/internal/Script.scala +++ b/main/src/main/scala/sbt/internal/Script.scala @@ -25,7 +25,8 @@ object Script { lazy val command = Command.command(Name) { state => val scriptArg = state.remainingCommands.headOption map { _.commandLine } getOrElse sys.error( - "No script file specified") + "No script file specified" + ) val scriptFile = new File(scriptArg).getAbsoluteFile val hash = Hash.halve(Hash.toHex(Hash(scriptFile.getAbsolutePath))) val base = new File(CommandUtil.bootDirectory(state), hash) @@ -51,14 +52,18 @@ object Script { } val scriptAsSource = sources in Compile := script :: Nil val asScript = scalacOptions ++= Seq("-Xscript", script.getName.stripSuffix(".scala")) - val scriptSettings = Seq(asScript, - scriptAsSource, - logLevel in Global := Level.Warn, - showSuccess in Global := false) - val append = Load.transformSettings(Load.projectScope(currentRef), - currentRef.build, - rootProject, - scriptSettings ++ embeddedSettings) + val scriptSettings = Seq( + asScript, + scriptAsSource, + logLevel in Global := Level.Warn, + showSuccess in Global := false + ) + val append = Load.transformSettings( + Load.projectScope(currentRef), + currentRef.build, + rootProject, + scriptSettings ++ embeddedSettings + ) val newStructure = Load.reapply(session.original ++ append, structure) val arguments = state.remainingCommands.drop(1).map(e => s""""${e.commandLine}"""") diff --git a/main/src/main/scala/sbt/internal/SessionSettings.scala b/main/src/main/scala/sbt/internal/SessionSettings.scala index 30911a384..ddca7d66c 100755 --- a/main/src/main/scala/sbt/internal/SessionSettings.scala +++ b/main/src/main/scala/sbt/internal/SessionSettings.scala @@ -40,8 +40,10 @@ final case class SessionSettings( currentEval: () => Eval ) { - assert(currentProject contains currentBuild, - s"Current build ($currentBuild) not associated with a current project.") + assert( + currentProject contains currentBuild, + s"Current build ($currentBuild) not associated with a current project." + ) /** * Modifiy the current state. @@ -52,9 +54,11 @@ final case class SessionSettings( * @return A new SessionSettings object */ def setCurrent(build: URI, project: String, eval: () => Eval): SessionSettings = - copy(currentBuild = build, - currentProject = currentProject.updated(build, project), - currentEval = eval) + copy( + currentBuild = build, + currentProject = currentProject.updated(build, project), + currentEval = eval + ) /** * @return The current ProjectRef with which we scope settings. @@ -147,7 +151,8 @@ object SessionSettings { val oldSettings = (oldState get Keys.sessionSettings).toList.flatMap(_.append).flatMap(_._2) if (newSession.append.isEmpty && oldSettings.nonEmpty) oldState.log.warn( - "Discarding " + pluralize(oldSettings.size, " session setting") + ". Use 'session save' to persist session settings.") + "Discarding " + pluralize(oldSettings.size, " session setting") + ". Use 'session save' to persist session settings." + ) } def removeRanges[T](in: Seq[T], ranges: Seq[(Int, Int)]): Seq[T] = { @@ -197,10 +202,12 @@ object SessionSettings { reapply(newSession.copy(original = newSession.mergeSettings, append = Map.empty), s) } - def writeSettings(pref: ProjectRef, - settings: List[SessionSetting], - original: Seq[Setting[_]], - structure: BuildStructure): (Seq[SessionSetting], Seq[Setting[_]]) = { + def writeSettings( + pref: ProjectRef, + settings: List[SessionSetting], + original: Seq[Setting[_]], + structure: BuildStructure + ): (Seq[SessionSetting], Seq[Setting[_]]) = { val project = Project.getProject(pref, structure).getOrElse(sys.error("Invalid project reference " + pref)) val writeTo: File = BuildPaths @@ -224,9 +231,10 @@ object SessionSettings { val RangePosition(_, r @ LineRange(start, end)) = s.pos settings find (_._1.key == s.key) match { case Some(ss @ (ns, newLines)) if !ns.init.dependencies.contains(ns.key) => - val shifted = ns withPos RangePosition(path, - LineRange(start - offs, - start - offs + newLines.size)) + val shifted = ns withPos RangePosition( + path, + LineRange(start - offs, start - offs + newLines.size) + ) (offs + end - start - newLines.size, shifted :: olds, ss +: repl) case _ => val shifted = s withPos RangePosition(path, r shift -offs) @@ -324,9 +332,11 @@ save, save-all lazy val parser = token(Space) ~> (token("list-all" ^^^ new Print(true)) | token("list" ^^^ new Print(false)) | token( - "clear" ^^^ new Clear(false)) | + "clear" ^^^ new Clear(false) + ) | token("save-all" ^^^ new Save(true)) | token("save" ^^^ new Save(false)) | token( - "clear-all" ^^^ new Clear(true)) | + "clear-all" ^^^ new Clear(true) + ) | remove) lazy val remove = token("remove") ~> token(Space) ~> natSelect.map(ranges => new Remove(ranges)) diff --git a/main/src/main/scala/sbt/internal/SettingCompletions.scala b/main/src/main/scala/sbt/internal/SettingCompletions.scala index 3a3f8388e..1952c5598 100644 --- a/main/src/main/scala/sbt/internal/SettingCompletions.scala +++ b/main/src/main/scala/sbt/internal/SettingCompletions.scala @@ -24,9 +24,11 @@ import DefaultParsers._ * The verbose summary will typically use more vertical space and show full details, * while the quiet summary will be a couple of lines and truncate information. */ -private[sbt] class SetResult(val session: SessionSettings, - val verboseSummary: String, - val quietSummary: String) +private[sbt] class SetResult( + val session: SessionSettings, + val verboseSummary: String, + val quietSummary: String +) /** Defines methods for implementing the `set` command.*/ private[sbt] object SettingCompletions { @@ -41,9 +43,12 @@ private[sbt] object SettingCompletions { val r = relation(extracted.structure, true) val allDefs = Def .flattenLocals( - Def.compiled(extracted.structure.settings, true)(structure.delegates, - structure.scopeLocal, - implicitly[Show[ScopedKey[_]]])) + Def.compiled(extracted.structure.settings, true)( + structure.delegates, + structure.scopeLocal, + implicitly[Show[ScopedKey[_]]] + ) + ) .keys val projectScope = Load.projectScope(currentRef) def resolve(s: Setting[_]): Seq[Setting[_]] = @@ -72,9 +77,11 @@ private[sbt] object SettingCompletions { val append = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings) val newSession = session.appendSettings(append map (a => (a, arg.split('\n').toList))) - val r = relation(newSession.mergeSettings, true)(structure.delegates, - structure.scopeLocal, - implicitly) + val r = relation(newSession.mergeSettings, true)( + structure.delegates, + structure.scopeLocal, + implicitly + ) setResult(newSession, r, append) } @@ -149,9 +156,11 @@ private[sbt] object SettingCompletions { } /** Parser for a Scope+AttributeKey (ScopedKey). */ - def scopedKeyParser(keyMap: Map[String, AttributeKey[_]], - settings: Settings[Scope], - context: ResolvedProject): Parser[ScopedKey[_]] = { + def scopedKeyParser( + keyMap: Map[String, AttributeKey[_]], + settings: Settings[Scope], + context: ResolvedProject + ): Parser[ScopedKey[_]] = { val cutoff = KeyRanks.MainCutoff val keyCompletions = fixedCompletions { (seen, level) => completeKey(seen, keyMap, level, cutoff, 10).toSet @@ -186,9 +195,11 @@ private[sbt] object SettingCompletions { * The completions are restricted to be more useful. Currently, this parser will suggest * only known axis values for configurations and tasks and only in that order. */ - def scopeParser(key: AttributeKey[_], - settings: Settings[Scope], - context: ResolvedProject): Parser[Scope] = { + def scopeParser( + key: AttributeKey[_], + settings: Settings[Scope], + context: ResolvedProject + ): Parser[Scope] = { val data = settings.data val allScopes = data.keys.toSeq val definedScopes = data.toSeq flatMap { @@ -277,11 +288,13 @@ private[sbt] object SettingCompletions { completeDescribed(seen, true, applicable)(assignDescription) } - def completeKey(seen: String, - keys: Map[String, AttributeKey[_]], - level: Int, - prominentCutoff: Int, - detailLimit: Int): Seq[Completion] = + def completeKey( + seen: String, + keys: Map[String, AttributeKey[_]], + level: Int, + prominentCutoff: Int, + detailLimit: Int + ): Seq[Completion] = completeSelectDescribed(seen, level, keys, detailLimit)(_.description) { case (_, v) => v.rank <= prominentCutoff } @@ -290,13 +303,15 @@ private[sbt] object SettingCompletions { seen: String, level: Int, definedChoices: Set[String], - allChoices: Map[String, T])(description: T => Option[String]): Seq[Completion] = + allChoices: Map[String, T] + )(description: T => Option[String]): Seq[Completion] = completeSelectDescribed(seen, level, allChoices, 10)(description) { 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] = { + description: T => Option[String] + )(prominent: (String, T) => Boolean): Seq[Completion] = { val applicable = all.toSeq.filter { case (k, _) => k startsWith seen } val prominentOnly = applicable filter { case (k, v) => prominent(k, v) } @@ -306,7 +321,8 @@ private[sbt] object SettingCompletions { completeDescribed(seen, showDescriptions, showKeys)(s => description(s).toList.mkString) } def completeDescribed[T](seen: String, showDescriptions: Boolean, in: Seq[(String, T)])( - description: T => String): Seq[Completion] = { + description: T => String + ): Seq[Completion] = { def appendString(id: String): String = id.stripPrefix(seen) + " " if (in.isEmpty) Nil @@ -337,7 +353,8 @@ private[sbt] object SettingCompletions { def keyType[S](key: AttributeKey[_])( onSetting: Manifest[_] => S, onTask: Manifest[_] => S, - onInput: Manifest[_] => S)(implicit tm: Manifest[Task[_]], im: Manifest[InputTask[_]]): S = { + onInput: Manifest[_] => S + )(implicit tm: Manifest[Task[_]], im: Manifest[InputTask[_]]): S = { def argTpe = key.manifest.typeArguments.head val TaskClass = tm.runtimeClass val InputTaskClass = im.runtimeClass diff --git a/main/src/main/scala/sbt/internal/SettingGraph.scala b/main/src/main/scala/sbt/internal/SettingGraph.scala index 8b74b51a4..68fab0353 100644 --- a/main/src/main/scala/sbt/internal/SettingGraph.scala +++ b/main/src/main/scala/sbt/internal/SettingGraph.scala @@ -19,9 +19,11 @@ import sbt.io.IO object SettingGraph { def apply(structure: BuildStructure, basedir: File, scoped: ScopedKey[_], generation: Int)( - implicit display: Show[ScopedKey[_]]): SettingGraph = { + implicit display: Show[ScopedKey[_]] + ): SettingGraph = { val cMap = flattenLocals( - compiled(structure.settings, false)(structure.delegates, structure.scopeLocal, display)) + compiled(structure.settings, false)(structure.delegates, structure.scopeLocal, display) + ) def loop(scoped: ScopedKey[_], generation: Int): SettingGraph = { val key = scoped.key val scope = scoped.scope @@ -34,14 +36,16 @@ object SettingGraph { // val related = cMap.keys.filter(k => k.key == key && k.scope != scope) // val reverse = reverseDependencies(cMap, scoped) - SettingGraph(display.show(scoped), - definedIn, - Project.scopedKeyData(structure, scope, key), - key.description, - basedir, - depends map { (x: ScopedKey[_]) => - loop(x, generation + 1) - }) + SettingGraph( + display.show(scoped), + definedIn, + Project.scopedKeyData(structure, scope, key), + key.description, + basedir, + depends map { (x: ScopedKey[_]) => + loop(x, generation + 1) + } + ) } loop(scoped, generation) } diff --git a/main/src/main/scala/sbt/internal/TaskSequential.scala b/main/src/main/scala/sbt/internal/TaskSequential.scala index 8fbab839e..d21fa1b8f 100644 --- a/main/src/main/scala/sbt/internal/TaskSequential.scala +++ b/main/src/main/scala/sbt/internal/TaskSequential.scala @@ -21,60 +21,81 @@ trait TaskSequential { last: Initialize[Task[B]] ): Initialize[Task[B]] = sequential(List(unitTask(task0)), last) - def sequential[A0, A1, B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + def sequential[A0, A1, B]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential(List(unitTask(task0), unitTask(task1)), last) - def sequential[A0, A1, A2, B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - task2: Initialize[Task[A2]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + def sequential[A0, A1, A2, B]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + task2: Initialize[Task[A2]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential(List(unitTask(task0), unitTask(task1), unitTask(task2)), last) - def sequential[A0, A1, A2, A3, B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - task2: Initialize[Task[A2]], - task3: Initialize[Task[A3]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + def sequential[A0, A1, A2, A3, B]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + task2: Initialize[Task[A2]], + task3: Initialize[Task[A3]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential(List(unitTask(task0), unitTask(task1), unitTask(task2), unitTask(task3)), last) - def sequential[A0, A1, A2, A3, A4, B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - task2: Initialize[Task[A2]], - task3: Initialize[Task[A3]], - task4: Initialize[Task[A4]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + def sequential[A0, A1, A2, A3, A4, B]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + task2: Initialize[Task[A2]], + task3: Initialize[Task[A3]], + task4: Initialize[Task[A4]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List(unitTask(task0), unitTask(task1), unitTask(task2), unitTask(task3), unitTask(task4)), - last) - def sequential[A0, A1, A2, A3, A4, A5, B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - task2: Initialize[Task[A2]], - task3: Initialize[Task[A3]], - task4: Initialize[Task[A4]], - task5: Initialize[Task[A5]], - last: Initialize[Task[B]]): Initialize[Task[B]] = - sequential(List(unitTask(task0), - unitTask(task1), - unitTask(task2), - unitTask(task3), - unitTask(task4), - unitTask(task5)), - last) - def sequential[A0, A1, A2, A3, A4, A5, A6, B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - task2: Initialize[Task[A2]], - task3: Initialize[Task[A3]], - task4: Initialize[Task[A4]], - task5: Initialize[Task[A5]], - task6: Initialize[Task[A6]], - last: Initialize[Task[B]]): Initialize[Task[B]] = - sequential(List(unitTask(task0), - unitTask(task1), - unitTask(task2), - unitTask(task3), - unitTask(task4), - unitTask(task5), - unitTask(task6)), - last) + last + ) + def sequential[A0, A1, A2, A3, A4, A5, B]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + task2: Initialize[Task[A2]], + task3: Initialize[Task[A3]], + task4: Initialize[Task[A4]], + task5: Initialize[Task[A5]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = + sequential( + List( + unitTask(task0), + unitTask(task1), + unitTask(task2), + unitTask(task3), + unitTask(task4), + unitTask(task5) + ), + last + ) + def sequential[A0, A1, A2, A3, A4, A5, A6, B]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + task2: Initialize[Task[A2]], + task3: Initialize[Task[A3]], + task4: Initialize[Task[A4]], + task5: Initialize[Task[A5]], + task6: Initialize[Task[A6]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = + sequential( + List( + unitTask(task0), + unitTask(task1), + unitTask(task2), + unitTask(task3), + unitTask(task4), + unitTask(task5), + unitTask(task6) + ), + last + ) def sequential[A0, A1, A2, A3, A4, A5, A6, A7, B]( task0: Initialize[Task[A0]], task1: Initialize[Task[A1]], @@ -84,16 +105,21 @@ trait TaskSequential { task5: Initialize[Task[A5]], task6: Initialize[Task[A6]], task7: Initialize[Task[A7]], - last: Initialize[Task[B]]): Initialize[Task[B]] = - sequential(List(unitTask(task0), - unitTask(task1), - unitTask(task2), - unitTask(task3), - unitTask(task4), - unitTask(task5), - unitTask(task6), - unitTask(task7)), - last) + last: Initialize[Task[B]] + ): Initialize[Task[B]] = + sequential( + List( + unitTask(task0), + unitTask(task1), + unitTask(task2), + unitTask(task3), + unitTask(task4), + unitTask(task5), + unitTask(task6), + unitTask(task7) + ), + last + ) def sequential[A0, A1, A2, A3, A4, A5, A6, A7, A8, B]( task0: Initialize[Task[A0]], task1: Initialize[Task[A1]], @@ -104,17 +130,20 @@ trait TaskSequential { task6: Initialize[Task[A6]], task7: Initialize[Task[A7]], task8: Initialize[Task[A8]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( - List(unitTask(task0), - unitTask(task1), - unitTask(task2), - unitTask(task3), - unitTask(task4), - unitTask(task5), - unitTask(task6), - unitTask(task7), - unitTask(task8)), + List( + unitTask(task0), + unitTask(task1), + unitTask(task2), + unitTask(task3), + unitTask(task4), + unitTask(task5), + unitTask(task6), + unitTask(task7), + unitTask(task8) + ), last ) def sequential[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, B]( @@ -128,7 +157,8 @@ trait TaskSequential { task7: Initialize[Task[A7]], task8: Initialize[Task[A8]], task9: Initialize[Task[A9]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -156,7 +186,8 @@ trait TaskSequential { task8: Initialize[Task[A8]], task9: Initialize[Task[A9]], task10: Initialize[Task[A10]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -186,7 +217,8 @@ trait TaskSequential { task9: Initialize[Task[A9]], task10: Initialize[Task[A10]], task11: Initialize[Task[A11]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -218,7 +250,8 @@ trait TaskSequential { task10: Initialize[Task[A10]], task11: Initialize[Task[A11]], task12: Initialize[Task[A12]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -252,7 +285,8 @@ trait TaskSequential { task11: Initialize[Task[A11]], task12: Initialize[Task[A12]], task13: Initialize[Task[A13]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -288,7 +322,8 @@ trait TaskSequential { task12: Initialize[Task[A12]], task13: Initialize[Task[A13]], task14: Initialize[Task[A14]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -326,7 +361,8 @@ trait TaskSequential { task13: Initialize[Task[A13]], task14: Initialize[Task[A14]], task15: Initialize[Task[A15]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -366,7 +402,8 @@ trait TaskSequential { task14: Initialize[Task[A14]], task15: Initialize[Task[A15]], task16: Initialize[Task[A16]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -433,45 +470,49 @@ trait TaskSequential { ), last ) - def sequential[A0, - A1, - A2, - A3, - A4, - A5, - A6, - A7, - A8, - A9, - A10, - A11, - A12, - A13, - A14, - A15, - A16, - A17, - A18, - B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - task2: Initialize[Task[A2]], - task3: Initialize[Task[A3]], - task4: Initialize[Task[A4]], - task5: Initialize[Task[A5]], - task6: Initialize[Task[A6]], - task7: Initialize[Task[A7]], - task8: Initialize[Task[A8]], - task9: Initialize[Task[A9]], - task10: Initialize[Task[A10]], - task11: Initialize[Task[A11]], - task12: Initialize[Task[A12]], - task13: Initialize[Task[A13]], - task14: Initialize[Task[A14]], - task15: Initialize[Task[A15]], - task16: Initialize[Task[A16]], - task17: Initialize[Task[A17]], - task18: Initialize[Task[A18]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + def sequential[ + A0, + A1, + A2, + A3, + A4, + A5, + A6, + A7, + A8, + A9, + A10, + A11, + A12, + A13, + A14, + A15, + A16, + A17, + A18, + B + ]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + task2: Initialize[Task[A2]], + task3: Initialize[Task[A3]], + task4: Initialize[Task[A4]], + task5: Initialize[Task[A5]], + task6: Initialize[Task[A6]], + task7: Initialize[Task[A7]], + task8: Initialize[Task[A8]], + task9: Initialize[Task[A9]], + task10: Initialize[Task[A10]], + task11: Initialize[Task[A11]], + task12: Initialize[Task[A12]], + task13: Initialize[Task[A13]], + task14: Initialize[Task[A14]], + task15: Initialize[Task[A15]], + task16: Initialize[Task[A16]], + task17: Initialize[Task[A17]], + task18: Initialize[Task[A18]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -496,47 +537,51 @@ trait TaskSequential { ), last ) - def sequential[A0, - A1, - A2, - A3, - A4, - A5, - A6, - A7, - A8, - A9, - A10, - A11, - A12, - A13, - A14, - A15, - A16, - A17, - A18, - A19, - B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - task2: Initialize[Task[A2]], - task3: Initialize[Task[A3]], - task4: Initialize[Task[A4]], - task5: Initialize[Task[A5]], - task6: Initialize[Task[A6]], - task7: Initialize[Task[A7]], - task8: Initialize[Task[A8]], - task9: Initialize[Task[A9]], - task10: Initialize[Task[A10]], - task11: Initialize[Task[A11]], - task12: Initialize[Task[A12]], - task13: Initialize[Task[A13]], - task14: Initialize[Task[A14]], - task15: Initialize[Task[A15]], - task16: Initialize[Task[A16]], - task17: Initialize[Task[A17]], - task18: Initialize[Task[A18]], - task19: Initialize[Task[A19]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + def sequential[ + A0, + A1, + A2, + A3, + A4, + A5, + A6, + A7, + A8, + A9, + A10, + A11, + A12, + A13, + A14, + A15, + A16, + A17, + A18, + A19, + B + ]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + task2: Initialize[Task[A2]], + task3: Initialize[Task[A3]], + task4: Initialize[Task[A4]], + task5: Initialize[Task[A5]], + task6: Initialize[Task[A6]], + task7: Initialize[Task[A7]], + task8: Initialize[Task[A8]], + task9: Initialize[Task[A9]], + task10: Initialize[Task[A10]], + task11: Initialize[Task[A11]], + task12: Initialize[Task[A12]], + task13: Initialize[Task[A13]], + task14: Initialize[Task[A14]], + task15: Initialize[Task[A15]], + task16: Initialize[Task[A16]], + task17: Initialize[Task[A17]], + task18: Initialize[Task[A18]], + task19: Initialize[Task[A19]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -562,49 +607,53 @@ trait TaskSequential { ), last ) - def sequential[A0, - A1, - A2, - A3, - A4, - A5, - A6, - A7, - A8, - A9, - A10, - A11, - A12, - A13, - A14, - A15, - A16, - A17, - A18, - A19, - A20, - B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - task2: Initialize[Task[A2]], - task3: Initialize[Task[A3]], - task4: Initialize[Task[A4]], - task5: Initialize[Task[A5]], - task6: Initialize[Task[A6]], - task7: Initialize[Task[A7]], - task8: Initialize[Task[A8]], - task9: Initialize[Task[A9]], - task10: Initialize[Task[A10]], - task11: Initialize[Task[A11]], - task12: Initialize[Task[A12]], - task13: Initialize[Task[A13]], - task14: Initialize[Task[A14]], - task15: Initialize[Task[A15]], - task16: Initialize[Task[A16]], - task17: Initialize[Task[A17]], - task18: Initialize[Task[A18]], - task19: Initialize[Task[A19]], - task20: Initialize[Task[A20]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + def sequential[ + A0, + A1, + A2, + A3, + A4, + A5, + A6, + A7, + A8, + A9, + A10, + A11, + A12, + A13, + A14, + A15, + A16, + A17, + A18, + A19, + A20, + B + ]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + task2: Initialize[Task[A2]], + task3: Initialize[Task[A3]], + task4: Initialize[Task[A4]], + task5: Initialize[Task[A5]], + task6: Initialize[Task[A6]], + task7: Initialize[Task[A7]], + task8: Initialize[Task[A8]], + task9: Initialize[Task[A9]], + task10: Initialize[Task[A10]], + task11: Initialize[Task[A11]], + task12: Initialize[Task[A12]], + task13: Initialize[Task[A13]], + task14: Initialize[Task[A14]], + task15: Initialize[Task[A15]], + task16: Initialize[Task[A16]], + task17: Initialize[Task[A17]], + task18: Initialize[Task[A18]], + task19: Initialize[Task[A19]], + task20: Initialize[Task[A20]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -631,51 +680,55 @@ trait TaskSequential { ), last ) - def sequential[A0, - A1, - A2, - A3, - A4, - A5, - A6, - A7, - A8, - A9, - A10, - A11, - A12, - A13, - A14, - A15, - A16, - A17, - A18, - A19, - A20, - A21, - B](task0: Initialize[Task[A0]], - task1: Initialize[Task[A1]], - task2: Initialize[Task[A2]], - task3: Initialize[Task[A3]], - task4: Initialize[Task[A4]], - task5: Initialize[Task[A5]], - task6: Initialize[Task[A6]], - task7: Initialize[Task[A7]], - task8: Initialize[Task[A8]], - task9: Initialize[Task[A9]], - task10: Initialize[Task[A10]], - task11: Initialize[Task[A11]], - task12: Initialize[Task[A12]], - task13: Initialize[Task[A13]], - task14: Initialize[Task[A14]], - task15: Initialize[Task[A15]], - task16: Initialize[Task[A16]], - task17: Initialize[Task[A17]], - task18: Initialize[Task[A18]], - task19: Initialize[Task[A19]], - task20: Initialize[Task[A20]], - task21: Initialize[Task[A21]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + def sequential[ + A0, + A1, + A2, + A3, + A4, + A5, + A6, + A7, + A8, + A9, + A10, + A11, + A12, + A13, + A14, + A15, + A16, + A17, + A18, + A19, + A20, + A21, + B + ]( + task0: Initialize[Task[A0]], + task1: Initialize[Task[A1]], + task2: Initialize[Task[A2]], + task3: Initialize[Task[A3]], + task4: Initialize[Task[A4]], + task5: Initialize[Task[A5]], + task6: Initialize[Task[A6]], + task7: Initialize[Task[A7]], + task8: Initialize[Task[A8]], + task9: Initialize[Task[A9]], + task10: Initialize[Task[A10]], + task11: Initialize[Task[A11]], + task12: Initialize[Task[A12]], + task13: Initialize[Task[A13]], + task14: Initialize[Task[A14]], + task15: Initialize[Task[A15]], + task16: Initialize[Task[A16]], + task17: Initialize[Task[A17]], + task18: Initialize[Task[A18]], + task19: Initialize[Task[A19]], + task20: Initialize[Task[A20]], + task21: Initialize[Task[A21]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = sequential( List( unitTask(task0), @@ -704,8 +757,10 @@ trait TaskSequential { last ) - def sequential[B](tasks: Seq[Initialize[Task[Unit]]], - last: Initialize[Task[B]]): Initialize[Task[B]] = + def sequential[B]( + tasks: Seq[Initialize[Task[Unit]]], + last: Initialize[Task[B]] + ): Initialize[Task[B]] = tasks.toList match { case Nil => Def.task { last.value } case x :: xs => diff --git a/main/src/main/scala/sbt/internal/TaskTimings.scala b/main/src/main/scala/sbt/internal/TaskTimings.scala index 3586eb4b1..9e095fdc3 100644 --- a/main/src/main/scala/sbt/internal/TaskTimings.scala +++ b/main/src/main/scala/sbt/internal/TaskTimings.scala @@ -52,10 +52,12 @@ private[sbt] final class TaskTimings(shutdown: Boolean) extends ExecuteProgress[ if (!shutdown) start = System.nanoTime } - def registered(state: Unit, - task: Task[_], - allDeps: Iterable[Task[_]], - pendingDeps: Iterable[Task[_]]) = { + def registered( + state: Unit, + task: Task[_], + allDeps: Iterable[Task[_]], + pendingDeps: Iterable[Task[_]] + ) = { pendingDeps foreach { t => if (transformNode(t).isEmpty) anonOwners.put(t, task) } diff --git a/main/src/main/scala/sbt/internal/parser/SbtParser.scala b/main/src/main/scala/sbt/internal/parser/SbtParser.scala index c8f0e9797..fa427ec92 100644 --- a/main/src/main/scala/sbt/internal/parser/SbtParser.scala +++ b/main/src/main/scala/sbt/internal/parser/SbtParser.scala @@ -85,7 +85,8 @@ private[sbt] object SbtParser { val reporter = reporters.get(fileName) if (reporter == null) { scalacGlobalInitReporter.getOrElse( - sys.error(s"Sbt forgot to initialize `scalacGlobalInitReporter`.")) + sys.error(s"Sbt forgot to initialize `scalacGlobalInitReporter`.") + ) } else reporter } @@ -139,9 +140,11 @@ private[sbt] object SbtParser { * The reporter id must be unique per parsing session. * @return */ - private[sbt] def parse(code: String, - filePath: String, - reporterId0: Option[String]): (Seq[Tree], String) = { + private[sbt] def parse( + code: String, + filePath: String, + reporterId0: Option[String] + ): (Seq[Tree], String) = { import defaultGlobalForParser._ val reporterId = reporterId0.getOrElse(s"$filePath-${Random.nextInt}") val reporter = globalReporter.getOrCreateReporter(reporterId) @@ -204,7 +207,8 @@ private[sbt] case class SbtParser(file: File, lines: Seq[String]) extends Parsed private def splitExpressions( file: File, - lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)], Seq[(String, Tree)]) = { + lines: Seq[String] + ): (Seq[(String, Int)], Seq[(String, LineRange)], Seq[(String, Tree)]) = { import sbt.internal.parser.MissingBracketHandler.findMissingText val indexedLines = lines.toIndexedSeq @@ -224,7 +228,8 @@ private[sbt] case class SbtParser(file: File, lines: Seq[String]) extends Parsed // Issue errors val positionLine = badTree.pos.line throw new MessageOnlyException( - s"""[$fileName]:$positionLine: Pattern matching in val statements is not supported""".stripMargin) + s"""[$fileName]:$positionLine: Pattern matching in val statements is not supported""".stripMargin + ) } val (imports: Seq[Tree], statements: Seq[Tree]) = parsedTrees partition { @@ -262,9 +267,9 @@ private[sbt] case class SbtParser(file: File, lines: Seq[String]) extends Parsed } val stmtTreeLineRange = statements flatMap convertStatement val importsLineRange = importsToLineRanges(content, imports) - (importsLineRange, - stmtTreeLineRange.map { case (stmt, _, lr) => (stmt, lr) }, - stmtTreeLineRange.map { case (stmt, tree, _) => (stmt, tree) }) + (importsLineRange, stmtTreeLineRange.map { case (stmt, _, lr) => (stmt, lr) }, stmtTreeLineRange.map { + case (stmt, tree, _) => (stmt, tree) + }) } /** @@ -300,8 +305,10 @@ private[sbt] case class SbtParser(file: File, lines: Seq[String]) extends Parsed * @param importsInOneLine - imports in line * @return - text */ - private def extractLine(modifiedContent: String, - importsInOneLine: Seq[((Int, Int), Int)]): String = { + private def extractLine( + modifiedContent: String, + importsInOneLine: Seq[((Int, Int), Int)] + ): String = { val (begin, end) = importsInOneLine.foldLeft((Int.MaxValue, Int.MinValue)) { case ((min, max), ((start, end), _)) => (min.min(start), max.max(end)) @@ -333,7 +340,8 @@ private[sbt] object MissingBracketHandler { positionLine: Int, fileName: String, originalException: Throwable, - reporterId: Option[String] = Some(Random.nextInt.toString)): String = { + reporterId: Option[String] = Some(Random.nextInt.toString) + ): String = { findClosingBracketIndex(content, positionEnd) match { case Some(index) => val text = content.substring(positionEnd, index + 1) @@ -342,16 +350,19 @@ private[sbt] object MissingBracketHandler { case Success(_) => text case Failure(_) => - findMissingText(content, - index + 1, - positionLine, - fileName, - originalException, - reporterId) + findMissingText( + content, + index + 1, + positionLine, + fileName, + originalException, + reporterId + ) } case _ => throw new MessageOnlyException( - s"""[$fileName]:$positionLine: ${originalException.getMessage}""".stripMargin) + s"""[$fileName]:$positionLine: ${originalException.getMessage}""".stripMargin + ) } } diff --git a/main/src/main/scala/sbt/internal/parser/SbtRefactorings.scala b/main/src/main/scala/sbt/internal/parser/SbtRefactorings.scala index 1e3893672..a82eb971e 100644 --- a/main/src/main/scala/sbt/internal/parser/SbtRefactorings.scala +++ b/main/src/main/scala/sbt/internal/parser/SbtRefactorings.scala @@ -26,8 +26,10 @@ private[sbt] object SbtRefactorings { * the first will be replaced and the other will be removed. * @return a SbtConfigFile with new lines which represent the contents of the refactored .sbt file. */ - def applySessionSettings(configFile: SbtConfigFile, - commands: Seq[SessionSetting]): SbtConfigFile = { + def applySessionSettings( + configFile: SbtConfigFile, + commands: Seq[SessionSetting] + ): SbtConfigFile = { val (file, lines) = configFile val split = SbtParser(FAKE_FILE, lines) val recordedCommands = recordCommands(commands, split) @@ -37,8 +39,10 @@ private[sbt] object SbtRefactorings { (file, newContent.lines.toList) } - private def replaceFromBottomToTop(modifiedContent: String, - sortedRecordedCommands: Seq[(Int, String, String)]) = { + private def replaceFromBottomToTop( + modifiedContent: String, + sortedRecordedCommands: Seq[(Int, String, String)] + ) = { sortedRecordedCommands.foldLeft(modifiedContent) { case (acc, (from, old, replacement)) => val before = acc.substring(0, from) diff --git a/main/src/main/scala/sbt/internal/server/Definition.scala b/main/src/main/scala/sbt/internal/server/Definition.scala index bc6a0a220..44e6e9efa 100644 --- a/main/src/main/scala/sbt/internal/server/Definition.scala +++ b/main/src/main/scala/sbt/internal/server/Definition.scala @@ -210,7 +210,8 @@ private[sbt] object Definition { private[sbt] def updateCache[F[_]](cache: Cache[Any])(cacheFile: String, useBinary: Boolean)( implicit mode: Mode[F], - flags: Flags): F[Any] = { + flags: Flags + ): F[Any] = { mode.M.flatMap(AnalysesAccess.getFrom(cache)) { case None => AnalysesAccess.putIn(cache, Set(cacheFile -> useBinary -> None), Option(Duration.Inf)) diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index df43cf534..9245e2ab8 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -27,10 +27,11 @@ 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) + ServerCapabilities( + textDocumentSync = TextDocumentSyncOptions(true, 0, false, false, SaveOptions(false)), + hoverProvider = false, + definitionProvider = true + ) } lazy val handler: ServerHandler = ServerHandler({ @@ -42,16 +43,22 @@ private[sbt] object LanguageServerProtocol { import internalJsonProtocol._ def json(r: JsonRpcRequestMessage) = r.params.getOrElse( - throw LangServerError(ErrorCodes.InvalidParams, - s"param is expected on '${r.method}' method.")) + 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.")) + 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)) () diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index b07e38775..80149dfc5 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -21,14 +21,15 @@ import sbt.internal.util.codec.JValueFormats import sbt.internal.protocol.{ JsonRpcRequestMessage, JsonRpcNotificationMessage } import sbt.util.Logger -final class NetworkChannel(val name: String, - connection: Socket, - structure: BuildStructure, - auth: Set[ServerAuthentication], - instance: ServerInstance, - handlers: Seq[ServerHandler], - val log: Logger) - extends CommandChannel +final class NetworkChannel( + val name: String, + connection: Socket, + structure: BuildStructure, + auth: Set[ServerAuthentication], + instance: ServerInstance, + handlers: Seq[ServerHandler], + val log: Logger +) extends CommandChannel with LanguageServerProtocol { import NetworkChannel._ @@ -206,7 +207,8 @@ final class NetworkChannel(val name: String, .fold( errorDesc => log.error( - s"Got invalid chunk from client (${new String(chunk.toArray, "UTF-8")}): " + errorDesc), + s"Got invalid chunk from client (${new String(chunk.toArray, "UTF-8")}): " + errorDesc + ), onCommand ) case _ => @@ -342,7 +344,8 @@ final class NetworkChannel(val name: String, private def onExecCommand(cmd: ExecCommand) = { if (initialized) { append( - Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name)))) + 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/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index fa106d23e..6ab872f5b 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -32,8 +32,10 @@ object SettingQuery { new ParsedExplicitValue(v) } - def projectRef(index: KeyIndex, - currentBuild: URI): Parser[ParsedExplicitAxis[ResolvedReference]] = { + def projectRef( + index: KeyIndex, + currentBuild: URI + ): Parser[ParsedExplicitAxis[ResolvedReference]] = { val global = token(Act.ZeroString ~ '/') ^^^ ParsedExplicitGlobal val trailing = '/' !!! "Expected '/' (if selecting a project)" global | explicitValue(Act.resolvedReference(index, currentBuild, trailing)) @@ -107,15 +109,21 @@ object SettingQuery { def toJson[A: JsonWriter](x: A): JValue = Converter toJsonUnsafe x - def getSettingJsonValue[A](structure: BuildStructure, - key: Def.ScopedKey[A]): Either[String, JValue] = - getSettingValue(structure, key) flatMap (value => - getJsonWriter(key.key) map { implicit jw: JsonWriter[A] => - toJson(value) - }) + def getSettingJsonValue[A]( + structure: BuildStructure, + key: Def.ScopedKey[A] + ): Either[String, JValue] = + getSettingValue(structure, key) flatMap ( + value => + getJsonWriter(key.key) map { implicit jw: JsonWriter[A] => + toJson(value) + } + ) - def handleSettingQueryEither(req: SettingQuery, - structure: BuildStructure): Either[String, SettingQuerySuccess] = { + def handleSettingQueryEither( + req: SettingQuery, + structure: BuildStructure + ): Either[String, SettingQuerySuccess] = { val key = Parser.parse(req.setting, scopedKeyParser(structure)) for { diff --git a/main/src/test/scala/DefaultsTest.scala b/main/src/test/scala/DefaultsTest.scala index b029123fc..b9399ece4 100644 --- a/main/src/test/scala/DefaultsTest.scala +++ b/main/src/test/scala/DefaultsTest.scala @@ -30,8 +30,10 @@ object DefaultsTest extends Specification { } "work correctly with excludes" in { - assertFiltered(List("Test*", "-Test2"), - Map("Test1" -> true, "Test2" -> false, "Foo" -> false)) + assertFiltered( + List("Test*", "-Test2"), + Map("Test1" -> true, "Test2" -> false, "Foo" -> false) + ) } "work correctly without includes" in { @@ -43,18 +45,24 @@ object DefaultsTest extends Specification { } "cope with multiple filters" in { - assertFiltered(List("T*1", "T*2", "-F*"), - Map("Test1" -> true, "Test2" -> true, "Foo" -> false)) + assertFiltered( + List("T*1", "T*2", "-F*"), + Map("Test1" -> true, "Test2" -> true, "Foo" -> false) + ) } "cope with multiple exclusion filters, no includes" in { - assertFiltered(List("-A*", "-F*"), - Map("Test1" -> true, "Test2" -> true, "AAA" -> false, "Foo" -> false)) + assertFiltered( + List("-A*", "-F*"), + Map("Test1" -> true, "Test2" -> true, "AAA" -> false, "Foo" -> false) + ) } "cope with multiple exclusion filters with includes" in { - assertFiltered(List("T*", "-T*1", "-T*2"), - Map("Test1" -> false, "Test2" -> false, "Test3" -> true)) + assertFiltered( + List("T*", "-T*1", "-T*2"), + Map("Test1" -> false, "Test2" -> false, "Test3" -> true) + ) } } diff --git a/main/src/test/scala/Delegates.scala b/main/src/test/scala/Delegates.scala index af77a8e23..4fa6319e9 100644 --- a/main/src/test/scala/Delegates.scala +++ b/main/src/test/scala/Delegates.scala @@ -47,7 +47,8 @@ object Delegates extends Properties("delegates") { } } property("Initial scope present with all combinations of Global axes") = allAxes( - (s, ds, _) => globalCombinations(s, ds)) + (s, ds, _) => globalCombinations(s, ds) + ) property("initial scope first") = forAll { (keys: Keys) => allDelegates(keys) { (scope, ds) => diff --git a/main/src/test/scala/ParseKey.scala b/main/src/test/scala/ParseKey.scala index 23d9d0778..ecc6374dc 100644 --- a/main/src/test/scala/ParseKey.scala +++ b/main/src/test/scala/ParseKey.scala @@ -58,15 +58,15 @@ object ParseKey extends Properties("Key parser test") { } property( - "An unspecified configuration axis resolves to the first configuration directly defining the key or else Zero") = - forAll { (skm: StructureKeyMask) => - import skm.{ structure, key } - val mask = ScopeMask(config = false) - val resolvedConfig = Resolve.resolveConfig(structure.extra, key.key, mask)(key.scope).config - parseCheck(structure, key, mask)( - sk => (sk.scope.config == resolvedConfig) || (sk.scope == Scope.GlobalScope) - ) :| s"Expected configuration: ${resolvedConfig map (_.name)}" - } + "An unspecified configuration axis resolves to the first configuration directly defining the key or else Zero" + ) = forAll { (skm: StructureKeyMask) => + import skm.{ structure, key } + val mask = ScopeMask(config = false) + val resolvedConfig = Resolve.resolveConfig(structure.extra, key.key, mask)(key.scope).config + parseCheck(structure, key, mask)( + sk => (sk.scope.config == resolvedConfig) || (sk.scope == Scope.GlobalScope) + ) :| s"Expected configuration: ${resolvedConfig map (_.name)}" + } implicit val arbStructure: Arbitrary[Structure] = Arbitrary { for { diff --git a/main/src/test/scala/PluginCommandTest.scala b/main/src/test/scala/PluginCommandTest.scala index 8262326ae..d40863578 100644 --- a/main/src/test/scala/PluginCommandTest.scala +++ b/main/src/test/scala/PluginCommandTest.scala @@ -38,23 +38,29 @@ object PluginCommandTest extends Specification { "The `plugin` command" should { "should work for plugins within nested in one package" in { - val output = processCommand("plugin sbt.PluginCommandTestPlugin0", - PluginCommandTestPlugin0, - PluginCommandTestPlugin1) + val output = processCommand( + "plugin sbt.PluginCommandTestPlugin0", + PluginCommandTestPlugin0, + PluginCommandTestPlugin1 + ) output must contain("sbt.PluginCommandTestPlugin0 is activated.") } "should work for plugins nested more than one package" in { - val output = processCommand("plugin sbt.subpackage.PluginCommandTestPlugin1", - PluginCommandTestPlugin0, - PluginCommandTestPlugin1) + val output = processCommand( + "plugin sbt.subpackage.PluginCommandTestPlugin1", + PluginCommandTestPlugin0, + PluginCommandTestPlugin1 + ) output must contain("sbt.subpackage.PluginCommandTestPlugin1 is activated.") } "suggest a plugin when given an incorrect plugin with a similar name" in { - val output = processCommand("plugin PluginCommandTestPlugin0", - PluginCommandTestPlugin0, - PluginCommandTestPlugin1) + val output = processCommand( + "plugin PluginCommandTestPlugin0", + PluginCommandTestPlugin0, + PluginCommandTestPlugin1 + ) output must contain( "Not a valid plugin: PluginCommandTestPlugin0 (similar: sbt.PluginCommandTestPlugin0, sbt.subpackage.PluginCommandTestPlugin1)" ) @@ -123,14 +129,16 @@ object FakeState { val loadedBuildUnit = Load.resolveProjects(base.toURI, partBuildUnit, _ => testProject.id) val units = Map(base.toURI -> loadedBuildUnit) - val buildStructure = new BuildStructure(units, - base.toURI, - settings, - data, - structureIndex, - streams, - delegates, - scopeLocal) + val buildStructure = new BuildStructure( + units, + base.toURI, + settings, + data, + structureIndex, + streams, + delegates, + scopeLocal + ) val attributes = AttributeMap.empty ++ AttributeMap( AttributeEntry(Keys.sessionSettings, sessionSettings), @@ -145,9 +153,11 @@ object FakeState { List(), State.newHistory, attributes, - GlobalLogging.initial(MainAppender.globalDefault(ConsoleOut.systemOut), - File.createTempFile("sbt", ".log"), - ConsoleOut.systemOut), + GlobalLogging.initial( + MainAppender.globalDefault(ConsoleOut.systemOut), + File.createTempFile("sbt", ".log"), + ConsoleOut.systemOut + ), None, State.Continue ) diff --git a/main/src/test/scala/PluginsTest.scala b/main/src/test/scala/PluginsTest.scala index 620e6941c..52f381cd1 100644 --- a/main/src/test/scala/PluginsTest.scala +++ b/main/src/test/scala/PluginsTest.scala @@ -42,7 +42,8 @@ object PluginsTest extends Specification { 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""") + - 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]( @@ -50,7 +51,8 @@ object PluginsTest extends Specification { - 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""") + - conflict: sbt.AI\\$$R is enabled by sbt.AI\\$$Q; excluded by sbt.AI\\$$T""" + ) } } } diff --git a/main/src/test/scala/sbt/internal/TestBuild.scala b/main/src/test/scala/sbt/internal/TestBuild.scala index e7bb2ead0..9d9750d04 100644 --- a/main/src/test/scala/sbt/internal/TestBuild.scala +++ b/main/src/test/scala/sbt/internal/TestBuild.scala @@ -48,11 +48,13 @@ abstract class TestBuild { lazy val delegated = scopes map env.delegates } - sealed case class Structure(env: Env, - current: ProjectRef, - data: Settings[Scope], - keyIndex: KeyIndex, - keyMap: Map[String, AttributeKey[_]]) { + sealed case class Structure( + env: Env, + current: ProjectRef, + data: Settings[Scope], + keyIndex: KeyIndex, + keyMap: Map[String, AttributeKey[_]] + ) { override def toString = env.toString + "\n" + "current: " + current + "\nSettings:\n\t" + showData + keyMap.keys .mkString("All keys:\n\t", ", ", "") @@ -64,13 +66,15 @@ abstract class TestBuild { } val extra: BuildUtil[Proj] = { val getp = (build: URI, project: String) => env.buildMap(build).projectMap(project) - new BuildUtil(keyIndex, - data, - env.root.uri, - env.rootProject, - getp, - _.configurations.map(c => ConfigKey(c.name)), - Relation.empty) + new BuildUtil( + keyIndex, + data, + env.root.uri, + env.rootProject, + getp, + _.configurations.map(c => ConfigKey(c.name)), + Relation.empty + ) } lazy val allAttributeKeys: Set[AttributeKey[_]] = { @@ -88,8 +92,10 @@ abstract class TestBuild { val taskAxesMappings = for ((scope, keys) <- data.data.toIterable; key <- keys.keys) yield - (ScopedKey(scope.copy(task = Zero), key), scope.task): (ScopedKey[_], - ScopeAxis[AttributeKey[_]]) + (ScopedKey(scope.copy(task = Zero), key), scope.task): ( + ScopedKey[_], + ScopeAxis[AttributeKey[_]] + ) val taskAxes = Relation.empty ++ taskAxesMappings val zero = new HashSet[ScopedKey[_]] @@ -270,10 +276,12 @@ abstract class TestBuild { listOfN(ig, g) } - implicit def genProjects(build: URI)(implicit genID: Gen[String], - maxDeps: Gen[Int], - count: Gen[Int], - confs: Gen[Seq[Configuration]]): Gen[Seq[Proj]] = + implicit def genProjects(build: URI)( + implicit genID: Gen[String], + maxDeps: Gen[Int], + count: Gen[Int], + confs: Gen[Seq[Configuration]] + ): Gen[Seq[Proj]] = genAcyclic(maxDeps, genID, count) { (id: String) => for (cs <- confs) yield { (deps: Seq[Proj]) => new Proj(id, deps.map { dep => @@ -282,21 +290,26 @@ abstract class TestBuild { } } - def genConfigs(implicit genName: Gen[String], - maxDeps: Gen[Int], - count: Gen[Int]): Gen[Seq[Configuration]] = + def genConfigs( + implicit genName: Gen[String], + maxDeps: Gen[Int], + count: Gen[Int] + ): Gen[Seq[Configuration]] = genAcyclicDirect[Configuration, String](maxDeps, genName, count)( (key, deps) => Configuration .of(key.capitalize, key) - .withExtendsConfigs(deps.toVector)) + .withExtendsConfigs(deps.toVector) + ) def genTasks(implicit genName: Gen[String], maxDeps: Gen[Int], count: Gen[Int]): Gen[Seq[Taskk]] = - genAcyclicDirect[Taskk, String](maxDeps, genName, count)((key, deps) => - new Taskk(AttributeKey[String](key), deps)) + genAcyclicDirect[Taskk, String](maxDeps, genName, count)( + (key, deps) => new Taskk(AttributeKey[String](key), deps) + ) def genAcyclicDirect[A, T](maxDeps: Gen[Int], keyGen: Gen[T], max: Gen[Int])( - make: (T, Seq[A]) => A): Gen[Seq[A]] = + make: (T, Seq[A]) => A + ): Gen[Seq[A]] = genAcyclic[A, T](maxDeps, keyGen, max) { t => Gen.const { deps => make(t, deps) @@ -304,7 +317,8 @@ abstract class TestBuild { } def genAcyclic[A, T](maxDeps: Gen[Int], keyGen: Gen[T], max: Gen[Int])( - make: T => Gen[Seq[A] => A]): Gen[Seq[A]] = + make: T => Gen[Seq[A] => A] + ): Gen[Seq[A]] = max flatMap { count => listOfN(count, keyGen) flatMap { keys => genAcyclic(maxDeps, keys.distinct)(make) @@ -325,9 +339,11 @@ abstract class TestBuild { (key, deps, mk) } - def genAcyclic[T](maxDeps: Gen[Int], - names: List[T], - acc: List[Gen[(T, Seq[T])]]): Gen[Seq[(T, Seq[T])]] = + def genAcyclic[T]( + maxDeps: Gen[Int], + names: List[T], + acc: List[Gen[(T, Seq[T])]] + ): Gen[Seq[(T, Seq[T])]] = names match { case Nil => sequence(acc) case x :: xs => diff --git a/main/src/test/scala/sbt/internal/parser/CheckIfParsedSpec.scala b/main/src/test/scala/sbt/internal/parser/CheckIfParsedSpec.scala index c230f8a68..807b31813 100644 --- a/main/src/test/scala/sbt/internal/parser/CheckIfParsedSpec.scala +++ b/main/src/test/scala/sbt/internal/parser/CheckIfParsedSpec.scala @@ -11,8 +11,8 @@ package parser abstract class CheckIfParsedSpec( implicit val splitter: SplitExpressions.SplitExpression = - EvaluateConfigurations.splitExpressions) - extends AbstractSpec { + EvaluateConfigurations.splitExpressions +) extends AbstractSpec { this.getClass.getName should { diff --git a/main/src/test/scala/sbt/internal/parser/CommentedXmlSpec.scala b/main/src/test/scala/sbt/internal/parser/CommentedXmlSpec.scala index a3cb444a7..95f53e91d 100644 --- a/main/src/test/scala/sbt/internal/parser/CommentedXmlSpec.scala +++ b/main/src/test/scala/sbt/internal/parser/CommentedXmlSpec.scala @@ -44,10 +44,7 @@ class CommentedXmlSpec extends CheckIfParsedSpec { | |publishMavenStyle := true | - """.stripMargin, - "Wrong Commented xml ", - false, - true), + """.stripMargin, "Wrong Commented xml ", false, true), (""" |val scmpom = taskKey[xml.NodeBuffer]("Node buffer") | @@ -67,23 +64,14 @@ class CommentedXmlSpec extends CheckIfParsedSpec { | |publishMavenStyle := true | - """.stripMargin, - "Commented xml ", - false, - true), + """.stripMargin, "Commented xml ", false, true), (""" |import sbt._ | |// - """.stripMargin, - "Xml in comment2", - false, - false) + """.stripMargin, "Xml in comment2", false, false) ) } diff --git a/main/src/test/scala/sbt/internal/parser/EmbeddedXmlSpec.scala b/main/src/test/scala/sbt/internal/parser/EmbeddedXmlSpec.scala index d939d5e8c..bc2c78d38 100644 --- a/main/src/test/scala/sbt/internal/parser/EmbeddedXmlSpec.scala +++ b/main/src/test/scala/sbt/internal/parser/EmbeddedXmlSpec.scala @@ -52,16 +52,10 @@ class EmbeddedXmlSpec extends CheckIfParsedSpec { protected val files = Seq( (""" |val p = - """.stripMargin, - "Xml modified closing tag at end of file", - false, - true), + """.stripMargin, "Xml modified closing tag at end of file", false, true), (""" |val p = - """.stripMargin, - "Xml at end of file", - false, - true), + """.stripMargin, "Xml at end of file", false, true), ("""| | |name := "play-html-compressor" @@ -98,10 +92,7 @@ class EmbeddedXmlSpec extends CheckIfParsedSpec { | |val tra = "" | - """.stripMargin, - "Xml in string", - false, - true), + """.stripMargin, "Xml in string", false, true), ("""| | |name := "play-html-compressor" @@ -131,10 +122,7 @@ class EmbeddedXmlSpec extends CheckIfParsedSpec { | | | - | """.stripMargin, - "Xml with attributes", - false, - true), + | """.stripMargin, "Xml with attributes", false, true), ( """ |scalaVersion := "2.10.2" diff --git a/main/src/test/scala/sbt/internal/parser/ErrorSpec.scala b/main/src/test/scala/sbt/internal/parser/ErrorSpec.scala index 845baccd7..77641efcc 100644 --- a/main/src/test/scala/sbt/internal/parser/ErrorSpec.scala +++ b/main/src/test/scala/sbt/internal/parser/ErrorSpec.scala @@ -50,7 +50,8 @@ class ErrorSpec extends AbstractSpec { buildSbt.length, 2, "fake.txt", - new MessageOnlyException("fake")) must throwA[MessageOnlyException] + new MessageOnlyException("fake") + ) must throwA[MessageOnlyException] } "handle xml error " in { diff --git a/main/src/test/scala/sbt/internal/parser/SessionSettingsSpec.scala b/main/src/test/scala/sbt/internal/parser/SessionSettingsSpec.scala index 3eb364ed9..8d8876091 100644 --- a/main/src/test/scala/sbt/internal/parser/SessionSettingsSpec.scala +++ b/main/src/test/scala/sbt/internal/parser/SessionSettingsSpec.scala @@ -33,8 +33,9 @@ abstract class AbstractSessionSettingsSpec(folder: String) extends AbstractSpec } } - private def runTestOnFiles(expectedResultAndMap: File => Seq[(List[String], Seq[SessionSetting])]) - : MatchResult[GenTraversableOnce[File]] = { + private def runTestOnFiles( + expectedResultAndMap: File => Seq[(List[String], Seq[SessionSetting])] + ): MatchResult[GenTraversableOnce[File]] = { val allFiles = rootDir .listFiles(new FilenameFilter() { diff --git a/main/src/test/scala/sbt/internal/parser/SplitExpressionsBehavior.scala b/main/src/test/scala/sbt/internal/parser/SplitExpressionsBehavior.scala index bda88bc0d..c4df5a0ce 100644 --- a/main/src/test/scala/sbt/internal/parser/SplitExpressionsBehavior.scala +++ b/main/src/test/scala/sbt/internal/parser/SplitExpressionsBehavior.scala @@ -15,7 +15,8 @@ import org.specs2.mutable.SpecificationLike trait SplitExpression { def split(s: String, file: File = new File("noFile"))( - implicit splitter: SplitExpressions.SplitExpression) = splitter(file, s.split("\n").toSeq) + implicit splitter: SplitExpressions.SplitExpression + ) = splitter(file, s.split("\n").toSeq) } trait SplitExpressionsBehavior extends SplitExpression { this: SpecificationLike => diff --git a/main/src/test/scala/sbt/internal/server/DefinitionTest.scala b/main/src/test/scala/sbt/internal/server/DefinitionTest.scala index 5cbff9163..d09c72e08 100644 --- a/main/src/test/scala/sbt/internal/server/DefinitionTest.scala +++ b/main/src/test/scala/sbt/internal/server/DefinitionTest.scala @@ -24,7 +24,8 @@ class DefinitionTest extends org.specs2.mutable.Specification { } "find valid standard scala identifier with comma" in { textProcessor.identifier("def foo(a: identifier, b: other) = ???", 13) must beSome( - "identifier") + "identifier" + ) } "find valid standard short scala identifier when caret is set at the start of it" in { textProcessor.identifier("val a = 0", 4) must beSome("a") @@ -88,11 +89,13 @@ class DefinitionTest extends org.specs2.mutable.Specification { } "match class in line version 4" in { textProcessor.classTraitObjectInLine("A")(" class A[A] ") must contain( - ("class A", 3)) + ("class A", 3) + ) } "match class in line version 5" in { textProcessor.classTraitObjectInLine("A")(" class A [A] ") must contain( - ("class A", 3)) + ("class A", 3) + ) } "match class in line version 6" in { textProcessor.classTraitObjectInLine("A")("class A[A[_]] {") must contain(("class A", 0)) @@ -111,11 +114,13 @@ class DefinitionTest extends org.specs2.mutable.Specification { } "match trait in line version 4" in { textProcessor.classTraitObjectInLine("A")(" trait A[A] ") must contain( - ("trait A", 3)) + ("trait A", 3) + ) } "match trait in line version 5" in { textProcessor.classTraitObjectInLine("A")(" trait A [A] ") must contain( - ("trait A", 3)) + ("trait A", 3) + ) } "match trait in line version 6" in { textProcessor.classTraitObjectInLine("A")("trait A[A[_]] {") must contain(("trait A", 0)) diff --git a/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala b/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala index a80819af1..b60121458 100644 --- a/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala +++ b/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala @@ -126,20 +126,24 @@ object SettingQueryTest extends org.specs2.mutable.Specification { val buildUnit: BuildUnit = { val loadedPlugins: LoadedPlugins = - noPlugins(projectStandard(baseFile), - config.copy(pluginManagement = config.pluginManagement.forPlugin)) + noPlugins( + projectStandard(baseFile), + config.copy(pluginManagement = config.pluginManagement.forPlugin) + ) val project: Project = { val project0 = Project("t", baseFile) settings projectSettings val fileToLoadedSbtFileMap = new mutable.HashMap[File, LoadedSbtFile] val autoPlugins = loadedPlugins.detected.deducePluginsFromProject(project0, state.log) val injectSettings = config.injectSettings - resolveProject(project0, - autoPlugins, - loadedPlugins, - injectSettings, - fileToLoadedSbtFileMap, - state.log) + resolveProject( + project0, + autoPlugins, + loadedPlugins, + injectSettings, + fileToLoadedSbtFileMap, + state.log + ) } val projects: Seq[Project] = Seq(project) @@ -160,7 +164,8 @@ object SettingQueryTest extends org.specs2.mutable.Specification { val units: Map[URI, LoadedBuildUnit] = loadedBuild.units val settings: Seq[Setting[_]] = finalTransforms( - buildConfigurations(loadedBuild, getRootProject(units), config.injectSettings)) + buildConfigurations(loadedBuild, getRootProject(units), config.injectSettings) + ) val delegates: Scope => Seq[Scope] = defaultDelegates(loadedBuild) val scopeLocal: ScopeLocal = EvaluateTask.injectStreams val display: Show[ScopedKey[_]] = Project showLoadingKey loadedBuild @@ -200,7 +205,8 @@ object SettingQueryTest extends org.specs2.mutable.Specification { "t/startYear" in qok("null", "scala.Option[Int]") "t/scalaArtifacts" in qok( """["scala-library","scala-compiler","scala-reflect","scala-actors","scalap"]""", - "scala.collection.Seq[java.lang.String]") + "scala.collection.Seq[java.lang.String]" + ) "t/libraryDependencies" in qok( """[{"organization":"org.scala-lang","name":"scala-library","revision":"2.12.1","isChanging":false,"isTransitive":true,"isForce":false,"explicitArtifacts":[],"inclusions":[],"exclusions":[],"extraAttributes":{},"crossVersion":{"type":"Disabled"}}]""", @@ -209,8 +215,10 @@ object SettingQueryTest extends org.specs2.mutable.Specification { "scalaVersion" in qko("Not a valid project ID: scalaVersion\\nscalaVersion\\n ^") "t/scalacOptions" in qko( - s"""Key ProjectRef(uri(\\"$baseUri\\"), \\"t\\") / Compile / scalacOptions is a task, can only query settings""") + s"""Key ProjectRef(uri(\\"$baseUri\\"), \\"t\\") / Compile / scalacOptions is a task, can only query settings""" + ) "t/fooo" in qko( - "Expected ':' (if selecting a configuration)\\nNot a valid key: fooo (similar: fork)\\nt/fooo\\n ^") + "Expected ':' (if selecting a configuration)\\nNot a valid key: fooo (similar: fork)\\nt/fooo\\n ^" + ) } } diff --git a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcNotificationMessageFormats.scala b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcNotificationMessageFormats.scala index b00ae5d07..d4fe42afe 100644 --- a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcNotificationMessageFormats.scala +++ b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcNotificationMessageFormats.scala @@ -17,7 +17,8 @@ trait JsonRpcNotificationMessageFormats { new JsonFormat[sbt.internal.protocol.JsonRpcNotificationMessage] { override def read[J]( jsOpt: Option[J], - unbuilder: Unbuilder[J]): sbt.internal.protocol.JsonRpcNotificationMessage = { + unbuilder: Unbuilder[J] + ): sbt.internal.protocol.JsonRpcNotificationMessage = { jsOpt match { case Some(js) => unbuilder.beginObject(js) @@ -32,8 +33,10 @@ trait JsonRpcNotificationMessageFormats { deserializationError("Expected JsObject but found None") } } - override def write[J](obj: sbt.internal.protocol.JsonRpcNotificationMessage, - builder: Builder[J]): Unit = { + override def write[J]( + obj: sbt.internal.protocol.JsonRpcNotificationMessage, + builder: Builder[J] + ): Unit = { builder.beginObject() builder.addField("jsonrpc", obj.jsonrpc) builder.addField("method", obj.method) 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 8a4987741..1ed949b31 100644 --- a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcRequestMessageFormats.scala +++ b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcRequestMessageFormats.scala @@ -15,8 +15,10 @@ trait JsonRpcRequestMessageFormats { implicit lazy val JsonRpcRequestMessageFormat : JsonFormat[sbt.internal.protocol.JsonRpcRequestMessage] = new JsonFormat[sbt.internal.protocol.JsonRpcRequestMessage] { - override def read[J](jsOpt: Option[J], - unbuilder: Unbuilder[J]): sbt.internal.protocol.JsonRpcRequestMessage = { + override def read[J]( + jsOpt: Option[J], + unbuilder: Unbuilder[J] + ): sbt.internal.protocol.JsonRpcRequestMessage = { jsOpt match { case Some(js) => unbuilder.beginObject(js) @@ -39,8 +41,10 @@ trait JsonRpcRequestMessageFormats { deserializationError("Expected JsObject but found None") } } - override def write[J](obj: sbt.internal.protocol.JsonRpcRequestMessage, - builder: Builder[J]): Unit = { + override def write[J]( + obj: sbt.internal.protocol.JsonRpcRequestMessage, + builder: Builder[J] + ): Unit = { builder.beginObject() builder.addField("jsonrpc", obj.jsonrpc) builder.addField("id", obj.id) diff --git a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseErrorFormats.scala b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseErrorFormats.scala index 110a6cae2..d330ac5bb 100644 --- a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseErrorFormats.scala +++ b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseErrorFormats.scala @@ -15,8 +15,10 @@ trait JsonRpcResponseErrorFormats { implicit lazy val JsonRpcResponseErrorFormat : JsonFormat[sbt.internal.protocol.JsonRpcResponseError] = new JsonFormat[sbt.internal.protocol.JsonRpcResponseError] { - override def read[J](jsOpt: Option[J], - unbuilder: Unbuilder[J]): sbt.internal.protocol.JsonRpcResponseError = { + override def read[J]( + jsOpt: Option[J], + unbuilder: Unbuilder[J] + ): sbt.internal.protocol.JsonRpcResponseError = { jsOpt match { case Some(js) => unbuilder.beginObject(js) @@ -31,8 +33,10 @@ trait JsonRpcResponseErrorFormats { deserializationError("Expected JsObject but found None") } } - override def write[J](obj: sbt.internal.protocol.JsonRpcResponseError, - builder: Builder[J]): Unit = { + override def write[J]( + obj: sbt.internal.protocol.JsonRpcResponseError, + builder: Builder[J] + ): Unit = { builder.beginObject() builder.addField("code", obj.code) builder.addField("message", obj.message) 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 a8794647f..db967420e 100644 --- a/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala +++ b/protocol/src/main/scala/sbt/internal/protocol/codec/JsonRpcResponseMessageFormats.scala @@ -25,7 +25,8 @@ trait JsonRpcResponseMessageFormats { new JsonFormat[sbt.internal.protocol.JsonRpcResponseMessage] { override def read[J]( jsOpt: Option[J], - unbuilder: Unbuilder[J]): sbt.internal.protocol.JsonRpcResponseMessage = { + unbuilder: Unbuilder[J] + ): sbt.internal.protocol.JsonRpcResponseMessage = { jsOpt match { case Some(js) => unbuilder.beginObject(js) @@ -50,8 +51,10 @@ trait JsonRpcResponseMessageFormats { deserializationError("Expected JsObject but found None") } } - override def write[J](obj: sbt.internal.protocol.JsonRpcResponseMessage, - builder: Builder[J]): Unit = { + override def write[J]( + obj: sbt.internal.protocol.JsonRpcResponseMessage, + builder: Builder[J] + ): Unit = { // Parse given id to Long or String judging by prefix def parseId(str: String): Either[Long, String] = { if (str.startsWith("\u2668")) Left(str.substring(1).toLong) diff --git a/run/src/main/scala/sbt/Fork.scala b/run/src/main/scala/sbt/Fork.scala index 74b70d024..3fc3e4142 100644 --- a/run/src/main/scala/sbt/Fork.scala +++ b/run/src/main/scala/sbt/Fork.scala @@ -64,9 +64,11 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) { case out: CustomOutput => (process #> out.output).run(connectInput = false) } } - private[this] def makeOptions(jvmOptions: Seq[String], - bootJars: Iterable[File], - arguments: Seq[String]): Seq[String] = { + private[this] def makeOptions( + jvmOptions: Seq[String], + bootJars: Iterable[File], + arguments: Seq[String] + ): Seq[String] = { val boot = if (bootJars.isEmpty) None else diff --git a/run/src/main/scala/sbt/Run.scala b/run/src/main/scala/sbt/Run.scala index 15469f86e..a1fb598d6 100644 --- a/run/src/main/scala/sbt/Run.scala +++ b/run/src/main/scala/sbt/Run.scala @@ -29,8 +29,8 @@ class ForkRun(config: ForkOptions) extends ScalaRun { if (exitCode == 0) Success(()) else Failure( - new RuntimeException( - s"""Nonzero exit code returned from $label: $exitCode""".stripMargin)) + new RuntimeException(s"""Nonzero exit code returned from $label: $exitCode""".stripMargin) + ) val process = fork(mainClass, classpath, options, log) def cancel() = { log.warn("Run canceled.") @@ -77,10 +77,12 @@ class Run(instance: ScalaInstance, trapExit: Boolean, nativeTmp: File) extends S if (trapExit) Run.executeTrapExit(execute(), log) else directExecute() } - private def run0(mainClassName: String, - classpath: Seq[File], - options: Seq[String], - log: Logger): Unit = { + private def run0( + mainClassName: String, + classpath: Seq[File], + options: Seq[String], + log: Logger + ): Unit = { log.debug(" Classpath:\n\t" + classpath.mkString("\n\t")) val loader = ClasspathUtilities.makeLoader(classpath, instance, nativeTmp) val main = getMainMethod(mainClassName, loader) @@ -112,7 +114,8 @@ class Run(instance: ScalaInstance, trapExit: Boolean, nativeTmp: File) extends S /** This module is an interface to starting the scala interpreter or runner.*/ object Run { def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger)( - implicit runner: ScalaRun) = + implicit runner: ScalaRun + ) = runner.run(mainClass, classpath, options, log) /** Executes the given function, trapping calls to System.exit. */ diff --git a/run/src/main/scala/sbt/SelectMainClass.scala b/run/src/main/scala/sbt/SelectMainClass.scala index b840bf839..86bcbcdb9 100644 --- a/run/src/main/scala/sbt/SelectMainClass.scala +++ b/run/src/main/scala/sbt/SelectMainClass.scala @@ -9,8 +9,10 @@ package sbt object SelectMainClass { // Some(SimpleReader.readLine _) - def apply(promptIfMultipleChoices: Option[String => Option[String]], - mainClasses: Seq[String]): Option[String] = { + def apply( + promptIfMultipleChoices: Option[String => Option[String]], + mainClasses: Seq[String] + ): Option[String] = { mainClasses.toList match { case Nil => None case head :: Nil => Some(head) diff --git a/run/src/main/scala/sbt/TrapExit.scala b/run/src/main/scala/sbt/TrapExit.scala index 54cf83760..2dea6d17f 100644 --- a/run/src/main/scala/sbt/TrapExit.scala +++ b/run/src/main/scala/sbt/TrapExit.scala @@ -113,8 +113,9 @@ object TrapExit { * Recurses into the causes of the given exception looking for a cause of type CauseType. If one is found, `withType` is called with that cause. * If not, `notType` is called with the root cause. */ - private def withCause[CauseType <: Throwable, T](e: Throwable)(withType: CauseType => T)( - notType: Throwable => T)(implicit mf: Manifest[CauseType]): T = { + private def withCause[CauseType <: Throwable, T]( + e: Throwable + )(withType: CauseType => T)(notType: Throwable => T)(implicit mf: Manifest[CauseType]): T = { val clazz = mf.runtimeClass if (clazz.isInstance(e)) withType(e.asInstanceOf[CauseType]) @@ -348,8 +349,10 @@ private final class TrapExit(delegateManager: SecurityManager) extends SecurityM // takes a snapshot of the threads in `toProcess`, acquiring nested locks on each group to do so // the thread groups are accumulated in `accum` and then the threads in each are collected all at // once while they are all locked. This is the closest thing to a snapshot that can be accomplished. - private[this] def threadsInGroups(toProcess: List[ThreadGroup], - accum: List[ThreadGroup]): List[Thread] = toProcess match { + private[this] def threadsInGroups( + toProcess: List[ThreadGroup], + accum: List[ThreadGroup] + ): List[Thread] = toProcess match { case group :: tail => // ThreadGroup implementation synchronizes on its methods, so by synchronizing here, we can workaround its quirks somewhat group.synchronized { @@ -521,9 +524,10 @@ private final class ExitCode { * The default uncaught exception handler for managed executions. * It logs the thread and the exception. */ -private final class LoggingExceptionHandler(log: Logger, - delegate: Option[Thread.UncaughtExceptionHandler]) - extends Thread.UncaughtExceptionHandler { +private final class LoggingExceptionHandler( + log: Logger, + delegate: Option[Thread.UncaughtExceptionHandler] +) extends Thread.UncaughtExceptionHandler { def uncaughtException(t: Thread, e: Throwable): Unit = { log.error("(" + t.getName + ") " + e.toString) log.trace(e) diff --git a/sbt/src/test/scala/sbt/ServerSpec.scala b/sbt/src/test/scala/sbt/ServerSpec.scala index 87c129e90..0f147786c 100644 --- a/sbt/src/test/scala/sbt/ServerSpec.scala +++ b/sbt/src/test/scala/sbt/ServerSpec.scala @@ -22,7 +22,8 @@ class ServerSpec extends AsyncFlatSpec with Matchers { withBuildSocket("handshake") { (out, in, tkn) => writeLine( """{ "jsonrpc": "2.0", "id": 3, "method": "sbt/setting", "params": { "setting": "handshake/name" } }""", - out) + out + ) Thread.sleep(100) assert(waitFor(in, 10) { s => s contains """"id":3""" @@ -55,9 +56,11 @@ object ServerSpec { private val threadFactory = new ThreadFactory() { override def newThread(runnable: Runnable): Thread = { val thread = - new Thread(threadGroup, - runnable, - s"sbt-test-server-threads-${nextThreadId.getAndIncrement}") + 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 @@ -84,8 +87,9 @@ object ServerSpec { def shutdown(): Unit = executor.shutdown() - def withBuildSocket(testBuild: String)( - f: (OutputStream, InputStream, Option[String]) => Future[Assertion]): Future[Assertion] = { + 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) @@ -162,8 +166,9 @@ object ServerSpec { writeEndLine } - def withBuildSocket(baseDirectory: File)( - f: (OutputStream, InputStream, Option[String]) => Future[Assertion]): Future[Assertion] = { + def withBuildSocket( + baseDirectory: File + )(f: (OutputStream, InputStream, Option[String]) => Future[Assertion]): Future[Assertion] = { backgroundRun(baseDirectory, Nil) val portfile = baseDirectory / "project" / "target" / "active.json" @@ -185,14 +190,16 @@ object ServerSpec { sendJsonRpc( """{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { } } }""", - out) + out + ) try { f(out, in, tkn) } finally { sendJsonRpc( """{ "jsonrpc": "2.0", "id": 9, "method": "sbt/exec", "params": { "commandLine": "exit" } }""", - out) + out + ) // shutdown() } } diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index dd9de5bc1..dabaeeca1 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -134,7 +134,8 @@ final class ScriptedTests( def logTests(size: Int, how: String) = log.info( - f"Running $size / $totalSize (${size * 100D / totalSize}%3.2f%%) scripted tests with $how") + f"Running $size / $totalSize (${size * 100D / totalSize}%3.2f%%) scripted tests with $how" + ) logTests(runFromSourceBasedTests.size, "RunFromSourceMain") logTests(launcherBasedTests.size, "sbt/launcher") diff --git a/tasks-standard/src/main/scala/sbt/Action.scala b/tasks-standard/src/main/scala/sbt/Action.scala index be5585cbd..6cd50824d 100644 --- a/tasks-standard/src/main/scala/sbt/Action.scala +++ b/tasks-standard/src/main/scala/sbt/Action.scala @@ -74,8 +74,10 @@ final case class Task[T](info: Info[T], work: Action[T]) { * @param attributes Arbitrary user-defined key/value pairs describing this task * @param post a transformation that takes the result of evaluating this task and produces user-defined key/value pairs. */ -final case class Info[T](attributes: AttributeMap = AttributeMap.empty, - post: T => AttributeMap = const(AttributeMap.empty)) { +final case class Info[T]( + attributes: AttributeMap = AttributeMap.empty, + post: T => AttributeMap = const(AttributeMap.empty) +) { import Info._ def name = attributes.get(Name) def description = attributes.get(Description) diff --git a/tasks-standard/src/main/scala/sbt/std/Streams.scala b/tasks-standard/src/main/scala/sbt/std/Streams.scala index 6206b9271..61f97f89e 100644 --- a/tasks-standard/src/main/scala/sbt/std/Streams.scala +++ b/tasks-standard/src/main/scala/sbt/std/Streams.scala @@ -126,10 +126,12 @@ object Streams { synchronized { streams.values.foreach(_.close()); streams.clear() } } - def apply[Key, J: IsoString](taskDirectory: Key => File, - name: Key => String, - mkLogger: (Key, PrintWriter) => ManagedLogger, - converter: SupportConverter[J]): Streams[Key] = new Streams[Key] { + def apply[Key, J: IsoString]( + taskDirectory: Key => File, + name: Key => String, + mkLogger: (Key, PrintWriter) => ManagedLogger, + converter: SupportConverter[J] + ): Streams[Key] = new Streams[Key] { def apply(a: Key): ManagedStreams[Key] = new ManagedStreams[Key] { private[this] var opened: List[Closeable] = Nil @@ -142,8 +144,9 @@ object Streams { make(a, sid)(f => new PlainOutput(new FileOutputStream(f), converter)) def readText(a: Key, sid: String = default): BufferedReader = - make(a, sid)(f => - new BufferedReader(new InputStreamReader(new FileInputStream(f), IO.defaultCharset))) + make(a, sid)( + f => new BufferedReader(new InputStreamReader(new FileInputStream(f), IO.defaultCharset)) + ) def readBinary(a: Key, sid: String = default): BufferedInputStream = make(a, sid)(f => new BufferedInputStream(new FileInputStream(f))) @@ -152,8 +155,13 @@ object Streams { make(a, sid)( f => new PrintWriter( - new DeferredWriter(new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(f), IO.defaultCharset))))) + new DeferredWriter( + new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(f), IO.defaultCharset) + ) + ) + ) + ) def binary(sid: String = default): BufferedOutputStream = make(a, sid)(f => new BufferedOutputStream(new FileOutputStream(f))) diff --git a/tasks-standard/src/main/scala/sbt/std/System.scala b/tasks-standard/src/main/scala/sbt/std/System.scala index 060bc0d48..ed2739646 100644 --- a/tasks-standard/src/main/scala/sbt/std/System.scala +++ b/tasks-standard/src/main/scala/sbt/std/System.scala @@ -58,8 +58,9 @@ object Transform { def uniform[T, D](tasks: Seq[Task[D]])(f: Seq[Result[D]] => Either[Task[T], T]): Node[Task, T] = toNode[T, λ[L[x] => List[L[D]]]](tasks.toList)(f)(AList.seq[D]) - def toNode[T, k[L[x]]](inputs: k[Task])(f: k[Result] => Either[Task[T], T])( - implicit a: AList[k]): Node[Task, T] = new Node[Task, T] { + def toNode[T, k[L[x]]]( + inputs: k[Task] + )(f: k[Result] => Either[Task[T], T])(implicit a: AList[k]): Node[Task, T] = new Node[Task, T] { type K[L[x]] = k[L] val in = inputs val alist = a diff --git a/tasks-standard/src/main/scala/sbt/std/TaskExtra.scala b/tasks-standard/src/main/scala/sbt/std/TaskExtra.scala index b94ea5e1c..52e754a81 100644 --- a/tasks-standard/src/main/scala/sbt/std/TaskExtra.scala +++ b/tasks-standard/src/main/scala/sbt/std/TaskExtra.scala @@ -39,22 +39,26 @@ sealed trait SingleInTask[S] { @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") + "0.13.0" + ) def mapR[T](f: Result[S] => T): Task[T] @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") + "0.13.0" + ) def flatFailure[T](f: Incomplete => Task[T]): Task[T] @deprecated( "Use the `failure` method to create a task that returns Incomplete when this task fails and then call `mapFailure` on the new task.", - "0.13.0") + "0.13.0" + ) def mapFailure[T](f: Incomplete => T): Task[T] @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") + "0.13.0" + ) def flatMapR[T](f: Result[S] => Task[T]): Task[T] } sealed trait TaskInfo[S] { @@ -160,10 +164,12 @@ trait TaskExtra { def andFinally(fin: => Unit): Task[S] = mapR(x => Result.tryValue[S]({ fin; x })) def doFinally(t: Task[Unit]): Task[S] = - flatMapR(x => - t.result.map { tx => - Result.tryValues[S](tx :: Nil, x) - }) + flatMapR( + x => + t.result.map { tx => + Result.tryValues[S](tx :: Nil, x) + } + ) def ||[T >: S](alt: Task[T]): Task[T] = flatMapR { case Value(v) => task(v); case Inc(_) => alt } @@ -175,8 +181,9 @@ trait TaskExtra { def named(s: String): Task[S] = in.copy(info = in.info.setName(s)) } - final implicit def pipeToProcess[Key](t: Task[_])(implicit streams: Task[TaskStreams[Key]], - key: Task[_] => Key): ProcessPipe = + final implicit def pipeToProcess[Key]( + t: Task[_] + )(implicit streams: Task[TaskStreams[Key]], key: Task[_] => Key): ProcessPipe = new ProcessPipe { def #|(p: ProcessBuilder): Task[Int] = pipe0(None, p) def pipe(sid: String)(p: ProcessBuilder): Task[Int] = pipe0(Some(sid), p) @@ -190,8 +197,9 @@ trait TaskExtra { } } - final implicit def binaryPipeTask[Key](in: Task[_])(implicit streams: Task[TaskStreams[Key]], - key: Task[_] => Key): BinaryPipe = + final implicit def binaryPipeTask[Key]( + in: Task[_] + )(implicit streams: Task[TaskStreams[Key]], key: Task[_] => Key): BinaryPipe = new BinaryPipe { def binary[T](f: BufferedInputStream => T): Task[T] = pipe0(None, f) def binary[T](sid: String)(f: BufferedInputStream => T): Task[T] = pipe0(Some(sid), f) @@ -206,8 +214,9 @@ trait TaskExtra { private def toFile(f: File) = (in: InputStream) => IO.transfer(in, f) } - final implicit def textPipeTask[Key](in: Task[_])(implicit streams: Task[TaskStreams[Key]], - key: Task[_] => Key): TextPipe = new TextPipe { + final implicit def textPipeTask[Key]( + in: Task[_] + )(implicit streams: Task[TaskStreams[Key]], key: Task[_] => Key): TextPipe = new TextPipe { def text[T](f: BufferedReader => T): Task[T] = pipe0(None, f) def text[T](sid: String)(f: BufferedReader => T): Task[T] = pipe0(Some(sid), f) @@ -216,8 +225,9 @@ trait TaskExtra { f(s.readText(key(in), sid)) } } - final implicit def linesTask[Key](in: Task[_])(implicit streams: Task[TaskStreams[Key]], - key: Task[_] => Key): TaskLines = new TaskLines { + final implicit def linesTask[Key]( + in: Task[_] + )(implicit streams: Task[TaskStreams[Key]], key: Task[_] => Key): TaskLines = new TaskLines { def lines: Task[List[String]] = lines0(None) def lines(sid: String): Task[List[String]] = lines0(Some(sid)) diff --git a/tasks-standard/src/test/scala/TaskGen.scala b/tasks-standard/src/test/scala/TaskGen.scala index 8fdace35d..ee7539ef9 100644 --- a/tasks-standard/src/test/scala/TaskGen.scala +++ b/tasks-standard/src/test/scala/TaskGen.scala @@ -24,9 +24,11 @@ object TaskGen extends std.TaskExtra { def run[T](root: Task[T], checkCycles: Boolean, maxWorkers: Int): Result[T] = { val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers) val dummies = std.Transform.DummyTaskMap(Nil) - val x = new Execute[Task](Execute.config(checkCycles), - Execute.noTriggers, - ExecuteProgress.empty[Task])(std.Transform(dummies)) + val x = new Execute[Task]( + Execute.config(checkCycles), + Execute.noTriggers, + ExecuteProgress.empty[Task] + )(std.Transform(dummies)) try { x.run(root)(service) } finally { shutdown() } } def tryRun[T](root: Task[T], checkCycles: Boolean, maxWorkers: Int): T = diff --git a/tasks-standard/src/test/scala/TaskSerial.scala b/tasks-standard/src/test/scala/TaskSerial.scala index 43aeea41f..8f2f8bf3c 100644 --- a/tasks-standard/src/test/scala/TaskSerial.scala +++ b/tasks-standard/src/test/scala/TaskSerial.scala @@ -50,9 +50,11 @@ object TaskSerial extends Properties("task serial") { } */ - def checkArbitrary(size: Int, - restrictions: ConcurrentRestrictions[Task[_]], - shouldSucceed: Boolean) = { + def checkArbitrary( + size: Int, + restrictions: ConcurrentRestrictions[Task[_]], + shouldSucceed: Boolean + ) = { val latch = task { new CountDownLatch(size) } def mktask = latch map { l => l.countDown() @@ -74,20 +76,26 @@ object TaskSerial extends Properties("task serial") { } object TaskTest { - def run[T](root: Task[T], - checkCycles: Boolean, - restrictions: ConcurrentRestrictions[Task[_]]): Result[T] = { + def run[T]( + root: Task[T], + checkCycles: Boolean, + restrictions: ConcurrentRestrictions[Task[_]] + ): Result[T] = { val (service, shutdown) = completionService[Task[_], Completed](restrictions, (x: String) => System.err.println(x)) - val x = new Execute[Task](Execute.config(checkCycles), - Execute.noTriggers, - ExecuteProgress.empty[Task])(taskToNode(idK[Task])) + val x = new Execute[Task]( + Execute.config(checkCycles), + Execute.noTriggers, + ExecuteProgress.empty[Task] + )(taskToNode(idK[Task])) try { x.run(root)(service) } finally { shutdown() } } - def tryRun[T](root: Task[T], - checkCycles: Boolean, - restrictions: ConcurrentRestrictions[Task[_]]): T = + def tryRun[T]( + root: Task[T], + checkCycles: Boolean, + restrictions: ConcurrentRestrictions[Task[_]] + ): T = run(root, checkCycles, restrictions) match { case Value(v) => v case Inc(i) => throw i diff --git a/tasks/src/main/scala/sbt/CompletionService.scala b/tasks/src/main/scala/sbt/CompletionService.scala index f88f6885a..622f60e12 100644 --- a/tasks/src/main/scala/sbt/CompletionService.scala +++ b/tasks/src/main/scala/sbt/CompletionService.scala @@ -37,14 +37,16 @@ object CompletionService { () => future.get() } - def manage[A, T](service: CompletionService[A, T])(setup: A => Unit, - cleanup: A => Unit): CompletionService[A, T] = + def manage[A, T]( + service: CompletionService[A, T] + )(setup: A => Unit, cleanup: A => Unit): CompletionService[A, T] = wrap(service) { (node, work) => () => setup(node) try { work() } finally { cleanup(node) } } - def wrap[A, T](service: CompletionService[A, T])( - w: (A, () => T) => (() => T)): CompletionService[A, T] = + def wrap[A, T]( + service: CompletionService[A, T] + )(w: (A, () => T) => (() => T)): CompletionService[A, T] = new CompletionService[A, T] { def submit(node: A, work: () => T) = service.submit(node, w(node, work)) def take() = service.take() diff --git a/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala b/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala index 7313eba62..b6e011f4c 100644 --- a/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala +++ b/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala @@ -124,8 +124,10 @@ object ConcurrentRestrictions { * @tparam A the task type * @tparam R the type of data that will be computed by the CompletionService. */ - def completionService[A, R](tags: ConcurrentRestrictions[A], - warn: String => Unit): (CompletionService[A, R], () => Unit) = { + 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(); () }) } @@ -134,9 +136,11 @@ object ConcurrentRestrictions { * Constructs a CompletionService suitable for backing task execution based on the provided restrictions on concurrent task execution * and using the provided Executor to manage execution on threads. */ - def completionService[A, R](backing: Executor, - tags: ConcurrentRestrictions[A], - warn: String => Unit): CompletionService[A, R] = { + def completionService[A, R]( + backing: Executor, + tags: ConcurrentRestrictions[A], + warn: String => Unit + ): CompletionService[A, R] = { /** Represents submitted work for a task.*/ final class Enqueue(val node: A, val work: () => R) @@ -180,7 +184,8 @@ object ConcurrentRestrictions { tagState = tags.remove(tagState, node) if (!tags.valid(tagState)) warn( - "Invalid restriction: removing a completed node from a valid system must result in a valid system.") + "Invalid restriction: removing a completed node from a valid system must result in a valid system." + ) submitValid(new LinkedList) } private[this] def errorAddingToIdle() = diff --git a/tasks/src/main/scala/sbt/Execute.scala b/tasks/src/main/scala/sbt/Execute.scala index bba13f448..903dde54f 100644 --- a/tasks/src/main/scala/sbt/Execute.scala +++ b/tasks/src/main/scala/sbt/Execute.scala @@ -27,8 +27,10 @@ private[sbt] object Execute { def config(checkCycles: Boolean, overwriteNode: Incomplete => Boolean = const(false)): Config = new Config(checkCycles, overwriteNode) - final class Config private[sbt] (val checkCycles: Boolean, - val overwriteNode: Incomplete => Boolean) + final class Config private[sbt] ( + val checkCycles: Boolean, + val overwriteNode: Incomplete => Boolean + ) final val checkPreAndPostConditions = sys.props.get("sbt.execute.extrachecks").exists(java.lang.Boolean.parseBoolean) @@ -40,14 +42,17 @@ private[sbt] trait NodeView[A[_]] { def apply[T](a: A[T]): Node[A, T] def inline[T](a: A[T]): Option[() => T] } -final class Triggers[A[_]](val runBefore: collection.Map[A[_], Seq[A[_]]], - val injectFor: collection.Map[A[_], Seq[A[_]]], - val onComplete: RMap[A, Result] => RMap[A, Result]) +final class Triggers[A[_]]( + val runBefore: collection.Map[A[_], Seq[A[_]]], + val injectFor: collection.Map[A[_], Seq[A[_]]], + val onComplete: RMap[A, Result] => RMap[A, Result] +) private[sbt] final class Execute[A[_] <: AnyRef]( config: Config, triggers: Triggers[A], - progress: ExecuteProgress[A])(implicit view: NodeView[A]) { + progress: ExecuteProgress[A] +)(implicit view: NodeView[A]) { type Strategy = CompletionService[A[_], Completed] private[this] val forward = idMap[A[_], IDSet[A[_]]] @@ -204,11 +209,12 @@ private[sbt] final class Execute[A[_] <: AnyRef]( val v = register(node) val deps = dependencies(v) ++ runBefore(node) val active = IDSet[A[_]](deps filter notDone) - progressState = progress.registered(progressState, - node, - deps, - active.toList - /** active is mutable, so take a snapshot */ + progressState = progress.registered( + progressState, + node, + deps, + active.toList + /** active is mutable, so take a snapshot */ ) if (active.isEmpty) @@ -282,7 +288,8 @@ private[sbt] final class Execute[A[_] <: AnyRef]( } } private[this] def rewrap[T]( - rawResult: Either[Incomplete, Either[A[T], T]]): Either[A[T], Result[T]] = + rawResult: Either[Incomplete, Either[A[T], T]] + ): Either[A[T], Result[T]] = rawResult match { case Left(i) => Right(Inc(i)) case Right(Right(v)) => Right(Value(v)) @@ -376,9 +383,11 @@ private[sbt] final class Execute[A[_] <: AnyRef]( if (all contains target) cyclic(node, target, "Cyclic reference") } def cyclic[T](caller: A[T], target: A[T], msg: String) = - throw new Incomplete(Some(caller), - message = Some(msg), - directCause = Some(new CyclicException(caller, target, msg))) + throw new Incomplete( + Some(caller), + message = Some(msg), + directCause = Some(new CyclicException(caller, target, msg)) + ) final class CyclicException[T](val caller: A[T], val target: A[T], msg: String) extends Exception(msg) diff --git a/tasks/src/main/scala/sbt/Incomplete.scala b/tasks/src/main/scala/sbt/Incomplete.scala index ad8296196..78c042ebd 100644 --- a/tasks/src/main/scala/sbt/Incomplete.scala +++ b/tasks/src/main/scala/sbt/Incomplete.scala @@ -21,12 +21,13 @@ import Incomplete.{ Error, Value => IValue } * @param causes a list of incompletions that prevented `node` from completing * @param directCause the exception that caused `node` to not complete */ -final case class Incomplete(node: Option[AnyRef], - tpe: IValue = Error, - message: Option[String] = None, - causes: Seq[Incomplete] = Nil, - directCause: Option[Throwable] = None) - extends Exception(message.orNull, directCause.orNull) +final case class Incomplete( + node: Option[AnyRef], + tpe: IValue = Error, + message: Option[String] = None, + causes: Seq[Incomplete] = Nil, + directCause: Option[Throwable] = None +) extends Exception(message.orNull, directCause.orNull) with sbt.internal.util.UnprintableException { override def toString = "Incomplete(node=" + node + ", tpe=" + tpe + ", msg=" + message + ", causes=" + causes + ", directCause=" + directCause + ")" diff --git a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala index ad547c511..204e69b1a 100644 --- a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala +++ b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala @@ -80,7 +80,8 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { /** Junit XML reports don't differentiate between ignored, skipped or pending tests */ val ignoredSkippedPending = count(TStatus.Ignored) + count(TStatus.Skipped) + count( - TStatus.Pending) + TStatus.Pending + ) val result = diff --git a/testing/src/main/scala/sbt/TestFramework.scala b/testing/src/main/scala/sbt/TestFramework.scala index 95fa55788..11b4b51c8 100644 --- a/testing/src/main/scala/sbt/TestFramework.scala +++ b/testing/src/main/scala/sbt/TestFramework.scala @@ -41,9 +41,11 @@ final class TestFramework(val implClassNames: String*) extends Serializable { } @tailrec - private def createFramework(loader: ClassLoader, - log: ManagedLogger, - frameworkClassNames: List[String]): Option[Framework] = { + private def createFramework( + loader: ClassLoader, + log: ManagedLogger, + frameworkClassNames: List[String] + ): Option[Framework] = { frameworkClassNames match { case head :: tail => try { @@ -64,10 +66,12 @@ final class TestFramework(val implClassNames: String*) extends Serializable { def create(loader: ClassLoader, log: ManagedLogger): Option[Framework] = createFramework(loader, log, implClassNames.toList) } -final class TestDefinition(val name: String, - val fingerprint: Fingerprint, - val explicitlySpecified: Boolean, - val selectors: Array[Selector]) { +final class TestDefinition( + val name: String, + val fingerprint: Fingerprint, + val explicitlySpecified: Boolean, + val selectors: Array[Selector] +) { override def toString = "Test " + name + " : " + TestFramework.toString(fingerprint) override def equals(t: Any) = t match { @@ -87,13 +91,16 @@ final class TestRunner( delegate.tasks( testDefs .map(df => new TaskDef(df.name, df.fingerprint, df.explicitlySpecified, df.selectors)) - .toArray) + .toArray + ) final def run(taskDef: TaskDef, testTask: TestTask): (SuiteResult, Seq[TestTask]) = { - val testDefinition = new TestDefinition(taskDef.fullyQualifiedName, - taskDef.fingerprint, - taskDef.explicitlySpecified, - taskDef.selectors) + val testDefinition = new TestDefinition( + taskDef.fullyQualifiedName, + taskDef.fingerprint, + taskDef.explicitlySpecified, + taskDef.selectors + ) log.debug("Running " + taskDef) val name = testDefinition.name @@ -141,7 +148,8 @@ object TestFramework { it.foreach( i => try f(i) - catch { case NonFatal(e) => log.trace(e); log.error(e.toString) }) + catch { case NonFatal(e) => log.trace(e); log.error(e.toString) } + ) private[sbt] def hashCode(f: Fingerprint): Int = f match { case s: SubclassFingerprint => (s.isModule, s.superclassName).hashCode @@ -180,12 +188,16 @@ object TestFramework { }, mappedTests, tests, log, listeners) } - private[this] def order(mapped: Map[String, TestFunction], - inputs: Vector[TestDefinition]): Vector[(String, TestFunction)] = + private[this] def order( + mapped: Map[String, TestFunction], + inputs: Vector[TestDefinition] + ): Vector[(String, TestFunction)] = for (d <- inputs; act <- mapped.get(d.name)) yield (d.name, act) - private[this] def testMap(frameworks: Seq[Framework], - tests: Seq[TestDefinition]): Map[Framework, Set[TestDefinition]] = { + private[this] def testMap( + frameworks: Seq[Framework], + tests: Seq[TestDefinition] + ): Map[Framework, Set[TestDefinition]] = { import scala.collection.mutable.{ HashMap, HashSet, Set } val map = new HashMap[Framework, Set[TestDefinition]] def assignTest(test: TestDefinition): Unit = { @@ -200,13 +212,14 @@ object TestFramework { map.toMap.mapValues(_.toSet) } - private def createTestTasks(loader: ClassLoader, - runners: Map[Framework, TestRunner], - tests: Map[Framework, Set[TestDefinition]], - ordered: Vector[TestDefinition], - log: ManagedLogger, - listeners: Vector[TestReportListener]) - : (() => Unit, Vector[(String, TestFunction)], TestResult => (() => Unit)) = { + private def createTestTasks( + loader: ClassLoader, + runners: Map[Framework, TestRunner], + tests: Map[Framework, Set[TestDefinition]], + ordered: Vector[TestDefinition], + log: ManagedLogger, + listeners: Vector[TestReportListener] + ): (() => Unit, Vector[(String, TestFunction)], TestResult => (() => Unit)) = { val testsListeners = listeners collect { case tl: TestsListener => tl } def foreachListenerSafe(f: TestsListener => Unit): () => Unit = @@ -232,19 +245,23 @@ object TestFramework { Thread.currentThread.setContextClassLoader(loader) try { eval } finally { Thread.currentThread.setContextClassLoader(oldLoader) } } - def createTestLoader(classpath: Seq[File], - scalaInstance: ScalaInstance, - tempDir: File): ClassLoader = { + def createTestLoader( + classpath: Seq[File], + scalaInstance: ScalaInstance, + tempDir: File + ): ClassLoader = { val interfaceJar = IO.classLocationFile(classOf[testing.Framework]) val interfaceFilter = (name: String) => name.startsWith("org.scalatools.testing.") || name.startsWith("sbt.testing.") val notInterfaceFilter = (name: String) => !interfaceFilter(name) - val dual = new DualLoader(scalaInstance.loader, - notInterfaceFilter, - x => true, - getClass.getClassLoader, - interfaceFilter, - x => false) + val dual = new DualLoader( + scalaInstance.loader, + notInterfaceFilter, + x => true, + getClass.getClassLoader, + interfaceFilter, + x => false + ) val main = ClasspathUtilities.makeLoader(classpath, dual, scalaInstance, tempDir) // TODO - There's actually an issue with the classpath facility such that unmanagedScalaInstances are not added // to the classpath correctly. We have a temporary workaround here. @@ -253,20 +270,26 @@ object TestFramework { else scalaInstance.allJars ++ (interfaceJar +: classpath) ClasspathUtilities.filterByClasspath(cp, main) } - def createTestFunction(loader: ClassLoader, - taskDef: TaskDef, - runner: TestRunner, - testTask: TestTask): TestFunction = - new TestFunction(taskDef, - runner, - (r: TestRunner) => withContextLoader(loader) { r.run(taskDef, testTask) }) { + def createTestFunction( + loader: ClassLoader, + taskDef: TaskDef, + runner: TestRunner, + testTask: TestTask + ): TestFunction = + new TestFunction( + taskDef, + runner, + (r: TestRunner) => withContextLoader(loader) { r.run(taskDef, testTask) } + ) { def tags = testTask.tags } } -abstract class TestFunction(val taskDef: TaskDef, - val runner: TestRunner, - fun: (TestRunner) => (SuiteResult, Seq[TestTask])) { +abstract class TestFunction( + val taskDef: TaskDef, + val runner: TestRunner, + fun: (TestRunner) => (SuiteResult, Seq[TestTask]) +) { def apply(): (SuiteResult, Seq[TestTask]) = fun(runner) From ebe554022659d9e9af6a30df21d4dbc52d924ccf Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 5 Apr 2018 08:25:32 +0100 Subject: [PATCH 137/176] Reformat some corner cases --- main-actions/src/main/scala/sbt/ForkTests.scala | 15 +++++++-------- main-actions/src/main/scala/sbt/Package.scala | 6 ++---- .../src/main/scala/sbt/RawCompileLike.scala | 5 ++--- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/main-actions/src/main/scala/sbt/ForkTests.scala b/main-actions/src/main/scala/sbt/ForkTests.scala index 4299f54ac..9934eca97 100755 --- a/main-actions/src/main/scala/sbt/ForkTests.scala +++ b/main-actions/src/main/scala/sbt/ForkTests.scala @@ -87,15 +87,14 @@ private[sbt] object ForkTests { val config = new ForkConfiguration(ConsoleAppender.formatEnabledInEnv, parallel) os.writeObject(config) - val taskdefs = opts.tests.map( - t => - new TaskDef( - t.name, - forkFingerprint(t.fingerprint), - t.explicitlySpecified, - t.selectors + val taskdefs = opts.tests.map { t => + new TaskDef( + t.name, + forkFingerprint(t.fingerprint), + t.explicitlySpecified, + t.selectors ) - ) + } os.writeObject(taskdefs.toArray) os.writeInt(runners.size) diff --git a/main-actions/src/main/scala/sbt/Package.scala b/main-actions/src/main/scala/sbt/Package.scala index 8ecb1ccf7..e0c29e95c 100644 --- a/main-actions/src/main/scala/sbt/Package.scala +++ b/main-actions/src/main/scala/sbt/Package.scala @@ -67,11 +67,9 @@ object Package { } setVersion(main) + type Inputs = Map[File, String] :+: FilesInfo[ModifiedFileInfo] :+: Manifest :+: HNil val cachedMakeJar = inputChanged(cacheStoreFactory make "inputs") { - ( - inChanged, - inputs: Map[File, String] :+: FilesInfo[ModifiedFileInfo] :+: Manifest :+: HNil - ) => + (inChanged, inputs: Inputs) => import exists.format val sources :+: _ :+: manifest :+: HNil = inputs inputChanged(cacheStoreFactory make "output") { (outChanged, jar: PlainFileInfo) => diff --git a/main-actions/src/main/scala/sbt/RawCompileLike.scala b/main-actions/src/main/scala/sbt/RawCompileLike.scala index cc4c0fe80..c19d55a26 100644 --- a/main-actions/src/main/scala/sbt/RawCompileLike.scala +++ b/main-actions/src/main/scala/sbt/RawCompileLike.scala @@ -54,9 +54,8 @@ object RawCompileLike { ): Gen = (sources, classpath, outputDirectory, options, maxErrors, log) => { type Inputs = - FilesInfo[HashFileInfo] :+: FilesInfo[ModifiedFileInfo] :+: Seq[File] :+: File :+: Seq[ - String - ] :+: Int :+: HNil + FilesInfo[HashFileInfo] :+: FilesInfo[ModifiedFileInfo] :+: Seq[File] :+: File :+: + Seq[String] :+: Int :+: HNil val inputs : Inputs = hash(sources.toSet ++ optionFiles(options, fileInputOpts)) :+: lastModified( classpath.toSet From a0e27c719c20a5d6bafb176d3d932f2097115ac3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 26 Mar 2018 22:37:38 +0100 Subject: [PATCH 138/176] Cleanup Resolve --- .../src/main/scala/sbt/internal/Resolve.scala | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/main/src/main/scala/sbt/internal/Resolve.scala b/main/src/main/scala/sbt/internal/Resolve.scala index 7e3fe0676..761ba5dd3 100644 --- a/main/src/main/scala/sbt/internal/Resolve.scala +++ b/main/src/main/scala/sbt/internal/Resolve.scala @@ -15,19 +15,19 @@ object Resolve { index: BuildUtil[_], current: ScopeAxis[Reference], key: AttributeKey[_], - mask: ScopeMask + mask: ScopeMask, ): Scope => Scope = { - val rs = - resolveProject(current, mask) _ :: - resolveExtra(mask) _ :: - resolveTask(mask) _ :: - resolveConfig(index, key, mask) _ :: - Nil + val rs = ( + resolveProject(current, mask) _ + :: resolveExtra(mask) _ + :: resolveTask(mask) _ + :: resolveConfig(index, key, mask) _ + :: Nil + ) scope => - (scope /: rs) { (s, f) => - f(s) - } + rs.foldLeft(scope)((s, f) => f(s)) } + def resolveTask(mask: ScopeMask)(scope: Scope): Scope = if (mask.task) scope else scope.copy(task = Zero) @@ -41,17 +41,16 @@ object Resolve { else scope.copy(extra = Zero) def resolveConfig[P](index: BuildUtil[P], key: AttributeKey[_], mask: ScopeMask)( - scope: Scope + scope: Scope, ): Scope = if (mask.config) scope else { val (resolvedRef, proj) = scope.project match { + case Zero | This => (None, index.rootProject(index.root)) case Select(ref) => val r = index resolveRef ref (Some(r), index.projectFor(r)) - case Zero | This => - (None, index.rootProject(index.root)) } val task = scope.task.toOption val keyIndex = index.keyIndex From 8db585d62a889e31f5db490f6a04c2cf54982e3c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:12:17 +0100 Subject: [PATCH 139/176] Refer to sbt/website instead of "the website project" in CONTRIBUTING --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a715576a0..41367f772 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,7 +49,7 @@ Effective bug reports are more likely to be fixed. These guidelines explain how ### Notes about Documentation -Documentation fixes and contributions are as much welcome as to patching the core. Visit [the website project][documentation] to learn about how to contribute. +Documentation fixes and contributions are as much welcome as to patching the core. Visit [sbt/website][documentation] to learn about how to contribute. ### Preliminaries From 2f880c6b8fc8ffa5b04fffdc3ac3e145ee9a4c8d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:13:12 +0100 Subject: [PATCH 140/176] Merge support/contrib info into support/issues+prs sections of CONTRIBUTING --- CONTRIBUTING.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 41367f772..7c38f19e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,12 +15,12 @@ Support [Lightbend] sponsors sbt and encourages contributions from the active community. Enterprises can adopt it for mission critical systems with confidence because Lightbend stands behind sbt with commercial support and services. -For community support please [ask] on StackOverflow with the tag "sbt". +For community support please [ask] on StackOverflow with the tag "sbt" (and the name of the sbt plugin(s) if any). - State the problem or question clearly and provide enough context. Code examples and `build.sbt` are often useful when appropriately edited. - There's also [Gitter sbt/sbt room][gitter], but Stackoverflow is recommended so others can benefit from the answers. -For professional support, [Lightbend], the maintainer of Scala compiler and sbt, provides: +For professional support, for instance if you need faster response times, [Lightbend], the maintainer of Scala compiler and sbt, provides: - [Lightbend Subscriptions][subscriptions], which includes Expert Support - Training @@ -47,6 +47,10 @@ When you find a bug in sbt we want to hear about it. Your bug reports play an im Effective bug reports are more likely to be fixed. These guidelines explain how to write such reports and pull requests. +Please open a GitHub issue when you are 90% sure it's an actual bug. + +If you have an enhancement idea, or a general discussion, bring it up to [sbt-contrib]. + ### Notes about Documentation Documentation fixes and contributions are as much welcome as to patching the core. Visit [sbt/website][documentation] to learn about how to contribute. @@ -58,14 +62,6 @@ Documentation fixes and contributions are as much welcome as to patching the cor - Open one case for each problem. - Proceed to the next steps for details. -### Where to get help and/or file a bug report - -sbt project uses GitHub Issues as a publicly visible todo list. Please open a GitHub issue when you are 90% sure it's an actual bug. - -- If you need help with sbt, please [ask] on StackOverflow with the tag "sbt" and the name of the sbt plugin if any. -- If you have an enhancement idea, or a general discussion, bring it up to [sbt-contrib]. -- If you need a faster response time, consider one of the [Lightbend subscriptions][subscriptions]. - ### What to report The developers need three things from you: **steps**, **problems**, and **expectations**. From f7d6cec030c22f552fb3982889f0dab35d45dd11 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:21:24 +0100 Subject: [PATCH 141/176] Drop the references to the contributing guidelines from templates I believe GitHub does a better job displaying this prominently, particularly for first time contributors. We can always bring back the pull request template if we have new content. --- ISSUE_TEMPLATE.md | 2 -- PULL_REQUEST_TEMPLATE.md | 1 - 2 files changed, 3 deletions(-) delete mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 3fab7acbb..30bd9fb9e 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,5 +1,3 @@ -(See the guidelines for contributing, linked above) - ## steps diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 0392fc0ee..000000000 --- a/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1 +0,0 @@ -(See the guidelines for contributing, linked above) From 2a12a7aeb952417866c46349f69b5af02ff22f23 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:30:01 +0100 Subject: [PATCH 142/176] Fix section headers in CONTRIBUTING --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c38f19e4..5e9393570 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -66,21 +66,21 @@ Documentation fixes and contributions are as much welcome as to patching the cor The developers need three things from you: **steps**, **problems**, and **expectations**. -### Steps +#### Steps The most important thing to remember about bug reporting is to clearly distinguish facts and opinions. What we need first is **the exact steps to reproduce your problems on our computers**. This is called *reproduction steps*, which is often shortened to "repro steps" or "steps." Describe your method of running sbt. Provide `build.sbt` that caused the problem and the version of sbt or Scala that was used. Provide sample Scala code if it's to do with incremental compilation. If possible, minimize the problem to reduce non-essential factors. Repro steps are the most important part of a bug report. If we cannot reproduce the problem in one way or the other, the problem can't be fixed. Telling us the error messages is not enough. -### Problems +#### Problems Next, describe the problems, or what *you think* is the problem. It might be "obvious" to you that it's a problem, but it could actually be an intentional behavior for some backward compatibility etc. For compilation errors, include the stack trace. The more raw info the better. -### Expectations +#### Expectations Same as the problems. Describe what *you think* should've happened. -### Notes +#### Notes Add an optional notes section to describe your analysis. From 701eda668baa9fba773533a4aec6611732691e5b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:30:11 +0100 Subject: [PATCH 143/176] typo in CONTRIBUTING --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e9393570..773736be0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,7 +82,7 @@ Same as the problems. Describe what *you think* should've happened. #### Notes -Add an optional notes section to describe your analysis. +Add any optional notes section to describe your analysis. ### Subject From d7f66b0c00f8fa111e8ba91fc9326d85c40b08fc Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:30:33 +0100 Subject: [PATCH 144/176] Shuffle a part of issue reporting in CONTRIBUTING --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 773736be0..8aa09a581 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -66,9 +66,11 @@ Documentation fixes and contributions are as much welcome as to patching the cor The developers need three things from you: **steps**, **problems**, and **expectations**. +The most important thing to remember about bug reporting is to clearly distinguish facts and opinions. + #### Steps -The most important thing to remember about bug reporting is to clearly distinguish facts and opinions. What we need first is **the exact steps to reproduce your problems on our computers**. This is called *reproduction steps*, which is often shortened to "repro steps" or "steps." Describe your method of running sbt. Provide `build.sbt` that caused the problem and the version of sbt or Scala that was used. Provide sample Scala code if it's to do with incremental compilation. If possible, minimize the problem to reduce non-essential factors. +What we need first is **the exact steps to reproduce your problems on our computers**. This is called *reproduction steps*, which is often shortened to "repro steps" or "steps." Describe your method of running sbt. Provide `build.sbt` that caused the problem and the version of sbt or Scala that was used. Provide sample Scala code if it's to do with incremental compilation. If possible, minimize the problem to reduce non-essential factors. Repro steps are the most important part of a bug report. If we cannot reproduce the problem in one way or the other, the problem can't be fixed. Telling us the error messages is not enough. From 76a621b996e382972329accb52f75ed27e324492 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:49:39 +0100 Subject: [PATCH 145/176] Not all PRs need notes --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8aa09a581..12195fb34 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -118,7 +118,7 @@ See below for the branch to work against. ### Adding notes -All pull requests are required to include a "Notes" file which documents the change. This file should reside in the +Most pull requests should include a "Notes" file which documents the change. This file should reside in the directory: From 2a4406717cbcb66a2f27a7aae5b524f99e17d2bb Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:49:25 +0100 Subject: [PATCH 146/176] Move PROFILING out of CONTRIBUTING --- CONTRIBUTING.md | 151 +---------------------------------------------- PROFILING.md | 153 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 150 deletions(-) create mode 100644 PROFILING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 12195fb34..0961e1686 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -232,156 +232,7 @@ command. To run a single test, such as the test in 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. +See [PROFILING](./PROFILING.md) Other notes for maintainers --------------------------- diff --git a/PROFILING.md b/PROFILING.md new file mode 100644 index 000000000..90f29a1b5 --- /dev/null +++ b/PROFILING.md @@ -0,0 +1,153 @@ +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. From 702ff1f51655a90e41f23cd269759fe7ce4e4cbc Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:51:19 +0100 Subject: [PATCH 147/176] Tweak Publishing VS Code Extensions --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0961e1686..b3d051950 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -239,8 +239,7 @@ Other notes for maintainers ### Publishing VS Code Extensions - -https://code.visualstudio.com/docs/extensions/publish-extension +Reference https://code.visualstudio.com/docs/extensions/publish-extension ``` $ sbt From 932ee5dc6aaf03f758974497e11b88008245e19a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 14:55:09 +0100 Subject: [PATCH 148/176] Split support into SUPPORT.md See https://help.github.com/articles/adding-support-resources-to-your-project/ --- CONTRIBUTING.md | 25 +++---------------------- SUPPORT.md | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 22 deletions(-) create mode 100644 SUPPORT.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3d051950..9fd26b301 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,33 +1,14 @@ [StackOverflow]: http://stackoverflow.com/tags/sbt - [ask]: https://stackoverflow.com/questions/ask?tags=sbt [Setup]: http://www.scala-sbt.org/release/docs/Getting-Started/Setup [Issues]: https://github.com/sbt/sbt/issues - [sbt-dev]: https://groups.google.com/d/forum/sbt-dev [sbt-contrib]: https://gitter.im/sbt/sbt-contrib - [Lightbend]: https://www.lightbend.com/ - [subscriptions]: https://www.lightbend.com/platform/subscription [327]: https://github.com/sbt/sbt/issues/327 - [gitter]: https://gitter.im/sbt/sbt [documentation]: https://github.com/sbt/website -Support -======= +Contributing +============ -[Lightbend] sponsors sbt and encourages contributions from the active community. Enterprises can adopt it for mission critical systems with confidence because Lightbend stands behind sbt with commercial support and services. - -For community support please [ask] on StackOverflow with the tag "sbt" (and the name of the sbt plugin(s) if any). - -- State the problem or question clearly and provide enough context. Code examples and `build.sbt` are often useful when appropriately edited. -- There's also [Gitter sbt/sbt room][gitter], but Stackoverflow is recommended so others can benefit from the answers. - -For professional support, for instance if you need faster response times, [Lightbend], the maintainer of Scala compiler and sbt, provides: - -- [Lightbend Subscriptions][subscriptions], which includes Expert Support -- Training -- Consulting - -How to contribute to sbt -======================== +(For support, see [SUPPORT](./SUPPORT.md)) There are lots of ways to contribute to sbt ecosystem depending on your interests and skill level. diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 000000000..c3c7d0c44 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,20 @@ + [ask]: https://stackoverflow.com/questions/ask?tags=sbt + [Lightbend]: https://www.lightbend.com/ + [subscriptions]: https://www.lightbend.com/platform/subscription + [gitter]: https://gitter.im/sbt/sbt + +Support +======= + +[Lightbend] sponsors sbt and encourages contributions from the active community. Enterprises can adopt it for mission critical systems with confidence because Lightbend stands behind sbt with commercial support and services. + +For community support please [ask] on StackOverflow with the tag "sbt" (and the name of the sbt plugin(s) if any). + +- State the problem or question clearly and provide enough context. Code examples and `build.sbt` are often useful when appropriately edited. +- There's also [Gitter sbt/sbt room][gitter], but Stackoverflow is recommended so others can benefit from the answers. + +For professional support, for instance if you need faster response times, [Lightbend], the maintainer of Scala compiler and sbt, provides: + +- [Lightbend Subscriptions][subscriptions], which includes Expert Support +- Training +- Consulting From 8f76c48e634848515697e8710f37a8c8996eb643 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 15:08:29 +0100 Subject: [PATCH 149/176] Document sbtOn in CONTRIBUTING.md --- CONTRIBUTING.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9fd26b301..7d94c648f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -183,6 +183,39 @@ When you run a locally built sbt, the JAR artifacts will be now cached under `$H One drawback of `-SNAPSHOT` version is that it's slow to resolve as it tries to hit all the resolvers. You can workaround that by using a version name like `1.$MINOR.$PATCH-LOCAL1`. A non-SNAPSHOT artifacts will now be cached under `$HOME/.ivy/cache/` directory, so you need to clear that out using [sbt-dirty-money](https://github.com/sbt/sbt-dirty-money)'s `cleanCache` task. +### Running sbt "from source" - `sbtOn` + +In addition to locally publishing a build of sbt, there is an alternative, experimental launcher within sbt/sbt +to be able to run sbt "from source", that is to compile sbt and run it from its resulting classfiles rather than +from published jar files. + +Such a launcher is available within sbt/sbt's build through a custom `sbtOn` command that takes as its first +argument the directory on which you want to run sbt, and the remaining arguments are passed _to_ that sbt +instance. For example: + +I have setup a minimal sbt build in the directory `/s/t`, to run sbt on that directory I call: + +```bash +> sbtOn /s/t +[info] Packaging /d/sbt/scripted/sbt/target/scala-2.12/scripted-sbt_2.12-1.2.0-SNAPSHOT.jar ... +[info] Done packaging. +[info] Running (fork) sbt.RunFromSourceMain /s/t +Listening for transport dt_socket at address: 5005 +[info] Loading settings from idea.sbt,global-plugins.sbt ... +[info] Loading global plugins from /Users/dnw/.dotfiles/.sbt/1.0/plugins +[info] Loading project definition from /s/t/project +[info] Set current project to t (in build file:/s/t/) +[info] sbt server started at local:///Users/dnw/.sbt/1.0/server/ce9baa494c7598e4d59b/sock +> show baseDirectory +[info] /s/t +> exit +[info] shutting down server +[success] Total time: 19 s, completed 25-Apr-2018 15:04:58 +``` + +Please note that this alternative launcher does _not_ have feature parity with sbt/launcher. (Meta) +contributions welcome! :-D + ### Diagnosing build failures Globally included plugins can interfere building `sbt`; if you are getting errors building sbt, try disabling all globally included plugins and try again. From 952858e68b7debc67e216c2a17445d25659c268a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 17:03:50 +0100 Subject: [PATCH 150/176] Restore references to CONTRIBUTING, with checkbox --- ISSUE_TEMPLATE.md | 2 ++ PULL_REQUEST_TEMPLATE.md | 1 + 2 files changed, 3 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 30bd9fb9e..c5d873e6a 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,3 +1,5 @@ +- [ ] I've read the [CONTRIBUTING](https://github.com/sbt/sbt/blob/1.x/CONTRIBUTING.md) guidelines + ## steps diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..47947ca33 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1 @@ +- [ ] I've read the [CONTRIBUTING](https://github.com/sbt/sbt/blob/1.x/CONTRIBUTING.md) guidelines From c2c46a60fe710ba2b0212859a92ac0eb103d5705 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Apr 2018 19:19:20 +0100 Subject: [PATCH 151/176] Document nightlies in CONTRIBUTING --- CONTRIBUTING.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d94c648f..04bf63037 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -177,6 +177,57 @@ $ sbt > compile ``` +### Using Jenkins sbt-snapshots nighties + +There is a Jenkins instance for sbt that every night builds and publishes (if successful) a timestamped version +of sbt to http://jenkins.scala-sbt.org/sbt-snapshots and is available for 4-5 weeks. To use it do the following: + +1. Set the `sbt.version` in `project/build.properties` + +```bash +echo "sbt.version=1.2.0-bin-20180423T192044" > project/build.properties +``` + +2. Create an sbt repositories file (`./repositories`) that includes that Maven repository: + +```properties +[repositories] + local + local-preloaded-ivy: file:///${sbt.preloaded-${sbt.global.base-${user.home}/.sbt}/preloaded/}, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext] + local-preloaded: file:///${sbt.preloaded-${sbt.global.base-${user.home}/.sbt}/preloaded/} + maven-central + sbt-maven-releases: https://repo.scala-sbt.org/scalasbt/maven-releases/, bootOnly + sbt-maven-snapshots: https://repo.scala-sbt.org/scalasbt/maven-snapshots/, bootOnly + typesafe-ivy-releases: https://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly + sbt-ivy-snapshots: https://repo.scala-sbt.org/scalasbt/ivy-snapshots/, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly + sbt-snapshots: https://jenkins.scala-sbt.org/sbt-snapshots +``` + +3. Start sbt with a stable launcher and the custom repositories file: + +```bash +$ sbt -sbt-jar ~/.sbt/launchers/1.1.4/sbt-launch.jar -Dsbt.repository.config=repositories +Getting org.scala-sbt sbt 1.2.0-bin-20180423T192044 (this may take some time)... +downloading https://jenkins.scala-sbt.org/sbt-snapshots/org/scala-sbt/sbt/1.2.0-bin-20180423T192044/sbt-1.2.0-bin-20180423T192044.jar ... + [SUCCESSFUL ] org.scala-sbt#sbt;1.2.0-bin-20180423T192044!sbt.jar (139ms) +... +[info] sbt server started at local:///Users/dnw/.sbt/1.0/server/936e0f52ed9baf6b6d83/sock +> show sbtVersion +[info] 1.2.0-bin-20180423T192044 +``` + +### Using Jenkins maven-snapshots nightlies + +As an alternative you can request a build that publishes to https://repo.scala-sbt.org/scalasbt/maven-snapshots +and stays there forever by: + +1. Logging into https://jenkins.scala-sbt.org/job/sbt-validator/ +2. Clicking "Build with Parameters" +3. Making sure `deploy_to_bintray` is enabled +4. Hitting "Build" + +Afterwhich start sbt with a stable launcher: `sbt -sbt-jar ~/.sbt/launchers/1.1.4/sbt-launch.jar` + ### Clearing out boot and local cache When you run a locally built sbt, the JAR artifacts will be now cached under `$HOME/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.$MINOR.$PATCH-SNAPSHOT` directory. To clear this out run: `reboot dev` command from sbt's session of your test application. From 06d7be0365cc087a079f47fc236f77ea6eaab00c Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 26 Apr 2018 04:44:12 -0400 Subject: [PATCH 152/176] handle X / y --- .../main/scala/sbt/std/TaskLinterDSL.scala | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala index 1c47216fa..98e26d4fe 100644 --- a/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala +++ b/main-settings/src/main/scala/sbt/std/TaskLinterDSL.scala @@ -24,9 +24,6 @@ abstract class BaseTaskLinterDSL extends LinterDSL { val isTask = convert.asPredicate(ctx) class traverser extends Traverser { private val unchecked = symbolOf[sbt.sbtUnchecked].asClass - private val taskKeyType = typeOf[sbt.TaskKey[_]] - private val settingKeyType = typeOf[sbt.SettingKey[_]] - private val inputKeyType = typeOf[sbt.InputKey[_]] private val initializeType = typeOf[sbt.Def.Initialize[_]] private val uncheckedWrappers = MutableSet.empty[Tree] var insideIf: Boolean = false @@ -57,8 +54,8 @@ abstract class BaseTaskLinterDSL extends LinterDSL { } } - @inline def isKey(tpe: Type): Boolean = - tpe <:< initializeType || tpe <:< taskKeyType || tpe <:< settingKeyType || tpe <:< inputKeyType + @inline def isKey(tpe: Type): Boolean = isInitialize(tpe) + @inline def isInitialize(tpe: Type): Boolean = tpe <:< initializeType def detectAndErrorOnKeyMissingValue(i: Ident): Unit = { if (isKey(i.tpe)) { @@ -74,6 +71,13 @@ abstract class BaseTaskLinterDSL extends LinterDSL { } else () } + def detectAndErrorOnKeyMissingValue(a: Apply): Unit = { + if (isInitialize(a.tpe)) { + val expr = "X / y" + ctx.error(a.pos, TaskLinterDSLFeedback.missingValueForInitialize(expr)) + } else () + } + override def traverse(tree: ctx.universe.Tree): Unit = { tree match { case ap @ Apply(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) => @@ -128,11 +132,13 @@ abstract class BaseTaskLinterDSL extends LinterDSL { rhs match { case i: Ident => detectAndErrorOnKeyMissingValue(i) case s: Select => detectAndErrorOnKeyMissingValue(s) + case a: Apply => detectAndErrorOnKeyMissingValue(a) case _ => () } case i: Ident => detectAndErrorOnKeyMissingValue(i) case s: Select => detectAndErrorOnKeyMissingValue(s) - case t => () + case a: Apply => detectAndErrorOnKeyMissingValue(a) + case _ => () } } traverseTrees(stmts) @@ -171,14 +177,13 @@ object TaskLinterDSLFeedback { private final val startGreen = if (ConsoleAppender.formatEnabledInEnv) AnsiColor.GREEN else "" private final val reset = if (ConsoleAppender.formatEnabledInEnv) AnsiColor.RESET else "" - private final val ProblemHeader = s"${startRed}Problem${reset}" - private final val SolutionHeader = s"${startGreen}Solution${reset}" + private final val ProblemHeader = s"${startRed}problem${reset}" + private final val SolutionHeader = s"${startGreen}solution${reset}" def useOfValueInsideAnon(task: String) = s"""${startBold}The evaluation of `$task` inside an anonymous function is prohibited.$reset | |${ProblemHeader}: Task invocations inside anonymous functions are evaluated independently of whether the anonymous function is invoked or not. - | |${SolutionHeader}: | 1. Make `$task` evaluation explicit outside of the function body if you don't care about its evaluation. | 2. Use a dynamic task to evaluate `$task` and pass that value as a parameter to an anonymous function. @@ -189,7 +194,6 @@ object TaskLinterDSLFeedback { | |${ProblemHeader}: `$task` is inside the if expression of a regular task. | Regular tasks always evaluate task inside the bodies of if expressions. - | |${SolutionHeader}: | 1. If you only want to evaluate it when the if predicate is true or false, use a dynamic task. | 2. Otherwise, make the static evaluation explicit by evaluating `$task` outside the if expression. @@ -198,8 +202,14 @@ object TaskLinterDSLFeedback { def missingValueForKey(key: String) = s"""${startBold}The key `$key` is not being invoked inside the task definition.$reset | - |${ProblemHeader}: Keys missing `.value` are not initialized and their dependency is not registered. - | + |${ProblemHeader}: Keys missing `.value` are not initialized and their dependency is not registered. |${SolutionHeader}: Replace `$key` by `$key.value` or remove it if unused. """.stripMargin + + def missingValueForInitialize(expr: String) = + s"""${startBold}The setting/task `$expr` is not being invoked inside the task definition.$reset + | + |${ProblemHeader}: Settings/tasks missing `.value` are not initialized and their dependency is not registered. + |${SolutionHeader}: Replace `$expr` by `($expr).value` or remove it if unused. + """.stripMargin } From d90f2734207acfd4dd0b8f3e86e0bf986673916a Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 26 Apr 2018 05:02:21 -0400 Subject: [PATCH 153/176] Remove deprecated commands -, --, and --- https://github.com/sbt/sbt/commit/86ae3c8c59df546dadb8c62901bac50c175af78a deprecated -, --, and ---. This removes the deprecated commands. --- main-command/src/main/scala/sbt/BasicCommands.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index e9d5b8a19..38e7fb663 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -56,7 +56,7 @@ object BasicCommands { client, read, alias - ) ++ compatCommands + ) def nop: Command = Command.custom(s => success(() => s)) def ignore: Command = Command.command(FailureWall)(idFun) From 3a4bc8dc0ba833a6d093b9bdb9b39494ab17d055 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 26 Apr 2018 14:03:35 +0100 Subject: [PATCH 154/176] Dropped deprecated "compat" commands & strings Fixes #4089 --- build.sbt | 5 +++++ .../main/scala/sbt/BasicCommandStrings.scala | 14 -------------- .../src/main/scala/sbt/BasicCommands.scala | 19 ------------------- main-command/src/main/scala/sbt/State.scala | 6 +----- 4 files changed, 6 insertions(+), 38 deletions(-) diff --git a/build.sbt b/build.sbt index 1c24cf775..39fa79c84 100644 --- a/build.sbt +++ b/build.sbt @@ -428,6 +428,11 @@ lazy val commandProj = (project in file("main-command")) contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats, mimaSettings, mimaBinaryIssueFilters ++= Vector( + // dropped private[sbt] method + exclude[DirectMissingMethodProblem]("sbt.BasicCommands.compatCommands"), + // dropped mainly internal command strings holder + exclude[MissingClassProblem]("sbt.BasicCommandStrings$Compat$"), + exclude[DirectMissingMethodProblem]("sbt.BasicCommands.rebootOptionParser"), // Changed the signature of Server method. nacho cheese. exclude[DirectMissingMethodProblem]("sbt.internal.server.Server.*"), diff --git a/main-command/src/main/scala/sbt/BasicCommandStrings.scala b/main-command/src/main/scala/sbt/BasicCommandStrings.scala index 6baee9509..b819f77a5 100644 --- a/main-command/src/main/scala/sbt/BasicCommandStrings.scala +++ b/main-command/src/main/scala/sbt/BasicCommandStrings.scala @@ -187,20 +187,6 @@ $AliasCommand name= def StashOnFailure = "sbtStashOnFailure" def PopOnFailure = "sbtPopOnFailure" - // commands with poor choices for names since they clash with the usual conventions for command line options - // these are not documented and are mainly internal commands and can be removed without a full deprecation cycle - object Compat { - def OnFailure = "-" - def ClearOnFailure = "--" - def FailureWall = "---" - def OnFailureDeprecated = deprecatedAlias(OnFailure, BasicCommandStrings.OnFailure) - def ClearOnFailureDeprecated = - deprecatedAlias(ClearOnFailure, BasicCommandStrings.ClearOnFailure) - def FailureWallDeprecated = deprecatedAlias(FailureWall, BasicCommandStrings.FailureWall) - private[this] def deprecatedAlias(oldName: String, newName: String): String = - s"The `$oldName` command is deprecated in favor of `$newName` and will be removed in a later version" - } - def FailureWall = "resumeFromFailure" def ClearOnFailure = "sbtClearOnFailure" diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index 38e7fb663..439821a6d 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -152,25 +152,6 @@ object BasicCommands { (s, arg) => s.copy(onFailure = Some(Exec(arg, s.source))) ) - private[sbt] def compatCommands = Seq( - Command.command(Compat.ClearOnFailure) { s => - s.log.warn(Compat.ClearOnFailureDeprecated) - s.copy(onFailure = None) - }, - Command.arb( - s => - token(Compat.OnFailure, hide = const(true)) - .flatMap(_ => otherCommandParser(s)) - ) { (s, arg) => - s.log.warn(Compat.OnFailureDeprecated) - s.copy(onFailure = Some(Exec(arg, s.source))) - }, - Command.command(Compat.FailureWall) { s => - s.log.warn(Compat.FailureWallDeprecated) - s - } - ) - def clearOnFailure: Command = Command.command(ClearOnFailure)(s => s.copy(onFailure = None)) def stashOnFailure: Command = diff --git a/main-command/src/main/scala/sbt/State.scala b/main-command/src/main/scala/sbt/State.scala index e65e58a03..f79b9d70d 100644 --- a/main-command/src/main/scala/sbt/State.scala +++ b/main-command/src/main/scala/sbt/State.scala @@ -285,11 +285,7 @@ object State { def log = s.globalLogging.full def handleError(t: Throwable): State = handleException(t, s, log) def fail = { - import BasicCommandStrings.Compat.{ FailureWall => CompatFailureWall } - val remaining = - s.remainingCommands.dropWhile( - c => c.commandLine != FailureWall && c.commandLine != CompatFailureWall - ) + val remaining = s.remainingCommands.dropWhile(c => c.commandLine != FailureWall) if (remaining.isEmpty) applyOnFailure(s, Nil, exit(ok = false)) else From 136dcb16d96d537c81290d48994c3c4bcaee248a Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 30 Apr 2018 22:50:23 -0400 Subject: [PATCH 155/176] Fix VS Code extension startup The change contributed in https://github.com/sbt/sbt/pull/4130 to start sbt within VS Code looked ok at first, but when I published the extension and started using it, I realized that it's a bit broken. Basically there's no cleanup logic (that I could find), so simply closing VS Code would leave `project/target/active.json` behind, which lets VS Code extension to attempt connection, and it fails because there's no server running. To workaround this, I'll attempt to connect to the socket to confirm sbt server is up. --- vscode-sbt-scala/client/package.json | 2 +- vscode-sbt-scala/client/src/extension.ts | 73 ++++++++++++++++++++---- vscode-sbt-scala/server/src/server.ts | 36 ++++++------ 3 files changed, 83 insertions(+), 28 deletions(-) diff --git a/vscode-sbt-scala/client/package.json b/vscode-sbt-scala/client/package.json index 1e729fc32..f4316539e 100644 --- a/vscode-sbt-scala/client/package.json +++ b/vscode-sbt-scala/client/package.json @@ -1,7 +1,7 @@ { "name": "vscode-sbt-scala", "displayName": "Scala (sbt)", - "version": "0.1.0", + "version": "0.2.0", "author": "Lightbend, Inc.", "license": "BSD-3-Clause", "publisher": "lightbend", diff --git a/vscode-sbt-scala/client/src/extension.ts b/vscode-sbt-scala/client/src/extension.ts index c5d3a1282..ff8206a37 100644 --- a/vscode-sbt-scala/client/src/extension.ts +++ b/vscode-sbt-scala/client/src/extension.ts @@ -1,28 +1,39 @@ 'use strict'; import * as path from 'path'; - -let fs = require('fs'); +import * as url from 'url'; +import * as net from 'net'; +let fs = require('fs'), + os = require('os'); import * as vscode from 'vscode'; import { ExtensionContext, workspace } from 'vscode'; // workspace, import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient'; +let terminal: vscode.Terminal = null; + +function delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export async function deactivate() { + if (terminal != null) { + terminal.sendText("exit"); + await delay(1000); + terminal.dispose(); + } +} + export async function activate(context: ExtensionContext) { // Start sbt - const terminal = vscode.window.createTerminal(`sbt`); + terminal = vscode.window.createTerminal(`sbt`); terminal.show(); terminal.sendText("sbt"); - - function delay(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); - } - // Wait for SBT server to start - let retries = 30; + let retries = 60; while (retries > 0) { retries--; await delay(1000); - if (fs.existsSync(path.join(workspace.rootPath, 'project', 'target', 'active.json'))) { + if (isServerUp()) { break; } } @@ -46,7 +57,47 @@ export async function activate(context: ExtensionContext) { return discoverToken(); } } - + + // Don't start VS Code connection until sbt server is confirmed to be up and running. + function isServerUp(): boolean { + let isFileThere = fs.existsSync(path.join(workspace.rootPath, 'project', 'target', 'active.json')); + if (!isFileThere) { + return false; + } else { + let skt = new net.Socket(); + try { + connectSocket(skt); + } catch(e) { + return false; + } + skt.end(); + return true; + } + } + + function connectSocket(socket: net.Socket):  net.Socket { + let u = discoverUrl(); + // let socket = net.Socket(); + if (u.protocol == 'tcp:') { + socket.connect(+u.port, '127.0.0.1'); + } else if (u.protocol == 'local:' && os.platform() == 'win32') { + let pipePath = '\\\\.\\pipe\\' + u.hostname; + socket.connect(pipePath); + } else if (u.protocol == 'local:') { + socket.connect(u.path); + } else { + throw 'Unknown protocol ' + u.protocol; + } + return socket; + } + + // the port file is hardcoded to a particular location relative to the build. + function discoverUrl(): url.Url { + let pf = path.join(process.cwd(), 'project', 'target', 'active.json'); + let portfile = JSON.parse(fs.readFileSync(pf)); + return url.parse(portfile.uri); + } + // the port file is hardcoded to a particular location relative to the build. function discoverToken(): any { let pf = path.join(workspace.rootPath, 'project', 'target', 'active.json'); diff --git a/vscode-sbt-scala/server/src/server.ts b/vscode-sbt-scala/server/src/server.ts index 05932d451..8404c7b74 100644 --- a/vscode-sbt-scala/server/src/server.ts +++ b/vscode-sbt-scala/server/src/server.ts @@ -2,32 +2,20 @@ import * as path from 'path'; import * as url from 'url'; -let net = require('net'), - fs = require('fs'), +import * as net from 'net'; +let fs = require('fs'), os = require('os'), stdin = process.stdin, stdout = process.stdout; -let u = discoverUrl(); - -let socket = net.Socket(); +let socket = new net.Socket(); socket.on('data', (chunk: any) => { // send it back to stdout stdout.write(chunk); }).on('end', () => { stdin.pause(); }); - -if (u.protocol == 'tcp:') { - socket.connect(u.port, '127.0.0.1'); -} else if (u.protocol == 'local:' && os.platform() == 'win32') { - let pipePath = '\\\\.\\pipe\\' + u.hostname; - socket.connect(pipePath); -} else if (u.protocol == 'local:') { - socket.connect(u.path); -} else { - throw 'Unknown protocol ' + u.protocol; -} +connectSocket(socket); stdin.resume(); stdin.on('data', (chunk: any) => { @@ -36,6 +24,22 @@ stdin.on('data', (chunk: any) => { socket.end(); }); +function connectSocket(socket: net.Socket): net.Socket { + let u = discoverUrl(); + // let socket = net.Socket(); + if (u.protocol == 'tcp:') { + socket.connect(+u.port, '127.0.0.1'); + } else if (u.protocol == 'local:' && os.platform() == 'win32') { + let pipePath = '\\\\.\\pipe\\' + u.hostname; + socket.connect(pipePath); + } else if (u.protocol == 'local:') { + socket.connect(u.path); + } else { + throw 'Unknown protocol ' + u.protocol; + } + return socket; +} + // the port file is hardcoded to a particular location relative to the build. function discoverUrl(): url.Url { let pf = path.join(process.cwd(), 'project', 'target', 'active.json'); From a2f70342dfd9dc0c65c18d9e19e0e06dd9839314 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 2 May 2018 01:09:30 -0400 Subject: [PATCH 156/176] Uncomment server customization --- sbt/src/server-test/handshake/build.sbt | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sbt/src/server-test/handshake/build.sbt b/sbt/src/server-test/handshake/build.sbt index 9c4f59ef8..02f5f81fd 100644 --- a/sbt/src/server-test/handshake/build.sbt +++ b/sbt/src/server-test/handshake/build.sbt @@ -1,23 +1,23 @@ -// import sbt.internal.ServerHandler +import sbt.internal.server.{ ServerHandler, ServerIntent } 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 - // ) - // }), + 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", From add6bde3961d3443ea90138b1be68588d4df7109 Mon Sep 17 00:00:00 2001 From: Tim Harper Date: Mon, 14 May 2018 12:39:44 -0600 Subject: [PATCH 157/176] Add timestamp field to JUnitXML report --- testing/src/main/scala/sbt/JUnitXmlTestsListener.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala index 2f92523e8..0ac2a6997 100644 --- a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala +++ b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala @@ -9,6 +9,7 @@ package sbt import java.io.{ File, IOException, PrintWriter, StringWriter } import java.net.InetAddress +import java.time.LocalDateTime import java.util.Hashtable import scala.collection.mutable.ListBuffer @@ -59,7 +60,7 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { * Gathers data for one Test Suite. We map test groups to TestSuites. * Each TestSuite gets its own output file. */ - class TestSuite(val name: String) { + class TestSuite(val name: String, timestamp: LocalDateTime) { val events: ListBuffer[TEvent] = new ListBuffer() /**Adds one test result to this suite.*/ @@ -83,7 +84,7 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { ) val result = - + { properties } { for (e <- events) yield Date: Mon, 14 May 2018 17:29:20 -0600 Subject: [PATCH 158/176] Remove milliseconds from format in order to comply with JUnit spec --- .../src/main/scala/sbt/JUnitXmlTestsListener.scala | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala index 0ac2a6997..33057b6e7 100644 --- a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala +++ b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala @@ -10,6 +10,8 @@ package sbt import java.io.{ File, IOException, PrintWriter, StringWriter } import java.net.InetAddress import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit import java.util.Hashtable import scala.collection.mutable.ListBuffer @@ -84,7 +86,7 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { ) val result = - + { properties } { for (e <- events) yield * * @@ -201,6 +205,12 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { // contort the user into not using spaces. private[this] def normalizeName(s: String) = s.replaceAll("""\s+""", "-") + /** + * Format the date, without milliseconds or the timezone, per the JUnit spec. + */ + private[this] def formatISO8601DateTime(d: LocalDateTime): String = + d.truncatedTo(ChronoUnit.SECONDS).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + private def writeSuite() = { val file = new File(targetDir, s"${normalizeName(withTestSuite(_.name))}.xml").getAbsolutePath // TODO would be nice to have a logger and log this with level debug From 3702132019e41f409256c0ab4cb55c9b42eb8d98 Mon Sep 17 00:00:00 2001 From: Tim Harper Date: Mon, 14 May 2018 17:31:05 -0600 Subject: [PATCH 159/176] Keep single parameter constructor for backwards bin compat. --- testing/src/main/scala/sbt/JUnitXmlTestsListener.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala index 33057b6e7..fc4c47118 100644 --- a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala +++ b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala @@ -63,6 +63,8 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { * Each TestSuite gets its own output file. */ class TestSuite(val name: String, timestamp: LocalDateTime) { + def this(name: String) = this(name, LocalDateTime.now()) + val events: ListBuffer[TEvent] = new ListBuffer() /**Adds one test result to this suite.*/ @@ -142,10 +144,7 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { /** * Starts a new, initially empty Suite with the given name. */ - override def startGroup(name: String): Unit = { - val timestamp = LocalDateTime.now() - testSuite.set(Some(new TestSuite(name, timestamp))) - } + override def startGroup(name: String): Unit = testSuite.set(Some(new TestSuite(name))) /** * Adds all details for the given even to the current suite. From 5cc0038a65a12c93cd9dcd7345128cf4ab99beee Mon Sep 17 00:00:00 2001 From: Tim Harper Date: Mon, 14 May 2018 20:57:04 -0600 Subject: [PATCH 160/176] Little better comment. Add timestamp to example. --- testing/src/main/scala/sbt/JUnitXmlTestsListener.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala index fc4c47118..9344305f5 100644 --- a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala +++ b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala @@ -156,11 +156,10 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { /** * called for each class or equivalent grouping * We map one group to one Testsuite, so for each Group - * we create an XML which implements the [[https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd JUnit xml - * spec]], and looks like this: + * we create [[https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd JUnit XML file]], and looks like this: * * - * + * * * * ... From 7dfe6097fbcd53f98457573f2205c2668c8306bc Mon Sep 17 00:00:00 2001 From: Alexander Samsig Date: Tue, 15 May 2018 12:13:51 +0200 Subject: [PATCH 161/176] Removed Load and LoadCommand since they are no longer in use. Changed add-defaults-commands directly since it is only used internally. Added deprecation warning for LoadFailed and LastGrep. --- main/src/main/scala/sbt/Main.scala | 29 ++++++++++++++++--- .../scala/sbt/internal/CommandStrings.scala | 10 ++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index ac1ad03be..2117f26f0 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -187,6 +187,7 @@ object BuiltinCommands { inspect, loadProjectImpl, loadFailed, + oldLoadFailed, Cross.crossBuild, Cross.switchVersion, PluginCross.pluginCross, @@ -468,13 +469,21 @@ object BuiltinCommands { @deprecated("Use `lastGrep` instead.", "1.2.0") def oldLastGrep: Command = - lastGrepCommand(OldLastGrepCommand, oldLastGrepBrief, oldLastGrepDetailed) + lastGrepCommand(OldLastGrepCommand, oldLastGrepBrief, oldLastGrepDetailed, { s => + s.log.warn(deprecationWarningText(OldLastGrepCommand, LastGrepCommand)) + lastGrepParser(s) + }) def lastGrep: Command = - lastGrepCommand(LastGrepCommand, lastGrepBrief, lastGrepDetailed) + lastGrepCommand(LastGrepCommand, lastGrepBrief, lastGrepDetailed, lastGrepParser) - private def lastGrepCommand(name: String, briefHelp: (String, String), detail: String): Command = - Command(name, briefHelp, detail)(lastGrepParser) { + private def lastGrepCommand( + name: String, + briefHelp: (String, String), + detail: String, + parser: State => Parser[(String, Option[AnyKeys])] + ): Command = + Command(name, briefHelp, detail)(parser) { case (s, (pattern, Some(sks))) => val (str, _, display) = extractLast(s) Output.lastGrep(sks, str.streams(s), pattern, printLast)(display) @@ -670,6 +679,18 @@ object BuiltinCommands { Command.make(ProjectCommand, projectBrief, projectDetailed)(ProjectNavigation.command) def loadFailed: Command = Command(LoadFailed)(loadProjectParser)(doLoadFailed) + @deprecated("Use `loadFailed` instead.", "1.2.0") + def oldLoadFailed: Command = + Command(OldLoadFailed) { s => + s.log.warn( + deprecationWarningText(OldLoadFailed, LoadFailed) + ) + loadProjectParser(s) + }(doLoadFailed) + + private[this] def deprecationWarningText(oldCommand: String, newCommand: String) = { + s"The `$oldCommand` command is deprecated in favor of `$newCommand` and will be removed in a later version" + } @tailrec private[this] def doLoadFailed(s: State, loadArg: String): State = { diff --git a/main/src/main/scala/sbt/internal/CommandStrings.scala b/main/src/main/scala/sbt/internal/CommandStrings.scala index 1239cc034..99ec4637e 100644 --- a/main/src/main/scala/sbt/internal/CommandStrings.scala +++ b/main/src/main/scala/sbt/internal/CommandStrings.scala @@ -280,16 +280,12 @@ $ProjectsCommand remove + def sbtrc = ".sbtrc" - def DefaultsCommand = "add-default-commands" + def DefaultsCommand = "addDefaultCommands" def DefaultsBrief = (DefaultsCommand, DefaultsDetailed) def DefaultsDetailed = "Registers default built-in commands" - def Load = "load" - def LoadLabel = "a project" - def LoadCommand = "load-commands" - def LoadCommandLabel = "commands" - - def LoadFailed = "load-failed" + def LoadFailed = "loadFailed" + def OldLoadFailed = "load-failed" def LoadProjectImpl = "loadp" def LoadProject = "reload" From b30159aded43bc945744ae6e21b69b30a873e5f3 Mon Sep 17 00:00:00 2001 From: alodavi Date: Tue, 15 May 2018 14:06:50 +0200 Subject: [PATCH 162/176] [alodavi/improving_loading_settings_messaging] logging the path instead of just the name --- main/src/main/scala/sbt/internal/Load.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 6d620ec7c..0a3cce27d 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -1039,7 +1039,7 @@ private[sbt] object Load { // Grab all the settings we already loaded from sbt files def settings(files: Seq[File]): Seq[Setting[_]] = { if (files.nonEmpty) - log.info(s"${files.map(_.getName).mkString("Loading settings from ", ",", " ...")}") + log.info(s"${files.map(_.getPath).mkString("Loading settings from ", ",", " ...")}") for { file <- files config <- (memoSettings get file).toSeq From 80601e78ad5711cc9ac1023c034e50b4f9484f12 Mon Sep 17 00:00:00 2001 From: alodavi Date: Tue, 15 May 2018 14:44:00 +0200 Subject: [PATCH 163/176] [alodavi/improving_loading_settings_messaging] added notes on the Pr --- .../improving_loading_settings_messaging.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 notes/1.1.4/improving_loading_settings_messaging.md diff --git a/notes/1.1.4/improving_loading_settings_messaging.md b/notes/1.1.4/improving_loading_settings_messaging.md new file mode 100644 index 000000000..683846bbb --- /dev/null +++ b/notes/1.1.4/improving_loading_settings_messaging.md @@ -0,0 +1,22 @@ +### Improvements + +Now when loading a project that has multiple build.sbt files the logger shows the path as well. +Before it was: + +```bash +[info] Loading settings from build.sbt ... +[info] Loading settings from build.sbt ... +[info] Loading settings from build.sbt ... +[info] Loading settings from build.sbt ... +``` + +Now it's: + +```bash +[info] Loading settings from /home/user/Work/personal/someProject/build.sbt ... +[info] Loading settings from /home/user/Work/personal/someProject/subProject1/build.sbt ... +[info] Loading settings from /home/user/Work/personal/someProject/subProject2/build.sbt ... +[info] Loading settings from /home/user/Work/personal/someProject/subProject3/build.sbt ... +``` + +This should solve the issue: https://github.com/sbt/sbt/issues/3607 \ No newline at end of file From b7c9862f163931d04a574ea7bc93c4e7dbfe3c42 Mon Sep 17 00:00:00 2001 From: tiqwab Date: Tue, 15 May 2018 23:09:43 +0900 Subject: [PATCH 164/176] Fix kebab-case commands: notify-users-about-shell, write-sbt-version --- main/src/main/scala/sbt/Main.scala | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 2117f26f0..b7c61ccb7 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -197,7 +197,9 @@ object BuiltinCommands { plugin, plugins, writeSbtVersion, + oldWriteSbtVersion, notifyUsersAboutShell, + oldNotifyUsersAboutShell, shell, startServer, eval, @@ -853,12 +855,19 @@ object BuiltinCommands { if (!java.lang.Boolean.getBoolean("sbt.skip.version.write") && !intendsToInvokeNew(state)) writeSbtVersionUnconditionally(state) - private def WriteSbtVersion = "write-sbt-version" + private def WriteSbtVersion = "writeSbtVersion" + private def OldWriteSbtVersion = "write-sbt-version" private def writeSbtVersion: Command = Command.command(WriteSbtVersion) { state => writeSbtVersion(state); state } + @deprecated("Use `writeSbtVersion` instead", "1.2.0") + private def oldWriteSbtVersion: Command = + Command.command(OldWriteSbtVersion) { state => + state.log.warn(deprecationWarningText(OldWriteSbtVersion, WriteSbtVersion)) + writeSbtVersion(state); state + } private def intendsToInvokeCompile(state: State) = state.remainingCommands exists (_.commandLine == Keys.compile.key.label) @@ -869,10 +878,17 @@ object BuiltinCommands { state.log info "Executing in batch mode. For better performance use sbt's shell" } - private def NotifyUsersAboutShell = "notify-users-about-shell" + private def NotifyUsersAboutShell = "notifyUsersAboutShell" + private def OldNotifyUsersAboutShell = "notify-users-about-shell" private def notifyUsersAboutShell: Command = Command.command(NotifyUsersAboutShell) { state => notifyUsersAboutShell(state); state } + @deprecated("Use `notifyUsersAboutShell` instead", "1.2.0") + private def oldNotifyUsersAboutShell: Command = + Command.command(OldNotifyUsersAboutShell) { state => + state.log.warn(deprecationWarningText(OldNotifyUsersAboutShell, NotifyUsersAboutShell)) + notifyUsersAboutShell(state); state + } } From ab35c21c9818d9effe38fd3682ba4c6d59df4862 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 21 May 2018 17:24:38 +0100 Subject: [PATCH 165/176] Drop deprecated write-sbt-version & notify-users-about-shell Introduced in https://github.com/sbt/sbt/pull/4169, these commands aren't "user-facing" and are quite new. So no need to keep the old kebab syntax. --- main/src/main/scala/sbt/Main.scala | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index b7c61ccb7..b29cca948 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -197,9 +197,7 @@ object BuiltinCommands { plugin, plugins, writeSbtVersion, - oldWriteSbtVersion, notifyUsersAboutShell, - oldNotifyUsersAboutShell, shell, startServer, eval, @@ -856,18 +854,11 @@ object BuiltinCommands { writeSbtVersionUnconditionally(state) private def WriteSbtVersion = "writeSbtVersion" - private def OldWriteSbtVersion = "write-sbt-version" private def writeSbtVersion: Command = Command.command(WriteSbtVersion) { state => writeSbtVersion(state); state } - @deprecated("Use `writeSbtVersion` instead", "1.2.0") - private def oldWriteSbtVersion: Command = - Command.command(OldWriteSbtVersion) { state => - state.log.warn(deprecationWarningText(OldWriteSbtVersion, WriteSbtVersion)) - writeSbtVersion(state); state - } private def intendsToInvokeCompile(state: State) = state.remainingCommands exists (_.commandLine == Keys.compile.key.label) @@ -879,16 +870,9 @@ object BuiltinCommands { } private def NotifyUsersAboutShell = "notifyUsersAboutShell" - private def OldNotifyUsersAboutShell = "notify-users-about-shell" private def notifyUsersAboutShell: Command = Command.command(NotifyUsersAboutShell) { state => notifyUsersAboutShell(state); state } - @deprecated("Use `notifyUsersAboutShell` instead", "1.2.0") - private def oldNotifyUsersAboutShell: Command = - Command.command(OldNotifyUsersAboutShell) { state => - state.log.warn(deprecationWarningText(OldNotifyUsersAboutShell, NotifyUsersAboutShell)) - notifyUsersAboutShell(state); state - } } From 67efea6248cd70a0b129fa39a8ca6fa347b5fba5 Mon Sep 17 00:00:00 2001 From: alodavi Date: Wed, 23 May 2018 17:20:41 +0200 Subject: [PATCH 166/176] [alodavi/improving_loading_settings_messaging] logging file and project name instead of only file name --- main/src/main/scala/sbt/internal/Load.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 0a3cce27d..8a316bbc1 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -1039,7 +1039,7 @@ private[sbt] object Load { // Grab all the settings we already loaded from sbt files def settings(files: Seq[File]): Seq[Setting[_]] = { if (files.nonEmpty) - log.info(s"${files.map(_.getPath).mkString("Loading settings from ", ",", " ...")}") + log.info(s"${files.map(_.getName).mkString(s"Loading settings for project ${p.id} from ", ",", " ...")}") for { file <- files config <- (memoSettings get file).toSeq From c31583e4f8f19a6c2d060b6a75b274050439c203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Mickevi=C4=8Dius?= Date: Wed, 18 Apr 2018 16:28:43 +0100 Subject: [PATCH 167/176] Discovery of java homes --- .../contraband-scala/sbt/JavaVersion.scala | 40 +++++++++++++++ main/src/main/contraband/main.contra | 6 +++ main/src/main/scala/sbt/Defaults.scala | 4 ++ main/src/main/scala/sbt/Keys.scala | 4 ++ .../main/scala/sbt/internal/CrossJava.scala | 50 +++++++++++++++++++ .../sbt-test/java/home-discovery/build.sbt | 5 ++ sbt/src/sbt-test/java/home-discovery/test | 1 + 7 files changed, 110 insertions(+) create mode 100644 main/src/main/contraband-scala/sbt/JavaVersion.scala create mode 100644 main/src/main/scala/sbt/internal/CrossJava.scala create mode 100644 sbt/src/sbt-test/java/home-discovery/build.sbt create mode 100644 sbt/src/sbt-test/java/home-discovery/test diff --git a/main/src/main/contraband-scala/sbt/JavaVersion.scala b/main/src/main/contraband-scala/sbt/JavaVersion.scala new file mode 100644 index 000000000..68d3941f1 --- /dev/null +++ b/main/src/main/contraband-scala/sbt/JavaVersion.scala @@ -0,0 +1,40 @@ +/** + * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt +final class JavaVersion private ( + val vendor: Option[String], + val version: String) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: JavaVersion => (this.vendor == x.vendor) && (this.version == x.version) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.JavaVersion".##) + vendor.##) + version.##) + } + override def toString: String = { + "JavaVersion(" + vendor + ", " + version + ")" + } + private[this] def copy(vendor: Option[String] = vendor, version: String = version): JavaVersion = { + new JavaVersion(vendor, version) + } + def withVendor(vendor: Option[String]): JavaVersion = { + copy(vendor = vendor) + } + def withVendor(vendor: String): JavaVersion = { + copy(vendor = Option(vendor)) + } + def withVersion(version: String): JavaVersion = { + copy(version = version) + } +} +object JavaVersion { + def apply(version: String): JavaVersion = new JavaVersion(None, version) + def apply(vendor: Option[String], version: String): JavaVersion = new JavaVersion(vendor, version) + def apply(vendor: String, version: String): JavaVersion = new JavaVersion(Option(vendor), version) +} diff --git a/main/src/main/contraband/main.contra b/main/src/main/contraband/main.contra index eb9e9f42c..10ec4a469 100644 --- a/main/src/main/contraband/main.contra +++ b/main/src/main/contraband/main.contra @@ -17,3 +17,9 @@ enum PluginTrigger { AllRequirements NoTrigger } + +type JavaVersion { + vendor: String + version: String! + #xcompanion def apply(version: String): JavaVersion = new JavaVersion(None, version) +} diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 4a8a34f1a..c4fa875ac 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -69,6 +69,7 @@ import sbt.librarymanagement.syntax._ import sbt.util.InterfaceUtil.{ toJavaFunction => f1 } import sbt.util._ import sbt.util.CacheImplicits._ +import scala.collection.immutable.ListMap import scala.concurrent.duration.FiniteDuration import scala.util.control.NonFatal import scala.xml.NodeSeq @@ -159,6 +160,9 @@ object Defaults extends BuildCommon { scalaHome :== None, apiURL := None, javaHome :== None, + discoveredJavaHomes := sbt.internal.CrossJava.discoverJavaHomes, + javaHomes :== ListMap.empty, + fullJavaHomes := discoveredJavaHomes.value ++ javaHomes.value, testForkedParallel :== false, javaOptions :== Nil, sbtPlugin :== false, diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 82191f556..751540de7 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -271,6 +271,10 @@ object Keys { val outputStrategy = settingKey[Option[sbt.OutputStrategy]]("Selects how to log output when running a main class.").withRank(DSetting) val connectInput = settingKey[Boolean]("If true, connects standard input when running a main class forked.").withRank(CSetting) val javaHome = settingKey[Option[File]]("Selects the Java installation used for compiling and forking. If None, uses the Java installation running the build.").withRank(ASetting) + val discoveredJavaHomes = settingKey[Map[JavaVersion, File]]("Discovered Java home directories") + val javaHomes = settingKey[Map[JavaVersion, File]]("The user-defined additional Java home directories") + val fullJavaHomes = taskKey[Map[JavaVersion, File]]("Combines discoveredJavaHomes and custom javaHomes.").withRank(CTask) + val javaOptions = taskKey[Seq[String]]("Options passed to a new JVM when forking.").withRank(BPlusTask) val envVars = taskKey[Map[String, String]]("Environment variables used when forking a new JVM").withRank(BTask) diff --git a/main/src/main/scala/sbt/internal/CrossJava.scala b/main/src/main/scala/sbt/internal/CrossJava.scala new file mode 100644 index 000000000..c93d44257 --- /dev/null +++ b/main/src/main/scala/sbt/internal/CrossJava.scala @@ -0,0 +1,50 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt +package internal + +import java.io.File +import scala.collection.immutable.ListMap +import sbt.io.IO +import sbt.io.syntax._ + +private[sbt] object CrossJava { + def discoverJavaHomes: ListMap[JavaVersion, File] = { + val configs = Vector(JavaDiscoverConfig.linux, JavaDiscoverConfig.macOS) + ListMap(configs flatMap { _.javaHomes }: _*) + } + + sealed trait JavaDiscoverConf { + def javaHomes: Vector[(JavaVersion, File)] + } + + object JavaDiscoverConfig { + val linux = new JavaDiscoverConf { + val base: File = file("/usr") / "lib" / "jvm" + val JavaHomeDir = """java-([0-9]+)-.*""".r + def javaHomes: Vector[(JavaVersion, File)] = + wrapNull(base.list()).collect { + case dir @ JavaHomeDir(ver) => JavaVersion(ver) -> (base / dir) + } + } + + val macOS = new JavaDiscoverConf { + val base: File = file("/Library") / "Java" / "JavaVirtualMachines" + val JavaHomeDir = """jdk-?(1\.)?([0-9]+).*""".r + def javaHomes: Vector[(JavaVersion, File)] = + wrapNull(base.list()).collect { + case dir @ JavaHomeDir(m, n) => + JavaVersion(n) -> (base / dir / "Contents" / "Home") + } + } + } + + def wrapNull(a: Array[String]): Vector[String] = + if (a eq null) Vector() + else a.toVector +} diff --git a/sbt/src/sbt-test/java/home-discovery/build.sbt b/sbt/src/sbt-test/java/home-discovery/build.sbt new file mode 100644 index 000000000..b5d79caeb --- /dev/null +++ b/sbt/src/sbt-test/java/home-discovery/build.sbt @@ -0,0 +1,5 @@ +Global / javaHomes += JavaVersion("6") -> file("/good/old/times/java-6") + +TaskKey[Unit]("check") := { + assert(fullJavaHomes.value(JavaVersion("6")).getAbsolutePath.contains("java-6")) +} diff --git a/sbt/src/sbt-test/java/home-discovery/test b/sbt/src/sbt-test/java/home-discovery/test new file mode 100644 index 000000000..15675b169 --- /dev/null +++ b/sbt/src/sbt-test/java/home-discovery/test @@ -0,0 +1 @@ +> check From aff9e0110cd7f4b942e194e1eb979f89fbf64f1f Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 3 May 2018 00:59:27 -0400 Subject: [PATCH 168/176] Accept both 1.x and x for 1.1 to 1.8 --- main/src/main/scala/sbt/Defaults.scala | 4 ++-- main/src/main/scala/sbt/internal/CrossJava.scala | 12 +++++++++++- sbt/src/sbt-test/java/home-discovery/build.sbt | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index c4fa875ac..f3e550205 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -160,9 +160,9 @@ object Defaults extends BuildCommon { scalaHome :== None, apiURL := None, javaHome :== None, - discoveredJavaHomes := sbt.internal.CrossJava.discoverJavaHomes, + discoveredJavaHomes := CrossJava.discoverJavaHomes, javaHomes :== ListMap.empty, - fullJavaHomes := discoveredJavaHomes.value ++ javaHomes.value, + fullJavaHomes := CrossJava.expandJavaHomes(discoveredJavaHomes.value ++ javaHomes.value), testForkedParallel :== false, javaOptions :== Nil, sbtPlugin :== false, diff --git a/main/src/main/scala/sbt/internal/CrossJava.scala b/main/src/main/scala/sbt/internal/CrossJava.scala index c93d44257..1c035a4d8 100644 --- a/main/src/main/scala/sbt/internal/CrossJava.scala +++ b/main/src/main/scala/sbt/internal/CrossJava.scala @@ -10,7 +10,6 @@ package internal import java.io.File import scala.collection.immutable.ListMap -import sbt.io.IO import sbt.io.syntax._ private[sbt] object CrossJava { @@ -44,6 +43,17 @@ private[sbt] object CrossJava { } } + // expand Java versions to 1-8 to 1.x, and vice versa to accept both "1.8" and "8" + private val oneDot = Map((1 to 8).toVector flatMap { i => + Vector(s"$i" -> s"1.$i", s"1.$i" -> s"$i") + }: _*) + def expandJavaHomes(hs: Map[JavaVersion, File]): Map[JavaVersion, File] = + hs flatMap { + case (k, v) => + if (oneDot.contains(k.version)) Vector(k -> v, k.withVersion(oneDot(k.version)) -> v) + else Vector(k -> v) + } + def wrapNull(a: Array[String]): Vector[String] = if (a eq null) Vector() else a.toVector diff --git a/sbt/src/sbt-test/java/home-discovery/build.sbt b/sbt/src/sbt-test/java/home-discovery/build.sbt index b5d79caeb..7dd72c1d1 100644 --- a/sbt/src/sbt-test/java/home-discovery/build.sbt +++ b/sbt/src/sbt-test/java/home-discovery/build.sbt @@ -1,5 +1,5 @@ Global / javaHomes += JavaVersion("6") -> file("/good/old/times/java-6") TaskKey[Unit]("check") := { - assert(fullJavaHomes.value(JavaVersion("6")).getAbsolutePath.contains("java-6")) + assert(fullJavaHomes.value(JavaVersion("1.6")).getAbsolutePath.contains("java-6")) } From 2da1aa61eb499c01d14ca64491b1a46f3599269a Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 3 May 2018 03:39:55 -0400 Subject: [PATCH 169/176] implement cross JDK forking ``` sbt:helloworld> java++ 10 [info] Reapplying settings... sbt:helloworld> run [info] Running (fork) Hello [info] 10.0.1 sbt:helloworld> java++ 8 [info] Reapplying settings... sbt:helloworld> run [info] Running (fork) Hello [info] 1.8.0_171 ``` --- .travis.yml | 12 +- .../contraband-scala/sbt/JavaVersion.scala | 28 +- main/src/main/contraband/main.contra | 8 +- main/src/main/scala/sbt/Cross.scala | 24 +- main/src/main/scala/sbt/Keys.scala | 3 +- main/src/main/scala/sbt/Main.scala | 3 + .../scala/sbt/internal/CommandStrings.scala | 25 ++ .../main/scala/sbt/internal/CrossJava.scala | 295 +++++++++++++++++- sbt/src/sbt-test/java/cross/A.scala | 16 + sbt/src/sbt-test/java/cross/build.sbt | 25 ++ sbt/src/sbt-test/java/cross/test | 6 + 11 files changed, 411 insertions(+), 34 deletions(-) create mode 100644 sbt/src/sbt-test/java/cross/A.scala create mode 100644 sbt/src/sbt-test/java/cross/build.sbt create mode 100644 sbt/src/sbt-test/java/cross/test diff --git a/.travis.yml b/.travis.yml index c6e2d000e..7f257e9ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ cache: directories: - $HOME/.ivy2/cache - $HOME/.sbt/boot + - $HOME/.jabba language: scala @@ -15,6 +16,15 @@ jdk: matrix: fast_finish: true +matrix: + include: + - env: SBT_CMD="scripted java/*" + sudo: true + before_install: + - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.9.4/install.sh | bash && . ~/.jabba/jabba.sh + install: + - sudo /home/travis/.jabba/bin/jabba install openjdk@1.10 + env: global: - secure: d3bu2KNwsVHwfhbGgO+gmRfDKBJhfICdCJFGWKf2w3Gv86AJZX9nuTYRxz0KtdvEHO5Xw8WTBZLPb2thSJqhw9OCm4J8TBAVqCP0ruUj4+aqBUFy4bVexQ6WKE6nWHs4JPzPk8c6uC1LG3hMuzlC8RGETXtL/n81Ef1u7NjyXjs= @@ -26,7 +36,7 @@ env: - SBT_CMD="scripted dependency-management/*2of4" - SBT_CMD="scripted dependency-management/*3of4" - SBT_CMD="scripted dependency-management/*4of4" - - SBT_CMD="scripted java/* package/* reporter/* run/* project-load/*" + - SBT_CMD="scripted package/* reporter/* run/* project-load/*" - SBT_CMD="scripted project/*1of2" - SBT_CMD="scripted project/*2of2" - SBT_CMD="scripted source-dependencies/*1of3" diff --git a/main/src/main/contraband-scala/sbt/JavaVersion.scala b/main/src/main/contraband-scala/sbt/JavaVersion.scala index 68d3941f1..4c630e3cd 100644 --- a/main/src/main/contraband-scala/sbt/JavaVersion.scala +++ b/main/src/main/contraband-scala/sbt/JavaVersion.scala @@ -5,23 +5,26 @@ // DO NOT EDIT MANUALLY package sbt final class JavaVersion private ( - val vendor: Option[String], - val version: String) extends Serializable { - + val numbers: Vector[Long], + val vendor: Option[String]) extends Serializable { + def numberStr: String = numbers.mkString(".") override def equals(o: Any): Boolean = o match { - case x: JavaVersion => (this.vendor == x.vendor) && (this.version == x.version) + case x: JavaVersion => (this.numbers == x.numbers) && (this.vendor == x.vendor) case _ => false } override def hashCode: Int = { - 37 * (37 * (37 * (17 + "sbt.JavaVersion".##) + vendor.##) + version.##) + 37 * (37 * (37 * (17 + "sbt.JavaVersion".##) + numbers.##) + vendor.##) } override def toString: String = { - "JavaVersion(" + vendor + ", " + version + ")" + vendor.map(_ + "@").getOrElse("") + numberStr } - private[this] def copy(vendor: Option[String] = vendor, version: String = version): JavaVersion = { - new JavaVersion(vendor, version) + private[this] def copy(numbers: Vector[Long] = numbers, vendor: Option[String] = vendor): JavaVersion = { + new JavaVersion(numbers, vendor) + } + def withNumbers(numbers: Vector[Long]): JavaVersion = { + copy(numbers = numbers) } def withVendor(vendor: Option[String]): JavaVersion = { copy(vendor = vendor) @@ -29,12 +32,9 @@ final class JavaVersion private ( def withVendor(vendor: String): JavaVersion = { copy(vendor = Option(vendor)) } - def withVersion(version: String): JavaVersion = { - copy(version = version) - } } object JavaVersion { - def apply(version: String): JavaVersion = new JavaVersion(None, version) - def apply(vendor: Option[String], version: String): JavaVersion = new JavaVersion(vendor, version) - def apply(vendor: String, version: String): JavaVersion = new JavaVersion(Option(vendor), version) + def apply(version: String): JavaVersion = sbt.internal.CrossJava.parseJavaVersion(version) + def apply(numbers: Vector[Long], vendor: Option[String]): JavaVersion = new JavaVersion(numbers, vendor) + def apply(numbers: Vector[Long], vendor: String): JavaVersion = new JavaVersion(numbers, Option(vendor)) } diff --git a/main/src/main/contraband/main.contra b/main/src/main/contraband/main.contra index 10ec4a469..5cabb0cd4 100644 --- a/main/src/main/contraband/main.contra +++ b/main/src/main/contraband/main.contra @@ -19,7 +19,11 @@ enum PluginTrigger { } type JavaVersion { + numbers: [Long] vendor: String - version: String! - #xcompanion def apply(version: String): JavaVersion = new JavaVersion(None, version) + + #x def numberStr: String = numbers.mkString(".") + #xtostring vendor.map(_ + "@").getOrElse("") + numberStr + + #xcompanion def apply(version: String): JavaVersion = sbt.internal.CrossJava.parseJavaVersion(version) } diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index c1257db5a..e68f2f20f 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -99,14 +99,23 @@ object Cross { } /** - * Parse the given command into either an aggregate command or a command for a project + * Parse the given command into a list of aggregate projects and command to issue. */ - private def parseCommand(command: String): Either[String, (String, String)] = { + private[sbt] def parseSlashCommand(extracted: Extracted)( + command: String): (Seq[ProjectRef], String) = { + import extracted._ import DefaultParsers._ val parser = (OpOrID <~ charClass(_ == '/', "/")) ~ any.* map { - case project ~ cmd => (project, cmd.mkString) + case seg1 ~ cmd => (seg1, cmd.mkString) + } + Parser.parse(command, parser) match { + case Right((seg1, cmd)) => + structure.allProjectRefs.find(_.project == seg1) match { + case Some(proj) => (Seq(proj), cmd) + case _ => (resolveAggregates(extracted), command) + } + case _ => (resolveAggregates(extracted), command) } - Parser.parse(command, parser).left.map(_ => command) } def crossBuild: Command = @@ -115,12 +124,7 @@ object Cross { private def crossBuildCommandImpl(state: State, args: CrossArgs): State = { val x = Project.extract(state) import x._ - - val (aggs, aggCommand) = parseCommand(args.command) match { - case Right((project, cmd)) => - (structure.allProjectRefs.filter(_.project == project), cmd) - case Left(cmd) => (resolveAggregates(x), cmd) - } + val (aggs, aggCommand) = parseSlashCommand(x)(args.command) val projCrossVersions = aggs map { proj => proj -> crossVersions(x, proj) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 751540de7..f9e57bc1a 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -222,6 +222,7 @@ object Keys { val scalaCompilerBridgeSource = settingKey[ModuleID]("Configures the module ID of the sources of the compiler bridge.").withRank(CSetting) val scalaArtifacts = settingKey[Seq[String]]("Configures the list of artifacts which should match the Scala binary version").withRank(CSetting) val enableBinaryCompileAnalysis = settingKey[Boolean]("Writes the analysis file in binary format") + val crossJavaVersions = settingKey[Seq[JavaVersion]]("The java versions used during JDK cross testing").withRank(BPlusSetting) val clean = taskKey[Unit]("Deletes files produced by the build, such as generated sources, compiled classes, and task caches.").withRank(APlusTask) val console = taskKey[Unit]("Starts the Scala interpreter with the project classes on the classpath.").withRank(APlusTask) @@ -273,7 +274,7 @@ object Keys { val javaHome = settingKey[Option[File]]("Selects the Java installation used for compiling and forking. If None, uses the Java installation running the build.").withRank(ASetting) val discoveredJavaHomes = settingKey[Map[JavaVersion, File]]("Discovered Java home directories") val javaHomes = settingKey[Map[JavaVersion, File]]("The user-defined additional Java home directories") - val fullJavaHomes = taskKey[Map[JavaVersion, File]]("Combines discoveredJavaHomes and custom javaHomes.").withRank(CTask) + val fullJavaHomes = settingKey[Map[JavaVersion, File]]("Combines discoveredJavaHomes and custom javaHomes.").withRank(CTask) val javaOptions = taskKey[Seq[String]]("Options passed to a new JVM when forking.").withRank(BPlusTask) val envVars = taskKey[Map[String, String]]("Environment variables used when forking a new JVM").withRank(BTask) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index b29cca948..d6185d41e 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -14,6 +14,7 @@ import sbt.internal.{ BuildUnit, CommandExchange, CommandStrings, + CrossJava, DefaultBackgroundJobService, EvaluateConfigurations, Inspect, @@ -190,6 +191,8 @@ object BuiltinCommands { oldLoadFailed, Cross.crossBuild, Cross.switchVersion, + CrossJava.switchJavaHome, + CrossJava.crossJavaHome, PluginCross.pluginCross, PluginCross.pluginSwitch, Cross.crossRestoreSession, diff --git a/main/src/main/scala/sbt/internal/CommandStrings.scala b/main/src/main/scala/sbt/internal/CommandStrings.scala index 99ec4637e..d30a31f29 100644 --- a/main/src/main/scala/sbt/internal/CommandStrings.scala +++ b/main/src/main/scala/sbt/internal/CommandStrings.scala @@ -415,4 +415,29 @@ $SwitchCommand [=][!] [-v] [] See also `help $CrossCommand` """ + + val JavaCrossCommand = "java+" + val JavaSwitchCommand = "java++" + + def javaCrossHelp: Help = Help.more(JavaCrossCommand, JavaCrossDetailed) + def javaSwitchHelp: Help = Help.more(JavaSwitchCommand, JavaSwitchDetailed) + + def JavaCrossDetailed = + s"""$JavaCrossCommand + Runs for each JDK version specified for cross-JDK testing. + For each string in `crossJavaVersions` in the current project, this command sets the + `javaHome` of all projects to the corresponding Java home, reloads the build, + and executes . When finished, it reloads the build with the original + `javaHome`. + Note that `Test / fork := true` is needed for `javaHome` to be effective. + See also `help $JavaSwitchCommand` +""" + + def JavaSwitchDetailed = + s"""$JavaSwitchCommand + Changes the JDK version and runs a command. + Sets the `javaHome` of all projects to and + reloads the build. If is provided, it is then executed. + See also `help $JavaSwitchCommand` +""" } diff --git a/main/src/main/scala/sbt/internal/CrossJava.scala b/main/src/main/scala/sbt/internal/CrossJava.scala index 1c035a4d8..0f8a4730b 100644 --- a/main/src/main/scala/sbt/internal/CrossJava.scala +++ b/main/src/main/scala/sbt/internal/CrossJava.scala @@ -10,11 +10,277 @@ package internal import java.io.File import scala.collection.immutable.ListMap +import sbt.io.Path import sbt.io.syntax._ +import sbt.Cross._ +import sbt.Def.{ ScopedKey, Setting } +import sbt.internal.util.complete.DefaultParsers._ +import sbt.internal.util.AttributeKey +import sbt.internal.util.complete.{ DefaultParsers, Parser } +import sbt.internal.CommandStrings.{ + JavaCrossCommand, + JavaSwitchCommand, + javaCrossHelp, + javaSwitchHelp +} private[sbt] object CrossJava { + // parses jabaa style version number adopt@1.8 + def parseJavaVersion(version: String): JavaVersion = { + def splitDot(s: String): Vector[Long] = + Option(s) match { + case Some(x) => x.split('.').toVector.filterNot(_ == "").map(_.toLong) + case _ => Vector() + } + def splitAt(s: String): Vector[String] = + Option(s) match { + case Some(x) => x.split('@').toVector + case _ => Vector() + } + splitAt(version) match { + case Vector(vendor, rest) => JavaVersion(splitDot(rest), Option(vendor)) + case Vector(rest) => JavaVersion(splitDot(rest), None) + case _ => sys.error(s"Invalid JavaVersion: $version") + } + } + + def lookupJavaHome(jv: JavaVersion, mappings: Map[JavaVersion, File]): File = { + mappings.get(jv) match { + case Some(dir) => dir + + // when looking for "10" it should match "openjdk@10" + case None if jv.vendor.isEmpty => + val noVendors: Map[JavaVersion, File] = mappings map { + case (k, v) => k.withVendor(None) -> v + } + noVendors.get(jv).getOrElse(javaHomeNotFound(jv, mappings)) + case _ => javaHomeNotFound(jv, mappings) + } + } + + private def javaHomeNotFound(version: JavaVersion, mappings: Map[JavaVersion, File]): Nothing = { + sys.error(s"""Java home for $version was not found in $mappings + | + |use Global / javaHomes += JavaVersion("$version") -> file(...)""".stripMargin) + } + + private case class SwitchTarget(version: Option[JavaVersion], home: Option[File], force: Boolean) + private case class SwitchJavaHome(target: SwitchTarget, verbose: Boolean, command: Option[String]) + + private def switchParser(state: State): Parser[SwitchJavaHome] = { + import DefaultParsers._ + def versionAndCommand(spacePresent: Boolean) = { + val x = Project.extract(state) + import x._ + val javaHomes = getJavaHomes(x, currentRef) + val knownVersions = javaHomes.keysIterator.map(_.numberStr).toVector + val version: Parser[SwitchTarget] = + (token( + (StringBasic <~ "@").? ~ ((NatBasic) ~ ("." ~> NatBasic).*) + .examples(knownVersions: _*) ~ "!".?) || token(StringBasic)) + .map { + case Left(((vendor, (v1, vs)), bang)) => + val force = bang.isDefined + val versionArg = (Vector(v1) ++ vs) map { _.toLong } + SwitchTarget(Option(JavaVersion(versionArg, vendor)), None, force) + case Right(home) => + SwitchTarget(None, Option(new File(home)), true) + } + val spacedVersion = + if (spacePresent) version + else version & spacedFirst(JavaSwitchCommand) + val verbose = Parser.opt(token(Space ~> "-v")) + val optionalCommand = Parser.opt(token(Space ~> matched(state.combinedParser))) + (spacedVersion ~ verbose ~ optionalCommand).map { + case v ~ verbose ~ command => + SwitchJavaHome(v, verbose.isDefined, command) + } + } + token(JavaSwitchCommand ~> OptSpace) flatMap { sp => + versionAndCommand(sp.nonEmpty) + } + } + + private def getJavaHomes(extracted: Extracted, + proj: ResolvedReference): Map[JavaVersion, File] = { + import extracted._ + (Keys.fullJavaHomes in proj get structure.data).get + } + + private def getCrossJavaVersions(extracted: Extracted, + proj: ResolvedReference): Seq[JavaVersion] = { + import extracted._ + import Keys._ + (crossJavaVersions in proj get structure.data).getOrElse(Nil) + } + + private def getCrossJavaHomes(extracted: Extracted, proj: ResolvedReference): Seq[File] = { + import extracted._ + import Keys._ + val fjh = (fullJavaHomes in proj get structure.data).get + (crossJavaVersions in proj get structure.data) map { jvs => + jvs map { jv => + lookupJavaHome(jv, fjh) + } + } getOrElse Vector() + } + + private def switchCommandImpl(state: State, switch: SwitchJavaHome): State = { + val extracted = Project.extract(state) + import extracted._ + import Keys.javaHome + + // filter out subprojects based on switch target e.g. "10" vs what's in crossJavaVersions + // for the subproject. Only if crossJavaVersions is non-empty, and does NOT include "10" + // it will skip the subproject. + val projects: Seq[(ResolvedReference, Seq[JavaVersion])] = { + val projectJavaVersions = + structure.allProjectRefs.map(proj => proj -> getCrossJavaVersions(extracted, proj)) + if (switch.target.force) projectJavaVersions + else + switch.target.version match { + case None => projectJavaVersions + case Some(v) => + projectJavaVersions flatMap { + case (proj, versions) => + if (versions.isEmpty || versions.contains(v)) Vector(proj -> versions) + else Vector() + } + } + } + + def setJavaHomeForProjects: State = { + val newSettings = projects.flatMap { + case (proj, javaVersions) => + val fjh = getJavaHomes(extracted, proj) + val home = switch.target match { + case SwitchTarget(Some(v), _, _) => lookupJavaHome(v, fjh) + case SwitchTarget(_, Some(h), _) => h + case _ => sys.error(s"unexpected ${switch.target}") + } + val scope = Scope(Select(proj), Zero, Zero, Zero) + Seq( + javaHome in scope := Some(home) + ) + } + + val filterKeys: Set[AttributeKey[_]] = Set(javaHome).map(_.key) + + val projectsContains: Reference => Boolean = projects.map(_._1).toSet.contains + + // Filter out any old javaHome version settings that were added, this is just for hygiene. + val filteredRawAppend = session.rawAppend.filter(_.key match { + case ScopedKey(Scope(Select(ref), Zero, Zero, Zero), key) + if filterKeys.contains(key) && projectsContains(ref) => + false + case _ => true + }) + + val newSession = session.copy(rawAppend = filteredRawAppend ++ newSettings) + + BuiltinCommands.reapply(newSession, structure, state) + } + + setJavaHomeForProjects + } + + def switchJavaHome: Command = + Command.arb(requireSession(switchParser), javaSwitchHelp)(switchCommandImpl) + + def crossJavaHome: Command = + Command.arb(requireSession(crossParser), javaCrossHelp)(crossJavaHomeCommandImpl) + + private case class CrossArgs(command: String, verbose: Boolean) + + /** + * Parse the given command into either an aggregate command or a command for a project + */ + private def crossParser(state: State): Parser[CrossArgs] = + token(JavaCrossCommand <~ OptSpace) flatMap { _ => + (token(Parser.opt("-v" <~ Space)) ~ token(matched(state.combinedParser))).map { + case (verbose, command) => CrossArgs(command, verbose.isDefined) + } & spacedFirst(JavaCrossCommand) + } + + private def crossJavaHomeCommandImpl(state: State, args: CrossArgs): State = { + val x = Project.extract(state) + import x._ + val (aggs, aggCommand) = Cross.parseSlashCommand(x)(args.command) + val projCrossVersions = aggs map { proj => + proj -> getCrossJavaHomes(x, proj) + } + // if we support javaHome, projVersions should be cached somewhere since + // running ++2.11.1 is at the root level is going to mess with the scalaVersion for the aggregated subproj + val projVersions = (projCrossVersions flatMap { + case (proj, versions) => versions map { proj.project -> _ } + }).toList + + val verbose = "" + // println(s"projVersions $projVersions") + + if (projVersions.isEmpty) { + state + } else { + // Detect whether a task or command has been issued + val allCommands = Parser.parse(aggCommand, Act.aggregatedKeyParser(x)) match { + case Left(_) => + // It's definitely not a task, check if it's a valid command, because we don't want to emit the warning + // message below for typos. + val validCommand = Parser.parse(aggCommand, state.combinedParser).isRight + + val distinctCrossConfigs = projCrossVersions.map(_._2.toSet).distinct + if (validCommand && distinctCrossConfigs.size > 1) { + state.log.warn( + "Issuing a Java cross building command, but not all sub projects have the same cross build " + + "configuration. This could result in subprojects cross building against Java versions that they are " + + "not compatible with. Try issuing cross building command with tasks instead, since sbt will be able " + + "to ensure that cross building is only done using configured project and Java version combinations " + + "that are configured.") + state.log.debug("Java versions configuration is:") + projCrossVersions.foreach { + case (project, versions) => state.log.debug(s"$project: $versions") + } + } + + // Execute using a blanket switch + projCrossVersions.toMap.apply(currentRef).flatMap { version => + // Force scala version + Seq(s"$JavaSwitchCommand $verbose $version!", aggCommand) + } + + case Right(_) => + // We have a key, we're likely to be able to cross build this using the per project behaviour. + + // Group all the projects by scala version + projVersions.groupBy(_._2).mapValues(_.map(_._1)).toSeq.flatMap { + case (version, Seq(project)) => + // If only one project for a version, issue it directly + Seq(s"$JavaSwitchCommand $verbose $version", s"$project/$aggCommand") + case (version, projects) if aggCommand.contains(" ") => + // If the command contains a space, then the `all` command won't work because it doesn't support issuing + // commands with spaces, so revert to running the command on each project one at a time + s"$JavaSwitchCommand $verbose $version" :: projects.map(project => + s"$project/$aggCommand") + case (version, projects) => + // First switch scala version, then use the all command to run the command on each project concurrently + Seq(s"$JavaSwitchCommand $verbose $version", + projects.map(_ + "/" + aggCommand).mkString("all ", " ", "")) + } + } + + allCommands.toList ::: captureCurrentSession(state, x) + } + } + + private val JavaCapturedSession = AttributeKey[Seq[Setting[_]]]("javaCrossCapturedSession") + + private def captureCurrentSession(state: State, extracted: Extracted): State = { + state.put(JavaCapturedSession, extracted.session.rawAppend) + } + def discoverJavaHomes: ListMap[JavaVersion, File] = { - val configs = Vector(JavaDiscoverConfig.linux, JavaDiscoverConfig.macOS) + import JavaDiscoverConfig._ + val configs = Vector(jabba, linux, macOS) ListMap(configs flatMap { _.javaHomes }: _*) } @@ -38,19 +304,36 @@ private[sbt] object CrossJava { def javaHomes: Vector[(JavaVersion, File)] = wrapNull(base.list()).collect { case dir @ JavaHomeDir(m, n) => - JavaVersion(n) -> (base / dir / "Contents" / "Home") + JavaVersion(nullBlank(m) + n) -> (base / dir / "Contents" / "Home") + } + } + + // See https://github.com/shyiko/jabba + val jabba = new JavaDiscoverConf { + val base: File = Path.userHome / ".jabba" / "jdk" + val JavaHomeDir = """([\w\-]+)\@(1\.)?([0-9]+).*""".r + def javaHomes: Vector[(JavaVersion, File)] = + wrapNull(base.list()).collect { + case dir @ JavaHomeDir(vendor, m, n) => + val v = JavaVersion(nullBlank(m) + n).withVendor(vendor) + if ((base / dir / "Contents" / "Home").exists) v -> (base / dir / "Contents" / "Home") + else v -> (base / dir) } } } - // expand Java versions to 1-8 to 1.x, and vice versa to accept both "1.8" and "8" - private val oneDot = Map((1 to 8).toVector flatMap { i => - Vector(s"$i" -> s"1.$i", s"1.$i" -> s"$i") + def nullBlank(s: String): String = + if (s eq null) "" + else s + + // expand Java versions to 1-20 to 1.x, and vice versa to accept both "1.8" and "8" + private val oneDot = Map((1L to 20L).toVector flatMap { i => + Vector(Vector(i) -> Vector(1L, i), Vector(1L, i) -> Vector(i)) }: _*) def expandJavaHomes(hs: Map[JavaVersion, File]): Map[JavaVersion, File] = hs flatMap { case (k, v) => - if (oneDot.contains(k.version)) Vector(k -> v, k.withVersion(oneDot(k.version)) -> v) + if (oneDot.contains(k.numbers)) Vector(k -> v, k.withNumbers(oneDot(k.numbers)) -> v) else Vector(k -> v) } diff --git a/sbt/src/sbt-test/java/cross/A.scala b/sbt/src/sbt-test/java/cross/A.scala new file mode 100644 index 000000000..e9ff1f72a --- /dev/null +++ b/sbt/src/sbt-test/java/cross/A.scala @@ -0,0 +1,16 @@ +package pkg + +import java.nio.file.{ Paths, Files } +import java.nio.charset.Charset + +object A extends App { + val out = Paths.get("out.txt") + val content = sys.props("java.version") + val w = Files.newBufferedWriter(out, Charset.forName("UTF-8")) + try { + w.write(content) + w.flush() + } finally { + w.close + } +} diff --git a/sbt/src/sbt-test/java/cross/build.sbt b/sbt/src/sbt-test/java/cross/build.sbt new file mode 100644 index 000000000..dad1dc347 --- /dev/null +++ b/sbt/src/sbt-test/java/cross/build.sbt @@ -0,0 +1,25 @@ +import complete.DefaultParsers._ + +val check = inputKey[Unit]("Runs the check") + +lazy val root = (project in file(".")) + .settings( + ThisBuild / scalaVersion := "2.12.6", + crossJavaVersions := List(JavaVersion("1.8")), + + // read out.txt and see if it starts with the passed in number + check := { + val arg1: Int = (Space ~> NatBasic).parsed + file("out.txt") match { + case out if out.exists => + IO.readLines(out).headOption match { + case Some(v) if v startsWith arg1.toString => () + case Some(v) if v startsWith s"1.$arg1" => () + case x => sys.error(s"unexpected value: $x") + } + case out => sys.error(s"$out doesn't exist") + } + }, + + Compile / run / fork := true, + ) diff --git a/sbt/src/sbt-test/java/cross/test b/sbt/src/sbt-test/java/cross/test new file mode 100644 index 000000000..ff76c51bf --- /dev/null +++ b/sbt/src/sbt-test/java/cross/test @@ -0,0 +1,6 @@ +> java+ run +> check 8 + +> java++ 10! +> run +> check 10 From 951eaa646f2cda69123ef8da8a2b5fd370106aa9 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 4 May 2018 19:09:25 -0400 Subject: [PATCH 170/176] jabba 0.9.6 (no sudo) Ref https://github.com/shyiko/jabba/issues/190 Bumping to jabba 0.9.6 fixes sporaditc permission issues. --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7f257e9ed..f75525898 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,11 +19,10 @@ matrix: matrix: include: - env: SBT_CMD="scripted java/*" - sudo: true before_install: - - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.9.4/install.sh | bash && . ~/.jabba/jabba.sh + - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.9.6/install.sh | bash && . ~/.jabba/jabba.sh install: - - sudo /home/travis/.jabba/bin/jabba install openjdk@1.10 + - /home/travis/.jabba/bin/jabba install openjdk@1.10 env: global: From 35e98f51fd386b15c27c678392e90acd44787cd1 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 5 May 2018 15:50:40 -0400 Subject: [PATCH 171/176] Adjust to upstream change --- main/src/main/scala/sbt/Cross.scala | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index e68f2f20f..420432707 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -233,14 +233,11 @@ object Cross { 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 ", " ", "") - } + val (aggs, aggCommand) = parseSlashCommand(x)(rawCmd) + aggs + .intersect(affectedRefs) + .map({ case ProjectRef(_, proj) => s"$proj/$aggCommand" }) + .mkString("all ", " ", "") } } From a7d85c87243b06ecbb5186902367a85197aa7c71 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 5 May 2018 15:51:29 -0400 Subject: [PATCH 172/176] Formatting --- main/src/main/scala/sbt/Cross.scala | 5 ++-- .../main/scala/sbt/internal/CrossJava.scala | 28 ++++++++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 420432707..78571d78b 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -101,8 +101,9 @@ object Cross { /** * Parse the given command into a list of aggregate projects and command to issue. */ - private[sbt] def parseSlashCommand(extracted: Extracted)( - command: String): (Seq[ProjectRef], String) = { + private[sbt] def parseSlashCommand( + extracted: Extracted + )(command: String): (Seq[ProjectRef], String) = { import extracted._ import DefaultParsers._ val parser = (OpOrID <~ charClass(_ == '/', "/")) ~ any.* map { diff --git a/main/src/main/scala/sbt/internal/CrossJava.scala b/main/src/main/scala/sbt/internal/CrossJava.scala index 0f8a4730b..5836b2c17 100644 --- a/main/src/main/scala/sbt/internal/CrossJava.scala +++ b/main/src/main/scala/sbt/internal/CrossJava.scala @@ -77,7 +77,8 @@ private[sbt] object CrossJava { val version: Parser[SwitchTarget] = (token( (StringBasic <~ "@").? ~ ((NatBasic) ~ ("." ~> NatBasic).*) - .examples(knownVersions: _*) ~ "!".?) || token(StringBasic)) + .examples(knownVersions: _*) ~ "!".? + ) || token(StringBasic)) .map { case Left(((vendor, (v1, vs)), bang)) => val force = bang.isDefined @@ -101,14 +102,18 @@ private[sbt] object CrossJava { } } - private def getJavaHomes(extracted: Extracted, - proj: ResolvedReference): Map[JavaVersion, File] = { + private def getJavaHomes( + extracted: Extracted, + proj: ResolvedReference + ): Map[JavaVersion, File] = { import extracted._ (Keys.fullJavaHomes in proj get structure.data).get } - private def getCrossJavaVersions(extracted: Extracted, - proj: ResolvedReference): Seq[JavaVersion] = { + private def getCrossJavaVersions( + extracted: Extracted, + proj: ResolvedReference + ): Seq[JavaVersion] = { import extracted._ import Keys._ (crossJavaVersions in proj get structure.data).getOrElse(Nil) @@ -235,7 +240,8 @@ private[sbt] object CrossJava { "configuration. This could result in subprojects cross building against Java versions that they are " + "not compatible with. Try issuing cross building command with tasks instead, since sbt will be able " + "to ensure that cross building is only done using configured project and Java version combinations " + - "that are configured.") + "that are configured." + ) state.log.debug("Java versions configuration is:") projCrossVersions.foreach { case (project, versions) => state.log.debug(s"$project: $versions") @@ -259,12 +265,14 @@ private[sbt] object CrossJava { case (version, projects) if aggCommand.contains(" ") => // If the command contains a space, then the `all` command won't work because it doesn't support issuing // commands with spaces, so revert to running the command on each project one at a time - s"$JavaSwitchCommand $verbose $version" :: projects.map(project => - s"$project/$aggCommand") + s"$JavaSwitchCommand $verbose $version" :: projects + .map(project => s"$project/$aggCommand") case (version, projects) => // First switch scala version, then use the all command to run the command on each project concurrently - Seq(s"$JavaSwitchCommand $verbose $version", - projects.map(_ + "/" + aggCommand).mkString("all ", " ", "")) + Seq( + s"$JavaSwitchCommand $verbose $version", + projects.map(_ + "/" + aggCommand).mkString("all ", " ", "") + ) } } From 72ebdeb1990667add76c40f3c28c29281a32d310 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 7 May 2018 02:15:42 -0400 Subject: [PATCH 173/176] jabba 0.10.1 https://github.com/shyiko/jabba/blob/master/CHANGELOG.md#0101---2018-05-07 Ref https://github.com/shyiko/jabba/issues/190 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f75525898..08a61721f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ matrix: include: - env: SBT_CMD="scripted java/*" before_install: - - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.9.6/install.sh | bash && . ~/.jabba/jabba.sh + - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.10.1/install.sh | bash && . ~/.jabba/jabba.sh install: - /home/travis/.jabba/bin/jabba install openjdk@1.10 From 9b7c224f93ef9ca2a81d43cdd1a406f15531376b Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 30 May 2018 00:59:12 -0400 Subject: [PATCH 174/176] use stringly-typed key so we can define it machine-wide --- main/src/main/scala/sbt/Keys.scala | 8 ++-- .../main/scala/sbt/internal/CrossJava.scala | 44 ++++++++++++------- sbt/src/sbt-test/java/cross/build.sbt | 2 +- .../sbt-test/java/home-discovery/build.sbt | 4 +- .../sbt/scriptedtest/ScriptedTests.scala | 1 + 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index f9e57bc1a..bf8c9549f 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -222,7 +222,7 @@ object Keys { val scalaCompilerBridgeSource = settingKey[ModuleID]("Configures the module ID of the sources of the compiler bridge.").withRank(CSetting) val scalaArtifacts = settingKey[Seq[String]]("Configures the list of artifacts which should match the Scala binary version").withRank(CSetting) val enableBinaryCompileAnalysis = settingKey[Boolean]("Writes the analysis file in binary format") - val crossJavaVersions = settingKey[Seq[JavaVersion]]("The java versions used during JDK cross testing").withRank(BPlusSetting) + val crossJavaVersions = settingKey[Seq[String]]("The java versions used during JDK cross testing").withRank(BPlusSetting) val clean = taskKey[Unit]("Deletes files produced by the build, such as generated sources, compiled classes, and task caches.").withRank(APlusTask) val console = taskKey[Unit]("Starts the Scala interpreter with the project classes on the classpath.").withRank(APlusTask) @@ -272,9 +272,9 @@ object Keys { val outputStrategy = settingKey[Option[sbt.OutputStrategy]]("Selects how to log output when running a main class.").withRank(DSetting) val connectInput = settingKey[Boolean]("If true, connects standard input when running a main class forked.").withRank(CSetting) val javaHome = settingKey[Option[File]]("Selects the Java installation used for compiling and forking. If None, uses the Java installation running the build.").withRank(ASetting) - val discoveredJavaHomes = settingKey[Map[JavaVersion, File]]("Discovered Java home directories") - val javaHomes = settingKey[Map[JavaVersion, File]]("The user-defined additional Java home directories") - val fullJavaHomes = settingKey[Map[JavaVersion, File]]("Combines discoveredJavaHomes and custom javaHomes.").withRank(CTask) + val discoveredJavaHomes = settingKey[Map[String, File]]("Discovered Java home directories") + val javaHomes = settingKey[Map[String, File]]("The user-defined additional Java home directories") + val fullJavaHomes = settingKey[Map[String, File]]("Combines discoveredJavaHomes and custom javaHomes.").withRank(CTask) val javaOptions = taskKey[Seq[String]]("Options passed to a new JVM when forking.").withRank(BPlusTask) val envVars = taskKey[Map[String, String]]("Environment variables used when forking a new JVM").withRank(BTask) diff --git a/main/src/main/scala/sbt/internal/CrossJava.scala b/main/src/main/scala/sbt/internal/CrossJava.scala index 5836b2c17..9ee668042 100644 --- a/main/src/main/scala/sbt/internal/CrossJava.scala +++ b/main/src/main/scala/sbt/internal/CrossJava.scala @@ -44,6 +44,11 @@ private[sbt] object CrossJava { } } + def lookupJavaHome(jv: String, mappings: Map[String, File]): File = { + val ms = mappings map { case (k, v) => (JavaVersion(k), v) } + lookupJavaHome(JavaVersion(jv), ms) + } + def lookupJavaHome(jv: JavaVersion, mappings: Map[JavaVersion, File]): File = { mappings.get(jv) match { case Some(dir) => dir @@ -72,7 +77,7 @@ private[sbt] object CrossJava { def versionAndCommand(spacePresent: Boolean) = { val x = Project.extract(state) import x._ - val javaHomes = getJavaHomes(x, currentRef) + val javaHomes = getJavaHomesTyped(x, currentRef) val knownVersions = javaHomes.keysIterator.map(_.numberStr).toVector val version: Parser[SwitchTarget] = (token( @@ -105,15 +110,22 @@ private[sbt] object CrossJava { private def getJavaHomes( extracted: Extracted, proj: ResolvedReference - ): Map[JavaVersion, File] = { + ): Map[String, File] = { import extracted._ (Keys.fullJavaHomes in proj get structure.data).get } + private def getJavaHomesTyped( + extracted: Extracted, + proj: ResolvedReference + ): Map[JavaVersion, File] = { + getJavaHomes(extracted, proj) map { case (k, v) => (JavaVersion(k), v) } + } + private def getCrossJavaVersions( extracted: Extracted, proj: ResolvedReference - ): Seq[JavaVersion] = { + ): Seq[String] = { import extracted._ import Keys._ (crossJavaVersions in proj get structure.data).getOrElse(Nil) @@ -138,7 +150,7 @@ private[sbt] object CrossJava { // filter out subprojects based on switch target e.g. "10" vs what's in crossJavaVersions // for the subproject. Only if crossJavaVersions is non-empty, and does NOT include "10" // it will skip the subproject. - val projects: Seq[(ResolvedReference, Seq[JavaVersion])] = { + val projects: Seq[(ResolvedReference, Seq[String])] = { val projectJavaVersions = structure.allProjectRefs.map(proj => proj -> getCrossJavaVersions(extracted, proj)) if (switch.target.force) projectJavaVersions @@ -157,7 +169,7 @@ private[sbt] object CrossJava { def setJavaHomeForProjects: State = { val newSettings = projects.flatMap { case (proj, javaVersions) => - val fjh = getJavaHomes(extracted, proj) + val fjh = getJavaHomesTyped(extracted, proj) val home = switch.target match { case SwitchTarget(Some(v), _, _) => lookupJavaHome(v, fjh) case SwitchTarget(_, Some(h), _) => h @@ -286,33 +298,33 @@ private[sbt] object CrossJava { state.put(JavaCapturedSession, extracted.session.rawAppend) } - def discoverJavaHomes: ListMap[JavaVersion, File] = { + def discoverJavaHomes: ListMap[String, File] = { import JavaDiscoverConfig._ val configs = Vector(jabba, linux, macOS) ListMap(configs flatMap { _.javaHomes }: _*) } sealed trait JavaDiscoverConf { - def javaHomes: Vector[(JavaVersion, File)] + def javaHomes: Vector[(String, File)] } object JavaDiscoverConfig { val linux = new JavaDiscoverConf { val base: File = file("/usr") / "lib" / "jvm" val JavaHomeDir = """java-([0-9]+)-.*""".r - def javaHomes: Vector[(JavaVersion, File)] = + def javaHomes: Vector[(String, File)] = wrapNull(base.list()).collect { - case dir @ JavaHomeDir(ver) => JavaVersion(ver) -> (base / dir) + case dir @ JavaHomeDir(ver) => JavaVersion(ver).toString -> (base / dir) } } val macOS = new JavaDiscoverConf { val base: File = file("/Library") / "Java" / "JavaVirtualMachines" val JavaHomeDir = """jdk-?(1\.)?([0-9]+).*""".r - def javaHomes: Vector[(JavaVersion, File)] = + def javaHomes: Vector[(String, File)] = wrapNull(base.list()).collect { case dir @ JavaHomeDir(m, n) => - JavaVersion(nullBlank(m) + n) -> (base / dir / "Contents" / "Home") + JavaVersion(nullBlank(m) + n).toString -> (base / dir / "Contents" / "Home") } } @@ -320,10 +332,10 @@ private[sbt] object CrossJava { val jabba = new JavaDiscoverConf { val base: File = Path.userHome / ".jabba" / "jdk" val JavaHomeDir = """([\w\-]+)\@(1\.)?([0-9]+).*""".r - def javaHomes: Vector[(JavaVersion, File)] = + def javaHomes: Vector[(String, File)] = wrapNull(base.list()).collect { case dir @ JavaHomeDir(vendor, m, n) => - val v = JavaVersion(nullBlank(m) + n).withVendor(vendor) + val v = JavaVersion(nullBlank(m) + n).withVendor(vendor).toString if ((base / dir / "Contents" / "Home").exists) v -> (base / dir / "Contents" / "Home") else v -> (base / dir) } @@ -338,10 +350,12 @@ private[sbt] object CrossJava { private val oneDot = Map((1L to 20L).toVector flatMap { i => Vector(Vector(i) -> Vector(1L, i), Vector(1L, i) -> Vector(i)) }: _*) - def expandJavaHomes(hs: Map[JavaVersion, File]): Map[JavaVersion, File] = + def expandJavaHomes(hs: Map[String, File]): Map[String, File] = hs flatMap { case (k, v) => - if (oneDot.contains(k.numbers)) Vector(k -> v, k.withNumbers(oneDot(k.numbers)) -> v) + val jv = JavaVersion(k) + if (oneDot.contains(jv.numbers)) + Vector(k -> v, jv.withNumbers(oneDot(jv.numbers)).toString -> v) else Vector(k -> v) } diff --git a/sbt/src/sbt-test/java/cross/build.sbt b/sbt/src/sbt-test/java/cross/build.sbt index dad1dc347..c71bd179e 100644 --- a/sbt/src/sbt-test/java/cross/build.sbt +++ b/sbt/src/sbt-test/java/cross/build.sbt @@ -5,7 +5,7 @@ val check = inputKey[Unit]("Runs the check") lazy val root = (project in file(".")) .settings( ThisBuild / scalaVersion := "2.12.6", - crossJavaVersions := List(JavaVersion("1.8")), + crossJavaVersions := List("1.8"), // read out.txt and see if it starts with the passed in number check := { diff --git a/sbt/src/sbt-test/java/home-discovery/build.sbt b/sbt/src/sbt-test/java/home-discovery/build.sbt index 7dd72c1d1..7177ca692 100644 --- a/sbt/src/sbt-test/java/home-discovery/build.sbt +++ b/sbt/src/sbt-test/java/home-discovery/build.sbt @@ -1,5 +1,5 @@ -Global / javaHomes += JavaVersion("6") -> file("/good/old/times/java-6") +Global / javaHomes += "6" -> file("/good/old/times/java-6") TaskKey[Unit]("check") := { - assert(fullJavaHomes.value(JavaVersion("1.6")).getAbsolutePath.contains("java-6")) + assert(fullJavaHomes.value("1.6").getAbsolutePath.contains("java-6")) } diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index dabaeeca1..77e3b7467 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -206,6 +206,7 @@ final class ScriptedTests( case "dependency-management/update-sbt-classifiers" => LauncherBased // tbd case "dependency-management/url" => LauncherBased // tbd case "java/argfile" => LauncherBased // sbt/Package$ + case "java/cross" => LauncherBased // sbt/Package$ case "java/basic" => LauncherBased // sbt/Package$ case "java/varargs-main" => LauncherBased // sbt/Package$ case "package/lazy-name" => LauncherBased // sbt/Package$ From 27e93601b57e037f2ba71de5683530591e249bf7 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Fri, 24 Nov 2017 12:43:50 -0800 Subject: [PATCH 175/176] Add warning for slow hostname lookups on OS X I spent a lot of time debugging why it took 5 seconds to run tests each time. It turns out that if the hostname is not set explicitly on os x, then getaddrinfo takes 5 seconds to try (and fail) to resolve the dns entry for the localhostname. This is easily fixed by setting the hostname, but it is not at all easy to figure out that a slow hostname lookup is the reason why tests are slow to start. I don't know if this is a common issue on other platforms, so only issue the warning on OS X. --- main/src/main/scala/sbt/internal/Load.scala | 4 +++- .../sbt/plugins/JUnitXmlReportPlugin.scala | 2 +- .../scala/sbt/JUnitXmlTestsListener.scala | 22 ++++++++++++++++--- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 8a316bbc1..c496a7f32 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -1039,7 +1039,9 @@ private[sbt] object Load { // Grab all the settings we already loaded from sbt files def settings(files: Seq[File]): Seq[Setting[_]] = { if (files.nonEmpty) - log.info(s"${files.map(_.getName).mkString(s"Loading settings for project ${p.id} from ", ",", " ...")}") + log.info( + s"${files.map(_.getName).mkString(s"Loading settings for project ${p.id} from ", ",", " ...")}" + ) for { file <- files config <- (memoSettings get file).toSeq diff --git a/main/src/main/scala/sbt/plugins/JUnitXmlReportPlugin.scala b/main/src/main/scala/sbt/plugins/JUnitXmlReportPlugin.scala index b65324be7..232265d68 100644 --- a/main/src/main/scala/sbt/plugins/JUnitXmlReportPlugin.scala +++ b/main/src/main/scala/sbt/plugins/JUnitXmlReportPlugin.scala @@ -30,6 +30,6 @@ object JUnitXmlReportPlugin extends AutoPlugin { // It might be a good idea to derive this setting into specific test scopes. override lazy val projectSettings: Seq[Setting[_]] = Seq( - testListeners += new JUnitXmlTestsListener(target.value.getAbsolutePath) + testListeners += new JUnitXmlTestsListener(target.value.getAbsolutePath, streams.value.log) ) } diff --git a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala index 9344305f5..a59e44ee7 100644 --- a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala +++ b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala @@ -13,8 +13,10 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit import java.util.Hashtable +import java.util.concurrent.TimeUnit.NANOSECONDS import scala.collection.mutable.ListBuffer +import scala.util.Properties import scala.xml.{ Elem, Node => XNode, XML } import testing.{ Event => TEvent, @@ -23,6 +25,7 @@ import testing.{ OptionalThrowable, TestSelector } +import util.Logger import sbt.protocol.testing.TestResult /** @@ -30,14 +33,27 @@ import sbt.protocol.testing.TestResult * report format. * @param outputDir path to the dir in which a folder with results is generated */ -class JUnitXmlTestsListener(val outputDir: String) extends TestsListener { +class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends TestsListener { + // This constructor is for binary compatibility with older versions of sbt. + def this(outputDir: String) = this(outputDir, null) /**Current hostname so we know which machine executed the tests*/ - val hostname = - try InetAddress.getLocalHost.getHostName + val hostname = { + val start = System.nanoTime + val name = try InetAddress.getLocalHost.getHostName catch { case _: IOException => "localhost" } + val elapsed = System.nanoTime - start + if ((NANOSECONDS.toSeconds(elapsed) >= 4) && Properties.isMac && logger != null) { + logger.warn( + s"Getting the hostname $name was slow (${elapsed / 1.0e6} ms). " + + "This is likely because the computer's hostname is not set. You can set the " + + "hostname with the command: scutil --set HostName $(scutil --get LocalHostName)." + ) + } + name + } /**The dir in which we put all result files. Is equal to the given dir + "/test-reports"*/ val targetDir = new File(outputDir + "/test-reports/") From 41fc25dba79e6728bc58a9b5745e279ee3a65eb6 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 13 Jun 2018 02:01:17 -0400 Subject: [PATCH 176/176] Fix scripted test --- scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index 77e3b7467..50883790e 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -167,6 +167,7 @@ final class ScriptedTests( 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/dotty-compiler-plugin" => 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