From d31b9c509384ecaf37f2ffaec8731250e3c60532 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 10 Aug 2017 11:24:29 +0100 Subject: [PATCH] Add, configure & enable Scalafmt --- .scalafmt.conf | 10 + .travis.yml | 2 +- build.sbt | 149 ++++++------ .../sbt/internal/util/ErrorHandling.scala | 17 +- .../scala/sbt/internal/util/ExitHook.scala | 10 +- .../sbt/internal/util/BufferedLogger.scala | 65 ++++-- .../sbt/internal/util/ConsoleAppender.scala | 106 +++++---- .../scala/sbt/internal/util/ConsoleOut.scala | 4 +- .../sbt/internal/util/GlobalLogging.scala | 62 +++-- .../sbt/internal/util/LoggerWriter.scala | 10 +- .../scala/sbt/internal/util/MainLogging.scala | 102 ++++++--- .../sbt/internal/util/ManagedLogger.scala | 59 +++-- .../scala/sbt/internal/util/MultiLogger.scala | 9 +- .../scala/sbt/internal/util/ObjectEvent.scala | 32 +-- .../scala/sbt/internal/util/StackTrace.scala | 3 +- .../sbt/internal/util/StringTypeTag.scala | 15 +- .../internal/util/codec/JValueFormats.scala | 8 +- .../internal/util/codec/PositionFormats.scala | 1 - .../internal/util/codec/ProblemFormats.scala | 1 - .../internal/util/codec/SeverityFormats.scala | 1 - .../util/codec/SuccessEventShowLines.scala | 2 +- .../util/codec/ThrowableShowLines.scala | 4 +- .../main/scala/sbt/util/AbtractLogger.scala | 1 + .../main/scala/sbt/util/InterfaceUtil.scala | 46 ++-- .../src/main/scala/sbt/util/Level.scala | 2 + .../src/main/scala/sbt/util/LogEvent.scala | 2 +- .../src/main/scala/sbt/util/LogExchange.scala | 12 +- .../src/main/scala/sbt/util/Logger.scala | 47 ++-- .../util-logging/src/test/scala/Escapes.scala | 85 ++++--- .../src/test/scala/LogWriterTest.scala | 58 +++-- .../src/test/scala/ManagedLoggerSpec.scala | 21 +- .../src/test/scala/TestLogger.scala | 13 +- .../scala/sbt/internal/util/Relation.scala | 69 ++++-- .../src/test/scala/RelationTest.scala | 51 ++--- .../internal/scripted/CommentHandler.scala | 2 +- .../sbt/internal/scripted/FileCommands.scala | 33 ++- .../internal/scripted/FilteredLoader.scala | 15 +- .../internal/scripted/HandlersProvider.scala | 2 +- .../sbt/internal/scripted/ScriptRunner.scala | 19 +- .../sbt/internal/scripted/ScriptedTests.scala | 216 ++++++++++-------- .../internal/scripted/StatementHandler.scala | 7 +- .../internal/scripted/TestScriptParser.scala | 74 +++--- project/Dependencies.scala | 12 +- project/plugins.sbt | 3 +- .../src/main/scala/sbt/util/Cache.scala | 3 +- .../main/scala/sbt/util/CacheImplicits.scala | 3 +- .../src/main/scala/sbt/util/CacheStore.scala | 28 ++- .../src/main/scala/sbt/util/FileInfo.scala | 13 +- .../src/main/scala/sbt/util/Input.scala | 4 +- .../src/main/scala/sbt/util/Output.scala | 3 +- .../main/scala/sbt/util/SeparatedCache.scala | 2 + .../main/scala/sbt/util/StampedFormat.scala | 15 +- util-cache/src/test/scala/CacheSpec.scala | 9 +- .../src/test/scala/SingletonCacheSpec.scala | 9 +- .../main/scala/sbt/util/ChangeReport.scala | 28 ++- .../main/scala/sbt/util/FileFunction.scala | 45 ++-- .../src/main/scala/sbt/util/Tracked.scala | 148 +++++++----- .../src/test/scala/sbt/util/TrackedSpec.scala | 5 +- 58 files changed, 1075 insertions(+), 702 deletions(-) create mode 100644 .scalafmt.conf diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 000000000..e4ab36511 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,10 @@ +maxColumn = 100 +project.git = true +project.excludeFilters = [ /sbt-test/, /input_sources/, /contraband-scala/ ] + +# http://docs.scala-lang.org/style/scaladoc.html recommends the JavaDoc style. +# scala/scala is written that way too https://github.com/scala/scala/blob/v2.12.2/src/library/scala/Predef.scala +docstrings = JavaDoc + +# This also seems more idiomatic to include whitespace in import x.{ yyy } +spaces.inImportCurlyBraces = true diff --git a/.travis.yml b/.travis.yml index b859331fe..1af85e971 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ scala: - 2.12.3 script: - - sbt -Dfile.encoding=UTF8 -J-XX:ReservedCodeCacheSize=256M "++$TRAVIS_SCALA_VERSION" mimaReportBinaryIssues test + - sbt -Dfile.encoding=UTF8 -J-XX:ReservedCodeCacheSize=256M "++$TRAVIS_SCALA_VERSION" mimaReportBinaryIssues scalafmt::test test:scalafmt::test sbt:scalafmt::test test cache: directories: diff --git a/build.sbt b/build.sbt index 4015f18d7..1c300d5ac 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ import Util._ //import com.typesafe.tools.mima.core._, ProblemFilters._ def baseVersion = "1.0.0-SNAPSHOT" -def internalPath = file("internal") +def internalPath = file("internal") def commonSettings: Seq[Setting[_]] = Seq( scalaVersion := scala212, @@ -18,42 +18,53 @@ def commonSettings: Seq[Setting[_]] = Seq( scalacOptions := { val old = scalacOptions.value scalaVersion.value match { - case sv if sv.startsWith("2.10") => old diff List("-Xfuture", "-Ywarn-unused", "-Ywarn-unused-import") + case sv if sv.startsWith("2.10") => + old diff List("-Xfuture", "-Ywarn-unused", "-Ywarn-unused-import") case sv if sv.startsWith("2.11") => old ++ List("-Ywarn-unused", "-Ywarn-unused-import") case _ => old ++ List("-Ywarn-unused", "-Ywarn-unused-import", "-YdisableFlatCpCaching") } }, scalacOptions in console in Compile -= "-Ywarn-unused-import", - scalacOptions in console in Test -= "-Ywarn-unused-import", + scalacOptions in console in Test -= "-Ywarn-unused-import", publishArtifact in Compile := true, publishArtifact in Test := false ) val mimaSettings = Def settings ( - mimaPreviousArtifacts := Set(organization.value % moduleName.value % "1.0.0-RC3" - cross (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled) + mimaPreviousArtifacts := Set( + organization.value % moduleName.value % "1.0.0-RC3" + cross (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled) ) ) -lazy val utilRoot: Project = (project in file(".")). - aggregate( - utilInterface, utilControl, utilPosition, - utilLogging, utilRelation, utilCache, utilTracking, utilTesting, +lazy val utilRoot: Project = (project in file(".")) + .aggregate( + utilInterface, + utilControl, + utilPosition, + utilLogging, + utilRelation, + utilCache, + utilTracking, + utilTesting, utilScripted - ). - settings( - inThisBuild(Seq( - git.baseVersion := baseVersion, - version := { - val v = version.value - if (v contains "SNAPSHOT") git.baseVersion.value - else v - }, - bintrayPackage := "util", - homepage := Some(url("https://github.com/sbt/util")), - description := "Util module for sbt", - scmInfo := Some(ScmInfo(url("https://github.com/sbt/util"), "git@github.com:sbt/util.git")) - )), + ) + .settings( + inThisBuild( + Seq( + git.baseVersion := baseVersion, + version := { + val v = version.value + if (v contains "SNAPSHOT") git.baseVersion.value + else v + }, + bintrayPackage := "util", + homepage := Some(url("https://github.com/sbt/util")), + description := "Util module for sbt", + scmInfo := Some(ScmInfo(url("https://github.com/sbt/util"), "git@github.com:sbt/util.git")), + scalafmtOnCompile := true, + scalafmtVersion := "1.1.0", + )), commonSettings, name := "Util Root", publish := {}, @@ -67,21 +78,19 @@ lazy val utilRoot: Project = (project in file(".")). // defines Java structures used across Scala versions, such as the API structures and relationships extracted by // the analysis compiler phases and passed back to sbt. The API structures are defined in a simple // format from which Java sources are generated by the datatype generator Projproject -lazy val utilInterface = (project in internalPath / "util-interface"). - settings( - commonSettings, - javaOnlySettings, - name := "Util Interface", - exportJars := true, - mimaSettings, - ) +lazy val utilInterface = (project in internalPath / "util-interface").settings( + commonSettings, + javaOnlySettings, + name := "Util Interface", + exportJars := true, + mimaSettings, +) -lazy val utilControl = (project in internalPath / "util-control"). - settings( - commonSettings, - name := "Util Control", - mimaSettings, - ) +lazy val utilControl = (project in internalPath / "util-control").settings( + commonSettings, + name := "Util Control", + mimaSettings, +) val utilPosition = (project in file("internal") / "util-position").settings( commonSettings, @@ -90,14 +99,15 @@ val utilPosition = (project in file("internal") / "util-position").settings( ) // logging -lazy val utilLogging = (project in internalPath / "util-logging"). - enablePlugins(ContrabandPlugin, JsonCodecPlugin). - dependsOn(utilInterface, utilTesting % Test). - settings( +lazy val utilLogging = (project in internalPath / "util-logging") + .enablePlugins(ContrabandPlugin, JsonCodecPlugin) + .dependsOn(utilInterface, utilTesting % Test) + .settings( commonSettings, crossScalaVersions := Seq(scala210, scala211, scala212), name := "Util Logging", - libraryDependencies ++= Seq(jline, log4jApi, log4jCore, disruptor, sjsonnewScalaJson.value, scalaReflect.value), + libraryDependencies ++= + Seq(jline, log4jApi, log4jCore, disruptor, sjsonnewScalaJson.value, scalaReflect.value), sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", contrabandFormatsForType in generateContrabands in Compile := { tpe => val old = (contrabandFormatsForType in generateContrabands in Compile).value @@ -109,68 +119,69 @@ lazy val utilLogging = (project in internalPath / "util-logging"). ) // Relation -lazy val utilRelation = (project in internalPath / "util-relation"). - dependsOn(utilTesting % Test). - settings( +lazy val utilRelation = (project in internalPath / "util-relation") + .dependsOn(utilTesting % Test) + .settings( commonSettings, name := "Util Relation", mimaSettings, ) // Persisted caching based on sjson-new -lazy val utilCache = (project in file("util-cache")). - dependsOn(utilTesting % Test). - settings( +lazy val utilCache = (project in file("util-cache")) + .dependsOn(utilTesting % Test) + .settings( commonSettings, name := "Util Cache", - libraryDependencies ++= Seq(sjsonnewScalaJson.value, sjsonnewMurmurhash.value, scalaReflect.value), + libraryDependencies ++= + Seq(sjsonnewScalaJson.value, sjsonnewMurmurhash.value, scalaReflect.value), mimaSettings, - ). - configure(addSbtIO) + ) + .configure(addSbtIO) // Builds on cache to provide caching for filesystem-related operations -lazy val utilTracking = (project in file("util-tracking")). - dependsOn(utilCache, utilTesting % Test). - settings( +lazy val utilTracking = (project in file("util-tracking")) + .dependsOn(utilCache, utilTesting % Test) + .settings( commonSettings, name := "Util Tracking", mimaSettings, - ). - configure(addSbtIO) + ) + .configure(addSbtIO) // Internal utility for testing -lazy val utilTesting = (project in internalPath / "util-testing"). - settings( +lazy val utilTesting = (project in internalPath / "util-testing") + .settings( commonSettings, crossScalaVersions := Seq(scala210, scala211, scala212), name := "Util Testing", libraryDependencies ++= Seq(scalaCheck, scalatest), mimaSettings, - ). - configure(addSbtIO) + ) + .configure(addSbtIO) -lazy val utilScripted = (project in internalPath / "util-scripted"). - dependsOn(utilLogging, utilInterface). - settings( +lazy val utilScripted = (project in internalPath / "util-scripted") + .dependsOn(utilLogging, utilInterface) + .settings( commonSettings, name := "Util Scripted", libraryDependencies ++= { scalaVersion.value match { case sv if sv startsWith "2.11" => Seq(parserCombinator211) case sv if sv startsWith "2.12" => Seq(parserCombinator211) - case _ => Seq() + case _ => Seq() } }, mimaSettings, - ). - configure(addSbtIO) + ) + .configure(addSbtIO) def customCommands: Seq[Setting[_]] = Seq( commands += Command.command("release") { state => // "clean" :: "+compile" :: - "+publishSigned" :: - "reload" :: - state + "+publishSigned" :: + "reload" :: + state } ) diff --git a/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala b/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala index ae0d5443e..f9b101453 100644 --- a/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala +++ b/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala @@ -7,23 +7,20 @@ import java.io.IOException object ErrorHandling { def translate[T](msg: => String)(f: => T) = - try { f } - catch { + try { f } catch { case e: IOException => throw new TranslatedIOException(msg + e.toString, e) case e: Exception => throw new TranslatedException(msg + e.toString, e) } def wideConvert[T](f: => T): Either[Throwable, T] = - try { Right(f) } - catch { + try { Right(f) } catch { case ex @ (_: Exception | _: StackOverflowError) => Left(ex) case err @ (_: ThreadDeath | _: VirtualMachineError) => throw err case x: Throwable => Left(x) } def convert[T](f: => T): Either[Exception, T] = - try { Right(f) } - catch { case e: Exception => Left(e) } + try { Right(f) } catch { case e: Exception => Left(e) } def reducedToString(e: Throwable): String = if (e.getClass == classOf[RuntimeException]) { @@ -32,7 +29,11 @@ object ErrorHandling { } else e.toString } -sealed class TranslatedException private[sbt] (msg: String, cause: Throwable) extends RuntimeException(msg, cause) { + +sealed class TranslatedException private[sbt] (msg: String, cause: Throwable) + extends RuntimeException(msg, cause) { override def toString = msg } -final class TranslatedIOException private[sbt] (msg: String, cause: IOException) extends TranslatedException(msg, cause) + +final class TranslatedIOException private[sbt] (msg: String, cause: IOException) + extends TranslatedException(msg, cause) diff --git a/internal/util-control/src/main/scala/sbt/internal/util/ExitHook.scala b/internal/util-control/src/main/scala/sbt/internal/util/ExitHook.scala index 823c64b01..09d25aa3e 100644 --- a/internal/util-control/src/main/scala/sbt/internal/util/ExitHook.scala +++ b/internal/util-control/src/main/scala/sbt/internal/util/ExitHook.scala @@ -5,16 +5,20 @@ package sbt.internal.util /** Defines a function to call as sbt exits.*/ trait ExitHook { + /** Subclasses should implement this method, which is called when this hook is executed. */ def runBeforeExiting(): Unit + } + object ExitHook { def apply(f: => Unit): ExitHook = new ExitHook { def runBeforeExiting() = f } } object ExitHooks { + /** Calls each registered exit hook, trapping any exceptions so that each hook is given a chance to run. */ def runExitHooks(exitHooks: Seq[ExitHook]): Seq[Throwable] = - exitHooks.flatMap(hook => - ErrorHandling.wideConvert(hook.runBeforeExiting()).left.toOption) -} \ No newline at end of file + exitHooks.flatMap(hook => ErrorHandling.wideConvert(hook.runBeforeExiting()).left.toOption) + +} diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala index 93686c334..b19fc2509 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala @@ -13,41 +13,43 @@ import java.util.concurrent.atomic.AtomicInteger object BufferedAppender { def generateName: String = "buffered-" + generateId.incrementAndGet + private val generateId: AtomicInteger = new AtomicInteger + def apply(delegate: Appender): BufferedAppender = apply(generateName, delegate) - def apply(name: String, delegate: Appender): BufferedAppender = - { - val appender = new BufferedAppender(name, delegate) - appender.start - appender - } + + def apply(name: String, delegate: Appender): BufferedAppender = { + val appender = new BufferedAppender(name, delegate) + appender.start + appender + } } /** - * Am appender that can buffer the logging done on it and then can flush the buffer + * An appender that can buffer the logging done on it and then can flush the buffer * to the delegate appender provided in the constructor. Use 'record()' to * start buffering and then 'play' to flush the buffer to the backing appender. * The logging level set at the time a message is originally logged is used, not * the level at the time 'play' is called. */ -class BufferedAppender private[BufferedAppender] (name: String, delegate: Appender) extends AbstractAppender(name, null, PatternLayout.createDefaultLayout(), true) { +class BufferedAppender private[BufferedAppender] (name: String, delegate: Appender) + extends AbstractAppender(name, null, PatternLayout.createDefaultLayout(), true) { + private[this] val buffer = new ListBuffer[XLogEvent] private[this] var recording = false - def append(event: XLogEvent): Unit = - { - if (recording) { - buffer += event.toImmutable - } else delegate.append(event) - } + def append(event: XLogEvent): Unit = { + if (recording) { + buffer += event.toImmutable + } else delegate.append(event) + } /** Enables buffering. */ def record() = synchronized { recording = true } def buffer[T](f: => T): T = { record() - try { f } - finally { stopQuietly() } + try { f } finally { stopQuietly() } } def bufferQuietly[T](f: => T): T = { record() @@ -70,10 +72,13 @@ class BufferedAppender private[BufferedAppender] (name: String, delegate: Append } buffer.clear() } + /** Clears buffered events and disables buffering. */ def clearBuffer(): Unit = synchronized { buffer.clear(); recording = false } + /** Plays buffered events and disables buffering. */ def stopBuffer(): Unit = synchronized { play(); clearBuffer() } + } /** @@ -93,8 +98,7 @@ class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { def record() = synchronized { recording = true } def buffer[T](f: => T): T = { record() - try { f } - finally { stopQuietly() } + try { f } finally { stopQuietly() } } def bufferQuietly[T](f: => T): T = { record() @@ -111,8 +115,10 @@ class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { * so that the messages are written consecutively. The buffer is cleared in the process. */ def play(): Unit = synchronized { delegate.logAll(buffer.toList); buffer.clear() } + /** Clears buffered events and disables buffering. */ def clear(): Unit = synchronized { buffer.clear(); recording = false } + /** Plays buffered events and disables buffering. */ def stop(): Unit = synchronized { play(); clear() } @@ -127,6 +133,7 @@ class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { delegate.setLevel(newLevel) () } + override def setSuccessEnabled(flag: Boolean): Unit = synchronized { super.setSuccessEnabled(flag) if (recording) @@ -135,6 +142,7 @@ class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { delegate.setSuccessEnabled(flag) () } + override def setTrace(level: Int): Unit = synchronized { super.setTrace(level) if (recording) @@ -144,12 +152,14 @@ class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { () } - def trace(t: => Throwable): Unit = - doBufferableIf(traceEnabled, new Trace(t), _.trace(t)) + def trace(t: => Throwable): Unit = doBufferableIf(traceEnabled, new Trace(t), _.trace(t)) + def success(message: => String): Unit = doBufferable(Level.Info, new Success(message), _.success(message)) + def log(level: Level.Value, message: => String): Unit = doBufferable(level, new Log(level, message), _.log(level, message)) + def logAll(events: Seq[LogEvent]): Unit = synchronized { if (recording) buffer ++= events @@ -157,11 +167,22 @@ class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { delegate.logAll(events) () } + def control(event: ControlEvent.Value, message: => String): Unit = doBufferable(Level.Info, new ControlEvent(event, message), _.control(event, message)) - private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit = + + private def doBufferable( + level: Level.Value, + appendIfBuffered: => LogEvent, + doUnbuffered: AbstractLogger => Unit + ): Unit = doBufferableIf(atLevel(level), appendIfBuffered, doUnbuffered) - private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit = synchronized { + + private def doBufferableIf( + condition: => Boolean, + appendIfBuffered: => LogEvent, + doUnbuffered: AbstractLogger => Unit + ): Unit = synchronized { if (condition) { if (recording) buffer += appendIfBuffered diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala index ef392e785..0685c69f0 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala @@ -51,10 +51,13 @@ object ConsoleLogger { * @param suppressedMessage How to show suppressed stack traces. * @return A new `ConsoleLogger` that logs to `out`. */ - def apply(out: ConsoleOut = ConsoleOut.systemOut, - ansiCodesSupported: Boolean = ConsoleAppender.formatEnabledInEnv, - useFormat: Boolean = ConsoleAppender.formatEnabledInEnv, - suppressedMessage: SuppressedTraceContext => Option[String] = ConsoleAppender.noSuppressedMessage): ConsoleLogger = + def apply( + out: ConsoleOut = ConsoleOut.systemOut, + ansiCodesSupported: Boolean = ConsoleAppender.formatEnabledInEnv, + useFormat: Boolean = ConsoleAppender.formatEnabledInEnv, + suppressedMessage: SuppressedTraceContext => Option[String] = + ConsoleAppender.noSuppressedMessage + ): ConsoleLogger = new ConsoleLogger(out, ansiCodesSupported, useFormat, suppressedMessage) } @@ -62,10 +65,12 @@ object ConsoleLogger { * A logger that logs to the console. On supported systems, the level labels are * colored. */ -class ConsoleLogger private[ConsoleLogger] (out: ConsoleOut, - override val ansiCodesSupported: Boolean, - useFormat: Boolean, - suppressedMessage: SuppressedTraceContext => Option[String]) extends BasicLogger { +class ConsoleLogger private[ConsoleLogger] ( + out: ConsoleOut, + override val ansiCodesSupported: Boolean, + useFormat: Boolean, + suppressedMessage: SuppressedTraceContext => Option[String] +) extends BasicLogger { private[sbt] val appender: ConsoleAppender = ConsoleAppender(generateName(), out, ansiCodesSupported, useFormat, suppressedMessage) @@ -160,7 +165,11 @@ object ConsoleAppender { * @param suppressedMessage How to handle stack traces. * @return A new `ConsoleAppender` that writes to `out`. */ - def apply(name: String, out: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String]): ConsoleAppender = + def apply( + name: String, + out: ConsoleOut, + suppressedMessage: SuppressedTraceContext => Option[String] + ): ConsoleAppender = apply(name, out, formatEnabledInEnv, formatEnabledInEnv, suppressedMessage) /** @@ -184,14 +193,16 @@ object ConsoleAppender { * formatting. * @return A new `ConsoleAppender` that writes to `out`. */ - def apply(name: String, - out: ConsoleOut, - ansiCodesSupported: Boolean, - useFormat: Boolean, - suppressedMessage: SuppressedTraceContext => Option[String]): ConsoleAppender = { - val appender = new ConsoleAppender(name, out, ansiCodesSupported, useFormat, suppressedMessage) - appender.start - appender + def apply( + name: String, + out: ConsoleOut, + ansiCodesSupported: Boolean, + useFormat: Boolean, + suppressedMessage: SuppressedTraceContext => Option[String] + ): ConsoleAppender = { + val appender = new ConsoleAppender(name, out, ansiCodesSupported, useFormat, suppressedMessage) + appender.start + appender } /** @@ -242,7 +253,9 @@ object ConsoleAppender { // this results in a linkage error as detected below. The detection is likely jvm specific, but the priority // is avoiding mistakenly identifying something as a launcher incompatibility when it is not case e: IncompatibleClassChangeError if e.getMessage == jline1to2CompatMsg => - throw new IncompatibleClassChangeError("JLine incompatibility detected. Check that the sbt launcher is version 0.13.x or later.") + throw new IncompatibleClassChangeError( + "JLine incompatibility detected. Check that the sbt launcher is version 0.13.x or later." + ) } private[this] def os = System.getProperty("os.name") @@ -262,11 +275,11 @@ object ConsoleAppender { * This logger is not thread-safe. */ class ConsoleAppender private[ConsoleAppender] ( - name: String, - out: ConsoleOut, - ansiCodesSupported: Boolean, - useFormat: Boolean, - suppressedMessage: SuppressedTraceContext => Option[String] + name: String, + out: ConsoleOut, + ansiCodesSupported: Boolean, + useFormat: Boolean, + suppressedMessage: SuppressedTraceContext => Option[String] ) extends AbstractAppender(name, null, LogExchange.dummyLayout, true) { import scala.Console.{ BLUE, GREEN, RED, YELLOW } @@ -275,12 +288,12 @@ class ConsoleAppender private[ConsoleAppender] ( else "" } - private val SUCCESS_LABEL_COLOR = GREEN + private val SUCCESS_LABEL_COLOR = GREEN private val SUCCESS_MESSAGE_COLOR = reset - private val NO_COLOR = reset + private val NO_COLOR = reset private var traceEnabledVar: Int = Int.MaxValue - + def setTrace(level: Int): Unit = synchronized { traceEnabledVar = level } /** @@ -307,9 +320,11 @@ class ConsoleAppender private[ConsoleAppender] ( out.lockObject.synchronized { if (traceLevel >= 0) write(StackTrace.trimmed(t, traceLevel)) - if (traceLevel <= 2) - for (msg <- suppressedMessage(new SuppressedTraceContext(traceLevel, ansiCodesSupported && useFormat))) + if (traceLevel <= 2) { + val ctx = new SuppressedTraceContext(traceLevel, ansiCodesSupported && useFormat) + for (msg <- suppressedMessage(ctx)) appendLog(NO_COLOR, "trace", NO_COLOR, msg) + } } /** @@ -365,10 +380,16 @@ class ConsoleAppender private[ConsoleAppender] ( * @param messageColor The color to use to format the message. * @param message The message to write. */ - private def appendLog(labelColor: String, label: String, messageColor: String, message: String): Unit = + private def appendLog( + labelColor: String, + label: String, + messageColor: String, + message: String + ): Unit = out.lockObject.synchronized { message.lines.foreach { line => - val labeledLine = s"$reset[${formatted(labelColor, label)}] ${formatted(messageColor, line)}" + val labeledLine = + s"$reset[${formatted(labelColor, label)}] ${formatted(messageColor, line)}" write(labeledLine) } } @@ -395,31 +416,30 @@ class ConsoleAppender private[ConsoleAppender] ( private def appendTraceEvent(te: TraceEvent): Unit = { val traceLevel = getTrace val throwableShowLines: ShowLines[Throwable] = - ShowLines[Throwable]( (t: Throwable) => { + ShowLines[Throwable]((t: Throwable) => { List(StackTrace.trimmed(t, traceLevel)) }) val codec: ShowLines[TraceEvent] = - ShowLines[TraceEvent]( (t: TraceEvent) => { + ShowLines[TraceEvent]((t: TraceEvent) => { throwableShowLines.showLines(t.message) }) codec.showLines(te).toVector foreach { appendLog(Level.Error, _) } } - private def appendMessageContent(level: Level.Value, o: AnyRef): Unit = { - def appendEvent(oe: ObjectEvent[_]): Unit = - { - val contentType = oe.contentType - if (contentType == "sbt.internal.util.TraceEvent") { - appendTraceEvent(oe.message.asInstanceOf[TraceEvent]) - } - else LogExchange.stringCodec[AnyRef](contentType) match { + private def appendMessageContent(level: Level.Value, o: AnyRef): Unit = { + def appendEvent(oe: ObjectEvent[_]): Unit = { + val contentType = oe.contentType + if (contentType == "sbt.internal.util.TraceEvent") { + appendTraceEvent(oe.message.asInstanceOf[TraceEvent]) + } else + LogExchange.stringCodec[AnyRef](contentType) match { case Some(codec) if contentType == "sbt.internal.util.SuccessEvent" => codec.showLines(oe.message.asInstanceOf[AnyRef]).toVector foreach { success(_) } case Some(codec) => - codec.showLines(oe.message.asInstanceOf[AnyRef]).toVector foreach { appendLog(level, _) } - case _ => appendLog(level, oe.message.toString) + codec.showLines(oe.message.asInstanceOf[AnyRef]).toVector foreach (appendLog(level, _)) + case _ => appendLog(level, oe.message.toString) } - } + } o match { case x: StringEvent => Vector(x.message) foreach { appendLog(level, _) } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala index b9834d7e8..37af255cb 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala @@ -12,8 +12,8 @@ sealed trait ConsoleOut { object ConsoleOut { def systemOut: ConsoleOut = printStreamOut(System.out) - def overwriteContaining(s: String): (String, String) => Boolean = (cur, prev) => - cur.contains(s) && prev.contains(s) + def overwriteContaining(s: String): (String, String) => Boolean = + (cur, prev) => cur.contains(s) && prev.contains(s) /** Move to beginning of previous line and clear the line. */ private[this] final val OverwriteLine = "\u001B[A\r\u001B[2K" diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala b/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala index 1dcf9d9f9..7249bdd27 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala @@ -16,10 +16,21 @@ import org.apache.logging.log4j.core.Appender * `backing` tracks the files that persist the global logging. * `newLogger` creates a new global logging configuration from a sink and backing configuration. */ -final case class GlobalLogging(full: ManagedLogger, console: ConsoleOut, backed: Appender, - backing: GlobalLogBacking, newAppender: (ManagedLogger, PrintWriter, GlobalLogBacking) => GlobalLogging) +final case class GlobalLogging( + full: ManagedLogger, + console: ConsoleOut, + backed: Appender, + backing: GlobalLogBacking, + newAppender: (ManagedLogger, PrintWriter, GlobalLogBacking) => GlobalLogging +) -final case class GlobalLogging1(full: Logger, console: ConsoleOut, backed: AbstractLogger, backing: GlobalLogBacking, newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging1) +final case class GlobalLogging1( + full: Logger, + console: ConsoleOut, + backed: AbstractLogger, + backing: GlobalLogBacking, + newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging1 +) /** * Tracks the files that persist the global logging. @@ -27,6 +38,7 @@ final case class GlobalLogging1(full: Logger, console: ConsoleOut, backed: Abstr * `newBackingFile` creates a new temporary location for the next backing file. */ final case class GlobalLogBacking(file: File, last: Option[File], newBackingFile: () => File) { + /** Shifts the current backing file to `last` and sets the current backing to `newFile`. */ def shift(newFile: File) = GlobalLogBacking(newFile, Some(file), newBackingFile) @@ -38,32 +50,38 @@ final case class GlobalLogBacking(file: File, last: Option[File], newBackingFile * Otherwise, no changes are made. */ def unshift = GlobalLogBacking(last getOrElse file, None, newBackingFile) + } + object GlobalLogBacking { - def apply(newBackingFile: => File): GlobalLogBacking = GlobalLogBacking(newBackingFile, None, newBackingFile _) + def apply(newBackingFile: => File): GlobalLogBacking = + GlobalLogBacking(newBackingFile, None, newBackingFile _) } object GlobalLogging { import java.util.concurrent.atomic.AtomicInteger - private def generateName: String = - "GlobalLogging" + generateId.incrementAndGet + + private def generateName: String = "GlobalLogging" + generateId.incrementAndGet private val generateId: AtomicInteger = new AtomicInteger - def initial1(newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging1, newBackingFile: => File, console: ConsoleOut): GlobalLogging1 = - { - val log = ConsoleLogger(console) - GlobalLogging1(log, console, log, GlobalLogBacking(newBackingFile), newLogger) - } + def initial1( + newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging1, + newBackingFile: => File, + console: ConsoleOut + ): GlobalLogging1 = { + val log = ConsoleLogger(console) + GlobalLogging1(log, console, log, GlobalLogBacking(newBackingFile), newLogger) + } - def initial(newAppender: (ManagedLogger, PrintWriter, GlobalLogBacking) => GlobalLogging, newBackingFile: => File, console: ConsoleOut): GlobalLogging = - { - val loggerName = generateName - val log = LogExchange.logger(loggerName) - val appender = ConsoleAppender(ConsoleAppender.generateName, console) - LogExchange.bindLoggerAppenders( - loggerName, List(appender -> Level.Info) - ) - GlobalLogging(log, console, appender, GlobalLogBacking(newBackingFile), newAppender) - } + def initial( + newAppender: (ManagedLogger, PrintWriter, GlobalLogBacking) => GlobalLogging, + newBackingFile: => File, + console: ConsoleOut + ): GlobalLogging = { + val loggerName = generateName + val log = LogExchange.logger(loggerName) + val appender = ConsoleAppender(ConsoleAppender.generateName, console) + LogExchange.bindLoggerAppenders(loggerName, List(appender -> Level.Info)) + GlobalLogging(log, console, appender, GlobalLogBacking(newBackingFile), newAppender) + } } - diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/LoggerWriter.scala b/internal/util-logging/src/main/scala/sbt/internal/util/LoggerWriter.scala index 7b440c200..91ff8bc48 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/LoggerWriter.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/LoggerWriter.scala @@ -9,7 +9,11 @@ import sbt.util._ * Provides a `java.io.Writer` interface to a `Logger`. Content is line-buffered and logged at `level`. * A line is delimited by `nl`, which is by default the platform line separator. */ -class LoggerWriter(delegate: Logger, unbufferedLevel: Option[Level.Value], nl: String = System.getProperty("line.separator")) extends java.io.Writer { +class LoggerWriter( + delegate: Logger, + unbufferedLevel: Option[Level.Value], + nl: String = System.getProperty("line.separator") +) extends java.io.Writer { def this(delegate: Logger, level: Level.Value) = this(delegate, Some(level)) def this(delegate: Logger) = this(delegate, None) @@ -17,6 +21,7 @@ class LoggerWriter(delegate: Logger, unbufferedLevel: Option[Level.Value], nl: S private[this] val lines = new collection.mutable.ListBuffer[String] override def close() = flush() + override def flush(): Unit = synchronized { if (buffer.nonEmpty) { @@ -24,12 +29,14 @@ class LoggerWriter(delegate: Logger, unbufferedLevel: Option[Level.Value], nl: S buffer.clear() } } + def flushLines(level: Level.Value): Unit = synchronized { for (line <- lines) delegate.log(level, line) lines.clear() } + override def write(content: Array[Char], offset: Int, length: Int): Unit = synchronized { buffer.appendAll(content, offset, length) @@ -44,6 +51,7 @@ class LoggerWriter(delegate: Logger, unbufferedLevel: Option[Level.Value], nl: S process() } } + private[this] def log(s: String): Unit = unbufferedLevel match { case None => lines += s; () diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/MainLogging.scala b/internal/util-logging/src/main/scala/sbt/internal/util/MainLogging.scala index dd08ba0bb..2a47587b0 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/MainLogging.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/MainLogging.scala @@ -10,47 +10,78 @@ object MainAppender { "GlobalBacking" + generateId.incrementAndGet private val generateId: AtomicInteger = new AtomicInteger - def multiLogger(log: ManagedLogger, config: MainAppenderConfig): ManagedLogger = - { - import config._ - // TODO - // console setTrace screenTrace - // backed setTrace backingTrace - // multi: Logger + def multiLogger(log: ManagedLogger, config: MainAppenderConfig): ManagedLogger = { + import config._ + // TODO + // console setTrace screenTrace + // backed setTrace backingTrace + // multi: Logger - // val log = LogExchange.logger(loggerName) - LogExchange.unbindLoggerAppenders(log.name) - LogExchange.bindLoggerAppenders( - log.name, - (consoleOpt.toList map { _ -> screenLevel }) ::: - List(backed -> backingLevel) ::: - (extra map { x => (x -> Level.Info) }) - ) - log - } + // val log = LogExchange.logger(loggerName) + LogExchange.unbindLoggerAppenders(log.name) + LogExchange.bindLoggerAppenders( + log.name, + (consoleOpt.toList map { _ -> screenLevel }) ::: + List(backed -> backingLevel) ::: + (extra map { x => + (x -> Level.Info) + }) + ) + log + } - def globalDefault(console: ConsoleOut): (ManagedLogger, PrintWriter, GlobalLogBacking) => GlobalLogging = - { - lazy val newAppender: (ManagedLogger, PrintWriter, GlobalLogBacking) => GlobalLogging = (log, writer, backing) => { + def globalDefault( + console: ConsoleOut + ): (ManagedLogger, PrintWriter, GlobalLogBacking) => GlobalLogging = { + lazy val newAppender: (ManagedLogger, PrintWriter, GlobalLogBacking) => GlobalLogging = + (log, writer, backing) => { val backed: Appender = defaultBacked(generateGlobalBackingName)(writer) val full = multiLogger(log, defaultMultiConfig(Option(console), backed, Nil)) GlobalLogging(full, console, backed, backing, newAppender) } - newAppender - } + newAppender + } - def defaultMultiConfig(consoleOpt: Option[ConsoleOut], backing: Appender, extra: List[Appender]): MainAppenderConfig = - MainAppenderConfig(consoleOpt map { defaultScreen(_, ConsoleAppender.noSuppressedMessage) }, backing, extra, - Level.Info, Level.Debug, -1, Int.MaxValue) - def defaultScreen(console: ConsoleOut): Appender = ConsoleAppender(ConsoleAppender.generateName, console) - def defaultScreen(console: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String]): Appender = + def defaultMultiConfig( + consoleOpt: Option[ConsoleOut], + backing: Appender, + extra: List[Appender] + ): MainAppenderConfig = + MainAppenderConfig( + consoleOpt map { defaultScreen(_, ConsoleAppender.noSuppressedMessage) }, + backing, + extra, + Level.Info, + Level.Debug, + -1, + Int.MaxValue + ) + + def defaultScreen(console: ConsoleOut): Appender = + ConsoleAppender(ConsoleAppender.generateName, console) + + def defaultScreen( + console: ConsoleOut, + suppressedMessage: SuppressedTraceContext => Option[String] + ): Appender = ConsoleAppender(ConsoleAppender.generateName, console, suppressedMessage = suppressedMessage) - def defaultScreen(name: String, console: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String]): Appender = + + def defaultScreen( + name: String, + console: ConsoleOut, + suppressedMessage: SuppressedTraceContext => Option[String] + ): Appender = ConsoleAppender(name, console, suppressedMessage = suppressedMessage) - def defaultBacked: PrintWriter => Appender = defaultBacked(generateGlobalBackingName, ConsoleAppender.formatEnabledInEnv) - def defaultBacked(loggerName: String): PrintWriter => Appender = defaultBacked(loggerName, ConsoleAppender.formatEnabledInEnv) - def defaultBacked(useFormat: Boolean): PrintWriter => Appender = defaultBacked(generateGlobalBackingName, useFormat) + def defaultBacked: PrintWriter => Appender = + defaultBacked(generateGlobalBackingName, ConsoleAppender.formatEnabledInEnv) + + def defaultBacked(loggerName: String): PrintWriter => Appender = + defaultBacked(loggerName, ConsoleAppender.formatEnabledInEnv) + + def defaultBacked(useFormat: Boolean): PrintWriter => Appender = + defaultBacked(generateGlobalBackingName, useFormat) + def defaultBacked(loggerName: String, useFormat: Boolean): PrintWriter => Appender = to => { ConsoleAppender( @@ -61,7 +92,12 @@ object MainAppender { } final case class MainAppenderConfig( - consoleOpt: Option[Appender], backed: Appender, extra: List[Appender], - screenLevel: Level.Value, backingLevel: Level.Value, screenTrace: Int, backingTrace: Int + consoleOpt: Option[Appender], + backed: Appender, + extra: List[Appender], + screenLevel: Level.Value, + backingLevel: Level.Value, + screenTrace: Int, + backingTrace: Int ) } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala index 5a9215ba8..0fa390c2e 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala @@ -14,33 +14,31 @@ import sbt.internal.util.codec.JsonProtocol._ * Delegates log events to the associated LogExchange. */ class ManagedLogger( - val name: String, - val channelName: Option[String], - val execId: Option[String], - xlogger: XLogger + val name: String, + val channelName: Option[String], + val execId: Option[String], + xlogger: XLogger ) extends Logger { override def trace(t: => Throwable): Unit = logEvent(Level.Error, TraceEvent("Error", t, channelName, execId)) - override def log(level: Level.Value, message: => String): Unit = - { - xlogger.log( - ConsoleAppender.toXLevel(level), - new ObjectMessage(StringEvent(level.toString, message, channelName, execId)) - ) - } - + override def log(level: Level.Value, message: => String): Unit = { + xlogger.log( + ConsoleAppender.toXLevel(level), + new ObjectMessage(StringEvent(level.toString, message, channelName, execId)) + ) + } + // send special event for success since it's not a real log level override def success(message: => String): Unit = { infoEvent[SuccessEvent](SuccessEvent(message)) } - def registerStringCodec[A: ShowLines: TypeTag]: Unit = - { - val tag = StringTypeTag[A] - val ev = implicitly[ShowLines[A]] - // println(s"registerStringCodec ${tag.key}") - val _ = LogExchange.getOrElseUpdateStringCodec(tag.key, ev) - } + def registerStringCodec[A: ShowLines: TypeTag]: Unit = { + val tag = StringTypeTag[A] + val ev = implicitly[ShowLines[A]] + // println(s"registerStringCodec ${tag.key}") + val _ = LogExchange.getOrElseUpdateStringCodec(tag.key, ev) + } registerStringCodec[Throwable] registerStringCodec[TraceEvent] registerStringCodec[SuccessEvent] @@ -48,18 +46,17 @@ class ManagedLogger( final def infoEvent[A: JsonFormat: TypeTag](event: => A): Unit = logEvent(Level.Info, event) final def warnEvent[A: JsonFormat: TypeTag](event: => A): Unit = logEvent(Level.Warn, event) final def errorEvent[A: JsonFormat: TypeTag](event: => A): Unit = logEvent(Level.Error, event) - def logEvent[A: JsonFormat: TypeTag](level: Level.Value, event: => A): Unit = - { - val v: A = event - val tag = StringTypeTag[A] - LogExchange.getOrElseUpdateJsonCodec(tag.key, implicitly[JsonFormat[A]]) - // println("logEvent " + tag.key) - val entry: ObjectEvent[A] = ObjectEvent(level, v, channelName, execId, tag.key) - xlogger.log( - ConsoleAppender.toXLevel(level), - new ObjectMessage(entry) - ) - } + def logEvent[A: JsonFormat: TypeTag](level: Level.Value, event: => A): Unit = { + val v: A = event + val tag = StringTypeTag[A] + LogExchange.getOrElseUpdateJsonCodec(tag.key, implicitly[JsonFormat[A]]) + // println("logEvent " + tag.key) + val entry: ObjectEvent[A] = ObjectEvent(level, v, channelName, execId, tag.key) + xlogger.log( + ConsoleAppender.toXLevel(level), + new ObjectMessage(entry) + ) + } @deprecated("No longer used.", "1.0.0") override def ansiCodesSupported = ConsoleAppender.formatEnabledInEnv diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/MultiLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/MultiLogger.scala index ef82fa10f..2d12a1b2f 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/MultiLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/MultiLogger.scala @@ -1,4 +1,3 @@ - /* sbt -- Simple Build Tool * Copyright 2008, 2009, 2010 Mark Harrah */ @@ -17,19 +16,25 @@ class MultiLogger(delegates: List[AbstractLogger]) extends BasicLogger { super.setLevel(newLevel) dispatch(new SetLevel(newLevel)) } + override def setTrace(level: Int): Unit = { super.setTrace(level) dispatch(new SetTrace(level)) } + override def setSuccessEnabled(flag: Boolean): Unit = { super.setSuccessEnabled(flag) dispatch(new SetSuccess(flag)) } + def trace(t: => Throwable): Unit = dispatch(new Trace(t)) def log(level: Level.Value, message: => String): Unit = dispatch(new Log(level, message)) def success(message: => String): Unit = dispatch(new Success(message)) def logAll(events: Seq[LogEvent]): Unit = delegates.foreach(_.logAll(events)) - def control(event: ControlEvent.Value, message: => String): Unit = delegates.foreach(_.control(event, message)) + + def control(event: ControlEvent.Value, message: => String): Unit = + delegates.foreach(_.control(event, message)) + private[this] def dispatch(event: LogEvent): Unit = { for (d <- delegates) { d.log(event) diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ObjectEvent.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ObjectEvent.scala index f8f288c21..c2c92437d 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ObjectEvent.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ObjectEvent.scala @@ -8,12 +8,12 @@ import sjsonnew.support.scalajson.unsafe.Converter import sjsonnew.shaded.scalajson.ast.unsafe.JValue final class ObjectEvent[A]( - val level: Level.Value, - val message: A, - val channelName: Option[String], - val execId: Option[String], - val contentType: String, - val json: JValue + val level: Level.Value, + val message: A, + val channelName: Option[String], + val execId: Option[String], + val contentType: String, + val json: JValue ) extends Serializable { override def toString: String = s"ObjectEvent($level, $message, $channelName, $execId, $contentType, $json)" @@ -21,12 +21,18 @@ final class ObjectEvent[A]( object ObjectEvent { def apply[A: JsonFormat]( - level: Level.Value, - message: A, - channelName: Option[String], - execId: Option[String], - contentType: String + level: Level.Value, + message: A, + channelName: Option[String], + execId: Option[String], + contentType: String ): ObjectEvent[A] = - new ObjectEvent(level, message, channelName, execId, contentType, - Converter.toJsonUnsafe(message)) + new ObjectEvent( + level, + message, + channelName, + execId, + contentType, + Converter.toJsonUnsafe(message) + ) } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/StackTrace.scala b/internal/util-logging/src/main/scala/sbt/internal/util/StackTrace.scala index 20821eefb..66468e2d5 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/StackTrace.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/StackTrace.scala @@ -5,6 +5,7 @@ package sbt.internal.util object StackTrace { def isSbtClass(name: String) = name.startsWith("sbt") || name.startsWith("xsbt") + /** * Return a printable representation of the stack trace associated * with t. Information about t and its Throwable causes is included. @@ -59,6 +60,6 @@ object StackTrace { appendStackTrace(c, false) } b.toString() - } + } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/StringTypeTag.scala b/internal/util-logging/src/main/scala/sbt/internal/util/StringTypeTag.scala index 5b90d9a12..00f30d7d2 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/StringTypeTag.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/StringTypeTag.scala @@ -8,14 +8,13 @@ final case class StringTypeTag[A](key: String) { } object StringTypeTag { - def apply[A: TypeTag]: StringTypeTag[A] = - { - val tag = implicitly[TypeTag[A]] - val tpe = tag.tpe - val k = typeToString(tpe) - // println(tpe.getClass.toString + " " + k) - StringTypeTag[A](k) - } + def apply[A: TypeTag]: StringTypeTag[A] = { + val tag = implicitly[TypeTag[A]] + val tpe = tag.tpe + val k = typeToString(tpe) + // println(tpe.getClass.toString + " " + k) + StringTypeTag[A](k) + } def typeToString(tpe: Type): String = tpe match { case TypeRef(_, sym, args) => diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/JValueFormats.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/JValueFormats.scala index 4e11f26e8..c0c79f7d1 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/JValueFormats.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/JValueFormats.scala @@ -17,7 +17,10 @@ trait JValueFormats { self: sjsonnew.BasicJsonProtocol => implicit val JBooleanFormat: JF[JBoolean] = projectFormat(_.get, (x: Boolean) => JBoolean(x)) implicit val JStringFormat: JF[JString] = projectFormat(_.value, (x: String) => JString(x)) - implicit val JNumberFormat: JF[JNumber] = projectFormat(x => BigDecimal(x.value), (x: BigDecimal) => JNumber(x.toString)) + + implicit val JNumberFormat: JF[JNumber] = + projectFormat(x => BigDecimal(x.value), (x: BigDecimal) => JNumber(x.toString)) + implicit val JArrayFormat: JF[JArray] = projectFormat[JArray, Array[JValue]](_.value, JArray(_)) implicit lazy val JObjectJsonWriter: JW[JObject] = new JW[JObject] { @@ -43,5 +46,6 @@ trait JValueFormats { self: sjsonnew.BasicJsonProtocol => def read[J](j: Option[J], u: Unbuilder[J]) = ??? // Is this even possible? with no Manifest[J]? } - implicit lazy val JValueFormat: JF[JValue] = jsonFormat[JValue](JValueJsonReader, JValueJsonWriter) + implicit lazy val JValueFormat: JF[JValue] = + jsonFormat[JValue](JValueJsonReader, JValueJsonWriter) } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/PositionFormats.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/PositionFormats.scala index d6ddf8049..e43ff03bf 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/PositionFormats.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/PositionFormats.scala @@ -1,7 +1,6 @@ /** * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. */ - package sbt.internal.util.codec import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder } import xsbti.Position diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala index cbb5f0010..9820289da 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala @@ -1,7 +1,6 @@ /** * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. */ - package sbt.internal.util.codec import xsbti.{ Problem, Severity, Position } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/SeverityFormats.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/SeverityFormats.scala index 7548a2ff1..d572a146f 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/SeverityFormats.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/SeverityFormats.scala @@ -1,7 +1,6 @@ /** * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. */ - package sbt.internal.util.codec import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/SuccessEventShowLines.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/SuccessEventShowLines.scala index e3b338719..99cd31539 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/SuccessEventShowLines.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/SuccessEventShowLines.scala @@ -6,7 +6,7 @@ import sbt.internal.util.SuccessEvent trait SuccessEventShowLines { implicit val sbtSuccessEventShowLines: ShowLines[SuccessEvent] = - ShowLines[SuccessEvent]( (e: SuccessEvent) => { + ShowLines[SuccessEvent]((e: SuccessEvent) => { Vector(e.message) }) } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ThrowableShowLines.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ThrowableShowLines.scala index 13abbdf8a..ace0b78fb 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ThrowableShowLines.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ThrowableShowLines.scala @@ -6,7 +6,7 @@ import sbt.internal.util.{ StackTrace, TraceEvent } trait ThrowableShowLines { implicit val sbtThrowableShowLines: ShowLines[Throwable] = - ShowLines[Throwable]( (t: Throwable) => { + ShowLines[Throwable]((t: Throwable) => { // 0 means enabled with default behavior. See StackTrace.scala. val traceLevel = 0 List(StackTrace.trimmed(t, traceLevel)) @@ -17,7 +17,7 @@ object ThrowableShowLines extends ThrowableShowLines trait TraceEventShowLines { implicit val sbtTraceEventShowLines: ShowLines[TraceEvent] = - ShowLines[TraceEvent]( (t: TraceEvent) => { + ShowLines[TraceEvent]((t: TraceEvent) => { ThrowableShowLines.sbtThrowableShowLines.showLines(t.message) }) } diff --git a/internal/util-logging/src/main/scala/sbt/util/AbtractLogger.scala b/internal/util-logging/src/main/scala/sbt/util/AbtractLogger.scala index 51b7f08b5..253238038 100644 --- a/internal/util-logging/src/main/scala/sbt/util/AbtractLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/util/AbtractLogger.scala @@ -13,6 +13,7 @@ abstract class AbstractLogger extends Logger { def control(event: ControlEvent.Value, message: => String): Unit def logAll(events: Seq[LogEvent]): Unit + /** Defined in terms of other methods in Logger and should not be called from them. */ final def log(event: LogEvent): Unit = { event match { diff --git a/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala b/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala index 63e4213cb..dc956ecbf 100644 --- a/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala +++ b/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala @@ -36,8 +36,15 @@ object InterfaceUtil { case None => Optional.empty[A]() } - def position(line0: Option[Integer], content: String, offset0: Option[Integer], pointer0: Option[Integer], - pointerSpace0: Option[String], sourcePath0: Option[String], sourceFile0: Option[File]): Position = + def position( + line0: Option[Integer], + content: String, + offset0: Option[Integer], + pointer0: Option[Integer], + pointerSpace0: Option[String], + sourcePath0: Option[String], + sourceFile0: Option[File] + ): Position = new ConcretePosition(line0, content, offset0, pointer0, pointerSpace0, sourcePath0, sourceFile0) def problem(cat: String, pos: Position, msg: String, sev: Severity): Problem = @@ -53,23 +60,22 @@ object InterfaceUtil { this.get2 == o.get2 case _ => false } - override def hashCode: Int = - { - var hash = 1 - hash = hash * 31 + this.get1.## - hash = hash * 31 + this.get2.## - hash - } + override def hashCode: Int = { + var hash = 1 + hash = hash * 31 + this.get1.## + hash = hash * 31 + this.get2.## + hash + } } private final class ConcretePosition( - line0: Option[Integer], - content: String, - offset0: Option[Integer], - pointer0: Option[Integer], - pointerSpace0: Option[String], - sourcePath0: Option[String], - sourceFile0: Option[File] + line0: Option[Integer], + content: String, + offset0: Option[Integer], + pointer0: Option[Integer], + pointerSpace0: Option[String], + sourcePath0: Option[String], + sourceFile0: Option[File] ) extends Position { val line = o2jo(line0) val lineContent = content @@ -81,10 +87,10 @@ object InterfaceUtil { } private final class ConcreteProblem( - cat: String, - pos: Position, - msg: String, - sev: Severity + cat: String, + pos: Position, + msg: String, + sev: Severity ) extends Problem { val category = cat val position = pos diff --git a/internal/util-logging/src/main/scala/sbt/util/Level.scala b/internal/util-logging/src/main/scala/sbt/util/Level.scala index 2f319cffd..fdc83178b 100644 --- a/internal/util-logging/src/main/scala/sbt/util/Level.scala +++ b/internal/util-logging/src/main/scala/sbt/util/Level.scala @@ -12,6 +12,7 @@ object Level extends Enumeration { val Info = Value(2, "info") val Warn = Value(3, "warn") val Error = Value(4, "error") + /** * Defines the label to use for success messages. * Because the label for levels is defined in this module, the success label is also defined here. @@ -23,6 +24,7 @@ object Level extends Enumeration { /** Returns the level with the given name wrapped in Some, or None if no level exists for that name. */ def apply(s: String) = values.find(s == _.toString) + /** Same as apply, defined for use in pattern matching. */ private[sbt] def unapply(s: String) = apply(s) } diff --git a/internal/util-logging/src/main/scala/sbt/util/LogEvent.scala b/internal/util-logging/src/main/scala/sbt/util/LogEvent.scala index bfc962891..c6ab6eecb 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LogEvent.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LogEvent.scala @@ -14,4 +14,4 @@ final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends object ControlEvent extends Enumeration { val Start, Header, Finish = Value -} \ No newline at end of file +} diff --git a/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala b/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala index 150814bc5..ba3114643 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala @@ -24,8 +24,16 @@ sealed abstract class LogExchange { val _ = context val ctx = XLogManager.getContext(false) match { case x: LoggerContext => x } val config = ctx.getConfiguration - val loggerConfig = LoggerConfig.createLogger(false, XLevel.DEBUG, name, - "true", Array[AppenderRef](), null, config, null) + val loggerConfig = LoggerConfig.createLogger( + false, + XLevel.DEBUG, + name, + "true", + Array[AppenderRef](), + null, + config, + null + ) config.addLogger(name, loggerConfig) ctx.updateLoggers val logger = ctx.getLogger(name) diff --git a/internal/util-logging/src/main/scala/sbt/util/Logger.scala b/internal/util-logging/src/main/scala/sbt/util/Logger.scala index 0bcea3b78..75d7a439d 100644 --- a/internal/util-logging/src/main/scala/sbt/util/Logger.scala +++ b/internal/util-logging/src/main/scala/sbt/util/Logger.scala @@ -22,6 +22,7 @@ abstract class Logger extends xLogger { final def info(message: => String): Unit = log(Level.Info, message) final def warn(message: => String): Unit = log(Level.Warn, message) final def error(message: => String): Unit = log(Level.Error, message) + // Added by sys.process.ProcessLogger final def err(message: => String): Unit = log(Level.Error, message) // sys.process.ProcessLogger @@ -62,12 +63,16 @@ object Logger { def log(level: Level.Value, message: => String): Unit = () } - implicit def absLog2PLog(log: AbstractLogger): ProcessLogger = new BufferedLogger(log) with ProcessLogger + implicit def absLog2PLog(log: AbstractLogger): ProcessLogger = + new BufferedLogger(log) with ProcessLogger + implicit def log2PLog(log: Logger): ProcessLogger = absLog2PLog(new FullLogger(log)) + implicit def xlog2Log(lg: xLogger): Logger = lg match { case l: Logger => l case _ => wrapXLogger(lg) } + private[this] def wrapXLogger(lg: xLogger): Logger = new Logger { import InterfaceUtil.toSupplier override def debug(msg: Supplier[String]): Unit = lg.debug(msg) @@ -78,23 +83,39 @@ object Logger { override def log(level: Level.Value, msg: Supplier[String]): Unit = lg.log(level, msg) def trace(t: => Throwable): Unit = trace(toSupplier(t)) def success(s: => String): Unit = info(toSupplier(s)) - def log(level: Level.Value, msg: => String): Unit = - { - val fmsg = toSupplier(msg) - level match { - case Level.Debug => lg.debug(fmsg) - case Level.Info => lg.info(fmsg) - case Level.Warn => lg.warn(fmsg) - case Level.Error => lg.error(fmsg) - } + def log(level: Level.Value, msg: => String): Unit = { + val fmsg = toSupplier(msg) + level match { + case Level.Debug => lg.debug(fmsg) + case Level.Info => lg.info(fmsg) + case Level.Warn => lg.warn(fmsg) + case Level.Error => lg.error(fmsg) } + } } def jo2o[A](o: Optional[A]): Option[A] = InterfaceUtil.jo2o(o) def o2jo[A](o: Option[A]): Optional[A] = InterfaceUtil.o2jo(o) - def position(line0: Option[Integer], content: String, offset0: Option[Integer], pointer0: Option[Integer], - pointerSpace0: Option[String], sourcePath0: Option[String], sourceFile0: Option[File]): Position = - InterfaceUtil.position(line0, content, offset0, pointer0, pointerSpace0, sourcePath0, sourceFile0) + + def position( + line0: Option[Integer], + content: String, + offset0: Option[Integer], + pointer0: Option[Integer], + pointerSpace0: Option[String], + sourcePath0: Option[String], + sourceFile0: Option[File] + ): Position = + InterfaceUtil.position( + line0, + content, + offset0, + pointer0, + pointerSpace0, + sourcePath0, + sourceFile0 + ) + def problem(cat: String, pos: Position, msg: String, sev: Severity): Problem = InterfaceUtil.problem(cat, pos, msg, sev) } diff --git a/internal/util-logging/src/test/scala/Escapes.scala b/internal/util-logging/src/test/scala/Escapes.scala index 0ae24a6e4..9db109d7f 100644 --- a/internal/util-logging/src/test/scala/Escapes.scala +++ b/internal/util-logging/src/test/scala/Escapes.scala @@ -8,26 +8,25 @@ import EscHelpers.{ ESC, hasEscapeSequence, isEscapeTerminator, removeEscapeSequ object Escapes extends Properties("Escapes") { property("genTerminator only generates terminators") = - forAllNoShrink(genTerminator) { (c: Char) => isEscapeTerminator(c) } + forAllNoShrink(genTerminator)((c: Char) => isEscapeTerminator(c)) property("genWithoutTerminator only generates terminators") = forAllNoShrink(genWithoutTerminator) { (s: String) => - s.forall { c => !isEscapeTerminator(c) } + s.forall(c => !isEscapeTerminator(c)) } - property("hasEscapeSequence is false when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) => - !hasEscapeSequence(s) - } + property("hasEscapeSequence is false when no escape character is present") = + forAllNoShrink(genWithoutEscape)((s: String) => !hasEscapeSequence(s)) - property("hasEscapeSequence is true when escape character is present") = forAllNoShrink(genWithRandomEscapes) { (s: String) => - hasEscapeSequence(s) - } + property("hasEscapeSequence is true when escape character is present") = + forAllNoShrink(genWithRandomEscapes)((s: String) => hasEscapeSequence(s)) - property("removeEscapeSequences is the identity when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) => - val removed: String = removeEscapeSequences(s) - ("Escape sequence removed: '" + removed + "'") |: - (removed == s) - } + property("removeEscapeSequences is the identity when no escape character is present") = + forAllNoShrink(genWithoutEscape) { (s: String) => + val removed: String = removeEscapeSequences(s) + ("Escape sequence removed: '" + removed + "'") |: + (removed == s) + } property("No escape characters remain after removeEscapeSequences") = forAll { (s: String) => val removed: String = removeEscapeSequences(s) @@ -36,22 +35,26 @@ object Escapes extends Properties("Escapes") { } property("removeEscapeSequences returns string without escape sequences") = - forAllNoShrink(genWithoutEscape, genEscapePairs) { (start: String, escapes: List[EscapeAndNot]) => - val withEscapes: String = start + (escapes.map { ean => ean.escape.makeString + ean.notEscape }).mkString("") - val removed: String = removeEscapeSequences(withEscapes) - val original = start + escapes.map(_.notEscape).mkString("") - val diffCharString = diffIndex(original, removed) - ("Input string : '" + withEscapes + "'") |: - ("Expected : '" + original + "'") |: - ("Escapes removed : '" + removed + "'") |: - (diffCharString) |: - (original == removed) + forAllNoShrink(genWithoutEscape, genEscapePairs) { + (start: String, escapes: List[EscapeAndNot]) => + val withEscapes: String = + start + escapes.map(ean => ean.escape.makeString + ean.notEscape).mkString("") + val removed: String = removeEscapeSequences(withEscapes) + val original = start + escapes.map(_.notEscape).mkString("") + val diffCharString = diffIndex(original, removed) + ("Input string : '" + withEscapes + "'") |: + ("Expected : '" + original + "'") |: + ("Escapes removed : '" + removed + "'") |: + (diffCharString) |: + (original == removed) } def diffIndex(expect: String, original: String): String = { var i = 0; while (i < expect.length && i < original.length) { - if (expect.charAt(i) != original.charAt(i)) return ("Differing character, idx: " + i + ", char: " + original.charAt(i) + ", expected: " + expect.charAt(i)) + if (expect.charAt(i) != original.charAt(i)) + return ("Differing character, idx: " + i + ", char: " + original.charAt(i) + + ", expected: " + expect.charAt(i)) i += 1 } if (expect.length != original.length) return s"Strings are different lengths!" @@ -59,13 +62,21 @@ object Escapes extends Properties("Escapes") { } final case class EscapeAndNot(escape: EscapeSequence, notEscape: String) { - override def toString = s"EscapeAntNot(escape = [$escape], notEscape = [${notEscape.map(_.toInt)}])" + override def toString = + s"EscapeAntNot(escape = [$escape], notEscape = [${notEscape.map(_.toInt)}])" } + // 2.10.5 warns on "implicit numeric widening" but it looks like a bug: https://issues.scala-lang.org/browse/SI-8450 final case class EscapeSequence(content: String, terminator: Char) { if (!content.isEmpty) { - assert(content.tail.forall(c => !isEscapeTerminator(c)), "Escape sequence content contains an escape terminator: '" + content + "'") - assert((content.head == '[') || !isEscapeTerminator(content.head), "Escape sequence content contains an escape terminator: '" + content.headOption + "'") + assert( + content.tail.forall(c => !isEscapeTerminator(c)), + "Escape sequence content contains an escape terminator: '" + content + "'" + ) + assert( + (content.head == '[') || !isEscapeTerminator(content.head), + "Escape sequence content contains an escape terminator: '" + content.headOption + "'" + ) } assert(isEscapeTerminator(terminator)) def makeString: String = ESC + content + terminator @@ -74,14 +85,20 @@ object Escapes extends Properties("Escapes") { if (content.isEmpty) s"ESC (${terminator.toInt})" else s"ESC ($content) (${terminator.toInt})" } + private[this] def noEscape(s: String): String = s.replace(ESC, ' ') - lazy val genEscapeSequence: Gen[EscapeSequence] = oneOf(genKnownSequence, genTwoCharacterSequence, genArbitraryEscapeSequence) - lazy val genEscapePair: Gen[EscapeAndNot] = for (esc <- genEscapeSequence; not <- genWithoutEscape) yield EscapeAndNot(esc, not) + lazy val genEscapeSequence: Gen[EscapeSequence] = + oneOf(genKnownSequence, genTwoCharacterSequence, genArbitraryEscapeSequence) + + lazy val genEscapePair: Gen[EscapeAndNot] = + for (esc <- genEscapeSequence; not <- genWithoutEscape) yield EscapeAndNot(esc, not) + lazy val genEscapePairs: Gen[List[EscapeAndNot]] = listOf(genEscapePair) lazy val genArbitraryEscapeSequence: Gen[EscapeSequence] = - for (content <- genWithoutTerminator if !content.isEmpty; term <- genTerminator) yield new EscapeSequence("[" + content, term) + for (content <- genWithoutTerminator if !content.isEmpty; term <- genTerminator) + yield new EscapeSequence("[" + content, term) lazy val genKnownSequence: Gen[EscapeSequence] = oneOf((misc ++ setGraphicsMode ++ setMode ++ resetMode).map(toEscapeSequence)) @@ -91,14 +108,15 @@ object Escapes extends Properties("Escapes") { lazy val misc = Seq("14;23H", "5;3f", "2A", "94B", "19C", "85D", "s", "u", "2J", "K") lazy val setGraphicsMode: Seq[String] = - for (txt <- 0 to 8; fg <- 30 to 37; bg <- 40 to 47) yield txt.toString + ";" + fg.toString + ";" + bg.toString + "m" + for (txt <- 0 to 8; fg <- 30 to 37; bg <- 40 to 47) + yield txt.toString + ";" + fg.toString + ";" + bg.toString + "m" lazy val resetMode = setModeLike('I') lazy val setMode = setModeLike('h') def setModeLike(term: Char): Seq[String] = (0 to 19).map(i => "=" + i.toString + term) lazy val genWithoutTerminator = - genRawString.map(_.filter { c => !isEscapeTerminator(c) && (c != '[') }) + genRawString.map(_.filter(c => !isEscapeTerminator(c) && (c != '['))) lazy val genTwoCharacterSequence = // 91 == [ which is the CSI escape sequence. @@ -108,7 +126,8 @@ object Escapes extends Properties("Escapes") { lazy val genWithoutEscape: Gen[String] = genRawString.map(noEscape) def genWithRandomEscapes: Gen[String] = - for (ls <- listOf(genRawString); end <- genRawString) yield ls.mkString("", ESC.toString, ESC.toString + end) + for (ls <- listOf(genRawString); end <- genRawString) + yield ls.mkString("", ESC.toString, ESC.toString + end) private def genRawString = Arbitrary.arbString.arbitrary } diff --git a/internal/util-logging/src/test/scala/LogWriterTest.scala b/internal/util-logging/src/test/scala/LogWriterTest.scala index f00663b4b..7c9b29e68 100644 --- a/internal/util-logging/src/test/scala/LogWriterTest.scala +++ b/internal/util-logging/src/test/scala/LogWriterTest.scala @@ -36,6 +36,7 @@ object LogWriterTest extends Properties("Log Writer") { case l: Log => "Log('" + Escape(l.msg) + "', " + l.level + ")" case _ => "Not Log" } + /** * Writes the given lines to the Writer. `lines` is taken to be a list of lines, which are * represented as separately written segments (ToLog instances). ToLog.`byCharacter` @@ -46,7 +47,7 @@ object LogWriterTest extends Properties("Log Writer") { val content = section.content val normalized = Escape.newline(content, newLine) if (section.byCharacter) - normalized.foreach { c => writer.write(c.toInt) } + normalized.foreach(c => writer.write(c.toInt)) else writer.write(normalized) } @@ -56,6 +57,7 @@ object LogWriterTest extends Properties("Log Writer") { /** Converts the given lines in segments to lines as Strings for checking the results of the test.*/ def toLines(lines: List[List[ToLog]]): List[String] = lines.map(_.map(_.contentOnly).mkString) + /** Checks that the expected `lines` were recorded as `events` at level `Lvl`.*/ def check(lines: List[String], events: List[LogEvent], Lvl: Level.Value): Boolean = (lines zip events) forall { @@ -64,10 +66,10 @@ object LogWriterTest extends Properties("Log Writer") { } /* The following are implicit generators to build up a write sequence. - * ToLog represents a written segment. NewLine represents one of the possible - * newline separators. A List[ToLog] represents a full line and always includes a - * final ToLog with a trailing '\n'. Newline characters are otherwise not present in - * the `content` of a ToLog instance.*/ + * ToLog represents a written segment. NewLine represents one of the possible + * newline separators. A List[ToLog] represents a full line and always includes a + * final ToLog with a trailing '\n'. Newline characters are otherwise not present in + * the `content` of a ToLog instance.*/ implicit lazy val arbOut: Arbitrary[Output] = Arbitrary(genOutput) implicit lazy val arbLog: Arbitrary[ToLog] = Arbitrary(genLog) @@ -76,7 +78,8 @@ object LogWriterTest extends Properties("Log Writer") { implicit lazy val arbLevel: Arbitrary[Level.Value] = Arbitrary(genLevel) implicit def genLine(implicit logG: Gen[ToLog]): Gen[List[ToLog]] = - for (l <- listOf[ToLog](MaxSegments); last <- logG) yield (addNewline(last) :: l.filter(!_.content.isEmpty)).reverse + for (l <- listOf[ToLog](MaxSegments); last <- logG) + yield (addNewline(last) :: l.filter(!_.content.isEmpty)).reverse implicit def genLog(implicit content: Arbitrary[String], byChar: Arbitrary[Boolean]): Gen[ToLog] = for (c <- content.arbitrary; by <- byChar.arbitrary) yield { @@ -98,7 +101,7 @@ object LogWriterTest extends Properties("Log Writer") { new ToLog(l.content + "\n", l.byCharacter) // \n will be replaced by a random line terminator for all lines def listOf[T](max: Int)(implicit content: Arbitrary[T]): Gen[List[T]] = - Gen.choose(0, max) flatMap { sz => listOfN(sz, content.arbitrary) } + Gen.choose(0, max) flatMap (sz => listOfN(sz, content.arbitrary)) } /* Helper classes*/ @@ -107,35 +110,43 @@ final class Output(val lines: List[List[ToLog]], val level: Level.Value) { override def toString = "Level: " + level + "\n" + lines.map(_.mkString).mkString("\n") } + final class NewLine(val str: String) { override def toString = Escape(str) } + final class ToLog(val content: String, val byCharacter: Boolean) { def contentOnly = Escape.newline(content, "") - override def toString = if (content.isEmpty) "" else "ToLog('" + Escape(contentOnly) + "', " + byCharacter + ")" + + override def toString = + if (content.isEmpty) "" else "ToLog('" + Escape(contentOnly) + "', " + byCharacter + ")" } + /** Defines some utility methods for escaping unprintable characters.*/ object Escape { + /** Escapes characters with code less than 20 by printing them as unicode escapes.*/ - def apply(s: String): String = - { - val builder = new StringBuilder(s.length) - for (c <- s) { - val char = c.toInt - def escaped = pad(char.toHexString.toUpperCase, 4, '0') - if (c < 20) builder.append("\\u").append(escaped) else builder.append(c) - } - builder.toString - } - def pad(s: String, minLength: Int, extra: Char) = - { - val diff = minLength - s.length - if (diff <= 0) s else List.fill(diff)(extra).mkString("", "", s) + def apply(s: String): String = { + val builder = new StringBuilder(s.length) + for (c <- s) { + val char = c.toInt + def escaped = pad(char.toHexString.toUpperCase, 4, '0') + if (c < 20) builder.append("\\u").append(escaped) else builder.append(c) } + builder.toString + } + + def pad(s: String, minLength: Int, extra: Char) = { + val diff = minLength - s.length + if (diff <= 0) s else List.fill(diff)(extra).mkString("", "", s) + } + /** Replaces a \n character at the end of a string `s` with `nl`.*/ def newline(s: String, nl: String): String = if (s.endsWith("\n")) s.substring(0, s.length - 1) + nl else s + } + /** Records logging events for later retrieval.*/ final class RecordingLogger extends BasicLogger { private var events: List[LogEvent] = Nil @@ -147,6 +158,7 @@ final class RecordingLogger extends BasicLogger { def log(level: Level.Value, message: => String): Unit = { events ::= new Log(level, message) } def success(message: => String): Unit = { events ::= new Success(message) } def logAll(es: Seq[LogEvent]): Unit = { events :::= es.toList } - def control(event: ControlEvent.Value, message: => String): Unit = { events ::= new ControlEvent(event, message) } + def control(event: ControlEvent.Value, message: => String): Unit = + events ::= new ControlEvent(event, message) } diff --git a/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala b/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala index 80e42a64d..a0cf1e569 100644 --- a/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala +++ b/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala @@ -24,7 +24,8 @@ class ManagedLoggerSpec extends FlatSpec with Matchers { import sjsonnew.BasicJsonProtocol._ val log = LogExchange.logger("foo") LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info)) - implicit val intShow: ShowLines[Int] = ShowLines({ (x: Int) => Vector(s"String representation of $x") }) + implicit val intShow: ShowLines[Int] = + ShowLines((x: Int) => Vector(s"String representation of $x")) log.registerStringCodec[Int] log.infoEvent(1) } @@ -33,7 +34,8 @@ class ManagedLoggerSpec extends FlatSpec with Matchers { import sjsonnew.BasicJsonProtocol._ val log = LogExchange.logger("foo") LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info)) - implicit val intArrayShow: ShowLines[Array[Int]] = ShowLines({ (x: Array[Int]) => Vector(s"String representation of ${x.mkString}") }) + implicit val intArrayShow: ShowLines[Array[Int]] = + ShowLines((x: Array[Int]) => Vector(s"String representation of ${x.mkString}")) log.registerStringCodec[Array[Int]] log.infoEvent(Array(1, 2, 3)) } @@ -42,7 +44,8 @@ class ManagedLoggerSpec extends FlatSpec with Matchers { import sjsonnew.BasicJsonProtocol._ val log = LogExchange.logger("foo") LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info)) - implicit val intVectorShow: ShowLines[Vector[Vector[Int]]] = ShowLines({ (xss: Vector[Vector[Int]]) => Vector(s"String representation of $xss") }) + implicit val intVectorShow: ShowLines[Vector[Vector[Int]]] = + ShowLines((xss: Vector[Vector[Int]]) => Vector(s"String representation of $xss")) log.registerStringCodec[Vector[Vector[Int]]] log.infoEvent(Vector(Vector(1, 2, 3))) } @@ -51,7 +54,9 @@ class ManagedLoggerSpec extends FlatSpec with Matchers { // this is passed into State normally val global0 = initialGlobalLogging val full = global0.full - (1 to 3).toList foreach { x => full.info(s"test$x") } + (1 to 3).toList foreach { x => + full.info(s"test$x") + } } // This is done in Mainloop.scala @@ -62,7 +67,7 @@ class ManagedLoggerSpec extends FlatSpec with Matchers { val out = new PrintWriter(writer) val g = global0.newAppender(global0.full, out, logBacking0) val full = g.full - (1 to 3).toList foreach { x => full.info(s"newAppender $x") } + (1 to 3).toList foreach (x => full.info(s"newAppender $x")) assert(logBacking0.file.exists) g } @@ -71,7 +76,7 @@ class ManagedLoggerSpec extends FlatSpec with Matchers { val out = new PrintWriter(writer) val g = global1.newAppender(global1.full, out, logBacking1) val full = g.full - (1 to 3).toList foreach { x => full.info(s"newAppender $x") } + (1 to 3).toList foreach (x => full.info(s"newAppender $x")) // println(logBacking.file) // print("Press enter to continue. ") // System.console.readLine @@ -81,6 +86,8 @@ class ManagedLoggerSpec extends FlatSpec with Matchers { val console = ConsoleOut.systemOut def initialGlobalLogging: GlobalLogging = GlobalLogging.initial( - MainAppender.globalDefault(console), File.createTempFile("sbt", ".log"), console + MainAppender.globalDefault(console), + File.createTempFile("sbt", ".log"), + console ) } diff --git a/internal/util-logging/src/test/scala/TestLogger.scala b/internal/util-logging/src/test/scala/TestLogger.scala index b9ddda148..a7554f3a5 100644 --- a/internal/util-logging/src/test/scala/TestLogger.scala +++ b/internal/util-logging/src/test/scala/TestLogger.scala @@ -3,10 +3,9 @@ package sbt.internal.util import sbt.util._ object TestLogger { - def apply[T](f: Logger => T): T = - { - val log = new BufferedLogger(ConsoleLogger()) - log.setLevel(Level.Debug) - log.bufferQuietly(f(log)) - } -} \ No newline at end of file + def apply[T](f: Logger => T): T = { + val log = new BufferedLogger(ConsoleLogger()) + log.setLevel(Level.Debug) + log.bufferQuietly(f(log)) + } +} diff --git a/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala b/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala index 788f39362..d107b3bc0 100644 --- a/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala +++ b/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala @@ -6,6 +6,7 @@ package sbt.internal.util import Relation._ object Relation { + /** Constructs a new immutable, finite relation that is initially empty. */ def empty[A, B]: Relation[A, B] = make(Map.empty, Map.empty) @@ -13,17 +14,18 @@ object Relation { * Constructs a [[Relation]] from underlying `forward` and `reverse` representations, without checking that they are consistent. * This is a low-level constructor and the alternatives [[empty]] and [[reconstruct]] should be preferred. */ - def make[A, B](forward: Map[A, Set[B]], reverse: Map[B, Set[A]]): Relation[A, B] = new MRelation(forward, reverse) + def make[A, B](forward: Map[A, Set[B]], reverse: Map[B, Set[A]]): Relation[A, B] = + new MRelation(forward, reverse) /** Constructs a relation such that for every entry `_1 -> _2s` in `forward` and every `_2` in `_2s`, `(_1, _2)` is in the relation. */ - def reconstruct[A, B](forward: Map[A, Set[B]]): Relation[A, B] = - { - val reversePairs = for ((a, bs) <- forward.view; b <- bs.view) yield (b, a) - val reverse = (Map.empty[B, Set[A]] /: reversePairs) { case (m, (b, a)) => add(m, b, a :: Nil) } - make(forward filter { case (a, bs) => bs.nonEmpty }, reverse) - } + def reconstruct[A, B](forward: Map[A, Set[B]]): Relation[A, B] = { + val reversePairs = for ((a, bs) <- forward.view; b <- bs.view) yield (b, a) + val reverse = (Map.empty[B, Set[A]] /: reversePairs) { case (m, (b, a)) => add(m, b, a :: Nil) } + make(forward filter { case (a, bs) => bs.nonEmpty }, reverse) + } - def merge[A, B](rels: Traversable[Relation[A, B]]): Relation[A, B] = (Relation.empty[A, B] /: rels)(_ ++ _) + def merge[A, B](rels: Traversable[Relation[A, B]]): Relation[A, B] = + (Relation.empty[A, B] /: rels)(_ ++ _) private[sbt] def remove[X, Y](map: M[X, Y], from: X, to: Y): M[X, Y] = map.get(from) match { @@ -34,46 +36,61 @@ object Relation { } private[sbt] def combine[X, Y](a: M[X, Y], b: M[X, Y]): M[X, Y] = - (a /: b) { (map, mapping) => add(map, mapping._1, mapping._2) } + (a /: b)((map, mapping) => add(map, mapping._1, mapping._2)) private[sbt] def add[X, Y](map: M[X, Y], from: X, to: Traversable[Y]): M[X, Y] = map.updated(from, get(map, from) ++ to) private[sbt] def get[X, Y](map: M[X, Y], t: X): Set[Y] = map.getOrElse(t, Set.empty[Y]) - private[sbt]type M[X, Y] = Map[X, Set[Y]] + private[sbt] type M[X, Y] = Map[X, Set[Y]] } /** Binary relation between A and B. It is a set of pairs (_1, _2) for _1 in A, _2 in B. */ trait Relation[A, B] { + /** Returns the set of all `_2`s such that `(_1, _2)` is in this relation. */ def forward(_1: A): Set[B] + /** Returns the set of all `_1`s such that `(_1, _2)` is in this relation. */ def reverse(_2: B): Set[A] + /** Includes `pair` in the relation. */ def +(pair: (A, B)): Relation[A, B] + /** Includes `(a, b)` in the relation. */ def +(a: A, b: B): Relation[A, B] + /** Includes in the relation `(a, b)` for all `b` in `bs`. */ def +(a: A, bs: Traversable[B]): Relation[A, B] + /** Returns the union of the relation `r` with this relation. */ def ++(r: Relation[A, B]): Relation[A, B] + /** Includes the given pairs in this relation. */ def ++(rs: Traversable[(A, B)]): Relation[A, B] + /** Removes all elements `(_1, _2)` for all `_1` in `_1s` from this relation. */ def --(_1s: Traversable[A]): Relation[A, B] + /** Removes all `pairs` from this relation. */ def --(pairs: TraversableOnce[(A, B)]): Relation[A, B] + /** Removes all `relations` from this relation. */ def --(relations: Relation[A, B]): Relation[A, B] + /** Removes all pairs `(_1, _2)` from this relation. */ def -(_1: A): Relation[A, B] + /** Removes `pair` from this relation. */ def -(pair: (A, B)): Relation[A, B] + /** Returns the set of all `_1`s such that `(_1, _2)` is in this relation. */ def _1s: collection.Set[A] + /** Returns the set of all `_2`s such that `(_1, _2)` is in this relation. */ def _2s: collection.Set[B] + /** Returns the number of pairs in this relation */ def size: Int @@ -110,10 +127,12 @@ trait Relation[A, B] { * The value associated with a given `_2` is the set of all `_1`s such that `(_1, _2)` is in this relation. */ def reverseMap: Map[B, Set[A]] + } // Note that we assume without checking that fwd and rev are consistent. -private final class MRelation[A, B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) extends Relation[A, B] { +private final class MRelation[A, B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) + extends Relation[A, B] { def forwardMap = fwd def reverseMap = rev @@ -125,25 +144,30 @@ private final class MRelation[A, B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) ex def size = (fwd.valuesIterator map (_.size)).sum - def all: Traversable[(A, B)] = fwd.iterator.flatMap { case (a, bs) => bs.iterator.map(b => (a, b)) }.toTraversable + def all: Traversable[(A, B)] = + fwd.iterator.flatMap { case (a, bs) => bs.iterator.map(b => (a, b)) }.toTraversable def +(pair: (A, B)) = this + (pair._1, Set(pair._2)) def +(from: A, to: B) = this + (from, to :: Nil) - def +(from: A, to: Traversable[B]) = if (to.isEmpty) this else - new MRelation(add(fwd, from, to), (rev /: to) { (map, t) => add(map, t, from :: Nil) }) + def +(from: A, to: Traversable[B]) = + if (to.isEmpty) this + else new MRelation(add(fwd, from, to), (rev /: to)((map, t) => add(map, t, from :: Nil))) def ++(rs: Traversable[(A, B)]) = ((this: Relation[A, B]) /: rs) { _ + _ } - def ++(other: Relation[A, B]) = new MRelation[A, B](combine(fwd, other.forwardMap), combine(rev, other.reverseMap)) + def ++(other: Relation[A, B]) = + new MRelation[A, B](combine(fwd, other.forwardMap), combine(rev, other.reverseMap)) def --(ts: Traversable[A]): Relation[A, B] = ((this: Relation[A, B]) /: ts) { _ - _ } - def --(pairs: TraversableOnce[(A, B)]): Relation[A, B] = ((this: Relation[A, B]) /: pairs) { _ - _ } + def --(pairs: TraversableOnce[(A, B)]): Relation[A, B] = ((this: Relation[A, B]) /: pairs)(_ - _) def --(relations: Relation[A, B]): Relation[A, B] = --(relations.all) + def -(pair: (A, B)): Relation[A, B] = new MRelation(remove(fwd, pair._1, pair._2), remove(rev, pair._2, pair._1)) + def -(t: A): Relation[A, B] = fwd.get(t) match { case Some(rs) => - val upRev = (rev /: rs) { (map, r) => remove(map, r, t) } + val upRev = (rev /: rs)((map, r) => remove(map, r, t)) new MRelation(fwd - t, upRev) case None => this } @@ -155,18 +179,21 @@ private final class MRelation[A, B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) ex (Relation.empty[A, B] ++ y, Relation.empty[A, B] ++ n) } - def groupBy[K](discriminator: ((A, B)) => K): Map[K, Relation[A, B]] = all.groupBy(discriminator) mapValues { Relation.empty[A, B] ++ _ } + def groupBy[K](discriminator: ((A, B)) => K): Map[K, Relation[A, B]] = + all.groupBy(discriminator) mapValues { Relation.empty[A, B] ++ _ } def contains(a: A, b: B): Boolean = forward(a)(b) override def equals(other: Any) = other match { // We assume that the forward and reverse maps are consistent, so we only use the forward map // for equality. Note that key -> Empty is semantically the same as key not existing. - case o: MRelation[A, B] => forwardMap.filterNot(_._2.isEmpty) == o.forwardMap.filterNot(_._2.isEmpty) - case _ => false + case o: MRelation[A, B] => + forwardMap.filterNot(_._2.isEmpty) == o.forwardMap.filterNot(_._2.isEmpty) + case _ => false } override def hashCode = fwd.filterNot(_._2.isEmpty).hashCode() - override def toString = all.map { case (a, b) => a + " -> " + b }.mkString("Relation [", ", ", "]") + override def toString = + all.map { case (a, b) => a + " -> " + b }.mkString("Relation [", ", ", "]") } diff --git a/internal/util-relation/src/test/scala/RelationTest.scala b/internal/util-relation/src/test/scala/RelationTest.scala index 31f68e0c3..47aacacdd 100644 --- a/internal/util-relation/src/test/scala/RelationTest.scala +++ b/internal/util-relation/src/test/scala/RelationTest.scala @@ -11,21 +11,20 @@ object RelationTest extends Properties("Relation") { val r = Relation.empty[Int, Double] ++ pairs check(r, pairs) } - def check(r: Relation[Int, Double], pairs: Seq[(Int, Double)]) = - { - val _1s = pairs.map(_._1).toSet - val _2s = pairs.map(_._2).toSet + def check(r: Relation[Int, Double], pairs: Seq[(Int, Double)]) = { + val _1s = pairs.map(_._1).toSet + val _2s = pairs.map(_._2).toSet - r._1s == _1s && r.forwardMap.keySet == _1s && - r._2s == _2s && r.reverseMap.keySet == _2s && - pairs.forall { - case (a, b) => - (r.forward(a) contains b) && - (r.reverse(b) contains a) && - (r.forwardMap(a) contains b) && - (r.reverseMap(b) contains a) - } + r._1s == _1s && r.forwardMap.keySet == _1s && + r._2s == _2s && r.reverseMap.keySet == _2s && + pairs.forall { + case (a, b) => + (r.forward(a) contains b) && + (r.reverse(b) contains a) && + (r.forwardMap(a) contains b) && + (r.reverseMap(b) contains a) } + } property("Does not contain removed entries") = forAll { (pairs: List[(Int, Double, Boolean)]) => val add = pairs.map { case (a, b, c) => (a, b) } @@ -39,17 +38,17 @@ object RelationTest extends Properties("Relation") { all(removeCoarse) { rem => ("_1s does not contain removed" |: (!r._1s.contains(rem))) && - ("Forward does not contain removed" |: r.forward(rem).isEmpty) && - ("Forward map does not contain removed" |: !r.forwardMap.contains(rem)) && - ("Removed is not a value in reverse map" |: !r.reverseMap.values.toSet.contains(rem)) + ("Forward does not contain removed" |: r.forward(rem).isEmpty) && + ("Forward map does not contain removed" |: !r.forwardMap.contains(rem)) && + ("Removed is not a value in reverse map" |: !r.reverseMap.values.toSet.contains(rem)) } && - all(removeFine) { - case (a, b) => - ("Forward does not contain removed" |: (!r.forward(a).contains(b))) && - ("Reverse does not contain removed" |: (!r.reverse(b).contains(a))) && - ("Forward map does not contain removed" |: (notIn(r.forwardMap, a, b))) && - ("Reverse map does not contain removed" |: (notIn(r.reverseMap, b, a))) - } + all(removeFine) { + case (a, b) => + ("Forward does not contain removed" |: (!r.forward(a).contains(b))) && + ("Reverse does not contain removed" |: (!r.reverse(b).contains(a))) && + ("Forward map does not contain removed" |: (notIn(r.forwardMap, a, b))) && + ("Reverse map does not contain removed" |: (notIn(r.reverseMap, b, a))) + } } property("Groups correctly") = forAll { (entries: List[(Int, Double)], randomInt: Int) => @@ -75,10 +74,10 @@ object RelationTest extends Properties("Relation") { object EmptyRelationTest extends Properties("Empty relation") { lazy val e = Relation.empty[Int, Double] - property("Forward empty") = forAll { (i: Int) => e.forward(i).isEmpty } - property("Reverse empty") = forAll { (i: Double) => e.reverse(i).isEmpty } + property("Forward empty") = forAll((i: Int) => e.forward(i).isEmpty) + property("Reverse empty") = forAll((i: Double) => e.reverse(i).isEmpty) property("Forward map empty") = e.forwardMap.isEmpty property("Reverse map empty") = e.reverseMap.isEmpty property("_1 empty") = e._1s.isEmpty property("_2 empty") = e._2s.isEmpty -} \ No newline at end of file +} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/CommentHandler.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/CommentHandler.scala index 370ae0005..373ae1334 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/CommentHandler.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/CommentHandler.scala @@ -7,4 +7,4 @@ package scripted object CommentHandler extends BasicStatementHandler { def apply(command: String, args: List[String]) = () -} \ No newline at end of file +} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala index 65b5af423..3b5daaef7 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala @@ -58,18 +58,20 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { val lines1 = IO.readLines(fromString(file1)) val lines2 = IO.readLines(fromString(file2)) if (lines1 != lines2) - scriptError("File contents are different:\n" + lines1.mkString("\n") + "\nAnd:\n" + lines2.mkString("\n")) + scriptError( + "File contents are different:\n" + lines1.mkString("\n") + + "\nAnd:\n" + lines2.mkString("\n") + ) } - def newer(a: String, b: String): Unit = - { - val pathA = fromString(a) - val pathB = fromString(b) - val isNewer = pathA.exists && (!pathB.exists || pathA.lastModified > pathB.lastModified) - if (!isNewer) { - scriptError(s"$pathA is not newer than $pathB") - } + def newer(a: String, b: String): Unit = { + val pathA = fromString(a) + val pathB = fromString(b) + val isNewer = pathA.exists && (!pathB.exists || pathA.lastModified > pathB.lastModified) + if (!isNewer) { + scriptError(s"$pathA is not newer than $pathB") } + } def exists(paths: List[String]): Unit = { val notPresent = fromStrings(paths).filter(!_.exists) if (notPresent.nonEmpty) @@ -127,9 +129,16 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { IO.copy(mapped.init pair map) () } + def wrongArguments(args: List[String]): Unit = - scriptError("Command '" + commandName + "' does not accept arguments (found '" + spaced(args) + "').") + scriptError( + "Command '" + commandName + "' does not accept arguments (found '" + spaced(args) + "')." + ) + def wrongArguments(requiredArgs: String, args: List[String]): Unit = - scriptError("Wrong number of arguments to " + commandName + " command. " + requiredArgs + " required, found: '" + spaced(args) + "'.") + scriptError( + "Wrong number of arguments to " + commandName + " command. " + + requiredArgs + " required, found: '" + spaced(args) + "'." + ) } -} \ No newline at end of file +} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FilteredLoader.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FilteredLoader.scala index cb2c3100d..6eccab312 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FilteredLoader.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FilteredLoader.scala @@ -7,13 +7,12 @@ package scripted final class FilteredLoader(parent: ClassLoader) extends ClassLoader(parent) { @throws(classOf[ClassNotFoundException]) - override final def loadClass(className: String, resolve: Boolean): Class[_] = - { - if (className.startsWith("java.") || className.startsWith("javax.")) - super.loadClass(className, resolve) - else - throw new ClassNotFoundException(className) - } + override final def loadClass(className: String, resolve: Boolean): Class[_] = { + if (className.startsWith("java.") || className.startsWith("javax.")) + super.loadClass(className, resolve) + else + throw new ClassNotFoundException(className) + } override def getResources(name: String) = null override def getResource(name: String) = null -} \ No newline at end of file +} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/HandlersProvider.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/HandlersProvider.scala index 3dcb4ef6d..a0d6a3636 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/HandlersProvider.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/HandlersProvider.scala @@ -2,4 +2,4 @@ package sbt.internal.scripted trait HandlersProvider { def getHandlers(config: ScriptConfig): Map[Char, StatementHandler] -} \ No newline at end of file +} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptRunner.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptRunner.scala index f43b54f39..a15458dc4 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptRunner.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptRunner.scala @@ -6,7 +6,7 @@ package internal package scripted final class TestException(statement: Statement, msg: String, exception: Throwable) - extends RuntimeException(statement.linePrefix + " " + msg, exception) + extends RuntimeException(statement.linePrefix + " " + msg, exception) class ScriptRunner { import scala.collection.mutable.HashMap @@ -15,14 +15,16 @@ class ScriptRunner { def processStatement(handler: StatementHandler, statement: Statement): 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) { err match { - case t: TestFailed => throw new TestException(statement, "Command failed: " + t.getMessage, null) - case _ => throw new TestException(statement, "Command failed", err) + case t: TestFailed => + throw new TestException(statement, "Command failed: " + t.getMessage, null) + case _ => throw new TestException(statement, "Command failed", err) } } else () @@ -36,13 +38,12 @@ class ScriptRunner { val handlers = Set() ++ statements.map(_._1) try { - handlers.foreach { handler => states(handler) = handler.initialState } + handlers.foreach(handler => states(handler) = handler.initialState) statements foreach (Function.tupled(processStatement)) } finally { for (handler <- handlers; state <- states.get(handler)) { - try { handler.finish(state.asInstanceOf[handler.State]) } - catch { case e: Exception => () } + try { handler.finish(state.asInstanceOf[handler.State]) } catch { case e: Exception => () } } } } -} \ No newline at end of file +} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala index fb1ba9eca..e88d4bb16 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala @@ -12,7 +12,12 @@ import sbt.internal.io.Resources import java.util.concurrent.atomic.AtomicInteger object ScriptedRunnerImpl { - def run(resourceBaseDirectory: File, bufferLog: Boolean, tests: Array[String], handlersProvider: HandlersProvider): Unit = { + def run( + resourceBaseDirectory: File, + bufferLog: Boolean, + tests: Array[String], + handlersProvider: HandlersProvider + ): Unit = { val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, handlersProvider) val logger = newLogger val allTests = get(tests, resourceBaseDirectory, logger) flatMap { @@ -36,15 +41,18 @@ object ScriptedRunnerImpl { ScriptedTest(group, name) } private[sbt] val generateId: AtomicInteger = new AtomicInteger - private[sbt] def newLogger: ManagedLogger = - { - val loggerName = "scripted-" + generateId.incrementAndGet - val x = LogExchange.logger(loggerName) - x - } + private[sbt] def newLogger: ManagedLogger = { + val loggerName = "scripted-" + generateId.incrementAndGet + val x = LogExchange.logger(loggerName) + x + } } -final class ScriptedTests(resourceBaseDirectory: File, bufferLog: Boolean, handlersProvider: HandlersProvider) { +final class ScriptedTests( + resourceBaseDirectory: File, + bufferLog: Boolean, + handlersProvider: HandlersProvider +) { private val testResources = new Resources(resourceBaseDirectory) private val consoleAppender: ConsoleAppender = ConsoleAppender() @@ -53,85 +61,96 @@ final class ScriptedTests(resourceBaseDirectory: File, bufferLog: Boolean, handl def scriptedTest(group: String, name: String, log: xsbti.Logger): Seq[() => Option[String]] = scriptedTest(group, name, Logger.xlog2Log(log)) + def scriptedTest(group: String, name: String, log: ManagedLogger): Seq[() => Option[String]] = - scriptedTest(group, name, { _ => () }, log) - def scriptedTest(group: String, name: String, prescripted: File => Unit, log: ManagedLogger): Seq[() => Option[String]] = { + scriptedTest(group, name, (_ => ()), log) + + def scriptedTest( + group: String, + name: String, + prescripted: File => Unit, + log: ManagedLogger + ): Seq[() => Option[String]] = { for (groupDir <- (resourceBaseDirectory * group).get; nme <- (groupDir * name).get) yield { val g = groupDir.getName val n = nme.getName val str = s"$g / $n" - () => { - println("Running " + str) - testResources.readWriteResourceDirectory(g, n) { testDirectory => - val disabled = new File(testDirectory, "disabled").isFile - if (disabled) { - log.info("D " + str + " [DISABLED]") - None - } else { - try { scriptedTest(str, testDirectory, prescripted, log); None } - catch { case _: TestException | _: PendingTestSuccessException => Some(str) } + () => + { + println("Running " + str) + testResources.readWriteResourceDirectory(g, n) { testDirectory => + val disabled = new File(testDirectory, "disabled").isFile + if (disabled) { + log.info("D " + str + " [DISABLED]") + None + } else { + try { scriptedTest(str, testDirectory, prescripted, log); None } catch { + case _: TestException | _: PendingTestSuccessException => Some(str) + } + } } } - } } } - private def scriptedTest(label: String, testDirectory: File, prescripted: File => Unit, log: ManagedLogger): Unit = - { - val buffered = BufferedAppender(consoleAppender) - LogExchange.unbindLoggerAppenders(log.name) - LogExchange.bindLoggerAppenders(log.name, (buffered -> Level.Debug) :: Nil) - if (bufferLog) { - buffered.record() - } - def createParser() = - { - // val fileHandler = new FileCommands(testDirectory) - // // val sbtHandler = new SbtHandler(testDirectory, launcher, buffered, launchOpts) - // new TestScriptParser(Map('$' -> fileHandler, /* '>' -> sbtHandler, */ '#' -> CommentHandler)) - val scriptConfig = new ScriptConfig(label, testDirectory, log) - new TestScriptParser(handlersProvider getHandlers scriptConfig) - } - val (file, pending) = { - val normal = new File(testDirectory, ScriptFilename) - val pending = new File(testDirectory, PendingScriptFilename) - if (pending.isFile) (pending, true) else (normal, false) - } - val pendingString = if (pending) " [PENDING]" else "" - - def runTest() = - { - val run = new ScriptRunner - val parser = createParser() - run(parser.parse(file)) - } - def testFailed(): Unit = { - if (pending) buffered.clearBuffer() else buffered.stopBuffer() - log.error("x " + label + pendingString) - } - - try { - prescripted(testDirectory) - runTest() - log.info("+ " + label + pendingString) - if (pending) throw new PendingTestSuccessException(label) - } catch { - case e: TestException => - testFailed() - e.getCause match { - case null | _: java.net.SocketException => log.error(" " + e.getMessage) - case _ => if (!pending) e.printStackTrace - } - if (!pending) throw e - case e: PendingTestSuccessException => - testFailed() - log.error(" Mark as passing to remove this failure.") - throw e - case e: Exception => - testFailed() - if (!pending) throw e - } finally { buffered.clearBuffer() } + private def scriptedTest( + label: String, + testDirectory: File, + prescripted: File => Unit, + log: ManagedLogger + ): Unit = { + val buffered = BufferedAppender(consoleAppender) + LogExchange.unbindLoggerAppenders(log.name) + LogExchange.bindLoggerAppenders(log.name, (buffered -> Level.Debug) :: Nil) + if (bufferLog) { + buffered.record() } + def createParser() = { + // val fileHandler = new FileCommands(testDirectory) + // // val sbtHandler = new SbtHandler(testDirectory, launcher, buffered, launchOpts) + // new TestScriptParser(Map('$' -> fileHandler, /* '>' -> sbtHandler, */ '#' -> CommentHandler)) + val scriptConfig = new ScriptConfig(label, testDirectory, log) + new TestScriptParser(handlersProvider getHandlers scriptConfig) + } + val (file, pending) = { + val normal = new File(testDirectory, ScriptFilename) + val pending = new File(testDirectory, PendingScriptFilename) + if (pending.isFile) (pending, true) else (normal, false) + } + val pendingString = if (pending) " [PENDING]" else "" + + def runTest() = { + val run = new ScriptRunner + val parser = createParser() + run(parser.parse(file)) + } + def testFailed(): Unit = { + if (pending) buffered.clearBuffer() else buffered.stopBuffer() + log.error("x " + label + pendingString) + } + + try { + prescripted(testDirectory) + runTest() + log.info("+ " + label + pendingString) + if (pending) throw new PendingTestSuccessException(label) + } catch { + case e: TestException => + testFailed() + e.getCause match { + case null | _: java.net.SocketException => log.error(" " + e.getMessage) + case _ => if (!pending) e.printStackTrace + } + if (!pending) throw e + case e: PendingTestSuccessException => + testFailed() + log.error(" Mark as passing to remove this failure.") + throw e + case e: Exception => + testFailed() + if (!pending) throw e + } finally { buffered.clearBuffer() } + } } // object ScriptedTests extends ScriptedRunner { @@ -148,31 +167,30 @@ object ListTests { import ListTests._ final class ListTests(baseDirectory: File, accept: ScriptedTest => Boolean, log: Logger) { def filter = DirectoryFilter -- HiddenFileFilter - def listTests: Seq[ScriptedTest] = - { - list(baseDirectory, filter) flatMap { group => - val groupName = group.getName - listTests(group).map(ScriptedTest(groupName, _)) - } - } - private[this] def listTests(group: File): Seq[String] = - { + def listTests: Seq[ScriptedTest] = { + list(baseDirectory, filter) flatMap { group => val groupName = group.getName - val allTests = list(group, filter).sortBy(_.getName) - if (allTests.isEmpty) { - log.warn("No tests in test group " + groupName) - Seq.empty - } else { - val (included, skipped) = allTests.toList.partition(test => accept(ScriptedTest(groupName, test.getName))) - if (included.isEmpty) - log.warn("Test group " + groupName + " skipped.") - else if (skipped.nonEmpty) { - log.warn("Tests skipped in group " + group.getName + ":") - skipped.foreach(testName => log.warn(" " + testName.getName)) - } - Seq(included.map(_.getName): _*) - } + listTests(group).map(ScriptedTest(groupName, _)) } + } + private[this] def listTests(group: File): Seq[String] = { + val groupName = group.getName + val allTests = list(group, filter).sortBy(_.getName) + if (allTests.isEmpty) { + log.warn("No tests in test group " + groupName) + Seq.empty + } else { + val (included, skipped) = + allTests.toList.partition(test => accept(ScriptedTest(groupName, test.getName))) + if (included.isEmpty) + log.warn("Test group " + groupName + " skipped.") + else if (skipped.nonEmpty) { + log.warn("Tests skipped in group " + group.getName + ":") + skipped.foreach(testName => log.warn(" " + testName.getName)) + } + Seq(included.map(_.getName): _*) + } + } } class PendingTestSuccessException(label: String) extends Exception { diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/StatementHandler.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/StatementHandler.scala index 15b7189ce..ac752a2cc 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/StatementHandler.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/StatementHandler.scala @@ -15,7 +15,10 @@ trait StatementHandler { trait BasicStatementHandler extends StatementHandler { final type State = Unit final def initialState = () - final def apply(command: String, arguments: List[String], state: Unit): Unit = apply(command, arguments) + + final def apply(command: String, arguments: List[String], state: Unit): Unit = + apply(command, arguments) + def apply(command: String, arguments: List[String]): Unit def finish(state: Unit) = () } @@ -23,4 +26,4 @@ trait BasicStatementHandler extends StatementHandler { /** Use when a stack trace is not useful */ final class TestFailed(msg: String) extends RuntimeException(msg) { override def fillInStackTrace = this -} \ No newline at end of file +} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/TestScriptParser.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/TestScriptParser.scala index 74f96eb81..ca0826621 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/TestScriptParser.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/TestScriptParser.scala @@ -19,8 +19,13 @@ successChar ::= '+' | '-' word ::= [^ \[\]]+ comment ::= '#' \S* nl nl ::= '\r' \'n' | '\n' | '\r' | eof -*/ -final case class Statement(command: String, arguments: List[String], successExpected: Boolean, line: Int) { + */ +final case class Statement( + command: String, + arguments: List[String], + successExpected: Boolean, + line: Int +) { def linePrefix = "{line " + line + "} " } @@ -41,43 +46,50 @@ class TestScriptParser(handlers: Map[Char, StatementHandler]) extends RegexParse if (handlers.keys.exists(key => key == '+' || key == '-')) sys.error("Start characters cannot be '+' or '-'") - def parse(scriptFile: File): List[(StatementHandler, Statement)] = parse(read(scriptFile), Some(scriptFile.getAbsolutePath)) + def parse(scriptFile: File): List[(StatementHandler, Statement)] = + parse(read(scriptFile), Some(scriptFile.getAbsolutePath)) def parse(script: String): List[(StatementHandler, Statement)] = parse(script, None) - private def parse(script: String, label: Option[String]): List[(StatementHandler, Statement)] = - { - parseAll(statements, script) match { - case Success(result, next) => result - case err: NoSuccess => - { - val labelString = label.map("'" + _ + "' ").getOrElse("") - sys.error("Could not parse test script, " + labelString + err.toString) - } + private def parse(script: String, label: Option[String]): List[(StatementHandler, Statement)] = { + parseAll(statements, script) match { + case Success(result, next) => result + case err: NoSuccess => { + val labelString = label.map("'" + _ + "' ").getOrElse("") + sys.error("Could not parse test script, " + labelString + err.toString) } } + } lazy val statements = rep1(space ~> statement <~ newline) - def statement: Parser[(StatementHandler, Statement)] = - { - trait PositionalStatement extends Positional { - def tuple: (StatementHandler, Statement) - } - positioned { - val command = (word | err("expected command")) - val arguments = rep(space ~> (word | failure("expected argument"))) - (successParser ~ (space ~> startCharacterParser <~ space) ~! command ~! arguments) ^^ - { - case successExpected ~ start ~ command ~ arguments => - new PositionalStatement { - def tuple = (handlers(start), new Statement(command, arguments, successExpected, pos.line)) - } - } - } ^^ (_.tuple) + + def statement: Parser[(StatementHandler, Statement)] = { + trait PositionalStatement extends Positional { + def tuple: (StatementHandler, Statement) } + positioned { + val command = (word | err("expected command")) + val arguments = rep(space ~> (word | failure("expected argument"))) + (successParser ~ (space ~> startCharacterParser <~ space) ~! command ~! arguments) ^^ { + case successExpected ~ start ~ command ~ arguments => + new PositionalStatement { + def tuple = + (handlers(start), new Statement(command, arguments, successExpected, pos.line)) + } + } + } ^^ (_.tuple) + } + def successParser: Parser[Boolean] = ('+' ^^^ true) | ('-' ^^^ false) | success(true) def space: Parser[String] = """[ \t]*""".r - lazy val word: Parser[String] = ("\'" ~> "[^'\n\r]*".r <~ "\'") | ("\"" ~> "[^\"\n\r]*".r <~ "\"") | WordRegex - def startCharacterParser: Parser[Char] = elem("start character", handlers.contains _) | - ((newline | err("expected start character " + handlers.keys.mkString("(", "", ")"))) ~> failure("end of input")) + + lazy val word: Parser[String] = + ("\'" ~> "[^'\n\r]*".r <~ "\'") | ("\"" ~> "[^\"\n\r]*".r <~ "\"") | WordRegex + + def startCharacterParser: Parser[Char] = + elem("start character", handlers.contains _) | + ( + (newline | err("expected start character " + handlers.keys.mkString("(", "", ")"))) + ~> failure("end of input") + ) def newline = """\s*([\n\r]|$)""".r } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 73e3a8304..6cc640ddf 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -44,9 +44,15 @@ object Dependencies { val scalatest = "org.scalatest" %% "scalatest" % "3.0.1" val parserCombinator211 = "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4" - val sjsonnew = Def.setting { "com.eed3si9n" %% "sjson-new-core" % contrabandSjsonNewVersion.value } - val sjsonnewScalaJson = Def.setting { "com.eed3si9n" %% "sjson-new-scalajson" % contrabandSjsonNewVersion.value } - val sjsonnewMurmurhash = Def.setting { "com.eed3si9n" %% "sjson-new-murmurhash" % contrabandSjsonNewVersion.value } + val sjsonnew = Def.setting { + "com.eed3si9n" %% "sjson-new-core" % contrabandSjsonNewVersion.value + } + val sjsonnewScalaJson = Def.setting { + "com.eed3si9n" %% "sjson-new-scalajson" % contrabandSjsonNewVersion.value + } + val sjsonnewMurmurhash = Def.setting { + "com.eed3si9n" %% "sjson-new-murmurhash" % contrabandSjsonNewVersion.value + } def log4jVersion = "2.8.1" val log4jApi = "org.apache.logging.log4j" % "log4j-api" % log4jVersion diff --git a/project/plugins.sbt b/project/plugins.sbt index 493521644..aa8c8024a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,4 @@ addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.3") addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.0") -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.17") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.17") +addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.10") diff --git a/util-cache/src/main/scala/sbt/util/Cache.scala b/util-cache/src/main/scala/sbt/util/Cache.scala index 78d694b27..8e4a4a7de 100644 --- a/util-cache/src/main/scala/sbt/util/Cache.scala +++ b/util-cache/src/main/scala/sbt/util/Cache.scala @@ -21,6 +21,7 @@ case class Miss[O](update: O => Unit) extends CacheResult[O] * A simple cache with keys of type `I` and values of type `O` */ trait Cache[I, O] { + /** * Queries the cache backed with store `store` for key `key`. */ @@ -59,7 +60,7 @@ object Cache { val result = default(key) update(result) result - } + } def debug[I](label: String, cache: SingletonCache[I]): SingletonCache[I] = new SingletonCache[I] { diff --git a/util-cache/src/main/scala/sbt/util/CacheImplicits.scala b/util-cache/src/main/scala/sbt/util/CacheImplicits.scala index 74cd51f68..b54a6b68c 100644 --- a/util-cache/src/main/scala/sbt/util/CacheImplicits.scala +++ b/util-cache/src/main/scala/sbt/util/CacheImplicits.scala @@ -3,5 +3,4 @@ package sbt.util import sjsonnew.BasicJsonProtocol object CacheImplicits extends CacheImplicits -trait CacheImplicits extends BasicCacheImplicits - with BasicJsonProtocol +trait CacheImplicits extends BasicCacheImplicits with BasicJsonProtocol diff --git a/util-cache/src/main/scala/sbt/util/CacheStore.scala b/util-cache/src/main/scala/sbt/util/CacheStore.scala index e29999471..054e07b6e 100644 --- a/util-cache/src/main/scala/sbt/util/CacheStore.scala +++ b/util-cache/src/main/scala/sbt/util/CacheStore.scala @@ -9,12 +9,15 @@ import sjsonnew.shaded.scalajson.ast.unsafe.JValue /** A `CacheStore` is used by the caching infrastructure to persist cached information. */ abstract class CacheStore extends Input with Output { + /** Delete the persisted information. */ def delete(): Unit + } object CacheStore { - implicit lazy val jvalueIsoString: IsoString[JValue] = IsoString.iso(CompactPrinter.apply, Parser.parseUnsafe) + implicit lazy val jvalueIsoString: IsoString[JValue] = + IsoString.iso(CompactPrinter.apply, Parser.parseUnsafe) /** Returns file-based CacheStore using standard JSON converter. */ def apply(cacheFile: File): CacheStore = file(cacheFile) @@ -25,6 +28,7 @@ object CacheStore { /** Factory that can make new stores. */ abstract class CacheStoreFactory { + /** Create a new store. */ def make(identifier: String): CacheStore @@ -36,7 +40,8 @@ abstract class CacheStoreFactory { } object CacheStoreFactory { - implicit lazy val jvalueIsoString: IsoString[JValue] = IsoString.iso(CompactPrinter.apply, Parser.parseUnsafe) + implicit lazy val jvalueIsoString: IsoString[JValue] = + IsoString.iso(CompactPrinter.apply, Parser.parseUnsafe) /** Returns directory-based CacheStoreFactory using standard JSON converter. */ def apply(base: File): CacheStoreFactory = directory(base) @@ -46,29 +51,38 @@ object CacheStoreFactory { } /** A factory that creates new stores persisted in `base`. */ -class DirectoryStoreFactory[J: IsoString](base: File, converter: SupportConverter[J]) extends CacheStoreFactory { +class DirectoryStoreFactory[J: IsoString](base: File, converter: SupportConverter[J]) + extends CacheStoreFactory { IO.createDirectory(base) def make(identifier: String): CacheStore = new FileBasedStore(base / identifier, converter) - def sub(identifier: String): CacheStoreFactory = new DirectoryStoreFactory(base / identifier, converter) + def sub(identifier: String): CacheStoreFactory = + new DirectoryStoreFactory(base / identifier, converter) } /** A `CacheStore` that persists information in `file`. */ class FileBasedStore[J: IsoString](file: File, converter: SupportConverter[J]) extends CacheStore { IO.touch(file, setModified = false) - def read[T: JsonReader]() = Using.fileInputStream(file)(stream => new PlainInput(stream, converter).read()) + def read[T: JsonReader]() = + Using.fileInputStream(file)(stream => new PlainInput(stream, converter).read()) def write[T: JsonWriter](value: T) = - Using.fileOutputStream(append = false)(file)(stream => new PlainOutput(stream, converter).write(value)) + Using.fileOutputStream(append = false)(file) { stream => + new PlainOutput(stream, converter).write(value) + } def delete() = IO.delete(file) def close() = () } /** A store that reads from `inputStream` and writes to `outputStream`. */ -class StreamBasedStore[J: IsoString](inputStream: InputStream, outputStream: OutputStream, converter: SupportConverter[J]) extends CacheStore { +class StreamBasedStore[J: IsoString]( + inputStream: InputStream, + outputStream: OutputStream, + converter: SupportConverter[J] +) extends CacheStore { def read[T: JsonReader]() = new PlainInput(inputStream, converter).read() def write[T: JsonWriter](value: T) = new PlainOutput(outputStream, converter).write(value) def delete() = () diff --git a/util-cache/src/main/scala/sbt/util/FileInfo.scala b/util-cache/src/main/scala/sbt/util/FileInfo.scala index 9c675e652..6fcb3d619 100644 --- a/util-cache/src/main/scala/sbt/util/FileInfo.scala +++ b/util-cache/src/main/scala/sbt/util/FileInfo.scala @@ -32,7 +32,8 @@ object HashModifiedFileInfo { private final case class PlainFile(file: File, exists: Boolean) extends PlainFileInfo private final case class FileModified(file: File, lastModified: Long) extends ModifiedFileInfo private final case class FileHash(file: File, hash: List[Byte]) extends HashFileInfo -private final case class FileHashModified(file: File, hash: List[Byte], lastModified: Long) extends HashModifiedFileInfo +private final case class FileHashModified(file: File, hash: List[Byte], lastModified: Long) + extends HashModifiedFileInfo final case class FilesInfo[F <: FileInfo] private (files: Set[F]) object FilesInfo { @@ -52,7 +53,8 @@ object FileInfo { type F <: FileInfo implicit def format: JsonFormat[F] - implicit def formats: JsonFormat[FilesInfo[F]] = projectFormat(_.files, (fs: Set[F]) => FilesInfo(fs)) + implicit def formats: JsonFormat[FilesInfo[F]] = + projectFormat(_.files, (fs: Set[F]) => FilesInfo(fs)) def apply(file: File): F def apply(files: Set[File]): FilesInfo[F] = FilesInfo(files map apply) @@ -113,7 +115,9 @@ object FileInfo { implicit def apply(file: File): HashFileInfo = FileHash(file.getAbsoluteFile, computeHash(file)) - private def computeHash(file: File): List[Byte] = try Hash(file).toList catch { case NonFatal(_) => Nil } + private def computeHash(file: File): List[Byte] = + try Hash(file).toList + catch { case NonFatal(_) => Nil } } object lastModified extends Style { @@ -140,7 +144,8 @@ object FileInfo { } } - implicit def apply(file: File): ModifiedFileInfo = FileModified(file.getAbsoluteFile, file.lastModified) + implicit def apply(file: File): ModifiedFileInfo = + FileModified(file.getAbsoluteFile, file.lastModified) } object exists extends Style { diff --git a/util-cache/src/main/scala/sbt/util/Input.scala b/util-cache/src/main/scala/sbt/util/Input.scala index 646660e59..ee89c30b3 100644 --- a/util-cache/src/main/scala/sbt/util/Input.scala +++ b/util-cache/src/main/scala/sbt/util/Input.scala @@ -7,7 +7,9 @@ import sbt.io.{ IO, Using } trait Input extends Closeable { def read[T: JsonReader](): T - def read[T: JsonReader](default: => T): T = try read[T]() catch { case NonFatal(_) => default } + def read[T: JsonReader](default: => T): T = + try read[T]() + catch { case NonFatal(_) => default } } class PlainInput[J: IsoString](input: InputStream, converter: SupportConverter[J]) extends Input { diff --git a/util-cache/src/main/scala/sbt/util/Output.scala b/util-cache/src/main/scala/sbt/util/Output.scala index cf4b27f12..db18f71a8 100644 --- a/util-cache/src/main/scala/sbt/util/Output.scala +++ b/util-cache/src/main/scala/sbt/util/Output.scala @@ -8,7 +8,8 @@ trait Output extends Closeable { def write[T: JsonWriter](value: T): Unit } -class PlainOutput[J: IsoString](output: OutputStream, converter: SupportConverter[J]) extends Output { +class PlainOutput[J: IsoString](output: OutputStream, converter: SupportConverter[J]) + extends Output { val isoFormat: IsoString[J] = implicitly def write[T: JsonWriter](value: T) = { diff --git a/util-cache/src/main/scala/sbt/util/SeparatedCache.scala b/util-cache/src/main/scala/sbt/util/SeparatedCache.scala index 0dc8dfceb..34a25345f 100644 --- a/util-cache/src/main/scala/sbt/util/SeparatedCache.scala +++ b/util-cache/src/main/scala/sbt/util/SeparatedCache.scala @@ -14,11 +14,13 @@ import CacheImplicits._ * A cache that stores a single value. */ trait SingletonCache[A] { + /** Reads the cache from the backing `from`. */ def read(from: Input): A /** Writes `value` to the backing `to`. */ def write(to: Output, value: A): Unit + } object SingletonCache { diff --git a/util-cache/src/main/scala/sbt/util/StampedFormat.scala b/util-cache/src/main/scala/sbt/util/StampedFormat.scala index c78186d1c..dddcefb42 100644 --- a/util-cache/src/main/scala/sbt/util/StampedFormat.scala +++ b/util-cache/src/main/scala/sbt/util/StampedFormat.scala @@ -10,13 +10,17 @@ object StampedFormat extends BasicJsonProtocol { withStamp(stamp(format))(format) } - def withStamp[T, S](stamp: S)(format: JsonFormat[T])(implicit formatStamp: JsonFormat[S], equivStamp: Equiv[S]): JsonFormat[T] = + def withStamp[T, S](stamp: S)(format: JsonFormat[T])( + implicit formatStamp: JsonFormat[S], + equivStamp: Equiv[S] + ): JsonFormat[T] = new JsonFormat[T] { override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): T = jsOpt match { case Some(js) => val stampedLength = unbuilder.beginArray(js) - if (stampedLength != 2) sys.error(s"Expected JsArray of size 2, found JsArray of size $stampedLength.") + if (stampedLength != 2) + sys.error(s"Expected JsArray of size 2, found JsArray of size $stampedLength.") val readStamp = unbuilder.nextElement val readValue = unbuilder.nextElement val actualStamp = formatStamp.read(Some(readStamp), unbuilder) @@ -34,7 +38,10 @@ object StampedFormat extends BasicJsonProtocol { builder.endArray() } } - private def stamp[T](format: JsonFormat[T])(implicit mf: Manifest[JsonFormat[T]]): Int = typeHash(mf) + + private def stamp[T](format: JsonFormat[T])(implicit mf: Manifest[JsonFormat[T]]): Int = + typeHash(mf) + private def typeHash[T](implicit mf: Manifest[T]) = mf.toString.hashCode -} \ No newline at end of file +} diff --git a/util-cache/src/test/scala/CacheSpec.scala b/util-cache/src/test/scala/CacheSpec.scala index 00481d227..468c647cd 100644 --- a/util-cache/src/test/scala/CacheSpec.scala +++ b/util-cache/src/test/scala/CacheSpec.scala @@ -13,7 +13,8 @@ import sbt.internal.util.UnitSpec class CacheSpec extends UnitSpec { - implicit val isoString: IsoString[JValue] = IsoString.iso(CompactPrinter.apply, Parser.parseUnsafe) + implicit val isoString: IsoString[JValue] = + IsoString.iso(CompactPrinter.apply, Parser.parseUnsafe) "A cache" should "NOT throw an exception if read without being written previously" in { testCache[String, Int] { @@ -68,10 +69,12 @@ class CacheSpec extends UnitSpec { } } - private def testCache[K, V](f: (Cache[K, V], CacheStore) => Unit)(implicit cache: Cache[K, V]): Unit = + private def testCache[K, V](f: (Cache[K, V], CacheStore) => Unit)( + implicit cache: Cache[K, V] + ): Unit = IO.withTemporaryDirectory { tmp => val store = new FileBasedStore(tmp / "cache-store", Converter) f(cache, store) } -} \ No newline at end of file +} diff --git a/util-cache/src/test/scala/SingletonCacheSpec.scala b/util-cache/src/test/scala/SingletonCacheSpec.scala index 9bfab82f8..84e91b627 100644 --- a/util-cache/src/test/scala/SingletonCacheSpec.scala +++ b/util-cache/src/test/scala/SingletonCacheSpec.scala @@ -42,7 +42,8 @@ class SingletonCacheSpec extends UnitSpec { } } - implicit val isoString: IsoString[JValue] = IsoString.iso(CompactPrinter.apply, Parser.parseUnsafe) + implicit val isoString: IsoString[JValue] = + IsoString.iso(CompactPrinter.apply, Parser.parseUnsafe) "A singleton cache" should "throw an exception if read without being written previously" in { testCache[Int] { @@ -83,10 +84,12 @@ class SingletonCacheSpec extends UnitSpec { } } - private def testCache[T](f: (SingletonCache[T], CacheStore) => Unit)(implicit cache: SingletonCache[T]): Unit = + private def testCache[T](f: (SingletonCache[T], CacheStore) => Unit)( + implicit cache: SingletonCache[T] + ): Unit = IO.withTemporaryDirectory { tmp => val store = new FileBasedStore(tmp / "cache-store", Converter) f(cache, store) } -} \ No newline at end of file +} diff --git a/util-tracking/src/main/scala/sbt/util/ChangeReport.scala b/util-tracking/src/main/scala/sbt/util/ChangeReport.scala index af8154729..d971e4241 100644 --- a/util-tracking/src/main/scala/sbt/util/ChangeReport.scala +++ b/util-tracking/src/main/scala/sbt/util/ChangeReport.scala @@ -10,28 +10,36 @@ object ChangeReport { override def modified = files override def markAllModified = this } + def unmodified[T](files: Set[T]): ChangeReport[T] = new EmptyChangeReport[T] { override def checked = files override def unmodified = files } } + /** The result of comparing some current set of objects against a previous set of objects.*/ trait ChangeReport[T] { + /** The set of all of the objects in the current set.*/ def checked: Set[T] + /** All of the objects that are in the same state in the current and reference sets.*/ def unmodified: Set[T] + /** * All checked objects that are not in the same state as the reference. This includes objects that are in both * sets but have changed and files that are only in one set. */ def modified: Set[T] // all changes, including added + /** All objects that are only in the current set.*/ def added: Set[T] + /** All objects only in the previous set*/ def removed: Set[T] def +++(other: ChangeReport[T]): ChangeReport[T] = new CompoundChangeReport(this, other) + /** * Generate a new report with this report's unmodified set included in the new report's modified set. The new report's * unmodified set is empty. The new report's added, removed, and checked sets are the same as in this report. @@ -45,14 +53,16 @@ trait ChangeReport[T] { def removed = ChangeReport.this.removed override def markAllModified = this } - override def toString = - { - val labels = List("Checked", "Modified", "Unmodified", "Added", "Removed") - val sets = List(checked, modified, unmodified, added, removed) - val keyValues = labels.zip(sets).map { case (label, set) => label + ": " + set.mkString(", ") } - keyValues.mkString("Change report:\n\t", "\n\t", "") - } + + override def toString = { + val labels = List("Checked", "Modified", "Unmodified", "Added", "Removed") + val sets = List(checked, modified, unmodified, added, removed) + val keyValues = labels.zip(sets).map { case (label, set) => label + ": " + set.mkString(", ") } + keyValues.mkString("Change report:\n\t", "\n\t", "") + } + } + class EmptyChangeReport[T] extends ChangeReport[T] { def checked = Set.empty[T] def unmodified = Set.empty[T] @@ -61,7 +71,9 @@ class EmptyChangeReport[T] extends ChangeReport[T] { def removed = Set.empty[T] override def toString = "No changes" } -private class CompoundChangeReport[T](a: ChangeReport[T], b: ChangeReport[T]) extends ChangeReport[T] { + +private class CompoundChangeReport[T](a: ChangeReport[T], b: ChangeReport[T]) + extends ChangeReport[T] { lazy val checked = a.checked ++ b.checked lazy val unmodified = a.unmodified ++ b.unmodified lazy val modified = a.modified ++ b.modified diff --git a/util-tracking/src/main/scala/sbt/util/FileFunction.scala b/util-tracking/src/main/scala/sbt/util/FileFunction.scala index cdc1cc4e8..0aa58ca4c 100644 --- a/util-tracking/src/main/scala/sbt/util/FileFunction.scala +++ b/util-tracking/src/main/scala/sbt/util/FileFunction.scala @@ -43,7 +43,9 @@ object FileFunction { * @param inStyle The strategy by which to detect state change in the input files from the previous run * @param action The work function, which receives a list of input files and returns a list of output files */ - def cached(cacheBaseDirectory: File, inStyle: FileInfo.Style)(action: Set[File] => Set[File]): Set[File] => Set[File] = + def cached(cacheBaseDirectory: File, inStyle: FileInfo.Style)( + action: Set[File] => Set[File] + ): Set[File] => Set[File] = cached(cacheBaseDirectory, inStyle = inStyle, outStyle = defaultOutStyle)(action) /** @@ -64,8 +66,12 @@ object FileFunction { * @param outStyle The strategy by which to detect state change in the output files from the previous run * @param action The work function, which receives a list of input files and returns a list of output files */ - def cached(cacheBaseDirectory: File, inStyle: FileInfo.Style, outStyle: FileInfo.Style)(action: Set[File] => Set[File]): Set[File] => Set[File] = - cached(CacheStoreFactory(cacheBaseDirectory), inStyle, outStyle)((in, out) => action(in.checked)) + def cached(cacheBaseDirectory: File, inStyle: FileInfo.Style, outStyle: FileInfo.Style)( + action: Set[File] => Set[File] + ): Set[File] => Set[File] = + cached(CacheStoreFactory(cacheBaseDirectory), inStyle, outStyle)( + (in, out) => action(in.checked) + ) /** * Generic change-detection helper used to help build / artifact generation / @@ -103,7 +109,9 @@ object FileFunction { * @param inStyle The strategy by which to detect state change in the input files from the previous run * @param action The work function, which receives a list of input files and returns a list of output files */ - def cached(storeFactory: CacheStoreFactory, inStyle: FileInfo.Style)(action: UpdateFunction): Set[File] => Set[File] = + def cached(storeFactory: CacheStoreFactory, inStyle: FileInfo.Style)( + action: UpdateFunction + ): Set[File] => Set[File] = cached(storeFactory, inStyle = inStyle, outStyle = defaultOutStyle)(action) /** @@ -124,20 +132,21 @@ object FileFunction { * @param outStyle The strategy by which to detect state change in the output files from the previous run * @param action The work function, which receives a list of input files and returns a list of output files */ - def cached(storeFactory: CacheStoreFactory, inStyle: FileInfo.Style, outStyle: FileInfo.Style)(action: UpdateFunction): Set[File] => Set[File] = - { - lazy val inCache = Difference.inputs(storeFactory.make("in-cache"), inStyle) - lazy val outCache = Difference.outputs(storeFactory.make("out-cache"), outStyle) - inputs => - { - inCache(inputs) { inReport => - outCache { outReport => - if (inReport.modified.isEmpty && outReport.modified.isEmpty) - outReport.checked - else - action(inReport, outReport) - } + def cached(storeFactory: CacheStoreFactory, inStyle: FileInfo.Style, outStyle: FileInfo.Style)( + action: UpdateFunction + ): Set[File] => Set[File] = { + lazy val inCache = Difference.inputs(storeFactory.make("in-cache"), inStyle) + lazy val outCache = Difference.outputs(storeFactory.make("out-cache"), outStyle) + inputs => + { + inCache(inputs) { inReport => + outCache { outReport => + if (inReport.modified.isEmpty && outReport.modified.isEmpty) + outReport.checked + else + action(inReport, outReport) } } - } + } + } } diff --git a/util-tracking/src/main/scala/sbt/util/Tracked.scala b/util-tracking/src/main/scala/sbt/util/Tracked.scala index 2cca19325..cfc1d089f 100644 --- a/util-tracking/src/main/scala/sbt/util/Tracked.scala +++ b/util-tracking/src/main/scala/sbt/util/Tracked.scala @@ -13,6 +13,7 @@ import sjsonnew.JsonFormat import sjsonnew.support.murmurhash.Hasher object Tracked { + /** * Creates a tracker that provides the last time it was evaluated. * If the function throws an exception. @@ -42,21 +43,24 @@ object Tracked { * If 'useStartTime' is false, the recorded time is when the evaluated function completes. * In both cases, the timestamp is not updated if the function throws an exception. */ - def tstamp(cacheFile: File, useStartTime: Boolean): Timestamp = tstamp(CacheStore(cacheFile), useStartTime) + def tstamp(cacheFile: File, useStartTime: Boolean): Timestamp = + tstamp(CacheStore(cacheFile), useStartTime) /** Creates a tracker that provides the difference between a set of input files for successive invocations.*/ def diffInputs(store: CacheStore, style: FileInfo.Style): Difference = Difference.inputs(store, style) /** Creates a tracker that provides the difference between a set of input files for successive invocations.*/ - def diffInputs(cacheFile: File, style: FileInfo.Style): Difference = diffInputs(CacheStore(cacheFile), style) + def diffInputs(cacheFile: File, style: FileInfo.Style): Difference = + diffInputs(CacheStore(cacheFile), style) /** Creates a tracker that provides the difference between a set of output files for successive invocations.*/ def diffOutputs(store: CacheStore, style: FileInfo.Style): Difference = Difference.outputs(store, style) /** Creates a tracker that provides the difference between a set of output files for successive invocations.*/ - def diffOutputs(cacheFile: File, style: FileInfo.Style): Difference = diffOutputs(CacheStore(cacheFile), style) + def diffOutputs(cacheFile: File, style: FileInfo.Style): Difference = + diffOutputs(CacheStore(cacheFile), style) /** Creates a tracker that provides the output of the most recent invocation of the function */ def lastOutput[I, O: JsonFormat](store: CacheStore)(f: (I, Option[O]) => O): I => O = { in => @@ -84,7 +88,9 @@ object Tracked { * cachedDoc(inputs)(() => exists(outputDirectory.allPaths.get.toSet)) * }}} */ - def outputChanged[A1: JsonFormat, A2](store: CacheStore)(f: (Boolean, A1) => A2): (() => A1) => A2 = p => { + def outputChanged[A1: JsonFormat, A2](store: CacheStore)( + f: (Boolean, A1) => A2 + ): (() => A1) => A2 = p => { val cache: SingletonCache[Long] = { import CacheImplicits.LongJsonFormat implicitly @@ -131,7 +137,9 @@ object Tracked { * cachedDoc(inputs)(() => exists(outputDirectory.allPaths.get.toSet)) * }}} */ - def inputChanged[I: JsonFormat: SingletonCache, O](store: CacheStore)(f: (Boolean, I) => O): I => O = { in => + def inputChanged[I: JsonFormat: SingletonCache, O](store: CacheStore)( + f: (Boolean, I) => O + ): I => O = { in => val cache: SingletonCache[Long] = { import CacheImplicits.LongJsonFormat implicitly @@ -159,7 +167,9 @@ object Tracked { * cachedDoc(inputs)(() => exists(outputDirectory.allPaths.get.toSet)) * }}} */ - def inputChanged[I: JsonFormat: SingletonCache, O](cacheFile: File)(f: (Boolean, I) => O): I => O = + def inputChanged[I: JsonFormat: SingletonCache, O](cacheFile: File)( + f: (Boolean, I) => O + ): I => O = inputChanged(CacheStore(cacheFile))(f) private final class CacheHelp[I: JsonFormat](val sc: SingletonCache[Long]) { @@ -186,23 +196,29 @@ object Tracked { } trait Tracked { + /** Cleans outputs and clears the cache.*/ def clean(): Unit + } -class Timestamp(val store: CacheStore, useStartTime: Boolean)(implicit format: JsonFormat[Long]) extends Tracked { + +class Timestamp(val store: CacheStore, useStartTime: Boolean)(implicit format: JsonFormat[Long]) + extends Tracked { def clean() = store.delete() + /** * Reads the previous timestamp, evaluates the provided function, * and then updates the timestamp if the function completes normally. */ - def apply[T](f: Long => T): T = - { - val start = now() - val result = f(readTimestamp) - store.write(if (useStartTime) start else now()) - result - } + def apply[T](f: Long => T): T = { + val start = now() + val result = f(readTimestamp) + store.write(if (useStartTime) start else now()) + result + } + private def now() = System.currentTimeMillis + def readTimestamp: Long = Try { store.read[Long] } getOrElse 0 } @@ -210,24 +226,30 @@ class Timestamp(val store: CacheStore, useStartTime: Boolean)(implicit format: J @deprecated("Use Tracked.inputChanged and Tracked.outputChanged instead", "1.0.1") class Changed[O: Equiv: JsonFormat](val store: CacheStore) extends Tracked { def clean() = store.delete() - def apply[O2](ifChanged: O => O2, ifUnchanged: O => O2): O => O2 = value => - { - if (uptodate(value)) - ifUnchanged(value) - else { - update(value) - ifChanged(value) - } - } - def update(value: O): Unit = store.write(value) //Using.fileOutputStream(false)(cacheFile)(stream => format.writes(stream, value)) + def apply[O2](ifChanged: O => O2, ifUnchanged: O => O2): O => O2 = value => { + if (uptodate(value)) + ifUnchanged(value) + else { + update(value) + ifChanged(value) + } + } + + def update(value: O): Unit = + store.write(value) //Using.fileOutputStream(false)(cacheFile)(stream => format.writes(stream, value)) + def uptodate(value: O): Boolean = { val equiv: Equiv[O] = implicitly equiv.equiv(value, store.read[O]) } } + object Difference { - def constructor(defineClean: Boolean, filesAreOutputs: Boolean): (CacheStore, FileInfo.Style) => Difference = + def constructor( + defineClean: Boolean, + filesAreOutputs: Boolean + ): (CacheStore, FileInfo.Style) => Difference = (store, style) => new Difference(store, style, defineClean, filesAreOutputs) /** @@ -236,55 +258,63 @@ object Difference { * before and after running the function. */ val outputs = constructor(true, true) + /** * Provides a constructor for a Difference that does nothing on a call to 'clean' and saves the * hash/last modified time of the files as they were prior to running the function. */ val inputs = constructor(false, false) + } -class Difference(val store: CacheStore, val style: FileInfo.Style, val defineClean: Boolean, val filesAreOutputs: Boolean) extends Tracked { - def clean() = - { - if (defineClean) IO.delete(raw(cachedFilesInfo)) else () - clearCache() - } + +class Difference( + val store: CacheStore, + val style: FileInfo.Style, + val defineClean: Boolean, + val filesAreOutputs: Boolean +) extends Tracked { + def clean() = { + if (defineClean) IO.delete(raw(cachedFilesInfo)) else () + clearCache() + } + private def clearCache() = store.delete() private def cachedFilesInfo = store.read(default = FilesInfo.empty[style.F])(style.formats).files private def raw(fs: Set[style.F]): Set[File] = fs.map(_.file) - def apply[T](files: Set[File])(f: ChangeReport[File] => T): T = - { - val lastFilesInfo = cachedFilesInfo - apply(files, lastFilesInfo)(f)(_ => files) - } + def apply[T](files: Set[File])(f: ChangeReport[File] => T): T = { + val lastFilesInfo = cachedFilesInfo + apply(files, lastFilesInfo)(f)(_ => files) + } - def apply[T](f: ChangeReport[File] => T)(implicit toFiles: T => Set[File]): T = - { - val lastFilesInfo = cachedFilesInfo - apply(raw(lastFilesInfo), lastFilesInfo)(f)(toFiles) - } + def apply[T](f: ChangeReport[File] => T)(implicit toFiles: T => Set[File]): T = { + val lastFilesInfo = cachedFilesInfo + apply(raw(lastFilesInfo), lastFilesInfo)(f)(toFiles) + } private def abs(files: Set[File]) = files.map(_.getAbsoluteFile) - private[this] def apply[T](files: Set[File], lastFilesInfo: Set[style.F])(f: ChangeReport[File] => T)(extractFiles: T => Set[File]): T = - { - val lastFiles = raw(lastFilesInfo) - val currentFiles = abs(files) - val currentFilesInfo = style(currentFiles) - val report = new ChangeReport[File] { - lazy val checked = currentFiles - lazy val removed = lastFiles -- checked // all files that were included previously but not this time. This is independent of whether the files exist. - lazy val added = checked -- lastFiles // all files included now but not previously. This is independent of whether the files exist. - lazy val modified = raw(lastFilesInfo -- currentFilesInfo.files) ++ added - lazy val unmodified = checked -- modified - } + private[this] def apply[T](files: Set[File], lastFilesInfo: Set[style.F])( + f: ChangeReport[File] => T + )(extractFiles: T => Set[File]): T = { + val lastFiles = raw(lastFilesInfo) + val currentFiles = abs(files) + val currentFilesInfo = style(currentFiles) - val result = f(report) - val info = if (filesAreOutputs) style(abs(extractFiles(result))) else currentFilesInfo - - store.write(info)(style.formats) - - result + val report = new ChangeReport[File] { + lazy val checked = currentFiles + lazy val removed = lastFiles -- checked // all files that were included previously but not this time. This is independent of whether the files exist. + lazy val added = checked -- lastFiles // all files included now but not previously. This is independent of whether the files exist. + lazy val modified = raw(lastFilesInfo -- currentFilesInfo.files) ++ added + lazy val unmodified = checked -- modified } + + val result = f(report) + val info = if (filesAreOutputs) style(abs(extractFiles(result))) else currentFilesInfo + + store.write(info)(style.formats) + + result + } } diff --git a/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala b/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala index e0cf74558..a2015d76f 100644 --- a/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala +++ b/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala @@ -11,7 +11,6 @@ import sbt.internal.util.UnitSpec class TrackedSpec extends UnitSpec { "lastOutput" should "store the last output" in { withStore { store => - val value = 5 val otherValue = 10 @@ -132,7 +131,6 @@ class TrackedSpec extends UnitSpec { } } - "tstamp tracker" should "have a timestamp of 0 on first invocation" in { withStore { store => Tracked.tstamp(store) { last => @@ -143,7 +141,6 @@ class TrackedSpec extends UnitSpec { it should "provide the last time a function has been evaluated" in { withStore { store => - Tracked.tstamp(store) { last => assert(last === 0) } @@ -161,4 +158,4 @@ class TrackedSpec extends UnitSpec { f(store) } -} \ No newline at end of file +}