Merge branch 'develop' into handle-interrupted-exception-in-completion-service-task

This commit is contained in:
Alex Zolotko 2020-02-25 13:09:52 +01:00 committed by GitHub
commit bbadaa5985
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
401 changed files with 12010 additions and 2983 deletions

View File

@ -8,15 +8,15 @@ assignees: ''
---
## steps
sbt version: *insert sbt version*
<!-- Describe exact steps to reproduce your problems on our computer, including sbt version and build.sbt -->
## problem
<!-- Next, describe the problem, or what you think is the problem. -->
## expectation
<!-- Describe what you think should've happened. -->
## notes

View File

@ -1,4 +1,5 @@
version = 2.0.0
version = 2.3.2
edition = 2019-10
maxColumn = 100
project.git = true
project.excludeFilters = [ "\\Wsbt-test\\W", "\\Winput_sources\\W", "\\Wcontraband-scala\\W" ]

View File

@ -1,16 +1,17 @@
sudo: false
dist: trusty
group: stable
dist: xenial
language: scala
env:
global:
- TRAVIS_JDK=adopt@1.11.0-1
- SCALA_212=2.12.10
- SCALA_213=2.13.1
- UTIL_TESTS="utilCache/test;utilControl/test;utilInterface/test;utilLogging/test;utilPosition/test;utilRelation/test;utilScripted/test;utilTracking/test"
# WHITESOURCE_PASSWORD=
- secure: d3bu2KNwsVHwfhbGgO+gmRfDKBJhfICdCJFGWKf2w3Gv86AJZX9nuTYRxz0KtdvEHO5Xw8WTBZLPb2thSJqhw9OCm4J8TBAVqCP0ruUj4+aqBUFy4bVexQ6WKE6nWHs4JPzPk8c6uC1LG3hMuzlC8RGETXtL/n81Ef1u7NjyXjs=
matrix:
- SBT_CMD=";mimaReportBinaryIssues ;scalafmtCheckAll ;headerCheck ;test:headerCheck ;whitesourceOnPush ;test:compile; publishLocal ;mainSettingsProj/test ;safeUnitTests ;otherUnitTests; doc"
- SBT_CMD="mimaReportBinaryIssues ; javafmtCheck ; Test / javafmtCheck; scalafmtCheckAll ; scalafmtSbtCheck; headerCheck ;test:headerCheck ;whitesourceOnPush ;test:compile; publishLocal; test; serverTestProj/test; doc; $UTIL_TESTS; ++$SCALA_213; $UTIL_TESTS"
- SBT_CMD="scripted actions/* apiinfo/* compiler-project/* ivy-deps-management/* reporter/* tests/* watch/* classloader-cache/* package/*"
- SBT_CMD="scripted dependency-management/* plugins/* project-load/* java/* run/* nio/*"
- SBT_CMD="repoOverrideTest:scripted dependency-management/*; scripted source-dependencies/* project/*"
@ -20,7 +21,7 @@ matrix:
include:
- env:
- TRAVIS_JDK=adopt@1.8.0-222
- SBT_CMD="scripted actions/* source-dependencies/*1of3 dependency-management/*1of4 java/*"
- SBT_CMD="++$SCALA_213; $UTIL_TESTS; ++$SCALA_212; $UTIL_TESTS; scripted actions/* source-dependencies/*1of3 dependency-management/*1of4 java/*"
before_install:
- curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.11.0/install.sh | bash && . ~/.jabba/jabba.sh

606
build.sbt
View File

@ -1,10 +1,10 @@
import Util._
import Dependencies._
import Sxr.sxr
import com.typesafe.tools.mima.core._, ProblemFilters._
import Util._
import com.typesafe.tools.mima.core.ProblemFilters._
import com.typesafe.tools.mima.core._
import local.Scripted
import scala.xml.{ Node => XmlNode, NodeSeq => XmlNodeSeq, _ }
import scala.xml.transform.{ RewriteRule, RuleTransformer }
import scala.util.Try
ThisBuild / version := {
@ -29,7 +29,7 @@ def buildLevelSettings: Seq[Setting[_]] =
},
bintrayPackage := "sbt",
bintrayReleaseOnPublish := false,
licenses := List("Apache-2.0" -> url("https://github.com/sbt/sbt/blob/0.13/LICENSE")),
licenses := List("Apache-2.0" -> url("https://github.com/sbt/sbt/blob/develop/LICENSE")),
javacOptions ++= Seq("-source", "1.8", "-target", "1.8"),
Compile / doc / javacOptions := Nil,
developers := List(
@ -52,7 +52,7 @@ def buildLevelSettings: Seq[Setting[_]] =
)
)
def commonSettings: Seq[Setting[_]] = Def.settings(
def commonBaseSettings: Seq[Setting[_]] = Def.settings(
headerLicense := Some(
HeaderLicense.Custom(
"""|sbt
@ -67,7 +67,12 @@ def commonSettings: Seq[Setting[_]] = Def.settings(
resolvers += Resolver.typesafeIvyRepo("releases").withName("typesafe-sbt-build-ivy-releases"),
resolvers += Resolver.sonatypeRepo("snapshots"),
resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/",
addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.4" cross CrossVersion.binary),
resolvers += Resolver.url(
"bintray-scala-hedgehog",
url("https://dl.bintray.com/hedgehogqa/scala-hedgehog")
)(Resolver.ivyStylePatterns),
testFrameworks += TestFramework("hedgehog.sbt.Framework"),
testFrameworks += TestFramework("verify.runner.Framework"),
concurrentRestrictions in Global += Util.testExclusiveRestriction,
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2"),
@ -83,6 +88,16 @@ def commonSettings: Seq[Setting[_]] = Def.settings(
s"https://github.com/sbt/sbt/tree/$tagOrSha€{FILE_PATH}.scala"
)
},
Compile / javafmtOnCompile := Def
.taskDyn(if ((scalafmtOnCompile).value) Compile / javafmt else Def.task(()))
.value,
Test / javafmtOnCompile := Def
.taskDyn(if ((Test / scalafmtOnCompile).value) Test / javafmt else Def.task(()))
.value,
Compile / unmanagedSources / inputFileStamps :=
(Compile / unmanagedSources / inputFileStamps).dependsOn(Compile / javafmtOnCompile).value,
Test / unmanagedSources / inputFileStamps :=
(Test / unmanagedSources / inputFileStamps).dependsOn(Test / javafmtOnCompile).value,
crossScalaVersions := Seq(baseScalaVersion),
bintrayPackage := (bintrayPackage in ThisBuild).value,
bintrayRepository := (bintrayRepository in ThisBuild).value,
@ -90,6 +105,11 @@ def commonSettings: Seq[Setting[_]] = Def.settings(
fork in compile := true,
fork in run := true
)
def commonSettings: Seq[Setting[_]] =
commonBaseSettings :+
addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.4" cross CrossVersion.binary)
def utilCommonSettings: Seq[Setting[_]] =
commonBaseSettings :+ (crossScalaVersions := (scala212 :: scala213 :: Nil))
def minimalSettings: Seq[Setting[_]] =
commonSettings ++ customCommands ++
@ -101,7 +121,8 @@ def baseSettings: Seq[Setting[_]] =
def testedBaseSettings: Seq[Setting[_]] =
baseSettings ++ testDependencies
def sbt10Plus =
val sbt13Plus = Seq("1.3.0")
val sbt10Plus =
Seq(
"1.0.0",
"1.0.1",
@ -116,15 +137,19 @@ def sbt10Plus =
"1.1.5",
"1.1.6",
"1.2.0",
"1.2.1", /*DOA,*/ "1.2.3",
"1.2.4", /*DOA,*/ "1.2.6",
"1.2.1",
/*DOA,*/ "1.2.3",
"1.2.4",
/*DOA,*/ "1.2.6",
"1.2.7",
"1.2.8",
) ++ sbt13Plus
def sbt13Plus = Seq("1.3.0")
val noUtilVersion =
Set("1.0.4", "1.1.4", "1.1.5", "1.1.6", "1.2.3", "1.2.4", "1.2.6", "1.2.7", "1.2.8")
def mimaSettings = mimaSettingsSince(sbt10Plus)
def mimaSettingsSince(versions: Seq[String]) = Def settings (
val mimaSettings = mimaSettingsSince(sbt10Plus)
val utilMimaSettings = mimaSettingsSince(sbt10Plus.filterNot(noUtilVersion))
def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[_]] = Def settings (
mimaPreviousArtifacts := {
val crossVersion = if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled
versions.map(v => organization.value % moduleName.value % v cross crossVersion).toSet
@ -139,9 +164,7 @@ def mimaSettingsSince(versions: Seq[String]) = Def settings (
),
)
val scriptedSbtReduxMimaSettings = Def settings (
mimaPreviousArtifacts := Set()
)
val scriptedSbtReduxMimaSettings = Def.settings(mimaPreviousArtifacts := Set())
lazy val sbtRoot: Project = (project in file("."))
.enablePlugins(ScriptedPlugin) // , SiteScaladocPlugin, GhpagesPlugin)
@ -188,6 +211,8 @@ lazy val sbtRoot: Project = (project in file("."))
skip in publish := true,
commands in Global += Command
.single("sbtOn")((state, dir) => s"sbtProj/test:runMain sbt.RunFromSourceMain $dir" :: state),
mimaSettings,
mimaPreviousArtifacts := Set.empty,
)
// This is used to configure an sbt-launcher for this version of sbt.
@ -208,7 +233,9 @@ lazy val bundledLauncherProj =
// mimaSettings, // TODO: Configure MiMa, deal with Proguard
publish := Release.deployLauncher.value,
publishLauncher := Release.deployLauncher.value,
packageBin in Compile := sbtLaunchJar.value
packageBin in Compile := sbtLaunchJar.value,
mimaSettings,
mimaPreviousArtifacts := Set()
)
/* ** subproject declarations ** */
@ -236,13 +263,25 @@ val collectionProj = (project in file("internal") / "util-collection")
// it's now abstract in KList and defined in both KCons & KNil.
exclude[FinalMethodProblem]("sbt.internal.util.KNil.foldr"),
exclude[DirectAbstractMethodProblem]("sbt.internal.util.KList.foldr"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Init*.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Settings0.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings#INode.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.TypeFunctions.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Settings.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings#MixedNode.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings#BindNode.this"),
exclude[IncompatibleSignatureProblem](
"sbt.internal.util.EvaluateSettings#BindNode.dependsOn"
),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Types.some")
),
)
.configure(addSbtUtilPosition)
.dependsOn(utilPosition)
// Command line-related utilities.
val completeProj = (project in file("internal") / "util-complete")
.dependsOn(collectionProj)
.dependsOn(collectionProj, utilControl, utilLogging)
.settings(
testedBaseSettings,
name := "Completion",
@ -250,31 +289,159 @@ val completeProj = (project in file("internal") / "util-complete")
mimaSettings,
// Parser is used publicly, so we can't break bincompat.
mimaBinaryIssueFilters := Seq(
exclude[DirectMissingMethodProblem]("sbt.internal.util.complete.SoftInvalid.apply"),
exclude[DirectMissingMethodProblem]("sbt.internal.util.complete.Invalid.apply"),
exclude[DirectMissingMethodProblem]("sbt.internal.util.complete.Finite.apply"),
exclude[DirectMissingMethodProblem]("sbt.internal.util.complete.Infinite.decrement"),
exclude[DirectMissingMethodProblem]("sbt.internal.util.complete.History.this"),
exclude[IncompatibleMethTypeProblem]("sbt.internal.util.complete.Completion.suggestion"),
exclude[IncompatibleMethTypeProblem]("sbt.internal.util.complete.Completion.token"),
exclude[IncompatibleMethTypeProblem]("sbt.internal.util.complete.Completion.displayOnly"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.complete.*"),
),
)
.configure(addSbtIO, addSbtUtilControl, addSbtUtilLogging)
.configure(addSbtIO)
// A logic with restricted negation as failure for a unique, stable model
val logicProj = (project in file("internal") / "util-logic")
.dependsOn(collectionProj)
.dependsOn(collectionProj, utilRelation)
.settings(
testedBaseSettings,
name := "Logic",
mimaSettings,
)
.configure(addSbtUtilRelation)
// 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 file("internal") / "util-interface").settings(
utilCommonSettings,
javaOnlySettings,
crossPaths := false,
name := "Util Interface",
exportJars := true,
utilMimaSettings,
)
lazy val utilControl = (project in file("internal") / "util-control").settings(
utilCommonSettings,
name := "Util Control",
utilMimaSettings,
)
lazy val utilPosition = (project in file("internal") / "util-position")
.settings(
utilCommonSettings,
name := "Util Position",
scalacOptions += "-language:experimental.macros",
libraryDependencies ++= Seq(scalaReflect.value, scalatest % "test"),
utilMimaSettings,
)
lazy val utilLogging = (project in file("internal") / "util-logging")
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
.dependsOn(utilInterface)
.settings(
utilCommonSettings,
name := "Util Logging",
libraryDependencies ++=
Seq(jline, log4jApi, log4jCore, disruptor, sjsonNewScalaJson.value, scalaReflect.value),
libraryDependencies ++= Seq(scalacheck % "test", scalatest % "test"),
libraryDependencies ++= (scalaVersion.value match {
case v if v.startsWith("2.12.") => List(compilerPlugin(silencerPlugin))
case _ => List()
}),
Compile / scalacOptions ++= (scalaVersion.value match {
case v if v.startsWith("2.12.") => List("-Ywarn-unused:-locals,-explicits,-privates")
case _ => List()
}),
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
managedSourceDirectories in Compile +=
baseDirectory.value / "src" / "main" / "contraband-scala",
contrabandFormatsForType in generateContrabands in Compile := { tpe =>
val old = (contrabandFormatsForType in generateContrabands in Compile).value
val name = tpe.removeTypeParameters.name
if (name == "Throwable") Nil
else old(tpe)
},
utilMimaSettings,
mimaBinaryIssueFilters ++= Seq(
exclude[DirectMissingMethodProblem]("sbt.internal.util.SuccessEvent.copy*"),
exclude[DirectMissingMethodProblem]("sbt.internal.util.TraceEvent.copy*"),
exclude[DirectMissingMethodProblem]("sbt.internal.util.StringEvent.copy*"),
// Private final class constructors changed
exclude[DirectMissingMethodProblem]("sbt.util.InterfaceUtil#ConcretePosition.this"),
exclude[DirectMissingMethodProblem]("sbt.util.InterfaceUtil#ConcreteProblem.this"),
exclude[ReversedMissingMethodProblem]("sbt.internal.util.ConsoleOut.flush"),
// This affects Scala 2.11 only it seems, so it's ok?
exclude[InheritedNewAbstractMethodProblem](
"sbt.internal.util.codec.JsonProtocol.LogOptionFormat"
),
exclude[InheritedNewAbstractMethodProblem](
"sbt.internal.util.codec.JsonProtocol.ProgressItemFormat"
),
exclude[InheritedNewAbstractMethodProblem](
"sbt.internal.util.codec.JsonProtocol.ProgressEventFormat"
),
),
)
.configure(addSbtIO)
lazy val utilRelation = (project in file("internal") / "util-relation")
.settings(
utilCommonSettings,
name := "Util Relation",
libraryDependencies ++= Seq(scalacheck % "test"),
utilMimaSettings,
)
// Persisted caching based on sjson-new
lazy val utilCache = (project in file("util-cache"))
.settings(
utilCommonSettings,
name := "Util Cache",
libraryDependencies ++=
Seq(sjsonNewScalaJson.value, sjsonNewMurmurhash.value, scalaReflect.value),
libraryDependencies ++= Seq(scalatest % "test"),
utilMimaSettings,
mimaBinaryIssueFilters ++= Seq(
// Added a method to a sealed trait, technically not a problem for Scala
exclude[ReversedMissingMethodProblem]("sbt.util.HashFileInfo.hashArray"),
)
)
.configure(addSbtIO)
// Builds on cache to provide caching for filesystem-related operations
lazy val utilTracking = (project in file("util-tracking"))
.dependsOn(utilCache)
.settings(
utilCommonSettings,
name := "Util Tracking",
libraryDependencies ++= Seq(scalatest % "test"),
utilMimaSettings,
)
.configure(addSbtIO)
lazy val utilScripted = (project in file("internal") / "util-scripted")
.dependsOn(utilLogging, utilInterface)
.settings(
utilCommonSettings,
name := "Util Scripted",
libraryDependencies += scalaParsers,
utilMimaSettings,
)
.configure(addSbtIO)
/* **** Intermediate-level Modules **** */
// Runner for uniform test interface
lazy val testingProj = (project in file("testing"))
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
.dependsOn(testAgentProj)
.dependsOn(testAgentProj, utilLogging)
.settings(
baseSettings,
name := "Testing",
libraryDependencies ++= scalaXml.value ++ Seq(
libraryDependencies ++= Seq(
scalaXml,
testInterface,
launcherInterface,
sjsonNewScalaJson.value
@ -313,7 +480,7 @@ lazy val testingProj = (project in file("testing"))
exclude[DirectMissingMethodProblem]("sbt.JUnitXmlTestsListener.testSuite"),
)
)
.configure(addSbtIO, addSbtCompilerClasspath, addSbtUtilLogging)
.configure(addSbtIO, addSbtCompilerClasspath)
// Testing agent for running tests in a separate process.
lazy val testAgentProj = (project in file("testing") / "agent")
@ -329,23 +496,32 @@ lazy val testAgentProj = (project in file("testing") / "agent")
// Basic task engine
lazy val taskProj = (project in file("tasks"))
.dependsOn(collectionProj)
.dependsOn(collectionProj, utilControl)
.settings(
testedBaseSettings,
name := "Tasks",
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
exclude[IncompatibleSignatureProblem]("sbt.Triggers.this"),
exclude[IncompatibleSignatureProblem]("sbt.Triggers.runBefore"),
exclude[IncompatibleSignatureProblem]("sbt.Triggers.injectFor"),
exclude[IncompatibleSignatureProblem]("sbt.Triggers.onComplete"),
exclude[DirectMissingMethodProblem]("sbt.Inc.apply"),
// ok because sbt.ExecuteProgress has been under private[sbt]
exclude[IncompatibleResultTypeProblem]("sbt.ExecuteProgress.initial"),
exclude[DirectMissingMethodProblem]("sbt.ExecuteProgress.*"),
exclude[ReversedMissingMethodProblem]("sbt.ExecuteProgress.*"),
exclude[IncompatibleSignatureProblem]("sbt.ExecuteProgress.*"),
// ok because sbt.Execute has been under private[sbt]
exclude[IncompatibleSignatureProblem]("sbt.Execute.*"),
exclude[IncompatibleSignatureProblem]("sbt.Execute#CyclicException.*"),
exclude[IncompatibleSignatureProblem]("sbt.NodeView.*"),
)
)
.configure(addSbtUtilControl)
// Standard task system. This provides map, flatMap, join, and more on top of the basic task model.
lazy val stdTaskProj = (project in file("tasks-standard"))
.dependsOn(collectionProj)
.dependsOn(collectionProj, utilLogging, utilCache)
.dependsOn(taskProj % "compile;test->test")
.settings(
testedBaseSettings,
@ -357,12 +533,12 @@ lazy val stdTaskProj = (project in file("tasks-standard"))
exclude[DirectMissingMethodProblem]("sbt.Task.mapTask"),
),
)
.configure(addSbtIO, addSbtUtilLogging, addSbtUtilCache)
.configure(addSbtIO)
// Embedded Scala code runner
lazy val runProj = (project in file("run"))
.enablePlugins(ContrabandPlugin)
.dependsOn(collectionProj)
.dependsOn(collectionProj, utilLogging, utilControl)
.settings(
testedBaseSettings,
name := "Run",
@ -383,7 +559,7 @@ lazy val runProj = (project in file("run"))
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#LoggedOutput.copy$default$*"),
)
)
.configure(addSbtIO, addSbtUtilLogging, addSbtUtilControl, addSbtCompilerClasspath)
.configure(addSbtIO, addSbtCompilerClasspath)
val sbtProjDepsCompileScopeFilter =
ScopeFilter(
@ -392,27 +568,15 @@ val sbtProjDepsCompileScopeFilter =
)
lazy val scriptedSbtReduxProj = (project in file("scripted-sbt-redux"))
.dependsOn(commandProj)
.dependsOn(sbtProj % "compile;test->test", commandProj, utilLogging, utilScripted)
.settings(
baseSettings,
name := "Scripted sbt Redux",
libraryDependencies ++= Seq(launcherInterface % "provided"),
resourceGenerators in Compile += Def task {
val mainClassDir = (classDirectory in Compile in LocalProject("sbtProj")).value
val testClassDir = (classDirectory in Test in LocalProject("sbtProj")).value
val classDirs = (classDirectory all sbtProjDepsCompileScopeFilter).value
val extDepsCp = (externalDependencyClasspath in Compile in LocalProject("sbtProj")).value
val cpStrings = (mainClassDir +: testClassDir +: classDirs) ++ extDepsCp.files map (_.toString)
val file = (resourceManaged in Compile).value / "RunFromSource.classpath"
if (!file.exists || Try(IO.readLines(file)).getOrElse(Nil).toSet != cpStrings.toSet) {
IO.writeLines(file, cpStrings)
}
List(file)
},
mimaSettings,
scriptedSbtReduxMimaSettings,
)
.configure(addSbtIO, addSbtUtilLogging, addSbtCompilerInterface, addSbtUtilScripted, addSbtLmCore)
.configure(addSbtIO, addSbtCompilerInterface, addSbtLmCore)
lazy val scriptedSbtOldProj = (project in file("scripted-sbt-old"))
.dependsOn(scriptedSbtReduxProj)
@ -425,6 +589,7 @@ lazy val scriptedSbtOldProj = (project in file("scripted-sbt-old"))
exclude[MissingClassProblem]("sbt.test.*"),
exclude[DirectMissingMethodProblem]("sbt.test.*"),
exclude[IncompatibleMethTypeProblem]("sbt.test.*"),
exclude[IncompatibleSignatureProblem]("sbt.test.*"),
),
)
@ -441,7 +606,16 @@ lazy val scriptedPluginProj = (project in file("scripted-plugin"))
// Implementation and support code for defining actions.
lazy val actionsProj = (project in file("main-actions"))
.dependsOn(completeProj, runProj, stdTaskProj, taskProj, testingProj)
.dependsOn(
completeProj,
runProj,
stdTaskProj,
taskProj,
testingProj,
utilLogging,
utilRelation,
utilTracking,
)
.settings(
testedBaseSettings,
name := "Actions",
@ -458,9 +632,6 @@ lazy val actionsProj = (project in file("main-actions"))
)
.configure(
addSbtIO,
addSbtUtilLogging,
addSbtUtilRelation,
addSbtUtilTracking,
addSbtCompilerInterface,
addSbtCompilerClasspath,
addSbtCompilerApiInfo,
@ -470,7 +641,7 @@ lazy val actionsProj = (project in file("main-actions"))
lazy val protocolProj = (project in file("protocol"))
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
.dependsOn(collectionProj)
.dependsOn(collectionProj, utilLogging)
.settings(
testedBaseSettings,
name := "Protocol",
@ -504,27 +675,32 @@ lazy val protocolProj = (project in file("protocol"))
exclude[DirectMissingMethodProblem]("sbt.protocol.SettingQuerySuccess.copy$default$*"),
// ignore missing methods in sbt.internal
exclude[DirectMissingMethodProblem]("sbt.internal.*"),
exclude[MissingTypesProblem]("sbt.internal.protocol.JsonRpcResponseError"),
)
)
.configure(addSbtUtilLogging)
// General command support and core commands not specific to a build system
lazy val commandProj = (project in file("main-command"))
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
.dependsOn(protocolProj, completeProj)
.dependsOn(protocolProj, completeProj, utilLogging)
.settings(
testedBaseSettings,
name := "Command",
libraryDependencies ++= Seq(launcherInterface, sjsonNewScalaJson.value, templateResolverApi),
libraryDependencies ++= (scalaVersion.value match {
case v if v.startsWith("2.12.") => List(compilerPlugin(silencerPlugin))
case _ => List()
}),
Compile / scalacOptions += "-Ywarn-unused:-locals,-explicits,-privates",
// Removing -Xfatal-warnings is necessary because BasicKeys contains a Key for a deprecated class.
Compile / scalacOptions -= "-Xfatal-warnings",
managedSourceDirectories in Compile +=
baseDirectory.value / "src" / "main" / "contraband-scala",
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats,
mimaSettings,
mimaBinaryIssueFilters ++= Vector(
exclude[DirectMissingMethodProblem]("sbt.Exit.apply"),
exclude[DirectMissingMethodProblem]("sbt.Reboot.apply"),
exclude[DirectMissingMethodProblem]("sbt.TemplateResolverInfo.apply"),
// dropped private[sbt] method
exclude[DirectMissingMethodProblem]("sbt.BasicCommands.compatCommands"),
// dropped mainly internal command strings holder
@ -560,7 +736,6 @@ lazy val commandProj = (project in file("main-command"))
)
.configure(
addSbtIO,
addSbtUtilLogging,
addSbtCompilerInterface,
addSbtCompilerClasspath,
addSbtLmCore,
@ -580,23 +755,52 @@ lazy val coreMacrosProj = (project in file("core-macros"))
// Fixes scope=Scope for Setting (core defined in collectionProj) to define the settings system used in build definitions
lazy val mainSettingsProj = (project in file("main-settings"))
.dependsOn(completeProj, commandProj, stdTaskProj, coreMacrosProj)
.dependsOn(
completeProj,
commandProj,
stdTaskProj,
coreMacrosProj,
utilLogging,
utilCache,
utilRelation,
)
.settings(
testedBaseSettings,
name := "Main Settings",
BuildInfoPlugin.buildInfoDefaultSettings,
addBuildInfoToConfig(Test),
buildInfoObject in Test := "TestBuildInfo",
buildInfoKeys in Test := Seq[BuildInfoKey](
classDirectory in Compile,
classDirectory in Test,
// WORKAROUND https://github.com/sbt/sbt-buildinfo/issues/117
BuildInfoKey.map((dependencyClasspath in Compile).taskValue) {
case (ident, cp) => ident -> cp.files
},
),
testOptions in Test ++= {
val cp = (Test / fullClasspathAsJars).value.map(_.data).mkString(java.io.File.pathSeparator)
val framework = TestFrameworks.ScalaTest
Tests.Argument(framework, s"-Dsbt.server.classpath=$cp") ::
Tests.Argument(framework, s"-Dsbt.server.version=${version.value}") ::
Tests.Argument(framework, s"-Dsbt.server.scala.version=${scalaVersion.value}") :: Nil
},
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
exclude[IncompatibleSignatureProblem]("sbt.Previous#References.getReferences"),
exclude[IncompatibleSignatureProblem]("sbt.Def.delegate"),
exclude[IncompatibleSignatureProblem]("sbt.Def.add"),
exclude[IncompatibleSignatureProblem]("sbt.Def.grouped"),
exclude[IncompatibleSignatureProblem]("sbt.Def.compile"),
exclude[IncompatibleSignatureProblem]("sbt.Def.asTransform"),
exclude[DirectMissingMethodProblem]("sbt.Def.StaticScopes"),
exclude[IncompatibleSignatureProblem]("sbt.Previous.this"),
exclude[DirectMissingMethodProblem]("sbt.BuildRef.apply"),
exclude[DirectMissingMethodProblem]("sbt.ScopeMask.apply"),
exclude[DirectMissingMethodProblem]("sbt.Def.intersect"),
exclude[DirectMissingMethodProblem]("sbt.LocalProject.apply"),
exclude[DirectMissingMethodProblem]("sbt.std.InitializeInstance.pure"),
exclude[DirectMissingMethodProblem]("sbt.std.InitializeInstance.flatten"),
exclude[DirectMissingMethodProblem]("sbt.std.InitializeInstance.map"),
exclude[DirectMissingMethodProblem]("sbt.std.InitializeInstance.app"),
exclude[DirectMissingMethodProblem]("sbt.std.ParserInstance.pure"),
exclude[DirectMissingMethodProblem]("sbt.std.ParserInstance.map"),
exclude[DirectMissingMethodProblem]("sbt.std.ParserInstance.app"),
exclude[DirectMissingMethodProblem]("sbt.std.ParserInstance.pure"),
exclude[DirectMissingMethodProblem]("sbt.std.TaskInstance.pure"),
exclude[DirectMissingMethodProblem]("sbt.std.TaskInstance.flatten"),
exclude[DirectMissingMethodProblem]("sbt.std.TaskInstance.map"),
exclude[DirectMissingMethodProblem]("sbt.std.TaskInstance.app"),
exclude[DirectMissingMethodProblem]("sbt.std.FullInstance.flatten"),
exclude[DirectMissingMethodProblem]("sbt.Scope.display012StyleMasked"),
// added a method to a sealed trait
exclude[InheritedNewAbstractMethodProblem]("sbt.Scoped.canEqual"),
@ -605,24 +809,17 @@ lazy val mainSettingsProj = (project in file("main-settings"))
)
.configure(
addSbtIO,
addSbtUtilLogging,
addSbtUtilCache,
addSbtUtilRelation,
addSbtCompilerInterface,
addSbtCompilerClasspath,
addSbtLmCore
)
lazy val zincLmIntegrationProj = (project in file("zinc-lm-integration"))
.enablePlugins(BuildInfoPlugin)
.settings(
name := "Zinc LM Integration",
testedBaseSettings,
buildInfo in Compile := Nil, // Only generate build info for tests
BuildInfoPlugin.buildInfoScopedSettings(Test),
buildInfoPackage in Test := "sbt.internal.inc",
buildInfoObject in Test := "ZincLmIntegrationBuildInfo",
buildInfoKeys in Test := List[BuildInfoKey]("zincVersion" -> zincVersion),
testOptions in Test +=
Tests.Argument(TestFrameworks.ScalaTest, s"-Dsbt.zinc.version=$zincVersion"),
mimaSettingsSince(sbt13Plus),
libraryDependencies += launcherInterface,
)
@ -638,29 +835,25 @@ lazy val mainProj = (project in file("main"))
runProj,
commandProj,
collectionProj,
scriptedSbtReduxProj,
scriptedPluginProj,
zincLmIntegrationProj
zincLmIntegrationProj,
utilLogging,
)
.settings(
testedBaseSettings,
name := "Main",
checkPluginCross := {
val sv = scalaVersion.value
val xs =
IO.readLines(baseDirectory.value / "src" / "main" / "scala" / "sbt" / "PluginCross.scala")
if (xs exists { s =>
s.contains(s""""$sv"""")
}) ()
else sys.error("PluginCross.scala does not match up with the scalaVersion " + sv)
val f = baseDirectory.value / "src" / "main" / "scala" / "sbt" / "PluginCross.scala"
if (!IO.readLines(f).exists(_.contains(s""""$sv"""")))
sys.error(s"PluginCross.scala does not match up with the scalaVersion $sv")
},
libraryDependencies ++= {
scalaXml.value ++
Seq(launcherInterface) ++
log4jDependencies ++
Seq(scalaCacheCaffeine, lmCoursierShaded)
},
Compile / scalacOptions -= "-Xfatal-warnings",
libraryDependencies ++=
(Seq(scalaXml, launcherInterface, scalaCacheCaffeine, lmCoursierShaded) ++ log4jModules),
libraryDependencies ++= (scalaVersion.value match {
case v if v.startsWith("2.12.") => List(compilerPlugin(silencerPlugin))
case _ => List()
}),
managedSourceDirectories in Compile +=
baseDirectory.value / "src" / "main" / "contraband-scala",
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
@ -672,7 +865,6 @@ lazy val mainProj = (project in file("main"))
exclude[ReversedMissingMethodProblem]("sbt.internal.KeyIndex.*"),
// internal
exclude[IncompatibleMethTypeProblem]("sbt.internal.server.LanguageServerReporter.*"),
// Changed signature or removed private[sbt] methods
exclude[DirectMissingMethodProblem]("sbt.Classpaths.unmanagedLibs0"),
exclude[DirectMissingMethodProblem]("sbt.Defaults.allTestGroupsTask"),
@ -680,19 +872,70 @@ lazy val mainProj = (project in file("main"))
exclude[IncompatibleMethTypeProblem]("sbt.Defaults.allTestGroupsTask"),
exclude[DirectMissingMethodProblem]("sbt.StandardMain.shutdownHook"),
exclude[MissingClassProblem]("sbt.internal.ResourceLoaderImpl"),
exclude[IncompatibleSignatureProblem]("sbt.internal.ConfigIndex.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.Inspect.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.ProjectIndex.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.BuildIndex.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.server.LanguageServerReporter.*"),
exclude[VirtualStaticMemberProblem]("sbt.internal.server.LanguageServerProtocol.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.librarymanagement.IvyXml.*"),
exclude[IncompatibleSignatureProblem]("sbt.ScriptedPlugin.*Settings"),
exclude[IncompatibleSignatureProblem]("sbt.plugins.SbtPlugin.*Settings"),
// Removed private internal classes
exclude[MissingClassProblem]("sbt.internal.ReverseLookupClassLoaderHolder$BottomClassLoader"),
exclude[MissingClassProblem]("sbt.internal.ReverseLookupClassLoaderHolder$ReverseLookupClassLoader$ResourceLoader"),
exclude[MissingClassProblem](
"sbt.internal.ReverseLookupClassLoaderHolder$ReverseLookupClassLoader$ResourceLoader"
),
exclude[MissingClassProblem]("sbt.internal.ReverseLookupClassLoaderHolder$ClassLoadingLock"),
exclude[MissingClassProblem]("sbt.internal.ReverseLookupClassLoaderHolder$ReverseLookupClassLoader"),
exclude[MissingClassProblem](
"sbt.internal.ReverseLookupClassLoaderHolder$ReverseLookupClassLoader"
),
exclude[MissingClassProblem]("sbt.internal.LayeredClassLoaderImpl"),
// false positives
exclude[DirectMissingMethodProblem]("sbt.plugins.IvyPlugin.requires"),
exclude[DirectMissingMethodProblem]("sbt.plugins.JUnitXmlReportPlugin.requires"),
exclude[DirectMissingMethodProblem]("sbt.plugins.Giter8TemplatePlugin.requires"),
exclude[DirectMissingMethodProblem]("sbt.plugins.JvmPlugin.requires"),
exclude[DirectMissingMethodProblem]("sbt.plugins.SbtPlugin.requires"),
exclude[DirectMissingMethodProblem]("sbt.ResolvedClasspathDependency.apply"),
exclude[DirectMissingMethodProblem]("sbt.ClasspathDependency.apply"),
exclude[IncompatibleSignatureProblem]("sbt.plugins.SemanticdbPlugin.globalSettings"),
// File -> Source
exclude[DirectMissingMethodProblem]("sbt.Defaults.cleanFilesTask"),
exclude[IncompatibleSignatureProblem]("sbt.Defaults.resourceConfigPaths"),
exclude[IncompatibleSignatureProblem]("sbt.Defaults.sourceConfigPaths"),
exclude[IncompatibleSignatureProblem]("sbt.Defaults.configPaths"),
exclude[IncompatibleSignatureProblem]("sbt.Defaults.paths"),
exclude[IncompatibleSignatureProblem]("sbt.Keys.csrPublications"),
exclude[IncompatibleSignatureProblem](
"sbt.coursierint.CoursierArtifactsTasks.coursierPublicationsTask"
),
exclude[IncompatibleSignatureProblem](
"sbt.coursierint.CoursierArtifactsTasks.coursierPublicationsTask"
),
exclude[IncompatibleSignatureProblem]("sbt.coursierint.LMCoursier.coursierConfiguration"),
exclude[IncompatibleSignatureProblem]("sbt.coursierint.LMCoursier.publicationsSetting"),
exclude[IncompatibleSignatureProblem]("sbt.Project.inThisBuild"),
exclude[IncompatibleSignatureProblem]("sbt.Project.inConfig"),
exclude[IncompatibleSignatureProblem]("sbt.Project.inTask"),
exclude[IncompatibleSignatureProblem]("sbt.Project.inScope"),
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inThisBuild"),
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inConfig"),
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inTask"),
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inScope"),
exclude[MissingTypesProblem]("sbt.internal.Load*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.Load*"),
// IvyConfiguration was replaced by InlineIvyConfiguration in the generic
// signature, this does not break compatibility regardless of what
// cast a compiler might have inserted based on the old signature
// since we're returning the same values as before.
exclude[IncompatibleSignatureProblem]("sbt.Classpaths.mkIvyConfiguration")
)
)
.configure(
addSbtIO,
addSbtUtilLogging,
addSbtLmCore,
addSbtLmImpl,
addSbtLmIvy,
addSbtCompilerInterface,
addSbtZincCompile
)
@ -701,7 +944,7 @@ lazy val mainProj = (project in file("main"))
// technically, we need a dependency on all of mainProj's dependencies, but we don't do that since this is strictly an integration project
// with the sole purpose of providing certain identifiers without qualification (with a package object)
lazy val sbtProj = (project in file("sbt"))
.dependsOn(mainProj, scriptedSbtReduxProj % "test->test")
.dependsOn(mainProj)
.settings(
testedBaseSettings,
name := "sbt",
@ -711,28 +954,43 @@ lazy val sbtProj = (project in file("sbt"))
javaOptions ++= Seq("-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"),
mimaSettings,
mimaBinaryIssueFilters ++= sbtIgnoredProblems,
BuildInfoPlugin.buildInfoDefaultSettings,
addBuildInfoToConfig(Test),
BuildInfoPlugin.buildInfoDefaultSettings,
buildInfoObject in Test := "TestBuildInfo",
buildInfoKeys in Test := Seq[BuildInfoKey](
version,
// WORKAROUND https://github.com/sbt/sbt-buildinfo/issues/117
BuildInfoKey.map((fullClasspath in Compile).taskValue) {
case (ident, cp) => ident -> cp.files
},
BuildInfoKey.map((dependencyClasspath in Compile).taskValue) {
case (ident, cp) => ident -> cp.files
},
classDirectory in Compile,
classDirectory in Test,
),
)
.settings(
Test / run / connectInput := true,
Test / run / outputStrategy := Some(StdoutOutput),
Test / run / fork := true,
testOptions in Test ++= {
val cp = (Test / fullClasspathAsJars).value.map(_.data).mkString(java.io.File.pathSeparator)
val framework = TestFrameworks.ScalaTest
Tests.Argument(framework, s"-Dsbt.server.classpath=$cp") ::
Tests.Argument(framework, s"-Dsbt.server.version=${version.value}") ::
Tests.Argument(framework, s"-Dsbt.server.scala.version=${scalaVersion.value}") :: Nil
},
)
.configure(addSbtIO, addSbtCompilerBridge)
lazy val serverTestProj = (project in file("server-test"))
.dependsOn(sbtProj % "test->test", scriptedSbtReduxProj % "test->test")
.settings(
testedBaseSettings,
crossScalaVersions := Seq(baseScalaVersion),
publish / skip := true,
// make server tests serial
Test / parallelExecution := false,
Test / run / connectInput := true,
Test / run / outputStrategy := Some(StdoutOutput),
Test / run / fork := true,
Test / fork := true,
Test / javaOptions ++= {
val cp = (Test / fullClasspathAsJars).value.map(_.data).mkString(java.io.File.pathSeparator)
List(
s"-Dsbt.server.classpath=$cp",
s"-Dsbt.server.version=${version.value}",
s"-Dsbt.server.scala.version=${scalaVersion.value}"
)
},
)
/*
lazy val sbtBig = (project in file(".big"))
.dependsOn(sbtProj)
@ -771,8 +1029,28 @@ lazy val sbtBig = (project in file(".big"))
)
*/
// util projects used by Zinc and Lm
lazy val lowerUtils = (project in (file("internal") / "lower"))
.aggregate(lowerUtilProjects.map(p => LocalProject(p.id)): _*)
.settings(
publish / skip := true,
crossScalaVersions := Nil,
)
lazy val upperModules = (project in (file("internal") / "upper"))
.aggregate((allProjects diff lowerUtilProjects).map(p => LocalProject(p.id)): _*)
.settings(
publish / skip := true,
crossScalaVersions := Nil,
)
lazy val sbtIgnoredProblems = {
Vector(
exclude[IncompatibleSignatureProblem]("sbt.package.some"),
exclude[IncompatibleSignatureProblem]("sbt.package.inThisBuild"),
exclude[IncompatibleSignatureProblem]("sbt.package.inConfig"),
exclude[IncompatibleSignatureProblem]("sbt.package.inTask"),
exclude[IncompatibleSignatureProblem]("sbt.package.inScope"),
exclude[MissingClassProblem]("buildinfo.BuildInfo"),
exclude[MissingClassProblem]("buildinfo.BuildInfo$"),
// Added more items to Import trait.
@ -818,7 +1096,7 @@ lazy val vscodePlugin = (project in file("vscode-sbt-scala"))
crossScalaVersions := Seq(baseScalaVersion),
skip in publish := true,
compile in Compile := {
val u = update.value
update.value: Unit
runNpm("run compile", baseDirectory.value, streams.value.log)
sbt.internal.inc.Analysis.empty
},
@ -847,32 +1125,20 @@ lazy val vscodePlugin = (project in file("vscode-sbt-scala"))
}
)
def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
// publishLocalBinAll.value // TODO: Restore scripted needing only binary jars.
publishAll.value
(sbtProj / Test / compile).value // make sure sbt.RunFromSourceMain is compiled
def scriptedTask(launch: Boolean): Def.Initialize[InputTask[Unit]] = Def.inputTask {
publishLocalBinAll.value
val launchJar = s"-Dsbt.launch.jar=${(bundledLauncherProj / Compile / packageBin).value}"
Scripted.doScripted(
(sbtLaunchJar in bundledLauncherProj).value,
(fullClasspath in scriptedSbtReduxProj in Test).value,
(scalaInstance in scriptedSbtReduxProj).value,
scriptedSource.value,
scriptedBufferLog.value,
Def.setting(Scripted.scriptedParser(scriptedSource.value)).parsed,
scriptedPrescripted.value,
scriptedLaunchOpts.value
)
}
def scriptedUnpublishedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
Scripted.doScripted(
(sbtLaunchJar in bundledLauncherProj).value,
(fullClasspath in scriptedSbtReduxProj in Test).value,
(scalaInstance in scriptedSbtReduxProj).value,
scriptedSource.value,
scriptedBufferLog.value,
Def.setting(Scripted.scriptedParser(scriptedSource.value)).parsed,
scriptedPrescripted.value,
scriptedLaunchOpts.value
scriptedLaunchOpts.value ++ (if (launch) Some(launchJar) else None),
scalaVersion.value,
version.value,
(scriptedSbtReduxProj / Test / fullClasspathAsJars).value.map(_.data),
streams.value.log
)
}
@ -900,6 +1166,18 @@ def allProjects =
sbtProj,
bundledLauncherProj,
coreMacrosProj
) ++ lowerUtilProjects
lazy val lowerUtilProjects =
Seq(
utilCache,
utilControl,
utilInterface,
utilLogging,
utilPosition,
utilRelation,
utilScripted,
utilTracking
)
lazy val nonRoots = allProjects.map(p => LocalProject(p.id))
@ -910,14 +1188,18 @@ ThisBuild / scriptedPrescripted := { _ =>
def otherRootSettings =
Seq(
scripted := scriptedTask.evaluated,
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
scripted := scriptedTask(false).evaluated,
scriptedUnpublished := scriptedTask(false).evaluated,
scriptedSource := (sourceDirectory in sbtProj).value / "sbt-test",
watchTriggers in scripted += scriptedSource.value.toGlob / **,
scriptedLaunchOpts := List("-Xmx1500M", "-Xms512M", "-server"),
publishAll := { val _ = (publishLocal).all(ScopeFilter(inAnyProject)).value },
publishLocalBinAll := { val _ = (publishLocalBin).all(ScopeFilter(inAnyProject)).value },
aggregate in bintrayRelease := false
watchTriggers in scriptedUnpublished := (watchTriggers in scripted).value,
scriptedLaunchOpts := List("-Xmx1500M", "-Xms512M", "-server") :::
(sys.props.get("sbt.ivy.home") match {
case Some(home) => List(s"-Dsbt.ivy.home=$home")
case _ => Nil
}),
publishLocalBinAll := (Compile / publishLocalBin).all(scriptedProjects).value,
aggregate in bintrayRelease := false,
) ++ inConfig(Scripted.RepoOverrideTest)(
Seq(
scriptedLaunchOpts := List(
@ -926,9 +1208,13 @@ def otherRootSettings =
"-server",
"-Dsbt.override.build.repos=true",
s"""-Dsbt.repository.config=${scriptedSource.value / "repo.config"}"""
),
scripted := scriptedTask.evaluated,
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
) :::
(sys.props.get("sbt.ivy.home") match {
case Some(home) => List(s"-Dsbt.ivy.home=$home")
case _ => Nil
}),
scripted := scriptedTask(true).evaluated,
scriptedUnpublished := scriptedTask(true).evaluated,
scriptedSource := (sourceDirectory in sbtProj).value / "repo-override-test"
)
)
@ -939,34 +1225,20 @@ lazy val docProjects: ScopeFilter = ScopeFilter(
sbtProj,
scriptedSbtReduxProj,
scriptedSbtOldProj,
scriptedPluginProj
scriptedPluginProj,
upperModules,
lowerUtils,
),
inConfigurations(Compile)
)
lazy val safeUnitTests = taskKey[Unit]("Known working tests (for both 2.10 and 2.11)")
lazy val safeProjects: ScopeFilter = ScopeFilter(
inAnyProject -- inProjects(sbtRoot, sbtProj),
inConfigurations(Test)
)
lazy val otherUnitTests = taskKey[Unit]("Unit test other projects")
lazy val otherProjects: ScopeFilter = ScopeFilter(
inProjects(
sbtProj
),
inConfigurations(Test)
)
lazy val javafmtOnCompile = taskKey[Unit]("Formats java sources before compile")
lazy val scriptedProjects = ScopeFilter(inAnyProject -- inProjects(vscodePlugin))
def customCommands: Seq[Setting[_]] = Seq(
commands += Command.command("setupBuildScala212") { state =>
s"""set scalaVersion in ThisBuild := "$scala212" """ ::
state
},
safeUnitTests := {
test.all(safeProjects).value
},
otherUnitTests := {
test.all(otherProjects).value
},
commands += Command.command("whitesourceOnPush") { state =>
sys.env.get("TRAVIS_EVENT_TYPE") match {
case Some("push") =>
@ -1037,10 +1309,17 @@ def customCommands: Seq[Setting[_]] = Seq(
}
)
ThisBuild / publishTo := {
val old = (ThisBuild / publishTo).value
sys.props.get("sbt.build.localmaven") match {
case Some(path) => Some(MavenCache("local-maven", file(path)))
case _ => old
}
}
ThisBuild / whitesourceProduct := "Lightbend Reactive Platform"
ThisBuild / whitesourceAggregateProjectName := {
// note this can get detached on tag build etc
val b = (ThisBuild / git.gitCurrentBranch).value
val b = sys.process.Process("git rev-parse --abbrev-ref HEAD").!!.trim
val Stable = """1\.([0-9]+)\.x""".r
b match {
case Stable(y) => "sbt-1." + y.toString + "-stable"
@ -1050,6 +1329,7 @@ ThisBuild / whitesourceAggregateProjectName := {
ThisBuild / whitesourceAggregateProjectToken := {
(ThisBuild / whitesourceAggregateProjectName).value match {
case "sbt-master" => "e7a1e55518c0489a98e9c7430c8b2ccd53d9f97c12ed46148b592ebe4c8bf128"
case "sbt-1.3-stable" => "7e38cbb4d2fc4599835cd5d2cfb41b150597a4147b15424bb65841664ab2ec0d"
case "sbt-1.2-stable" => "54f2313767aa47198971e65595670ee16e1ad0000d20458588e72d3ac2c34763"
case _ => "" // it's ok to fail here
}

View File

@ -20,7 +20,7 @@ object ContextUtil {
* Constructs an object with utility methods for operating in the provided macro context `c`.
* Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types.
*/
def apply[C <: blackbox.Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c)
def apply[C <: blackbox.Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c: C)
/**
* Helper for implementing a no-argument macro that is introduced via an implicit.
@ -33,6 +33,7 @@ object ContextUtil {
c: blackbox.Context
)(f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] = {
import c.universe._
c.macroApplication match {
case s @ Select(Apply(_, t :: Nil), _) => f(c.Expr[Any](t), s.pos)
case a @ Apply(_, t :: Nil) => f(c.Expr[Any](t), a.pos)
@ -106,7 +107,10 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
if isWrapper(nme.decodedName.toString, tpe.tpe, qual) =>
()
case tree =>
if (tree.symbol ne null) defs += tree.symbol;
if (tree.symbol ne null) {
defs += tree.symbol
()
}
super.traverse(tree)
}
}

View File

@ -158,7 +158,7 @@ object Instance {
val param =
treeCopy.ValDef(variable, util.parameterModifiers, variable.name, variable.tpt, EmptyTree)
val typeApplied =
TypeApply(util.select(instance, MapName), variable.tpt :: TypeTree(treeType) :: Nil)
TypeApply(util.select(instance, MapName), variable.tpt :: (TypeTree(treeType): Tree) :: Nil)
val f = util.createFunction(param :: Nil, body, functionSym)
val mapped = ApplyTree(typeApplied, input.expr :: f :: Nil)
if (t.isLeft) mapped else flatten(mapped)
@ -223,8 +223,7 @@ object Instance {
def map[S, T](in: M[S], f: S => T): M[T] = a.map(in, (bv: B[S]) => b.map(bv, f))
def app[K[L[x]], Z](in: K[M], f: K[Id] => Z)(implicit alist: AList[K]): A[B[Z]] = {
val g: K[B] => B[Z] = in => b.app[K, Z](in, f)
type Split[L[x]] = K[(L B)#l]
a.app[Split, B[Z]](in, g)(AList.asplit(alist))
a.app[AList.SplitK[K, B]#l, B[Z]](in, g)(AList.asplit(alist))
}
}
}

View File

@ -19,7 +19,9 @@ object MixedBuilder extends TupleBuilder {
def make(
c: blackbox.Context
)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = {
val delegate = if (inputs.size > TupleNBuilder.MaxInputs) KListBuilder else TupleNBuilder
val delegate =
if (inputs.size > TupleNBuilder.MaxInputs) (KListBuilder: TupleBuilder)
else (TupleNBuilder: TupleBuilder)
delegate.make(c)(mt, inputs)
}
}

View File

@ -91,9 +91,12 @@ object AList {
): N[P[A]] = f(a)
}
type ASplit[K[L[x]], B[x]] = AList[λ[L[x] => K[(L B)#l]]]
/** Example: calling `AList.SplitK[K, Task]#l` returns the type lambda `A[x] => K[A[Task[x]]`. */
sealed trait SplitK[K[L[x]], B[x]] { type l[A[x]] = K[(A B)#l] }
/** AList that operates on the outer type constructor `A` of a composition `[x] A[B[x]]` for type constructors `A` and `B`*/
type ASplit[K[L[x]], B[x]] = AList[SplitK[K, B]#l]
/** AList that operates on the outer type constructor `A` of a composition `[x] A[B[x]]` for type constructors `A` and `B`. */
def asplit[K[L[x]], B[x]](base: AList[K]): ASplit[K, B] = new ASplit[K, B] {
type Split[L[x]] = K[(L B)#l]

View File

@ -216,16 +216,17 @@ private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any])
def contains[T](k: AttributeKey[T]) = backing.contains(k)
def put[T](k: AttributeKey[T], value: T): AttributeMap =
new BasicAttributeMap(backing.updated(k, value))
new BasicAttributeMap(backing.updated(k, value: Any))
def keys: Iterable[AttributeKey[_]] = backing.keys
def ++(o: Iterable[AttributeEntry[_]]): AttributeMap =
new BasicAttributeMap(o.foldLeft(backing)((b, e) => b.updated(e.key, e.value)))
new BasicAttributeMap(o.foldLeft(backing)((b, e) => b.updated(e.key, e.value: Any)))
def ++(o: AttributeMap): AttributeMap = o match {
case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing)
case _ => o ++ this
case bam: BasicAttributeMap =>
new BasicAttributeMap(Map(backing.toSeq ++ bam.backing.toSeq: _*))
case _ => o ++ this
}
def entries: Iterable[AttributeEntry[_]] =

View File

@ -13,7 +13,7 @@ import sjsonnew._
import Types.:+:
trait HListFormats {
implicit val lnilFormat1: JsonFormat[HNil] = forHNil(HNil)
implicit val lnilFormat1: JsonFormat[HNil] = forHNil(HNil: HNil)
implicit val lnilFormat2: JsonFormat[HNil.type] = forHNil(HNil)
private def forHNil[A <: HNil](hnil: A): JsonFormat[A] = new JsonFormat[A] {
@ -73,7 +73,7 @@ trait HListFormats {
}
}
implicit val lnilHListJF1: HListJF[HNil] = hnilHListJF(HNil)
implicit val lnilHListJF1: HListJF[HNil] = hnilHListJF(HNil: HNil)
implicit val lnilHListJF2: HListJF[HNil.type] = hnilHListJF(HNil)
implicit def hnilHListJF[A <: HNil](hnil: A): HListJF[A] = new HListJF[A] {

View File

@ -25,9 +25,11 @@ trait IDSet[T] {
object IDSet {
implicit def toTraversable[T]: IDSet[T] => Traversable[T] = _.all
def apply[T](values: T*): IDSet[T] = apply(values)
def apply[T](values: T*): IDSet[T] = fromIterable(values)
def apply[T](values: Iterable[T]): IDSet[T] = {
def apply[T](values: Iterable[T]): IDSet[T] = fromIterable(values)
private def fromIterable[T](values: Iterable[T]): IDSet[T] = {
val s = create[T]
s ++= values
s

View File

@ -42,8 +42,8 @@ abstract class EvaluateSettings[ScopeType] {
)
case b: Bind[s, A1$] @unchecked => new BindNode[s, A1$](transform(b.in), x => transform(b.f(x)))
case v: Value[A1$] @unchecked => constant(v.value)
case v: ValidationCapture[A1$] @unchecked => strictConstant(v.key)
case t: TransformCapture => strictConstant(t.f)
case v: ValidationCapture[A1$] @unchecked => strictConstant(v.key: A1$)
case t: TransformCapture => strictConstant(t.f: A1$)
case o: Optional[s, A1$] @unchecked =>
o.a match {
case None => constant(() => o.f(None))
@ -120,7 +120,8 @@ abstract class EvaluateSettings[ScopeType] {
private[this] def keyString =
(static.toSeq.flatMap {
case (key, value) => if (value eq this) init.showFullKey.show(key) :: Nil else Nil
case (key, value) =>
if (value eq this) init.showFullKey.show(key) :: Nil else List.empty[String]
}).headOption getOrElse "non-static"
final def get: T = synchronized {
@ -130,7 +131,10 @@ abstract class EvaluateSettings[ScopeType] {
final def doneOrBlock(from: INode[_]): Boolean = synchronized {
val ready = state == Evaluated
if (!ready) blocking += from
if (!ready) {
blocking += from
()
}
registerIfNew()
ready
}
@ -191,9 +195,10 @@ abstract class EvaluateSettings[ScopeType] {
registerIfNew()
state match {
case Evaluated => submitCallComplete(by, value)
case _ => calledBy += by
case _ =>
calledBy += by
()
}
()
}
protected def dependsOn: Seq[INode[_]]

View File

@ -62,7 +62,7 @@ sealed abstract class KNil extends KList[NothingK] {
final def apply[N[x], Z](f: KNil => Z)(implicit ap: Applicative[N]): N[Z] = ap.pure(f(KNil))
final def traverse[N[_], P[_]](f: NothingK ~> (N P)#l)(implicit np: Applicative[N]): N[KNil] =
np.pure(KNil)
np.pure(KNil: KNil)
}
case object KNil extends KNil {

View File

@ -72,8 +72,8 @@ object IMap {
val mapped = backing.iterator.map {
case (k, v) =>
f(v) match {
case Left(l) => Left((k, l))
case Right(r) => Right((k, r))
case Left(l) => Left((k, l)): Either[(K[_], VL[_]), (K[_], VR[_])]
case Right(r) => Right((k, r)): Either[(K[_], VL[_]), (K[_], VR[_])]
}
}
val (l, r) = Util.separateE[(K[_], VL[_]), (K[_], VR[_])](mapped.toList)

View File

@ -11,6 +11,7 @@ import scala.language.existentials
import Types._
import sbt.util.Show
import Util.{ nil, nilSeq }
sealed trait Settings[ScopeType] {
def data: Map[ScopeType, AttributeMap]
@ -177,7 +178,7 @@ trait Init[ScopeType] {
val (defaults, others) = Util.separate[Setting[_], DefaultSetting[_], Setting[_]](ss) {
case u: DefaultSetting[_] => Left(u); case s => Right(s)
}
defaults.distinct ++ others
(defaults.distinct: Seq[Setting[_]]) ++ others
}
def compiled(init: Seq[Setting[_]], actual: Boolean = true)(
@ -382,10 +383,16 @@ trait Init[ScopeType] {
def flattenLocals(compiled: CompiledMap): Map[ScopedKey[_], Flattened] = {
val locals = compiled flatMap {
case (key, comp) => if (key.key.isLocal) Seq[Compiled[_]](comp) else Nil
case (key, comp) =>
if (key.key.isLocal) Seq(comp)
else nilSeq[Compiled[_]]
}
val ordered = Dag.topologicalSort(locals)(
_.dependencies.flatMap(dep => if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil)
_.dependencies.flatMap(
dep =>
if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep))
else nilSeq[Compiled[_]]
)
)
def flatten(
cmap: Map[ScopedKey[_], Flattened],
@ -394,7 +401,9 @@ trait Init[ScopeType] {
): Flattened =
new Flattened(
key,
deps.flatMap(dep => if (dep.key.isLocal) cmap(dep).dependencies else dep :: Nil)
deps.flatMap(
dep => if (dep.key.isLocal) cmap(dep).dependencies else Seq[ScopedKey[_]](dep).toIterable
)
)
val empty = Map.empty[ScopedKey[_], Flattened]
@ -405,8 +414,7 @@ trait Init[ScopeType] {
compiled flatMap {
case (key, comp) =>
if (key.key.isLocal)
Nil
if (key.key.isLocal) nilSeq[(ScopedKey[_], Flattened)]
else
Seq[(ScopedKey[_], Flattened)]((key, flatten(flattenedLocals, key, comp.dependencies)))
}
@ -515,8 +523,8 @@ trait Init[ScopeType] {
d.outputs ++= out
out
} else
Nil
} getOrElse Nil
nilSeq
} getOrElse nilSeq
}
derivedForKey.flatMap(localAndDerived)
}
@ -527,7 +535,7 @@ trait Init[ScopeType] {
def process(rem: List[Setting[_]]): Unit = rem match {
case s :: ss =>
val sk = s.key
val ds = if (processed.add(sk)) deriveFor(sk) else Nil
val ds = if (processed.add(sk)) deriveFor(sk) else nil
addDefs(ds)
process(ds ::: ss)
case Nil =>
@ -539,7 +547,7 @@ trait Init[ScopeType] {
val allDefs = addLocal(init)(scopeLocal)
allDefs.flatMap {
case d: DerivedSetting[_] => (derivedToStruct get d map (_.outputs)).toSeq.flatten
case s => s :: Nil
case s => s :: nil
}
}
@ -608,7 +616,8 @@ trait Init[ScopeType] {
) extends SettingsDefinition {
def settings = this :: Nil
def definitive: Boolean = !init.dependencies.contains(key)
def dependencies: Seq[ScopedKey[_]] = remove(init.dependencies, key)
def dependencies: Seq[ScopedKey[_]] =
remove(init.dependencies.asInstanceOf[Seq[ScopedKey[T]]], key)
def mapReferenced(g: MapScoped): Setting[T] = make(key, init mapReferenced g, pos)
def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] =

View File

@ -16,7 +16,7 @@ object Signals {
try {
val signals = new Signals0
signals.withHandler(signal, handler, action)
} catch { case _: LinkageError => Right(action()) }
} catch { case _: LinkageError => Right(action()): Either[Throwable, T] }
result match {
case Left(e) => throw e

View File

@ -55,4 +55,12 @@ object Util {
lazy val isNonCygwinWindows: Boolean = isWindows && !isCygwin
lazy val isCygwinWindows: Boolean = isWindows && isCygwin
def nil[A]: List[A] = List.empty[A]
def nilSeq[A]: Seq[A] = Seq.empty[A]
def none[A]: Option[A] = (None: Option[A])
implicit class AnyOps[A](private val value: A) extends AnyVal {
def some: Option[A] = (Some(value): Option[A])
}
}

View File

@ -108,8 +108,10 @@ private[sbt] object JLine {
case "jline.UnsupportedTerminal" => "none"
case x => x
}
if (newValue != null) System.setProperty(TerminalProperty, newValue)
()
if (newValue != null) {
System.setProperty(TerminalProperty, newValue)
()
}
}
protected[this] val originalIn = new FileInputStream(FileDescriptor.in)
@ -149,7 +151,7 @@ private[sbt] object JLine {
cr.setBellEnabled(false)
val h = historyPath match {
case None => new MemoryHistory
case Some(file) => new FileHistory(file)
case Some(file) => (new FileHistory(file): MemoryHistory)
}
h.setMaxSize(MaxHistorySize)
cr.setHistory(h)

View File

@ -9,6 +9,7 @@ package sbt.internal.util
package complete
import sbt.io.IO
import Util.{ AnyOps, nil }
object HistoryCommands {
val Start = "!"
@ -57,7 +58,7 @@ object HistoryCommands {
lazy val last = Last ^^^ { execute(_.!!) }
lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => (h: History) =>
{ printHistory(h, MaxLines, show); Some(Nil) }
{ printHistory(h, MaxLines, show); nil[String].some }
}
lazy val execStr = flag('?') ~ token(any.+.string, "<string>") map {
@ -70,7 +71,7 @@ object HistoryCommands {
execute(h => if (neg) h !- value else h ! value)
}
lazy val help = success((h: History) => { printHelp(); Some(Nil) })
lazy val help = success((h: History) => { printHelp(); nil[String].some })
def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) => {
val command = f(h).filterNot(_.startsWith(Start))
@ -79,7 +80,7 @@ object HistoryCommands {
h.path foreach { h =>
IO.writeLines(h, lines)
}
Some(command.toList)
command.toList.some
}
val actionParser: Parser[complete.History => Option[List[String]]] =

View File

@ -319,7 +319,7 @@ trait ParserMain {
implicit def richParser[A](a: Parser[A]): RichParser[A] = new RichParser[A] {
def ~[B](b: Parser[B]) = seqParser(a, b)
def ||[B](b: Parser[B]) = choiceParser(a, b)
def |[B >: A](b: Parser[B]) = homParser(a, b)
def |[B >: A](b: Parser[B]) = homParser[B](a, b)
def ? = opt(a)
def * = zeroOrMore(a)
def + = oneOrMore(a)
@ -327,7 +327,9 @@ trait ParserMain {
def id = a
def ^^^[B](value: B): Parser[B] = a map (_ => value)
def ??[B >: A](alt: B): Parser[B] = a.? map { _ getOrElse alt }
def ??[B >: A](alt: B): Parser[B] = a.? map { x =>
x.getOrElse[B](alt)
}
def <~[B](b: Parser[B]): Parser[A] = (a ~ b) map { case av ~ _ => av }
def ~>[B](b: Parser[B]): Parser[B] = (a ~ b) map { case _ ~ bv => bv }
def !!!(msg: String): Parser[A] = onFailure(a, msg)
@ -477,7 +479,7 @@ trait ParserMain {
if (ci >= s.length)
a.resultEmpty.toEither.left.map { msgs0 => () =>
val msgs = msgs0()
val nonEmpty = if (msgs.isEmpty) "Unexpected end of input" :: Nil else msgs
val nonEmpty = if (msgs.isEmpty) Seq("Unexpected end of input") else msgs
(nonEmpty, ci)
} else
loop(ci, a derive s(ci))
@ -601,7 +603,8 @@ trait ParserMain {
def seq0[T](p: Seq[Parser[T]], errors: => Seq[String]): Parser[Seq[T]] = {
val (newErrors, valid) = separate(p) {
case Invalid(f) => Left(f.errors _); case ok => Right(ok)
case Invalid(f) => Left(f.errors _): Either[() => Seq[String], Parser[T]]
case ok => Right(ok): Either[() => Seq[String], Parser[T]]
}
def combinedErrors = errors ++ newErrors.flatMap(_())
if (valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors)
@ -966,10 +969,11 @@ private final class Repeat[T](
lazy val resultEmpty: Result[Seq[T]] = {
val partialAccumulatedOption =
partial match {
case None => Value(accumulatedReverse)
case Some(partialPattern) => partialPattern.resultEmpty.map(_ :: accumulatedReverse)
case None => (Value(accumulatedReverse): Result[List[T]])
case Some(partialPattern) =>
partialPattern.resultEmpty.map(_ :: accumulatedReverse)
}
(partialAccumulatedOption app repeatedParseEmpty)(_ reverse_::: _)
(partialAccumulatedOption app repeatedParseEmpty)((x, y) => (x reverse_::: y): Seq[T])
}
private def repeatedParseEmpty: Result[List[T]] = {

View File

@ -22,6 +22,7 @@ import java.lang.Character.{
}
import scala.annotation.tailrec
import sbt.internal.util.Util.nilSeq
/** Provides standard implementations of commonly useful [[Parser]]s. */
trait Parsers {
@ -130,7 +131,7 @@ trait Parsers {
* Matches a non-empty String consisting of whitespace characters.
* The suggested tab completion is a single, constant space character.
*/
lazy val Space = SpaceClass.+.examples(" ")
lazy val Space: Parser[Seq[Char]] = SpaceClass.+.examples(" ")
/**
* Matches a possibly empty String consisting of whitespace characters.
@ -274,7 +275,7 @@ trait Parsers {
* The result is the (possibly empty) sequence of results from the multiple `rep` applications. The `sep` results are discarded.
*/
def repsep[T](rep: Parser[T], sep: Parser[_]): Parser[Seq[T]] =
rep1sep(rep, sep) ?? Nil
rep1sep(rep, sep) ?? nilSeq[T]
/**
* Applies `rep` one or more times, separated by `sep`.

View File

@ -0,0 +1,3 @@
Simple Build Tool: Control Component
Copyright 2009 Mark Harrah
Licensed under BSD-style license (see LICENSE)

View File

@ -0,0 +1,49 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import java.io.IOException
object ErrorHandling {
def translate[T](msg: => String)(f: => T) =
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 {
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) }
def reducedToString(e: Throwable): String =
if (e.getClass == classOf[RuntimeException]) {
val msg = e.getMessage
if (msg == null || msg.isEmpty) e.toString else msg
} else
e.toString
}
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)

View File

@ -0,0 +1,28 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
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)
}

View File

@ -0,0 +1,28 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
final class MessageOnlyException(override val toString: String) extends RuntimeException(toString)
/**
* A dummy exception for the top-level exception handler to know that an exception
* has been handled, but is being passed further up to indicate general failure.
*/
final class AlreadyHandledException(val underlying: Throwable) extends RuntimeException
/**
* A marker trait for a top-level exception handler to know that this exception
* doesn't make sense to display.
*/
trait UnprintableException extends Throwable
/**
* A marker trait that refines UnprintableException to indicate to a top-level exception handler
* that the code throwing this exception has already provided feedback to the user about the error condition.
*/
trait FeedbackProvidedException extends UnprintableException

View File

@ -0,0 +1,22 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package xsbti;
import java.util.function.Supplier;
public interface Logger {
void error(Supplier<String> msg);
void warn(Supplier<String> msg);
void info(Supplier<String> msg);
void debug(Supplier<String> msg);
void trace(Supplier<Throwable> exception);
}

View File

@ -0,0 +1,53 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package xsbti;
import java.io.File;
import java.util.Optional;
public interface Position {
Optional<Integer> line();
String lineContent();
Optional<Integer> offset();
// pointer to the column position of the error/warning
Optional<Integer> pointer();
Optional<String> pointerSpace();
Optional<String> sourcePath();
Optional<File> sourceFile();
// Default values to avoid breaking binary compatibility
default Optional<Integer> startOffset() {
return Optional.empty();
}
default Optional<Integer> endOffset() {
return Optional.empty();
}
default Optional<Integer> startLine() {
return Optional.empty();
}
default Optional<Integer> startColumn() {
return Optional.empty();
}
default Optional<Integer> endLine() {
return Optional.empty();
}
default Optional<Integer> endColumn() {
return Optional.empty();
}
}

View File

@ -0,0 +1,29 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package xsbti;
import java.util.Optional;
public interface Problem {
String category();
Severity severity();
String message();
Position position();
// Default value to avoid breaking binary compatibility
/**
* If present, the string shown to the user when displaying this Problem. Otherwise, the Problem
* will be shown in an implementation-defined way based on the values of its other fields.
*/
default Optional<String> rendered() {
return Optional.empty();
}
}

View File

@ -0,0 +1,14 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package xsbti;
public enum Severity {
Info,
Warn,
Error
}

View File

@ -0,0 +1,15 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package xsbti;
/** Used to pass a pair of values. */
public interface T2<A1, A2> {
public A1 get1();
public A2 get2();
}

View File

@ -0,0 +1,3 @@
Simple Build Tool: Logging Component
Copyright 2008, 2009, 2010 Mark Harrah, Tony Sloane
Licensed under BSD-style license (see LICENSE)

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util
abstract class AbstractEntry(
val channelName: Option[String],
val execId: Option[String]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: AbstractEntry => (this.channelName == x.channelName) && (this.execId == x.execId)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.util.AbstractEntry".##) + channelName.##) + execId.##)
}
override def toString: String = {
"AbstractEntry(" + channelName + ", " + execId + ")"
}
}
object AbstractEntry {
}

View File

@ -0,0 +1,15 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util
/** value for logging options like color */
sealed abstract class LogOption extends Serializable
object LogOption {
case object Always extends LogOption
case object Never extends LogOption
case object Auto extends LogOption
}

View File

@ -0,0 +1,59 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util
/** used by super shell */
final class ProgressEvent private (
val level: String,
val items: Vector[sbt.internal.util.ProgressItem],
val lastTaskCount: Option[Int],
channelName: Option[String],
execId: Option[String]) extends sbt.internal.util.AbstractEntry(channelName, execId) with Serializable {
override def equals(o: Any): Boolean = o match {
case x: ProgressEvent => (this.level == x.level) && (this.items == x.items) && (this.lastTaskCount == x.lastTaskCount) && (this.channelName == x.channelName) && (this.execId == x.execId)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.util.ProgressEvent".##) + level.##) + items.##) + lastTaskCount.##) + channelName.##) + execId.##)
}
override def toString: String = {
"ProgressEvent(" + level + ", " + items + ", " + lastTaskCount + ", " + channelName + ", " + execId + ")"
}
private[this] def copy(level: String = level, items: Vector[sbt.internal.util.ProgressItem] = items, lastTaskCount: Option[Int] = lastTaskCount, channelName: Option[String] = channelName, execId: Option[String] = execId): ProgressEvent = {
new ProgressEvent(level, items, lastTaskCount, channelName, execId)
}
def withLevel(level: String): ProgressEvent = {
copy(level = level)
}
def withItems(items: Vector[sbt.internal.util.ProgressItem]): ProgressEvent = {
copy(items = items)
}
def withLastTaskCount(lastTaskCount: Option[Int]): ProgressEvent = {
copy(lastTaskCount = lastTaskCount)
}
def withLastTaskCount(lastTaskCount: Int): ProgressEvent = {
copy(lastTaskCount = Option(lastTaskCount))
}
def withChannelName(channelName: Option[String]): ProgressEvent = {
copy(channelName = channelName)
}
def withChannelName(channelName: String): ProgressEvent = {
copy(channelName = Option(channelName))
}
def withExecId(execId: Option[String]): ProgressEvent = {
copy(execId = execId)
}
def withExecId(execId: String): ProgressEvent = {
copy(execId = Option(execId))
}
}
object ProgressEvent {
def apply(level: String, items: Vector[sbt.internal.util.ProgressItem], lastTaskCount: Option[Int], channelName: Option[String], execId: Option[String]): ProgressEvent = new ProgressEvent(level, items, lastTaskCount, channelName, execId)
def apply(level: String, items: Vector[sbt.internal.util.ProgressItem], lastTaskCount: Int, channelName: String, execId: String): ProgressEvent = new ProgressEvent(level, items, Option(lastTaskCount), Option(channelName), Option(execId))
}

View File

@ -0,0 +1,41 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util
/**
* used by super shell
* @param name name of a task
* @param elapsedMicros current elapsed time in micro seconds
*/
final class ProgressItem private (
val name: String,
val elapsedMicros: Long) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ProgressItem => (this.name == x.name) && (this.elapsedMicros == x.elapsedMicros)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.util.ProgressItem".##) + name.##) + elapsedMicros.##)
}
override def toString: String = {
"ProgressItem(" + name + ", " + elapsedMicros + ")"
}
private[this] def copy(name: String = name, elapsedMicros: Long = elapsedMicros): ProgressItem = {
new ProgressItem(name, elapsedMicros)
}
def withName(name: String): ProgressItem = {
copy(name = name)
}
def withElapsedMicros(elapsedMicros: Long): ProgressItem = {
copy(elapsedMicros = elapsedMicros)
}
}
object ProgressItem {
def apply(name: String, elapsedMicros: Long): ProgressItem = new ProgressItem(name, elapsedMicros)
}

View File

@ -0,0 +1,51 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util
final class StringEvent private (
val level: String,
val message: String,
channelName: Option[String],
execId: Option[String]) extends sbt.internal.util.AbstractEntry(channelName, execId) with Serializable {
override def equals(o: Any): Boolean = o match {
case x: StringEvent => (this.level == x.level) && (this.message == x.message) && (this.channelName == x.channelName) && (this.execId == x.execId)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.util.StringEvent".##) + level.##) + message.##) + channelName.##) + execId.##)
}
override def toString: String = {
"StringEvent(" + level + ", " + message + ", " + channelName + ", " + execId + ")"
}
private[this] def copy(level: String = level, message: String = message, channelName: Option[String] = channelName, execId: Option[String] = execId): StringEvent = {
new StringEvent(level, message, channelName, execId)
}
def withLevel(level: String): StringEvent = {
copy(level = level)
}
def withMessage(message: String): StringEvent = {
copy(message = message)
}
def withChannelName(channelName: Option[String]): StringEvent = {
copy(channelName = channelName)
}
def withChannelName(channelName: String): StringEvent = {
copy(channelName = Option(channelName))
}
def withExecId(execId: Option[String]): StringEvent = {
copy(execId = execId)
}
def withExecId(execId: String): StringEvent = {
copy(execId = Option(execId))
}
}
object StringEvent {
def apply(level: String, message: String, channelName: Option[String], execId: Option[String]): StringEvent = new StringEvent(level, message, channelName, execId)
def apply(level: String, message: String, channelName: String, execId: String): StringEvent = new StringEvent(level, message, Option(channelName), Option(execId))
}

View File

@ -0,0 +1,32 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util
final class SuccessEvent private (
val message: String) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: SuccessEvent => (this.message == x.message)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + "sbt.internal.util.SuccessEvent".##) + message.##)
}
override def toString: String = {
"SuccessEvent(" + message + ")"
}
private[this] def copy(message: String = message): SuccessEvent = {
new SuccessEvent(message)
}
def withMessage(message: String): SuccessEvent = {
copy(message = message)
}
}
object SuccessEvent {
def apply(message: String): SuccessEvent = new SuccessEvent(message)
}

View File

@ -0,0 +1,51 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util
final class TraceEvent private (
val level: String,
val message: Throwable,
channelName: Option[String],
execId: Option[String]) extends sbt.internal.util.AbstractEntry(channelName, execId) with Serializable {
override def equals(o: Any): Boolean = o match {
case x: TraceEvent => (this.level == x.level) && (this.message == x.message) && (this.channelName == x.channelName) && (this.execId == x.execId)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.util.TraceEvent".##) + level.##) + message.##) + channelName.##) + execId.##)
}
override def toString: String = {
"TraceEvent(" + level + ", " + message + ", " + channelName + ", " + execId + ")"
}
private[this] def copy(level: String = level, message: Throwable = message, channelName: Option[String] = channelName, execId: Option[String] = execId): TraceEvent = {
new TraceEvent(level, message, channelName, execId)
}
def withLevel(level: String): TraceEvent = {
copy(level = level)
}
def withMessage(message: Throwable): TraceEvent = {
copy(message = message)
}
def withChannelName(channelName: Option[String]): TraceEvent = {
copy(channelName = channelName)
}
def withChannelName(channelName: String): TraceEvent = {
copy(channelName = Option(channelName))
}
def withExecId(execId: Option[String]): TraceEvent = {
copy(execId = execId)
}
def withExecId(execId: String): TraceEvent = {
copy(execId = Option(execId))
}
}
object TraceEvent {
def apply(level: String, message: Throwable, channelName: Option[String], execId: Option[String]): TraceEvent = new TraceEvent(level, message, channelName, execId)
def apply(level: String, message: Throwable, channelName: String, execId: String): TraceEvent = new TraceEvent(level, message, Option(channelName), Option(execId))
}

View File

@ -0,0 +1,11 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util.codec
import _root_.sjsonnew.JsonFormat
trait AbstractEntryFormats { self: sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.StringEventFormats with sbt.internal.util.codec.TraceEventFormats with sbt.internal.util.codec.ProgressItemFormats with sbt.internal.util.codec.ProgressEventFormats =>
implicit lazy val AbstractEntryFormat: JsonFormat[sbt.internal.util.AbstractEntry] = flatUnionFormat3[sbt.internal.util.AbstractEntry, sbt.internal.util.StringEvent, sbt.internal.util.TraceEvent, sbt.internal.util.ProgressEvent]("type")
}

View File

@ -0,0 +1,15 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util.codec
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.util.codec.StringEventFormats
with sbt.internal.util.codec.TraceEventFormats
with sbt.internal.util.codec.ProgressItemFormats
with sbt.internal.util.codec.ProgressEventFormats
with sbt.internal.util.codec.AbstractEntryFormats
with sbt.internal.util.codec.SuccessEventFormats
with sbt.internal.util.codec.LogOptionFormats
object JsonProtocol extends JsonProtocol

View File

@ -0,0 +1,31 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait LogOptionFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val LogOptionFormat: JsonFormat[sbt.internal.util.LogOption] = new JsonFormat[sbt.internal.util.LogOption] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.LogOption = {
__jsOpt match {
case Some(__js) =>
unbuilder.readString(__js) match {
case "Always" => sbt.internal.util.LogOption.Always
case "Never" => sbt.internal.util.LogOption.Never
case "Auto" => sbt.internal.util.LogOption.Auto
}
case None =>
deserializationError("Expected JsString but found None")
}
}
override def write[J](obj: sbt.internal.util.LogOption, builder: Builder[J]): Unit = {
val str = obj match {
case sbt.internal.util.LogOption.Always => "Always"
case sbt.internal.util.LogOption.Never => "Never"
case sbt.internal.util.LogOption.Auto => "Auto"
}
builder.writeString(str)
}
}
}

View File

@ -0,0 +1,35 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait ProgressEventFormats { self: sbt.internal.util.codec.ProgressItemFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ProgressEventFormat: JsonFormat[sbt.internal.util.ProgressEvent] = new JsonFormat[sbt.internal.util.ProgressEvent] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.ProgressEvent = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val level = unbuilder.readField[String]("level")
val items = unbuilder.readField[Vector[sbt.internal.util.ProgressItem]]("items")
val lastTaskCount = unbuilder.readField[Option[Int]]("lastTaskCount")
val channelName = unbuilder.readField[Option[String]]("channelName")
val execId = unbuilder.readField[Option[String]]("execId")
unbuilder.endObject()
sbt.internal.util.ProgressEvent(level, items, lastTaskCount, channelName, execId)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.util.ProgressEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("level", obj.level)
builder.addField("items", obj.items)
builder.addField("lastTaskCount", obj.lastTaskCount)
builder.addField("channelName", obj.channelName)
builder.addField("execId", obj.execId)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait ProgressItemFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ProgressItemFormat: JsonFormat[sbt.internal.util.ProgressItem] = new JsonFormat[sbt.internal.util.ProgressItem] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.ProgressItem = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val name = unbuilder.readField[String]("name")
val elapsedMicros = unbuilder.readField[Long]("elapsedMicros")
unbuilder.endObject()
sbt.internal.util.ProgressItem(name, elapsedMicros)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.util.ProgressItem, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("name", obj.name)
builder.addField("elapsedMicros", obj.elapsedMicros)
builder.endObject()
}
}
}

View File

@ -0,0 +1,33 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait StringEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val StringEventFormat: JsonFormat[sbt.internal.util.StringEvent] = new JsonFormat[sbt.internal.util.StringEvent] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.StringEvent = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val level = unbuilder.readField[String]("level")
val message = unbuilder.readField[String]("message")
val channelName = unbuilder.readField[Option[String]]("channelName")
val execId = unbuilder.readField[Option[String]]("execId")
unbuilder.endObject()
sbt.internal.util.StringEvent(level, message, channelName, execId)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.util.StringEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("level", obj.level)
builder.addField("message", obj.message)
builder.addField("channelName", obj.channelName)
builder.addField("execId", obj.execId)
builder.endObject()
}
}
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait SuccessEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val SuccessEventFormat: JsonFormat[sbt.internal.util.SuccessEvent] = new JsonFormat[sbt.internal.util.SuccessEvent] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.SuccessEvent = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val message = unbuilder.readField[String]("message")
unbuilder.endObject()
sbt.internal.util.SuccessEvent(message)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.util.SuccessEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("message", obj.message)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait TaskProgressFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TaskProgressFormat: JsonFormat[sbt.internal.util.TaskProgress] = new JsonFormat[sbt.internal.util.TaskProgress] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.TaskProgress = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val name = unbuilder.readField[String]("name")
val elapsedMicros = unbuilder.readField[Option[Long]]("elapsedMicros")
unbuilder.endObject()
sbt.internal.util.TaskProgress(name, elapsedMicros)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.util.TaskProgress, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("name", obj.name)
builder.addField("elapsedMicros", obj.elapsedMicros)
builder.endObject()
}
}
}

View File

@ -0,0 +1,33 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.util.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait TraceEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TraceEventFormat: JsonFormat[sbt.internal.util.TraceEvent] = new JsonFormat[sbt.internal.util.TraceEvent] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.TraceEvent = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val level = unbuilder.readField[String]("level")
val message = unbuilder.readField[Throwable]("message")
val channelName = unbuilder.readField[Option[String]]("channelName")
val execId = unbuilder.readField[Option[String]]("execId")
unbuilder.endObject()
sbt.internal.util.TraceEvent(level, message, channelName, execId)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.util.TraceEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("level", obj.level)
builder.addField("message", obj.message)
builder.addField("channelName", obj.channelName)
builder.addField("execId", obj.execId)
builder.endObject()
}
}
}

View File

@ -0,0 +1,33 @@
package sbt.internal.util
@target(Java)
@codecPackage("sbt.internal.util.codec")
@fullCodec("JsonProtocol")
enum Severity
{
Info, Warn, Error
}
type Position {
line: Int
lineContent: String!
offset: Int
pointer: Int
pointerSpace: String
sourcePath: String
sourceFile: java.io.File
startOffset: Int
endOffset: Int
startLine: Int
startColumn: Int
endLine: Int
endColumn: Int
}
type Problem {
category: String!
severity: Severity!
message: String!
position: Position!
rendered: String
}

View File

@ -0,0 +1,51 @@
package sbt.internal.util
@target(Scala)
@codecPackage("sbt.internal.util.codec")
@fullCodec("JsonProtocol")
interface AbstractEntry {
channelName: String
execId: String
}
type StringEvent implements sbt.internal.util.AbstractEntry {
level: String!
message: String!
channelName: String
execId: String
}
type TraceEvent implements sbt.internal.util.AbstractEntry {
level: String!
message: Throwable!
channelName: String
execId: String
}
## used by super shell
type ProgressEvent implements sbt.internal.util.AbstractEntry {
level: String!
items: [sbt.internal.util.ProgressItem]
lastTaskCount: Int
channelName: String
execId: String
}
## used by super shell
type ProgressItem {
## name of a task
name: String!
## current elapsed time in micro seconds
elapsedMicros: Long!
}
type SuccessEvent {
message: String!
}
## value for logging options like color
enum LogOption {
Always
Never
Auto
}

View File

@ -0,0 +1,17 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package com.github.ghik.silencer
import scala.annotation.Annotation
/**
* When silencer compiler plugin is enabled, this annotation suppresses all warnings emitted by scalac for some portion
* of source code. It can be applied on any definition (`class`, def`, `val`, `var`, etc.) or on arbitrary expression,
* e.g. {123; 456}: @silent`
*/
class silent extends Annotation

View File

@ -0,0 +1,23 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
/** Implements the level-setting methods of Logger.*/
abstract class BasicLogger extends AbstractLogger {
private var traceEnabledVar: Int = java.lang.Integer.MAX_VALUE
private var level: Level.Value = Level.Info
private var successEnabledVar = true
def successEnabled: Boolean = synchronized { successEnabledVar }
def setSuccessEnabled(flag: Boolean): Unit = synchronized { successEnabledVar = flag }
def getLevel: Level.Value = synchronized { level }
def setLevel(newLevel: Level.Value): Unit = synchronized { level = newLevel }
def setTrace(level: Int): Unit = synchronized { traceEnabledVar = level }
def getTrace: Int = synchronized { traceEnabledVar }
}

View File

@ -0,0 +1,215 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
import scala.collection.mutable.ListBuffer
import org.apache.logging.log4j.core.{ LogEvent => XLogEvent, Appender }
import org.apache.logging.log4j.core.appender.AbstractAppender
import org.apache.logging.log4j.core.layout.PatternLayout
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
}
}
/**
* 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, Array.empty) {
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)
()
}
/** Enables buffering. */
def record() = synchronized { recording = true }
def buffer[T](f: => T): T = {
record()
try {
f
} finally {
stopQuietly()
}
}
def bufferQuietly[T](f: => T): T = {
record()
try {
val result = f
clearBuffer()
result
} catch { case e: Throwable => stopQuietly(); throw e }
}
def stopQuietly() = synchronized {
try {
stopBuffer()
} catch { case _: Exception => () }
}
/**
* Flushes the buffer to the delegate logger. This method calls logAll on the delegate
* so that the messages are written consecutively. The buffer is cleared in the process.
*/
def play(): Unit =
synchronized {
buffer.toList foreach {
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() }
}
/**
* A logger that can buffer the logging done on it and then can flush the buffer
* to the delegate logger provided in the constructor. Use 'startRecording' to
* start buffering and then 'play' from to flush the buffer to the backing logger.
* The logging level set at the time a message is originally logged is used, not
* the level at the time 'play' is called.
*
* This class assumes that it is the only client of the delegate logger.
*/
class BufferedLogger(delegate: AbstractLogger) extends BasicLogger {
private[this] val buffer = new ListBuffer[LogEvent]
private[this] var recording = false
/** Enables buffering. */
def record() = synchronized { recording = true }
def buffer[T](f: => T): T = {
record()
try {
f
} finally {
stopQuietly()
}
}
def bufferQuietly[T](f: => T): T = {
record()
try {
val result = f
clear()
result
} catch { case e: Throwable => stopQuietly(); throw e }
}
def stopQuietly() = synchronized {
try {
stop()
} catch { case _: Exception => () }
}
/**
* Flushes the buffer to the delegate logger. This method calls logAll on the delegate
* 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() }
@deprecated("No longer used.", "1.0.0")
override def ansiCodesSupported = delegate.ansiCodesSupported
override def setLevel(newLevel: Level.Value): Unit = synchronized {
super.setLevel(newLevel)
if (recording)
buffer += new SetLevel(newLevel)
else
delegate.setLevel(newLevel)
()
}
override def setSuccessEnabled(flag: Boolean): Unit = synchronized {
super.setSuccessEnabled(flag)
if (recording)
buffer += new SetSuccess(flag)
else
delegate.setSuccessEnabled(flag)
()
}
override def setTrace(level: Int): Unit = synchronized {
super.setTrace(level)
if (recording)
buffer += new SetTrace(level)
else
delegate.setTrace(level)
()
}
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
else
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 =
doBufferableIf(atLevel(level), appendIfBuffered, doUnbuffered)
private def doBufferableIf(
condition: => Boolean,
appendIfBuffered: => LogEvent,
doUnbuffered: AbstractLogger => Unit
): Unit = synchronized {
if (condition) {
if (recording)
buffer += appendIfBuffered
else
doUnbuffered(delegate)
}
()
}
}

View File

@ -0,0 +1,623 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
import java.io.{ PrintStream, PrintWriter }
import java.lang.StringBuilder
import java.util.Locale
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger, AtomicReference }
import org.apache.logging.log4j.{ Level => XLevel }
import org.apache.logging.log4j.message.{ Message, ObjectMessage, ReusableObjectMessage }
import org.apache.logging.log4j.core.{ LogEvent => XLogEvent }
import org.apache.logging.log4j.core.appender.AbstractAppender
import scala.util.control.NonFatal
import ConsoleAppender._
object ConsoleLogger {
// These are provided so other modules do not break immediately.
@deprecated("Use EscHelpers.ESC instead", "0.13.x")
final val ESC = EscHelpers.ESC
@deprecated("Use EscHelpers.isEscapeTerminator instead", "0.13.x")
private[sbt] def isEscapeTerminator(c: Char): Boolean = EscHelpers.isEscapeTerminator(c)
@deprecated("Use EscHelpers.hasEscapeSequence instead", "0.13.x")
def hasEscapeSequence(s: String): Boolean = EscHelpers.hasEscapeSequence(s)
@deprecated("Use EscHelpers.removeEscapeSequences instead", "0.13.x")
def removeEscapeSequences(s: String): String = EscHelpers.removeEscapeSequences(s)
@deprecated("Use ConsoleAppender.formatEnabledInEnv instead", "0.13.x")
val formatEnabled = ConsoleAppender.formatEnabledInEnv
@deprecated("Use ConsoleAppender.noSuppressedMessage instead", "0.13.x")
val noSuppressedMessage = ConsoleAppender.noSuppressedMessage
/**
* A new `ConsoleLogger` that logs to `out`.
*
* @param out Where to log the messages.
* @return A new `ConsoleLogger` that logs to `out`.
*/
def apply(out: PrintStream): ConsoleLogger = apply(ConsoleOut.printStreamOut(out))
/**
* A new `ConsoleLogger` that logs to `out`.
*
* @param out Where to log the messages.
* @return A new `ConsoleLogger` that logs to `out`.
*/
def apply(out: PrintWriter): ConsoleLogger = apply(ConsoleOut.printWriterOut(out))
/**
* A new `ConsoleLogger` that logs to `out`.
*
* @param out Where to log the messages.
* @param ansiCodesSupported `true` if `out` supported ansi codes, `false` otherwise.
* @param useFormat `true` to show formatting, `false` to remove it from messages.
* @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 =
new ConsoleLogger(out, ansiCodesSupported, useFormat, suppressedMessage)
}
/**
* 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 {
private[sbt] val appender: ConsoleAppender =
ConsoleAppender(generateName(), out, ansiCodesSupported, useFormat, suppressedMessage)
override def control(event: ControlEvent.Value, message: => String): Unit =
appender.control(event, message)
override def log(level: Level.Value, message: => String): Unit =
if (atLevel(level)) {
appender.appendLog(level, message)
}
override def success(message: => String): Unit =
if (successEnabled) {
appender.success(message)
}
override def trace(t: => Throwable): Unit =
appender.trace(t, getTrace)
override def logAll(events: Seq[LogEvent]) =
out.lockObject.synchronized { events.foreach(log) }
}
object ConsoleAppender {
private[sbt] def cursorUp(n: Int): String = s"\u001B[${n}A"
private[sbt] def cursorDown(n: Int): String = s"\u001B[${n}B"
private[sbt] def scrollUp(n: Int): String = s"\u001B[${n}S"
private[sbt] final val DeleteLine = "\u001B[2K"
private[sbt] final val CursorLeft1000 = "\u001B[1000D"
private[sbt] final val CursorDown1 = cursorDown(1)
private[sbt] lazy val terminalWidth = usingTerminal { t =>
t.getWidth
}
private[this] val showProgressHolder: AtomicBoolean = new AtomicBoolean(false)
def setShowProgress(b: Boolean): Unit = showProgressHolder.set(b)
def showProgress: Boolean = showProgressHolder.get
/** Hide stack trace altogether. */
val noSuppressedMessage = (_: SuppressedTraceContext) => None
/**
* Indicates whether formatting has been disabled in environment variables.
* 1. -Dsbt.log.noformat=true means no formatting.
* 2. -Dsbt.color=always/auto/never/true/false
* 3. -Dsbt.colour=always/auto/never/true/false
* 4. -Dsbt.log.format=always/auto/never/true/false
*/
val formatEnabledInEnv: Boolean = {
def useColorDefault: Boolean = {
// This approximates that both stdin and stdio are connected,
// so by default color will be turned off for pipes and redirects.
val hasConsole = Option(java.lang.System.console).isDefined
ansiSupported && hasConsole
}
sys.props.get("sbt.log.noformat") match {
case Some(_) => !java.lang.Boolean.getBoolean("sbt.log.noformat")
case _ =>
sys.props
.get("sbt.color")
.orElse(sys.props.get("sbt.colour"))
.orElse(sys.props.get("sbt.log.format"))
.flatMap({ s =>
parseLogOption(s) match {
case LogOption.Always => Some(true)
case LogOption.Never => Some(false)
case _ => None
}
})
.getOrElse(useColorDefault)
}
}
private[sbt] def parseLogOption(s: String): LogOption =
s.toLowerCase match {
case "always" => LogOption.Always
case "auto" => LogOption.Auto
case "never" => LogOption.Never
case "true" => LogOption.Always
case "false" => LogOption.Never
case _ => LogOption.Auto
}
private[this] val generateId: AtomicInteger = new AtomicInteger
/**
* A new `ConsoleAppender` that writes to standard output.
*
* @return A new `ConsoleAppender` that writes to standard output.
*/
def apply(): ConsoleAppender = apply(ConsoleOut.systemOut)
/**
* A new `ConsoleAppender` that appends log message to `out`.
*
* @param out Where to write messages.
* @return A new `ConsoleAppender`.
*/
def apply(out: PrintStream): ConsoleAppender = apply(ConsoleOut.printStreamOut(out))
/**
* A new `ConsoleAppender` that appends log messages to `out`.
*
* @param out Where to write messages.
* @return A new `ConsoleAppender`.
*/
def apply(out: PrintWriter): ConsoleAppender = apply(ConsoleOut.printWriterOut(out))
/**
* A new `ConsoleAppender` that writes to `out`.
*
* @param out Where to write messages.
* @return A new `ConsoleAppender that writes to `out`.
*/
def apply(out: ConsoleOut): ConsoleAppender = apply(generateName(), out)
/**
* A new `ConsoleAppender` identified by `name`, and that writes to standard output.
*
* @param name An identifier for the `ConsoleAppender`.
* @return A new `ConsoleAppender` that writes to standard output.
*/
def apply(name: String): ConsoleAppender = apply(name, ConsoleOut.systemOut)
/**
* A new `ConsoleAppender` identified by `name`, and that writes to `out`.
*
* @param name An identifier for the `ConsoleAppender`.
* @param out Where to write messages.
* @return A new `ConsoleAppender` that writes to `out`.
*/
def apply(name: String, out: ConsoleOut): ConsoleAppender = apply(name, out, formatEnabledInEnv)
/**
* A new `ConsoleAppender` identified by `name`, and that writes to `out`.
*
* @param name An identifier for the `ConsoleAppender`.
* @param out Where to write messages.
* @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 =
apply(name, out, formatEnabledInEnv, formatEnabledInEnv, suppressedMessage)
/**
* A new `ConsoleAppender` identified by `name`, and that writes to `out`.
*
* @param name An identifier for the `ConsoleAppender`.
* @param out Where to write messages.
* @param useFormat `true` to enable format (color, bold, etc.), `false` to remove formatting.
* @return A new `ConsoleAppender` that writes to `out`.
*/
def apply(name: String, out: ConsoleOut, useFormat: Boolean): ConsoleAppender =
apply(name, out, formatEnabledInEnv, useFormat, noSuppressedMessage)
/**
* A new `ConsoleAppender` identified by `name`, and that writes to `out`.
*
* @param name An identifier for the `ConsoleAppender`.
* @param out Where to write messages.
* @param ansiCodesSupported `true` if the output stream supports ansi codes, `false` otherwise.
* @param useFormat `true` to enable format (color, bold, etc.), `false` to remove
* 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
}
/**
* Converts the Log4J `level` to the corresponding sbt level.
*
* @param level A level, as represented by Log4J.
* @return The corresponding level in sbt's world.
*/
def toLevel(level: XLevel): Level.Value =
level match {
case XLevel.OFF => Level.Debug
case XLevel.FATAL => Level.Error
case XLevel.ERROR => Level.Error
case XLevel.WARN => Level.Warn
case XLevel.INFO => Level.Info
case XLevel.DEBUG => Level.Debug
case _ => Level.Debug
}
/**
* Converts the sbt `level` to the corresponding Log4J level.
*
* @param level A level, as represented by sbt.
* @return The corresponding level in Log4J's world.
*/
def toXLevel(level: Level.Value): XLevel =
level match {
case Level.Error => XLevel.ERROR
case Level.Warn => XLevel.WARN
case Level.Info => XLevel.INFO
case Level.Debug => XLevel.DEBUG
}
private[sbt] def generateName(): String = "out-" + generateId.incrementAndGet
private[this] def jline1to2CompatMsg = "Found class jline.Terminal, but interface was expected"
private[this] def ansiSupported =
try {
usingTerminal { t =>
t.isAnsiSupported
}
} catch {
case NonFatal(_) => !isWindows
}
/**
* For accessing the JLine Terminal object.
* This ensures re-enabling echo after getting the Terminal.
*/
private[this] def usingTerminal[T](f: jline.Terminal => T): T = {
val t = jline.TerminalFactory.get
t.restore
val result = f(t)
t.restore
result
}
private[this] def os = System.getProperty("os.name")
private[this] def isWindows = os.toLowerCase(Locale.ENGLISH).indexOf("windows") >= 0
}
// See http://stackoverflow.com/questions/24205093/how-to-create-a-custom-appender-in-log4j2
// for custom appender using Java.
// http://logging.apache.org/log4j/2.x/manual/customconfig.html
// https://logging.apache.org/log4j/2.x/log4j-core/apidocs/index.html
/**
* A logger that logs to the console. On supported systems, the level labels are
* colored.
*
* This logger is not thread-safe.
*/
class ConsoleAppender private[ConsoleAppender] (
name: String,
out: ConsoleOut,
ansiCodesSupported: Boolean,
useFormat: Boolean,
suppressedMessage: SuppressedTraceContext => Option[String]
) extends AbstractAppender(name, null, LogExchange.dummyLayout, true, Array.empty) {
import scala.Console.{ BLUE, GREEN, RED, YELLOW }
private val progressState: AtomicReference[ProgressState] = new AtomicReference(null)
private[sbt] def setProgressState(state: ProgressState) = progressState.set(state)
/**
* Splits a log message into individual lines and interlaces each line with
* the task progress report to reduce the appearance of flickering. It is assumed
* that this method is only called while holding the out.lockObject.
*/
private def supershellInterlaceMsg(msg: String): Unit = {
val state = progressState.get
import state._
val progress = progressLines.get
msg.linesIterator.foreach { l =>
out.println(s"$DeleteLine$l")
if (progress.length > 0) {
val pad = if (padding.get > 0) padding.decrementAndGet() else 0
val width = ConsoleAppender.terminalWidth
val len: Int = progress.foldLeft(progress.length)(_ + terminalLines(width)(_))
deleteConsoleLines(blankZone + pad)
progress.foreach(printProgressLine)
out.print(cursorUp(blankZone + len + padding.get))
}
}
out.flush()
}
private def printProgressLine(line: String): Unit = {
out.print(DeleteLine)
out.println(line)
}
/**
* Receives a new task report and replaces the old one. In the event that the new
* report has fewer lines than the previous report, padding lines are added on top
* so that the console log lines remain contiguous. When a console line is printed
* at the info or greater level, we can decrement the padding because the console
* line will have filled in the blank line.
*/
private def updateProgressState(pe: ProgressEvent): Unit = {
val state = progressState.get
import state._
val sorted = pe.items.sortBy(x => x.elapsedMicros)
val info = sorted map { item =>
val elapsed = item.elapsedMicros / 1000000L
s" | => ${item.name} ${elapsed}s"
}
val width = ConsoleAppender.terminalWidth
val currentLength = info.foldLeft(info.length)(_ + terminalLines(width)(_))
val previousLines = progressLines.getAndSet(info)
val prevLength = previousLines.foldLeft(previousLines.length)(_ + terminalLines(width)(_))
val prevPadding = padding.get
val newPadding = math.max(0, prevLength + prevPadding - currentLength)
padding.set(newPadding)
deleteConsoleLines(newPadding)
deleteConsoleLines(blankZone)
info.foreach(printProgressLine)
out.print(cursorUp(blankZone + currentLength + newPadding))
out.flush()
}
private def terminalLines(width: Int): String => Int =
(progressLine: String) => if (width > 0) (progressLine.length - 1) / width else 0
private def deleteConsoleLines(n: Int): Unit = {
(1 to n) foreach { _ =>
out.println(DeleteLine)
}
}
private val reset: String = {
if (ansiCodesSupported && useFormat) scala.Console.RESET
else ""
}
private val SUCCESS_LABEL_COLOR = GREEN
private val SUCCESS_MESSAGE_COLOR = reset
private val NO_COLOR = reset
private var traceEnabledVar: Int = Int.MaxValue
def setTrace(level: Int): Unit = synchronized { traceEnabledVar = level }
/**
* Returns the number of lines for stacktrace.
*/
def getTrace: Int = synchronized { traceEnabledVar }
override def append(event: XLogEvent): Unit = {
val level = ConsoleAppender.toLevel(event.getLevel)
val message = event.getMessage
appendMessage(level, message)
}
/**
* Logs the stack trace of `t`, possibly shortening it.
*
* The `traceLevel` parameter configures how the stack trace will be shortened.
* See `StackTrace.trimmed`.
*
* @param t The `Throwable` whose stack trace to log.
* @param traceLevel How to shorten the stack trace.
*/
def trace(t: => Throwable, traceLevel: Int): Unit =
out.lockObject.synchronized {
if (traceLevel >= 0)
write(StackTrace.trimmed(t, traceLevel))
if (traceLevel <= 2) {
val ctx = new SuppressedTraceContext(traceLevel, ansiCodesSupported && useFormat)
for (msg <- suppressedMessage(ctx))
appendLog(NO_COLOR, "trace", NO_COLOR, msg)
}
}
/**
* Logs a `ControlEvent` to the log.
*
* @param event The kind of `ControlEvent`.
* @param message The message to log.
*/
def control(event: ControlEvent.Value, message: => String): Unit =
appendLog(labelColor(Level.Info), Level.Info.toString, BLUE, message)
/**
* Appends the message `message` to the to the log at level `level`.
*
* @param level The importance level of the message.
* @param message The message to log.
*/
def appendLog(level: Level.Value, message: => String): Unit = {
appendLog(labelColor(level), level.toString, NO_COLOR, message)
}
/**
* Formats `msg` with `format, wrapped between `RESET`s
*
* @param format The format to use
* @param msg The message to format
* @return The formatted message.
*/
private def formatted(format: String, msg: String): String = {
val builder = new java.lang.StringBuilder(reset.length * 2 + format.length + msg.length)
builder.append(reset).append(format).append(msg).append(reset).toString
}
/**
* Select the right color for the label given `level`.
*
* @param level The label to consider to select the color.
* @return The color to use to color the label.
*/
private def labelColor(level: Level.Value): String =
level match {
case Level.Error => RED
case Level.Warn => YELLOW
case _ => NO_COLOR
}
/**
* Appends a full message to the log. Each line is prefixed with `[$label]`, written in
* `labelColor` if formatting is enabled. The lines of the messages are colored with
* `messageColor` if formatting is enabled.
*
* @param labelColor The color to use to format the label.
* @param label The label to prefix each line with. The label is shown between square
* brackets.
* @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 =
out.lockObject.synchronized {
val builder: StringBuilder =
new StringBuilder(labelColor.length + label.length + messageColor.length + reset.length * 3)
message.linesIterator.foreach { line =>
builder.ensureCapacity(
labelColor.length + label.length + messageColor.length + line.length + reset.length * 3 + 3
)
builder.setLength(0)
def fmted(a: String, b: String) = builder.append(reset).append(a).append(b).append(reset)
builder.append(reset).append('[')
fmted(labelColor, label)
builder.append("] ")
fmted(messageColor, line)
write(builder.toString)
}
}
// success is called by ConsoleLogger.
private[sbt] def success(message: => String): Unit = {
appendLog(SUCCESS_LABEL_COLOR, Level.SuccessLabel, SUCCESS_MESSAGE_COLOR, message)
}
private def write(msg: String): Unit = {
val toWrite =
if (!useFormat || !ansiCodesSupported) EscHelpers.removeEscapeSequences(msg) else msg
if (progressState.get != null) {
supershellInterlaceMsg(toWrite)
} else {
out.println(toWrite)
}
}
private def appendMessage(level: Level.Value, msg: Message): Unit =
msg match {
case o: ObjectMessage => appendMessageContent(level, o.getParameter)
case o: ReusableObjectMessage => appendMessageContent(level, o.getParameter)
case _ => appendLog(level, msg.getFormattedMessage)
}
private def appendTraceEvent(te: TraceEvent): Unit = {
val traceLevel = getTrace
if (traceLevel >= 0) {
val throwableShowLines: ShowLines[Throwable] =
ShowLines[Throwable]((t: Throwable) => {
List(StackTrace.trimmed(t, traceLevel))
})
val codec: ShowLines[TraceEvent] =
ShowLines[TraceEvent]((t: TraceEvent) => {
throwableShowLines.showLines(t.message)
})
codec.showLines(te).toVector foreach { appendLog(Level.Error, _) }
}
if (traceLevel <= 2) {
suppressedMessage(new SuppressedTraceContext(traceLevel, ansiCodesSupported && useFormat)) foreach {
appendLog(Level.Error, _)
}
}
}
private def appendProgressEvent(pe: ProgressEvent): Unit =
if (progressState.get != null) {
out.lockObject.synchronized(updateProgressState(pe))
}
private def appendMessageContent(level: Level.Value, o: AnyRef): Unit = {
def appendEvent(oe: ObjectEvent[_]): Unit = {
val contentType = oe.contentType
contentType match {
case "sbt.internal.util.TraceEvent" => appendTraceEvent(oe.message.asInstanceOf[TraceEvent])
case "sbt.internal.util.ProgressEvent" =>
appendProgressEvent(oe.message.asInstanceOf[ProgressEvent])
case _ =>
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)
}
}
}
o match {
case x: StringEvent => Vector(x.message) foreach { appendLog(level, _) }
case x: ObjectEvent[_] => appendEvent(x)
case _ => Vector(o.toString) foreach { appendLog(level, _) }
}
}
}
final class SuppressedTraceContext(val traceLevel: Int, val useFormat: Boolean)
private[sbt] final class ProgressState(
val progressLines: AtomicReference[Seq[String]],
val padding: AtomicInteger,
val blankZone: Int
) {
def this(blankZone: Int) = this(new AtomicReference(Nil), new AtomicInteger(0), blankZone)
def reset(): Unit = {
progressLines.set(Nil)
padding.set(0)
}
}

View File

@ -0,0 +1,81 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import java.io.{ BufferedWriter, PrintStream, PrintWriter }
sealed trait ConsoleOut {
val lockObject: AnyRef
def print(s: String): Unit
def println(s: String): Unit
def println(): Unit
def flush(): Unit
}
object ConsoleOut {
def systemOut: ConsoleOut = printStreamOut(System.out)
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"
/**
* ConsoleOut instance that is backed by System.out. It overwrites the previously printed line
* if the function `f(lineToWrite, previousLine)` returns true.
*
* The ConsoleOut returned by this method assumes that the only newlines are from println calls
* and not in the String arguments.
*/
def systemOutOverwrite(f: (String, String) => Boolean): ConsoleOut = new ConsoleOut {
val lockObject = System.out
private[this] var last: Option[String] = None
private[this] var current = new java.lang.StringBuffer
def print(s: String): Unit = synchronized { current.append(s); () }
def println(s: String): Unit = synchronized { current.append(s); println() }
def println(): Unit = synchronized {
val s = current.toString
if (ConsoleAppender.formatEnabledInEnv && last.exists(lmsg => f(s, lmsg)))
lockObject.print(OverwriteLine)
lockObject.println(s)
last = Some(s)
current.setLength(0)
}
def flush(): Unit = synchronized {
val s = current.toString
if (ConsoleAppender.formatEnabledInEnv && last.exists(lmsg => f(s, lmsg)))
lockObject.print(OverwriteLine)
lockObject.print(s)
last = Some(s)
current.setLength(0)
}
}
def printStreamOut(out: PrintStream): ConsoleOut = new ConsoleOut {
val lockObject = out
def print(s: String) = out.print(s)
def println(s: String) = out.println(s)
def println() = out.println()
def flush() = out.flush()
}
def printWriterOut(out: PrintWriter): ConsoleOut = new ConsoleOut {
val lockObject = out
def print(s: String) = out.print(s)
def println(s: String) = { out.println(s); flush() }
def println() = { out.println(); flush() }
def flush() = { out.flush() }
}
def bufferedWriterOut(out: BufferedWriter): ConsoleOut = new ConsoleOut {
val lockObject = out
def print(s: String) = out.write(s)
def println(s: String) = { out.write(s); println() }
def println() = { out.newLine(); flush() }
def flush() = { out.flush() }
}
}

View File

@ -0,0 +1,99 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
object EscHelpers {
/** Escape character, used to introduce an escape sequence. */
final val ESC = '\u001B'
/**
* An escape terminator is a character in the range `@` (decimal value 64) to `~` (decimal value 126).
* It is the final character in an escape sequence.
*
* cf. http://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes
*/
private[sbt] def isEscapeTerminator(c: Char): Boolean =
c >= '@' && c <= '~'
/**
* Test if the character AFTER an ESC is the ANSI CSI.
*
* see: http://en.wikipedia.org/wiki/ANSI_escape_code
*
* The CSI (control sequence instruction) codes start with ESC + '['. This is for testing the second character.
*
* There is an additional CSI (one character) that we could test for, but is not frequnetly used, and we don't
* check for it.
*
* cf. http://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes
*/
private def isCSI(c: Char): Boolean = c == '['
/**
* Tests whether or not a character needs to immediately terminate the ANSI sequence.
*
* c.f. http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
*/
private def isAnsiTwoCharacterTerminator(c: Char): Boolean =
(c >= '@') && (c <= '_')
/**
* Returns true if the string contains the ESC character.
*
* TODO - this should handle raw CSI (not used much)
*/
def hasEscapeSequence(s: String): Boolean =
s.indexOf(ESC) >= 0
/**
* Returns the string `s` with escape sequences removed.
* An escape sequence starts with the ESC character (decimal value 27) and ends with an escape terminator.
* @see isEscapeTerminator
*/
def removeEscapeSequences(s: String): String =
if (s.isEmpty || !hasEscapeSequence(s))
s
else {
val sb = new java.lang.StringBuilder
nextESC(s, 0, sb)
sb.toString
}
private[this] def nextESC(s: String, start: Int, sb: java.lang.StringBuilder): Unit = {
val escIndex = s.indexOf(ESC, start)
if (escIndex < 0) {
sb.append(s, start, s.length)
()
} else {
sb.append(s, start, escIndex)
val next: Int =
if (escIndex + 1 >= s.length) skipESC(s, escIndex + 1)
// If it's a CSI we skip past it and then look for a terminator.
else if (isCSI(s.charAt(escIndex + 1))) skipESC(s, escIndex + 2)
else if (isAnsiTwoCharacterTerminator(s.charAt(escIndex + 1))) escIndex + 2
else {
// There could be non-ANSI character sequences we should make sure we handle here.
skipESC(s, escIndex + 1)
}
nextESC(s, next, sb)
}
}
/** Skips the escape sequence starting at `i-1`. `i` should be positioned at the character after the ESC that starts the sequence. */
private[this] def skipESC(s: String, i: Int): Int = {
if (i >= s.length) {
i
} else if (isEscapeTerminator(s.charAt(i))) {
i + 1
} else {
skipESC(s, i + 1)
}
}
}

View File

@ -0,0 +1,40 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
import com.github.ghik.silencer.silent
/**
* A filter logger is used to delegate messages but not the logging level to another logger. This means
* that messages are logged at the higher of the two levels set by this logger and its delegate.
*/
class FilterLogger(delegate: AbstractLogger) extends BasicLogger {
@silent override lazy val ansiCodesSupported = delegate.ansiCodesSupported
def trace(t: => Throwable): Unit = {
if (traceEnabled)
delegate.trace(t)
}
override def setSuccessEnabled(flag: Boolean): Unit = delegate.setSuccessEnabled(flag)
override def successEnabled = delegate.successEnabled
override def setTrace(level: Int): Unit = delegate.setTrace(level)
override def getTrace = delegate.getTrace
def log(level: Level.Value, message: => String): Unit = {
if (atLevel(level))
delegate.log(level, message)
}
def success(message: => String): Unit = {
if (successEnabled)
delegate.success(message)
}
def control(event: ControlEvent.Value, message: => String): Unit = {
if (atLevel(Level.Info))
delegate.control(event, message)
}
def logAll(events: Seq[LogEvent]): Unit = delegate.logAll(events)
}

View File

@ -0,0 +1,39 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
import com.github.ghik.silencer.silent
/** Promotes the simple Logger interface to the full AbstractLogger interface. */
class FullLogger(delegate: Logger) extends BasicLogger {
@deprecated("No longer used.", "1.0.0")
@silent override val ansiCodesSupported: Boolean = delegate.ansiCodesSupported
def trace(t: => Throwable): Unit = {
if (traceEnabled)
delegate.trace(t)
}
def log(level: Level.Value, message: => String): Unit = {
if (atLevel(level))
delegate.log(level, message)
}
def success(message: => String): Unit =
if (successEnabled)
delegate.success(message)
def control(event: ControlEvent.Value, message: => String): Unit =
info(message)
def logAll(events: Seq[LogEvent]): Unit = events.foreach(log)
}
object FullLogger {
def apply(delegate: Logger): AbstractLogger =
delegate match {
case d: AbstractLogger => d
case _ => new FullLogger(delegate)
}
}

View File

@ -0,0 +1,91 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
import java.io.{ File, PrintWriter }
import org.apache.logging.log4j.core.Appender
/**
* Provides the current global logging configuration.
*
* `full` is the current global logger. It should not be set directly because it is generated as needed from `backing.newLogger`.
* `console` is where all logging from all ConsoleLoggers should go.
* `backed` is the Logger that other loggers should feed into.
* `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 GlobalLogging1(
full: Logger,
console: ConsoleOut,
backed: AbstractLogger,
backing: GlobalLogBacking,
newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging1
)
/**
* Tracks the files that persist the global logging.
* `file` is the current backing file. `last` is the previous backing file, if there is one.
* `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)
/** Shifts the current backing file to `last` and sets the current backing to a new temporary file generated by `newBackingFile`. */
def shiftNew() = shift(newBackingFile())
/**
* If there is a previous backing file in `last`, that becomes the current backing file and the previous backing is cleared.
* Otherwise, no changes are made.
*/
def unshift = GlobalLogBacking(last getOrElse file, None, newBackingFile)
}
object GlobalLogBacking {
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 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 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)
}
}

View File

@ -0,0 +1,64 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
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 {
def this(delegate: Logger, level: Level.Value) = this(delegate, Some(level))
def this(delegate: Logger) = this(delegate, None)
private[this] val buffer = new StringBuilder
private[this] val lines = new collection.mutable.ListBuffer[String]
override def close() = flush()
override def flush(): Unit =
synchronized {
if (buffer.nonEmpty) {
log(buffer.toString)
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)
process()
}
private[this] def process(): Unit = {
val i = buffer.indexOf(nl)
if (i >= 0) {
log(buffer.substring(0, i))
buffer.delete(0, i + nl.length)
process()
}
}
private[this] def log(s: String): Unit = unbufferedLevel match {
case None =>
lines += s; ()
case Some(level) => delegate.log(level, s)
}
}

View File

@ -0,0 +1,115 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
import java.io.PrintWriter
import org.apache.logging.log4j.core.Appender
object MainAppender {
import java.util.concurrent.atomic.AtomicInteger
private def generateGlobalBackingName: String =
"GlobalBacking" + generateId.incrementAndGet
private val generateId: AtomicInteger = new AtomicInteger
def multiLogger(log: ManagedLogger, config: MainAppenderConfig): ManagedLogger = {
import config._
// TODO
// backed setTrace backingTrace
// multi: Logger
LogExchange.unbindLoggerAppenders(log.name)
LogExchange.bindLoggerAppenders(
log.name,
(consoleOpt.toList map { appender =>
appender match {
case a: ConsoleAppender =>
a.setTrace(screenTrace)
case _ => ()
}
appender -> 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) => {
val backed: Appender = defaultBacked(generateGlobalBackingName)(writer)
val full = multiLogger(log, defaultMultiConfig(Option(console), backed, Nil))
GlobalLogging(full, console, backed, backing, 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 =
ConsoleAppender(ConsoleAppender.generateName, console, suppressedMessage = suppressedMessage)
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(loggerName: String, useFormat: Boolean): PrintWriter => Appender =
to => {
ConsoleAppender(
ConsoleAppender.generateName,
ConsoleOut.printWriterOut(to),
useFormat = useFormat
)
}
final case class MainAppenderConfig(
consoleOpt: Option[Appender],
backed: Appender,
extra: List[Appender],
screenLevel: Level.Value,
backingLevel: Level.Value,
screenTrace: Int,
backingTrace: Int
)
}

View File

@ -0,0 +1,66 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
import org.apache.logging.log4j.{ Logger => XLogger }
import org.apache.logging.log4j.message.ObjectMessage
import sjsonnew.JsonFormat
import scala.reflect.runtime.universe.TypeTag
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
) 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))
)
}
private lazy val SuccessEventTag = scala.reflect.runtime.universe.typeTag[SuccessEvent]
// send special event for success since it's not a real log level
override def success(message: => String): Unit = {
infoEvent[SuccessEvent](SuccessEvent(message))(
implicitly[JsonFormat[SuccessEvent]],
SuccessEventTag
)
}
def registerStringCodec[A: ShowLines: TypeTag]: Unit = {
LogExchange.registerStringCodec[A]
}
final def debugEvent[A: JsonFormat: TypeTag](event: => A): Unit = logEvent(Level.Debug, event)
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)
)
}
@deprecated("No longer used.", "1.0.0")
override def ansiCodesSupported = ConsoleAppender.formatEnabledInEnv
}

View File

@ -0,0 +1,48 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
import com.github.ghik.silencer.silent
// note that setting the logging level on this logger has no effect on its behavior, only
// on the behavior of the delegates.
class MultiLogger(delegates: List[AbstractLogger]) extends BasicLogger {
@deprecated("No longer used.", "1.0.0")
override lazy val ansiCodesSupported = delegates exists supported
@silent private[this] def supported = (_: AbstractLogger).ansiCodesSupported
override def setLevel(newLevel: Level.Value): Unit = {
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))
private[this] def dispatch(event: LogEvent): Unit = {
for (d <- delegates) {
d.log(event)
}
}
}

View File

@ -0,0 +1,45 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package util
import sbt.util.Level
import sjsonnew.JsonFormat
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
) extends Serializable {
override def toString: String =
s"ObjectEvent($level, $message, $channelName, $execId, $contentType, $json)"
}
object ObjectEvent {
def apply[A: JsonFormat](
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)
)
}

View File

@ -0,0 +1,85 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.io.IO
import scala.collection.mutable.ListBuffer
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.
* The number of lines to be included for each Throwable is configured
* via d which should be greater than or equal to 0.
*
* - If d is 0, then all elements are included up to (but not including)
* the first element that comes from sbt.
* - If d is greater than 0, then up to that many lines are included,
* where the line for the Throwable is counted plus one line for each stack element.
* Less lines will be included if there are not enough stack elements.
*
* See also ConsoleAppender where d <= 2 is treated specially by
* printing a prepared statement.
*/
def trimmedLines(t: Throwable, d: Int): List[String] = {
require(d >= 0)
val b = new ListBuffer[String]()
def appendStackTrace(t: Throwable, first: Boolean): Unit = {
val include: StackTraceElement => Boolean =
if (d == 0)
element => !isSbtClass(element.getClassName)
else {
var count = d - 1
(_ => { count -= 1; count >= 0 })
}
def appendElement(e: StackTraceElement): Unit = {
b.append("\tat " + e)
()
}
if (!first) b.append("Caused by: " + t.toString)
else b.append(t.toString)
val els = t.getStackTrace()
var i = 0
while ((i < els.size) && include(els(i))) {
appendElement(els(i))
i += 1
}
}
appendStackTrace(t, true)
var c = t
while (c.getCause() != null) {
c = c.getCause()
appendStackTrace(c, false)
}
b.toList
}
/**
* Return a printable representation of the stack trace associated
* with t. Information about t and its Throwable causes is included.
* The number of lines to be included for each Throwable is configured
* via d which should be greater than or equal to 0.
*
* - If d is 0, then all elements are included up to (but not including)
* the first element that comes from sbt.
* - If d is greater than 0, then up to that many lines are included,
* where the line for the Throwable is counted plus one line for each stack element.
* Less lines will be included if there are not enough stack elements.
*/
def trimmed(t: Throwable, d: Int): String =
trimmedLines(t, d).mkString(IO.Newline)
}

View File

@ -0,0 +1,51 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import scala.reflect.runtime.universe._
/** This is used to carry type information in JSON. */
final case class StringTypeTag[A](key: String) {
override def toString: String = key
}
object StringTypeTag {
def apply[A: TypeTag]: StringTypeTag[A] =
synchronized {
def doApply: StringTypeTag[A] = {
val tag = implicitly[TypeTag[A]]
val tpe = tag.tpe
val k = typeToString(tpe)
// println(tpe.getClass.toString + " " + k)
StringTypeTag[A](k)
}
def retry(n: Int): StringTypeTag[A] =
try {
doApply
} catch {
case e: NullPointerException =>
if (n < 1) throw new RuntimeException("NPE in StringTypeTag", e)
else {
Thread.sleep(1)
retry(n - 1)
}
}
retry(3)
}
def typeToString(tpe: Type): String =
tpe match {
case TypeRef(_, sym, args) =>
if (args.nonEmpty) {
val typeCon = tpe.typeSymbol.fullName
val typeArgs = args map typeToString
s"""$typeCon[${typeArgs.mkString(",")}]"""
} else tpe.toString
case _ => tpe.toString
}
}

View File

@ -0,0 +1,59 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package util.codec
import sjsonnew.{ JsonWriter => JW, JsonReader => JR, JsonFormat => JF, _ }
import sjsonnew.shaded.scalajson.ast.unsafe._
trait JValueFormats { self: sjsonnew.BasicJsonProtocol =>
implicit val JNullFormat: JF[JNull.type] = new JF[JNull.type] {
def write[J](x: JNull.type, b: Builder[J]) = b.writeNull()
def read[J](j: Option[J], u: Unbuilder[J]) = JNull
}
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 JArrayFormat: JF[JArray] = projectFormat[JArray, Array[JValue]](_.value, JArray(_))
implicit lazy val JObjectJsonWriter: JW[JObject] = new JW[JObject] {
def write[J](x: JObject, b: Builder[J]) = {
b.beginObject()
x.value foreach (jsonField => JValueFormat.addField(jsonField.field, jsonField.value, b))
b.endObject()
}
}
implicit lazy val JValueJsonWriter: JW[JValue] = new JW[JValue] {
def write[J](x: JValue, b: Builder[J]) = x match {
case x: JNull.type => JNullFormat.write(x, b)
case x: JBoolean => JBooleanFormat.write(x, b)
case x: JString => JStringFormat.write(x, b)
case x: JNumber => JNumberFormat.write(x, b)
case x: JArray => JArrayFormat.write(x, b)
case x: JObject => JObjectJsonWriter.write(x, b)
}
}
// This passes through JValue, or returns JNull instead of blowing up with unimplemented.
implicit lazy val JValueJsonReader: JR[JValue] = new JR[JValue] {
def read[J](j: Option[J], u: Unbuilder[J]) = j match {
case Some(x: JValue) => x
case Some(x) => sys.error(s"Uknown AST $x")
case _ => JNull
}
}
implicit lazy val JValueFormat: JF[JValue] =
jsonFormat[JValue](JValueJsonReader, JValueJsonWriter)
}

View File

@ -0,0 +1,73 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util.codec
import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder }
import xsbti.Position
import java.util.Optional
trait PositionFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val PositionFormat: JsonFormat[Position] = new JsonFormat[Position] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Position = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val line0 = unbuilder.readField[Optional[java.lang.Integer]]("line")
val lineContent0 = unbuilder.readField[String]("lineContent")
val offset0 = unbuilder.readField[Optional[java.lang.Integer]]("offset")
val pointer0 = unbuilder.readField[Optional[java.lang.Integer]]("pointer")
val pointerSpace0 = unbuilder.readField[Optional[String]]("pointerSpace")
val sourcePath0 = unbuilder.readField[Optional[String]]("sourcePath")
val sourceFile0 = unbuilder.readField[Optional[java.io.File]]("sourceFile")
val startOffset0 = unbuilder.readField[Optional[java.lang.Integer]]("startOffset")
val endOffset0 = unbuilder.readField[Optional[java.lang.Integer]]("endOffset")
val startLine0 = unbuilder.readField[Optional[java.lang.Integer]]("startLine")
val startColumn0 = unbuilder.readField[Optional[java.lang.Integer]]("startColumn")
val endLine0 = unbuilder.readField[Optional[java.lang.Integer]]("endLine")
val endColumn0 = unbuilder.readField[Optional[java.lang.Integer]]("endColumn")
unbuilder.endObject()
new Position() {
override val line = line0
override val lineContent = lineContent0
override val offset = offset0
override val pointer = pointer0
override val pointerSpace = pointerSpace0
override val sourcePath = sourcePath0
override val sourceFile = sourceFile0
override val startOffset = startOffset0
override val endOffset = endOffset0
override val startLine = startLine0
override val startColumn = startColumn0
override val endLine = endLine0
override val endColumn = endColumn0
}
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: Position, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("line", obj.line)
builder.addField("lineContent", obj.lineContent)
builder.addField("offset", obj.offset)
builder.addField("pointer", obj.pointer)
builder.addField("pointerSpace", obj.pointerSpace)
builder.addField("sourcePath", obj.sourcePath)
builder.addField("sourceFile", obj.sourceFile)
builder.addField("startOffset", obj.startOffset)
builder.addField("endOffset", obj.endOffset)
builder.addField("startLine", obj.startLine)
builder.addField("startColumn", obj.startColumn)
builder.addField("endLine", obj.endLine)
builder.addField("endColumn", obj.endColumn)
builder.endObject()
}
}
}

View File

@ -0,0 +1,48 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util.codec
import xsbti.{ Problem, Severity, Position }
import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder }
import java.util.Optional
trait ProblemFormats { self: SeverityFormats with PositionFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ProblemFormat: JsonFormat[Problem] = new JsonFormat[Problem] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Problem = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val category0 = unbuilder.readField[String]("category")
val severity0 = unbuilder.readField[Severity]("severity")
val message0 = unbuilder.readField[String]("message")
val position0 = unbuilder.readField[Position]("position")
val rendered0 = unbuilder.readField[Optional[String]]("rendered")
unbuilder.endObject()
new Problem {
override val category = category0
override val position = position0
override val message = message0
override val severity = severity0
override val rendered = rendered0
}
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: Problem, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("category", obj.category)
builder.addField("severity", obj.severity)
builder.addField("message", obj.message)
builder.addField("position", obj.position)
builder.addField("rendered", obj.rendered)
builder.endObject()
}
}
}

View File

@ -0,0 +1,36 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util.codec
import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder }
import xsbti.Severity;
trait SeverityFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val SeverityFormat: JsonFormat[Severity] = new JsonFormat[Severity] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Severity = {
jsOpt match {
case Some(js) =>
unbuilder.readString(js) match {
case "Info" => Severity.Info
case "Warn" => Severity.Warn
case "Error" => Severity.Error
}
case None =>
deserializationError("Expected JsString but found None")
}
}
override def write[J](obj: Severity, builder: Builder[J]): Unit = {
val str = obj match {
case Severity.Info => "Info"
case Severity.Warn => "Warn"
case Severity.Error => "Error"
}
builder.writeString(str)
}
}
}

View File

@ -0,0 +1,21 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal.util.codec
import sbt.util.ShowLines
import sbt.internal.util.SuccessEvent
trait SuccessEventShowLines {
implicit val sbtSuccessEventShowLines: ShowLines[SuccessEvent] =
ShowLines[SuccessEvent]((e: SuccessEvent) => {
Vector(e.message)
})
}
object SuccessEventShowLines extends SuccessEventShowLines

View File

@ -0,0 +1,32 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal.util.codec
import sbt.util.ShowLines
import sbt.internal.util.{ StackTrace, TraceEvent }
trait ThrowableShowLines {
implicit val sbtThrowableShowLines: ShowLines[Throwable] =
ShowLines[Throwable]((t: Throwable) => {
// 0 means enabled with default behavior. See StackTrace.scala.
val traceLevel = 0
List(StackTrace.trimmed(t, traceLevel))
})
}
object ThrowableShowLines extends ThrowableShowLines
trait TraceEventShowLines {
implicit val sbtTraceEventShowLines: ShowLines[TraceEvent] =
ShowLines[TraceEvent]((t: TraceEvent) => {
ThrowableShowLines.sbtThrowableShowLines.showLines(t.message)
})
}
object TraceEventShowLines extends TraceEventShowLines

View File

@ -0,0 +1,36 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
abstract class AbstractLogger extends Logger {
def getLevel: Level.Value
def setLevel(newLevel: Level.Value): Unit
def setTrace(flag: Int): Unit
def getTrace: Int
final def traceEnabled: Boolean = getTrace >= 0
def successEnabled: Boolean
def setSuccessEnabled(flag: Boolean): Unit
def atLevel(level: Level.Value): Boolean = level.id >= getLevel.id
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 {
case s: Success => success(s.msg)
case l: Log => log(l.level, l.msg)
case t: Trace => trace(t.exception)
case setL: SetLevel => setLevel(setL.newLevel)
case setT: SetTrace => setTrace(setT.level)
case setS: SetSuccess => setSuccessEnabled(setS.enabled)
case c: ControlEvent => control(c.event, c.msg)
}
}
}

View File

@ -0,0 +1,178 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
import xsbti.{ Position, Problem, Severity, T2 }
import java.io.File
import java.util.Optional
import java.util.function.Supplier
object InterfaceUtil {
def toSupplier[A](a: => A): Supplier[A] = new Supplier[A] {
override def get: A = a
}
import java.util.function.{ Function => JavaFunction }
def toJavaFunction[A1, R](f: A1 => R): JavaFunction[A1, R] = new JavaFunction[A1, R] {
override def apply(t: A1): R = f(t)
}
def t2[A1, A2](x: (A1, A2)): T2[A1, A2] = new ConcreteT2(x._1, x._2)
def toOption[A](m: Optional[A]): Option[A] =
if (m.isPresent) Some(m.get) else None
def toOptional[A](o: Option[A]): Optional[A] =
o match {
case Some(v) => Optional.of(v)
case None => Optional.empty()
}
def jo2o[A](o: Optional[A]): Option[A] =
if (o.isPresent) Some(o.get)
else None
def o2jo[A](o: Option[A]): Optional[A] =
o match {
case Some(v) => Optional.ofNullable(v)
case None => Optional.empty[A]()
}
@deprecated("Use the overload of this method with more arguments", "1.2.2")
def position(
line0: Option[Integer],
content: String,
offset0: Option[Integer],
pointer0: Option[Integer],
pointerSpace0: Option[String],
sourcePath0: Option[String],
sourceFile0: Option[File]
): Position =
position(
line0,
content,
offset0,
pointer0,
pointerSpace0,
sourcePath0,
sourceFile0,
None,
None,
None,
None,
None,
None
)
def position(
line0: Option[Integer],
content: String,
offset0: Option[Integer],
pointer0: Option[Integer],
pointerSpace0: Option[String],
sourcePath0: Option[String],
sourceFile0: Option[File],
startOffset0: Option[Integer],
endOffset0: Option[Integer],
startLine0: Option[Integer],
startColumn0: Option[Integer],
endLine0: Option[Integer],
endColumn0: Option[Integer]
): Position =
new ConcretePosition(
line0,
content,
offset0,
pointer0,
pointerSpace0,
sourcePath0,
sourceFile0,
startOffset0,
endOffset0,
startLine0,
startColumn0,
endLine0,
endColumn0
)
@deprecated("Use the overload of this method with more arguments", "1.2.2")
def problem(cat: String, pos: Position, msg: String, sev: Severity): Problem =
problem(cat, pos, msg, sev, None)
def problem(
cat: String,
pos: Position,
msg: String,
sev: Severity,
rendered: Option[String]
): Problem =
new ConcreteProblem(cat, pos, msg, sev, rendered)
private final class ConcreteT2[A1, A2](a1: A1, a2: A2) extends T2[A1, A2] {
val get1: A1 = a1
val get2: A2 = a2
override def toString: String = s"ConcreteT2($a1, $a2)"
override def equals(o: Any): Boolean = o match {
case o: ConcreteT2[A1, A2] =>
this.get1 == o.get1 &&
this.get2 == o.get2
case _ => false
}
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],
startOffset0: Option[Integer],
endOffset0: Option[Integer],
startLine0: Option[Integer],
startColumn0: Option[Integer],
endLine0: Option[Integer],
endColumn0: Option[Integer]
) extends Position {
val line = o2jo(line0)
val lineContent = content
val offset = o2jo(offset0)
val pointer = o2jo(pointer0)
val pointerSpace = o2jo(pointerSpace0)
val sourcePath = o2jo(sourcePath0)
val sourceFile = o2jo(sourceFile0)
override val startOffset = o2jo(startOffset0)
override val endOffset = o2jo(endOffset0)
override val startLine = o2jo(startLine0)
override val startColumn = o2jo(startColumn0)
override val endLine = o2jo(endLine0)
override val endColumn = o2jo(endColumn0)
}
private final class ConcreteProblem(
cat: String,
pos: Position,
msg: String,
sev: Severity,
rendered0: Option[String]
) extends Problem {
val category = cat
val position = pos
val message = msg
val severity = sev
override val rendered = o2jo(rendered0)
override def toString = s"[$severity] $pos: $message"
}
}

View File

@ -0,0 +1,34 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
/**
* An enumeration defining the levels available for logging. A level includes all of the levels
* with id larger than its own id. For example, Warn (id=3) includes Error (id=4).
*/
object Level extends Enumeration {
val Debug = Value(1, "debug")
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.
*/
val SuccessLabel = "success"
def union(a: Value, b: Value) = if (a.id < b.id) a else b
def unionAll(vs: Seq[Value]) = vs reduceLeft union
/** 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)
}

View File

@ -0,0 +1,21 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
sealed trait LogEvent
final class Success(val msg: String) extends LogEvent
final class Log(val level: Level.Value, val msg: String) extends LogEvent
final class Trace(val exception: Throwable) extends LogEvent
final class SetLevel(val newLevel: Level.Value) extends LogEvent
final class SetTrace(val level: Int) extends LogEvent
final class SetSuccess(val enabled: Boolean) extends LogEvent
final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent
object ControlEvent extends Enumeration {
val Start, Header, Finish = Value
}

View File

@ -0,0 +1,154 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
import sbt.internal.util._
import org.apache.logging.log4j.{ LogManager => XLogManager, Level => XLevel }
import org.apache.logging.log4j.core._
import org.apache.logging.log4j.core.appender.AsyncAppender
import org.apache.logging.log4j.core.config.{ AppenderRef, LoggerConfig }
import org.apache.logging.log4j.core.layout.PatternLayout
import scala.collection.JavaConverters._
import scala.collection.concurrent
import scala.reflect.runtime.universe.TypeTag
import sjsonnew.JsonFormat
// http://logging.apache.org/log4j/2.x/manual/customconfig.html
// https://logging.apache.org/log4j/2.x/log4j-core/apidocs/index.html
sealed abstract class LogExchange {
private[sbt] lazy val context: LoggerContext = init()
private[sbt] lazy val builtInStringCodecs: Unit = initStringCodecs()
private[sbt] lazy val asyncStdout: AsyncAppender = buildAsyncStdout
private[sbt] val jsonCodecs: concurrent.Map[String, JsonFormat[_]] = concurrent.TrieMap()
private[sbt] val stringCodecs: concurrent.Map[String, ShowLines[_]] = concurrent.TrieMap()
def logger(name: String): ManagedLogger = logger(name, None, None)
def logger(name: String, channelName: Option[String], execId: Option[String]): ManagedLogger = {
val _ = context
val codecs = builtInStringCodecs
val ctx = XLogManager.getContext(false) match { case x: LoggerContext => x }
val config = ctx.getConfiguration
val loggerConfig = LoggerConfig.createLogger(
false,
XLevel.DEBUG,
name,
// disable the calculation of caller location as it is very expensive
// https://issues.apache.org/jira/browse/LOG4J2-153
"false",
Array[AppenderRef](),
null,
config,
null
)
config.addLogger(name, loggerConfig)
ctx.updateLoggers
val logger = ctx.getLogger(name)
new ManagedLogger(name, channelName, execId, logger)
}
def unbindLoggerAppenders(loggerName: String): Unit = {
val lc = loggerConfig(loggerName)
lc.getAppenders.asScala foreach {
case (k, v) => lc.removeAppender(k)
}
}
def bindLoggerAppenders(loggerName: String, appenders: List[(Appender, Level.Value)]): Unit = {
val lc = loggerConfig(loggerName)
appenders foreach {
case (x, lv) => lc.addAppender(x, ConsoleAppender.toXLevel(lv), null)
}
}
def loggerConfig(loggerName: String): LoggerConfig = {
val ctx = XLogManager.getContext(false) match { case x: LoggerContext => x }
val config = ctx.getConfiguration
config.getLoggerConfig(loggerName)
}
// Construct these StringTypeTags manually, because they're used at the very startup of sbt
// and we'll try not to initialize the universe by using the StringTypeTag.apply that requires a TypeTag
// A better long-term solution could be to make StringTypeTag.apply a macro.
lazy val stringTypeTagThrowable = StringTypeTag[Throwable]("scala.Throwable")
lazy val stringTypeTagTraceEvent = StringTypeTag[TraceEvent]("sbt.internal.util.TraceEvent")
lazy val stringTypeTagSuccessEvent = StringTypeTag[SuccessEvent]("sbt.internal.util.SuccessEvent")
private[sbt] def initStringCodecs(): Unit = {
import sbt.internal.util.codec.ThrowableShowLines._
import sbt.internal.util.codec.TraceEventShowLines._
import sbt.internal.util.codec.SuccessEventShowLines._
registerStringCodecByStringTypeTag(stringTypeTagThrowable)
registerStringCodecByStringTypeTag(stringTypeTagTraceEvent)
registerStringCodecByStringTypeTag(stringTypeTagSuccessEvent)
}
// This is a dummy layout to avoid casting error during PatternLayout.createDefaultLayout()
// that was originally used for ConsoleAppender.
// The stacktrace shows it's having issue initializing default DefaultConfiguration.
// Since we currently do not use Layout inside ConsoleAppender, the actual pattern is not relevant.
private[sbt] lazy val dummyLayout: PatternLayout = {
val _ = context
val ctx = XLogManager.getContext(false) match { case x: LoggerContext => x }
val config = ctx.getConfiguration
val lo = PatternLayout.newBuilder
.withConfiguration(config)
.withPattern(PatternLayout.SIMPLE_CONVERSION_PATTERN)
.build
lo
}
def jsonCodec[A](tag: String): Option[JsonFormat[A]] =
jsonCodecs.get(tag) map { _.asInstanceOf[JsonFormat[A]] }
def hasJsonCodec(tag: String): Boolean =
jsonCodecs.contains(tag)
def getOrElseUpdateJsonCodec[A](tag: String, v: JsonFormat[A]): JsonFormat[A] =
jsonCodecs.getOrElseUpdate(tag, v).asInstanceOf[JsonFormat[A]]
def stringCodec[A](tag: String): Option[ShowLines[A]] =
stringCodecs.get(tag) map { _.asInstanceOf[ShowLines[A]] }
def hasStringCodec(tag: String): Boolean =
stringCodecs.contains(tag)
def getOrElseUpdateStringCodec[A](tag: String, v: ShowLines[A]): ShowLines[A] =
stringCodecs.getOrElseUpdate(tag, v).asInstanceOf[ShowLines[A]]
def registerStringCodec[A: ShowLines: TypeTag]: Unit = {
val tag = StringTypeTag[A]
registerStringCodecByStringTypeTag(tag)
}
private[sbt] def registerStringCodecByStringTypeTag[A: ShowLines](tag: StringTypeTag[A]): Unit = {
val ev = implicitly[ShowLines[A]]
val _ = getOrElseUpdateStringCodec(tag.key, ev)
}
private[sbt] def buildAsyncStdout: AsyncAppender = {
val ctx = XLogManager.getContext(false) match { case x: LoggerContext => x }
val config = ctx.getConfiguration
val appender = ConsoleAppender("Stdout")
// CustomConsoleAppenderImpl.createAppender("Stdout", layout, null, null)
appender.start
config.addAppender(appender)
val asyncAppender: AsyncAppender = AsyncAppender
.newBuilder()
.setName("AsyncStdout")
.setAppenderRefs(Array(AppenderRef.createAppenderRef("Stdout", XLevel.DEBUG, null)))
.setBlocking(false)
.setConfiguration(config)
.build
asyncAppender.start
config.addAppender(asyncAppender)
asyncAppender
}
private[sbt] def init(): LoggerContext = {
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory
import org.apache.logging.log4j.core.config.Configurator
val builder = ConfigurationBuilderFactory.newConfigurationBuilder
builder.setConfigurationName("sbt.util.logging")
val ctx = Configurator.initialize(builder.build())
ctx match { case x: LoggerContext => x }
}
}
object LogExchange extends LogExchange

View File

@ -0,0 +1,127 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
import xsbti.{ Logger => xLogger }
import xsbti.{ Position, Problem, Severity }
import sys.process.ProcessLogger
import sbt.internal.util.{ BufferedLogger, FullLogger }
import java.io.File
import java.util.Optional
import java.util.function.Supplier
/**
* This is intended to be the simplest logging interface for use by code that wants to log.
* It does not include configuring the logger.
*/
abstract class Logger extends xLogger {
final def verbose(message: => String): Unit = debug(message)
final def debug(message: => String): Unit = log(Level.Debug, message)
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
final def out(message: => String): Unit = log(Level.Info, message)
@deprecated("No longer used.", "1.0.0")
def ansiCodesSupported: Boolean = false
def trace(t: => Throwable): Unit
def success(message: => String): Unit
def log(level: Level.Value, message: => String): Unit
def debug(msg: Supplier[String]): Unit = log(Level.Debug, msg)
def warn(msg: Supplier[String]): Unit = log(Level.Warn, msg)
def info(msg: Supplier[String]): Unit = log(Level.Info, msg)
def error(msg: Supplier[String]): Unit = log(Level.Error, msg)
def trace(msg: Supplier[Throwable]): Unit = trace(msg.get())
def log(level: Level.Value, msg: Supplier[String]): Unit = log(level, msg.get)
}
object Logger {
def transferLevels(oldLog: AbstractLogger, newLog: AbstractLogger): Unit = {
newLog.setLevel(oldLog.getLevel)
newLog.setTrace(oldLog.getTrace)
}
val Null: AbstractLogger = new AbstractLogger {
def getLevel: Level.Value = Level.Error
def setLevel(newLevel: Level.Value): Unit = ()
def getTrace: Int = 0
def setTrace(flag: Int): Unit = ()
def successEnabled: Boolean = false
def setSuccessEnabled(flag: Boolean): Unit = ()
def control(event: ControlEvent.Value, message: => String): Unit = ()
def logAll(events: Seq[LogEvent]): Unit = ()
def trace(t: => Throwable): Unit = ()
def success(message: => String): Unit = ()
def log(level: Level.Value, message: => String): Unit = ()
}
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)
override def warn(msg: Supplier[String]): Unit = lg.warn(msg)
override def info(msg: Supplier[String]): Unit = lg.info(msg)
override def error(msg: Supplier[String]): Unit = lg.error(msg)
override def trace(msg: Supplier[Throwable]): Unit = lg.trace(msg)
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 jo2o[A](o: Optional[A]): Option[A] = InterfaceUtil.jo2o(o)
def o2jo[A](o: Option[A]): Optional[A] = InterfaceUtil.o2jo(o)
@deprecated("Use InterfaceUtil.position", "1.2.2")
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
)
@deprecated("Use InterfaceUtil.problem", "1.2.2")
def problem(cat: String, pos: Position, msg: String, sev: Severity): Problem =
InterfaceUtil.problem(cat, pos, msg, sev)
}

View File

@ -0,0 +1,22 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
trait ShowLines[A] {
def showLines(a: A): Seq[String]
}
object ShowLines {
def apply[A](f: A => Seq[String]): ShowLines[A] =
new ShowLines[A] {
def showLines(a: A): Seq[String] = f(a)
}
implicit class ShowLinesOp[A: ShowLines](a: A) {
def lines: Seq[String] = implicitly[ShowLines[A]].showLines(a)
}
}

View File

@ -0,0 +1 @@
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

View File

@ -0,0 +1,150 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import org.scalacheck._
import Prop._
import Gen.{ listOf, oneOf }
import EscHelpers.{ ESC, hasEscapeSequence, isEscapeTerminator, removeEscapeSequences }
object Escapes extends Properties("Escapes") {
property("genTerminator only generates terminators") =
forAllNoShrink(genTerminator)((c: Char) => isEscapeTerminator(c))
property("genWithoutTerminator only generates terminators") =
forAllNoShrink(genWithoutTerminator) { (s: String) =>
s.forall(c => !isEscapeTerminator(c))
}
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("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)
("Escape sequence removed: '" + removed + "'") |:
!hasEscapeSequence(removed)
}
private[this] final val ecs = ESC.toString
private val partialEscapeSequences =
Gen.oneOf(Gen const ecs, Gen const ecs ++ "[", Gen.choose('@', '_').map(ecs :+ _))
property("removeEscapeSequences handles partial escape sequences") =
forAll(partialEscapeSequences) { s =>
val removed: String = removeEscapeSequences(s)
s"Escape sequence removed: '$removed'" |: !hasEscapeSequence(removed)
}
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)
}
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))
i += 1
}
if (expect.length != original.length) return s"Strings are different lengths!"
"No differences found"
}
final case class EscapeAndNot(escape: EscapeSequence, notEscape: String) {
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(isEscapeTerminator(terminator))
def makeString: String = ESC + content + terminator
override def toString =
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 genEscapePairs: Gen[List[EscapeAndNot]] = listOf(genEscapePair)
lazy val genArbitraryEscapeSequence: Gen[EscapeSequence] =
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))
def toEscapeSequence(s: String): EscapeSequence = EscapeSequence(s.init, s.last)
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"
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 != '[')))
lazy val genTwoCharacterSequence =
// 91 == [ which is the CSI escape sequence.
oneOf((64 to 95)) filter (_ != 91) map (c => new EscapeSequence("", c.toChar))
lazy val genTerminator: Gen[Char] = Gen.choose('@', '~')
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)
private def genRawString = Arbitrary.arbString.arbitrary
}

View File

@ -0,0 +1,34 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
import sbt.internal.util._
import org.scalatest._
class LogExchangeSpec extends FlatSpec with Matchers {
import LogExchange._
checkTypeTag("stringTypeTagThrowable", stringTypeTagThrowable, StringTypeTag[Throwable])
checkTypeTag("stringTypeTagTraceEvent", stringTypeTagTraceEvent, StringTypeTag[TraceEvent])
checkTypeTag("stringTypeTagSuccessEvent", stringTypeTagSuccessEvent, StringTypeTag[SuccessEvent])
private def checkTypeTag[A](name: String, inc: StringTypeTag[A], exp: StringTypeTag[A]): Unit =
s"LogExchange.$name" should s"match real StringTypeTag[$exp]" in {
val StringTypeTag(incomingString) = inc
val StringTypeTag(expectedString) = exp
if ((incomingString startsWith "scala.") || (expectedString startsWith "scala.")) {
// > historically [Scala] has been inconsistent whether `scala.` is included, or not
// > would it be hard to make the test accept either result?
// https://github.com/scala/community-builds/pull/758#issuecomment-409760633
assert((incomingString stripPrefix "scala.") == (expectedString stripPrefix "scala."))
} else {
assert(incomingString == expectedString)
}
}
}

View File

@ -0,0 +1,168 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import sbt.util._
import org.scalacheck._
import Arbitrary._
import Gen.{ listOfN, oneOf }
import Prop._
import java.io.Writer
object LogWriterTest extends Properties("Log Writer") {
final val MaxLines = 100
final val MaxSegments = 10
/* Tests that content written through a LoggerWriter is properly passed to the underlying Logger.
* Each line, determined by the specified newline separator, must be logged at the correct logging level. */
property("properly logged") = forAll { (output: Output, newLine: NewLine) =>
import output.{ lines, level }
val log = new RecordingLogger
val writer = new LoggerWriter(log, Some(level), newLine.str)
logLines(writer, lines, newLine.str)
val events = log.getEvents
("Recorded:\n" + events.map(show).mkString("\n")) |:
check(toLines(lines), events, level)
}
/**
* Displays a LogEvent in a useful format for debugging. In particular, we are only interested in `Log` types
* and non-printable characters should be escaped
*/
def show(event: LogEvent): String =
event match {
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`
* indicates whether to write the segment by character (true) or all at once (false)
*/
def logLines(writer: Writer, lines: List[List[ToLog]], newLine: String): Unit = {
for (line <- lines; section <- line) {
val content = section.content
val normalized = Escape.newline(content, newLine)
if (section.byCharacter)
normalized.foreach(c => writer.write(c.toInt))
else
writer.write(normalized)
}
writer.flush()
}
/** 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 {
case (line, log: Log) => log.level == Lvl && line == log.msg
case _ => false
}
/* 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.*/
implicit lazy val arbOut: Arbitrary[Output] = Arbitrary(genOutput)
implicit lazy val arbLog: Arbitrary[ToLog] = Arbitrary(genLog)
implicit lazy val arbLine: Arbitrary[List[ToLog]] = Arbitrary(genLine)
implicit lazy val arbNewLine: Arbitrary[NewLine] = Arbitrary(genNewLine)
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
implicit def genLog(implicit content: Arbitrary[String], byChar: Arbitrary[Boolean]): Gen[ToLog] =
for (c <- content.arbitrary; by <- byChar.arbitrary) yield {
assert(c != null)
new ToLog(removeNewlines(c), by)
}
implicit lazy val genNewLine: Gen[NewLine] =
for (str <- oneOf("\n", "\r", "\r\n")) yield new NewLine(str)
implicit lazy val genLevel: Gen[Level.Value] =
oneOf(Level.values.toSeq)
implicit lazy val genOutput: Gen[Output] =
for (ls <- listOf[List[ToLog]](MaxLines); lv <- genLevel) yield new Output(ls, lv)
def removeNewlines(s: String) = s.replaceAll("""[\n\r]+""", "")
def addNewline(l: ToLog): ToLog =
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))
}
/* Helper classes*/
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 + ")"
}
/** 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)
}
/** 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
def getEvents = events.reverse
override def ansiCodesSupported = true
def trace(t: => Throwable): Unit = { events ::= new Trace(t) }
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)
}

View File

@ -0,0 +1,141 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import org.scalatest._
import sbt.util._
import java.io.{ File, PrintWriter }
import sbt.io.Using
class ManagedLoggerSpec extends FlatSpec with Matchers {
"ManagedLogger" should "log to console" in {
val log = LogExchange.logger("foo")
LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info))
log.info("test")
log.debug("test")
}
it should "support event logging" in {
import sjsonnew.BasicJsonProtocol._
val log = LogExchange.logger("foo")
LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info))
log.infoEvent(1)
}
it should "validate performance improvement of disabling location calculation for async loggers" in {
val log = LogExchange.logger("foo")
LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info))
val before = System.currentTimeMillis()
1 to 10000 foreach { _ =>
log.debug("test")
}
val after = System.currentTimeMillis()
log.info(s"Peformance test took: ${after - before}ms")
}
it should "support logging Throwable out of the box" in {
import sbt.internal.util.codec.JsonProtocol._
val log = LogExchange.logger("foo")
LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info))
log.infoEvent(SuccessEvent("yes"))
}
it should "allow registering Show[Int]" in {
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"))
log.registerStringCodec[Int]
log.infoEvent(1)
}
it should "allow registering Show[Array[Int]]" in {
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}"))
log.registerStringCodec[Array[Int]]
log.infoEvent(Array(1, 2, 3))
}
it should "allow registering Show[Vector[Vector[Int]]]" in {
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"))
log.registerStringCodec[Vector[Vector[Int]]]
log.infoEvent(Vector(Vector(1, 2, 3)))
}
it should "be thread safe" in {
import java.util.concurrent.{ Executors, TimeUnit }
val pool = Executors.newFixedThreadPool(100)
for {
i <- 1 to 10000
} {
pool.submit(new Runnable {
def run(): Unit = {
val stringTypeTag = StringTypeTag[List[Int]]
val log = LogExchange.logger(s"foo$i")
LogExchange.bindLoggerAppenders(s"foo$i", List(LogExchange.asyncStdout -> Level.Info))
if (i % 100 == 0) {
log.info(s"foo$i test $stringTypeTag")
}
Thread.sleep(1)
}
})
}
pool.shutdown
pool.awaitTermination(30, TimeUnit.SECONDS)
}
"global logging" should "log immediately after initialization" in {
// this is passed into State normally
val global0 = initialGlobalLogging
val full = global0.full
(1 to 3).toList foreach { x =>
full.info(s"test$x")
}
}
// This is done in Mainloop.scala
it should "create a new backing with newAppender" in {
val global0 = initialGlobalLogging
val logBacking0 = global0.backing
val global1 = Using.fileWriter(append = true)(logBacking0.file) { writer =>
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"))
assert(logBacking0.file.exists)
g
}
val logBacking1 = global1.backing
Using.fileWriter(append = true)(logBacking1.file) { writer =>
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"))
// println(logBacking.file)
// print("Press enter to continue. ")
// System.console.readLine
assert(logBacking1.file.exists)
}
}
val console = ConsoleOut.systemOut
def initialGlobalLogging: GlobalLogging = GlobalLogging.initial(
MainAppender.globalDefault(console),
File.createTempFile("sbt", ".log"),
console
)
}

View File

@ -0,0 +1,18 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
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))
}
}

View File

@ -118,9 +118,9 @@ object Logic {
val (pos, neg) = (posSeq.toSet, negSeq.toSet)
val problem =
checkContradictions(pos, neg) orElse
checkOverlap(clauses, pos) orElse
checkAcyclic(clauses)
(checkContradictions(pos, neg): Option[LogicException]) orElse
(checkOverlap(clauses, pos): Option[LogicException]) orElse
(checkAcyclic(clauses): Option[LogicException])
problem.toLeft(
reduce0(clauses, initialFacts, Matched.empty)
@ -249,7 +249,8 @@ object Logic {
else {
val unproven = Clauses(unprovenClauses)
val nextFacts: Set[Literal] =
if (newlyProven.nonEmpty) newlyProven.toSet else inferFailure(unproven)
if (newlyProven.nonEmpty) newlyProven.toSet[Literal]
else inferFailure(unproven)
reduce0(unproven, nextFacts, newState)
}
}
@ -283,7 +284,7 @@ object Logic {
}
}
private[this] def negated(atoms: Set[Atom]): Set[Literal] = atoms.map(a => Negated(a))
private[this] def negated(atoms: Set[Atom]): Set[Literal] = atoms.map(a => (Negated(a): Literal))
/**
* Computes the set of atoms in `clauses` that directly or transitively take a negated atom as input.
@ -386,7 +387,9 @@ object Logic {
None
else {
val newLits = lits -- facts
val newF = if (newLits.isEmpty) True else And(newLits)
val newF =
if (newLits.isEmpty) (True: Formula)
else (And(newLits): Formula)
Some(newF) // 1.
}
case True => Some(True)

View File

@ -0,0 +1,74 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import scala.language.experimental.macros
sealed trait SourcePosition
sealed trait FilePosition extends SourcePosition {
def path: String
def startLine: Int
}
case object NoPosition extends SourcePosition
final case class LinePosition(path: String, startLine: Int) extends FilePosition
final case class LineRange(start: Int, end: Int) {
def shift(n: Int) = new LineRange(start + n, end + n)
}
final case class RangePosition(path: String, range: LineRange) extends FilePosition {
def startLine = range.start
}
object SourcePosition {
/** Creates a SourcePosition by using the enclosing position of the invocation of this method.
* @return SourcePosition
*/
def fromEnclosing(): SourcePosition = macro SourcePositionMacro.fromEnclosingImpl
}
import scala.annotation.tailrec
import scala.reflect.macros.blackbox
import scala.reflect.internal.util.UndefinedPosition
final class SourcePositionMacro(val c: blackbox.Context) {
import c.universe.{ NoPosition => _, _ }
def fromEnclosingImpl(): Expr[SourcePosition] = {
val pos = c.enclosingPosition
if (!pos.isInstanceOf[UndefinedPosition] && pos.line >= 0 && pos.source != null) {
val f = pos.source.file
val name = constant[String](ownerSource(f.path, f.name))
val line = constant[Int](pos.line)
reify { LinePosition(name.splice, line.splice) }
} else
reify { NoPosition }
}
private[this] def ownerSource(path: String, name: String): String = {
@tailrec def inEmptyPackage(s: Symbol): Boolean =
s != NoSymbol && (
s.owner == c.mirror.EmptyPackage
|| s.owner == c.mirror.EmptyPackageClass
|| inEmptyPackage(s.owner)
)
c.internal.enclosingOwner match {
case ec if !ec.isStatic => name
case ec if inEmptyPackage(ec) => path
case ec => s"(${ec.fullName}) $name"
}
}
private[this] def constant[T: WeakTypeTag](t: T): Expr[T] = c.Expr[T](Literal(Constant(t)))
}

View File

@ -0,0 +1,25 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import org.scalatest._
class SourcePositionSpec extends FlatSpec {
"SourcePosition()" should "return a sane SourcePosition" in {
val filename = "SourcePositionSpec.scala"
val lineNumber = 16
SourcePosition.fromEnclosing() match {
case LinePosition(path, startLine) => assert(path === filename && startLine === lineNumber)
case RangePosition(path, range) => assert(path === filename && inRange(range, lineNumber))
case NoPosition => fail("No source position found")
}
}
private def inRange(range: LineRange, lineNo: Int) =
range.start until range.end contains lineNo
}

View File

@ -0,0 +1,206 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
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)
/**
* 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)
/** 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 = reversePairs.foldLeft(Map.empty[B, Set[A]]) {
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] =
rels.foldLeft(Relation.empty[A, B])(_ ++ _)
private[sbt] def remove[X, Y](map: M[X, Y], from: X, to: Y): M[X, Y] =
map.get(from) match {
case Some(tos) =>
val newSet = tos - to
if (newSet.isEmpty) map - from else map.updated(from, newSet)
case None => map
}
private[sbt] def combine[X, Y](a: M[X, Y], b: M[X, Y]): M[X, Y] =
b.foldLeft(a)((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]]
}
/** 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
/** Returns true iff `(a,b)` is in this relation*/
def contains(a: A, b: B): Boolean
/** Returns a relation with only pairs `(a,b)` for which `f(a,b)` is true.*/
def filter(f: (A, B) => Boolean): Relation[A, B]
/**
* Returns a pair of relations: the first contains only pairs `(a,b)` for which `f(a,b)` is true and
* the other only pairs `(a,b)` for which `f(a,b)` is false.
*/
def partition(f: (A, B) => Boolean): (Relation[A, B], Relation[A, B])
/** Partitions this relation into a map of relations according to some discriminator function. */
def groupBy[K](discriminator: ((A, B)) => K): Map[K, Relation[A, B]]
/** Returns all pairs in this relation.*/
def all: Traversable[(A, B)]
/**
* Represents this relation as a `Map` from a `_1` to the set of `_2`s such that `(_1, _2)` is in this relation.
*
* Specifically, there is one entry for each `_1` such that `(_1, _2)` is in this relation for some `_2`.
* The value associated with a given `_1` is the set of all `_2`s such that `(_1, _2)` is in this relation.
*/
def forwardMap: Map[A, Set[B]]
/**
* Represents this relation as a `Map` from a `_2` to the set of `_1`s such that `(_1, _2)` is in this relation.
*
* Specifically, there is one entry for each `_2` such that `(_1, _2)` is in this relation for some `_1`.
* 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] {
def forwardMap = fwd
def reverseMap = rev
def forward(t: A) = get(fwd, t)
def reverse(t: B) = get(rev, t)
def _1s = fwd.keySet
def _2s = rev.keySet
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 +(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), to.foldLeft(rev)((map, t) => add(map, t, from :: Nil)))
def ++(rs: Traversable[(A, B)]) = rs.foldLeft(this: Relation[A, B]) { _ + _ }
def ++(other: Relation[A, B]) =
new MRelation[A, B](combine(fwd, other.forwardMap), combine(rev, other.reverseMap))
def --(ts: Traversable[A]): Relation[A, B] = ts.foldLeft(this: Relation[A, B]) { _ - _ }
def --(pairs: TraversableOnce[(A, B)]): Relation[A, B] =
pairs.foldLeft(this: Relation[A, B])(_ - _)
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 = rs.foldLeft(rev)((map, r) => remove(map, r, t))
new MRelation(fwd - t, upRev)
case None => this
}
def filter(f: (A, B) => Boolean): Relation[A, B] = Relation.empty[A, B] ++ all.filter(f.tupled)
def partition(f: (A, B) => Boolean): (Relation[A, B], Relation[A, B]) = {
val (y, n) = all.partition(f.tupled)
(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] ++ _ }).toMap
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
}
override def hashCode = fwd.filterNot(_._2.isEmpty).hashCode()
override def toString =
all.map { case (a, b) => a + " -> " + b }.mkString("Relation [", ", ", "]")
}

View File

@ -0,0 +1,87 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import org.scalacheck._
import Prop._
object RelationTest extends Properties("Relation") {
property("Added entry check") = forAll { (pairs: List[(Int, Double)]) =>
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
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) }
val added = Relation.empty[Int, Double] ++ add
val removeFine = pairs.collect { case (a, b, true) => (a, b) }
val removeCoarse = removeFine.map(_._1)
val r = added -- removeCoarse
def notIn[X, Y](map: Map[X, Set[Y]], a: X, b: Y) = map.get(a).forall(set => !(set contains b))
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))
} &&
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) =>
val splitInto = math.abs(randomInt) % 10 + 1 // Split into 1-10 groups.
val rel = Relation.empty[Int, Double] ++ entries
val grouped = rel groupBy (_._1 % splitInto)
all(grouped.toSeq) {
case (k, rel_k) => rel_k._1s forall { _ % splitInto == k }
}
}
property("Computes size correctly") = forAll { (entries: List[(Int, Double)]) =>
val rel = Relation.empty[Int, Double] ++ entries
val expected = rel.all.size // Note: not entries.length, as entries may have duplicates.
val computed = rel.size
"Expected size: %d. Computed size: %d.".format(expected, computed) |: expected == computed
}
def all[T](s: Seq[T])(p: T => Prop): Prop =
if (s.isEmpty) true else s.map(p).reduceLeft(_ && _)
}
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 map empty") = e.forwardMap.isEmpty
property("Reverse map empty") = e.reverseMap.isEmpty
property("_1 empty") = e._1s.isEmpty
property("_2 empty") = e._2s.isEmpty
}

View File

@ -0,0 +1,37 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.scripted;
import java.io.File;
import xsbti.Logger;
public class ScriptConfig {
private String label;
private File testDirectory;
private Logger logger;
public ScriptConfig(String label, File testDirectory, Logger logger) {
this.label = label;
this.testDirectory = testDirectory;
this.logger = logger;
}
public String label() {
return this.label;
}
public File testDirectory() {
return this.testDirectory;
}
public Logger logger() {
return this.logger;
}
}

View File

@ -0,0 +1,14 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package scripted
object CommentHandler extends BasicStatementHandler {
def apply(command: String, args: List[String]) = ()
}

View File

@ -0,0 +1,150 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package scripted
import java.io.File
import sbt.io.{ IO, Path }
import sbt.io.syntax._
import Path._
import sbt.io.IO
class FileCommands(baseDirectory: File) extends BasicStatementHandler {
lazy val commands = commandMap
def commandMap =
Map(
"touch" nonEmpty touch _,
"delete" nonEmpty delete _,
"exists" nonEmpty exists _,
"mkdir" nonEmpty makeDirectories _,
"absent" nonEmpty absent _,
// "sync" twoArg("Two directory paths", sync _),
"newer" twoArg ("Two paths", newer _),
"pause" noArg {
println("Pausing in " + baseDirectory)
/*readLine("Press enter to continue. ") */
print("Press enter to continue. ")
System.console.readLine
println()
},
"sleep" oneArg ("Time in milliseconds", time => Thread.sleep(time.toLong)),
"exec" nonEmpty (execute _),
"copy" copy (to => rebase(baseDirectory, to)),
"copy-file" twoArg ("Two paths", copyFile _),
"must-mirror" twoArg ("Two paths", diffFiles _),
"copy-flat" copy flat
)
def apply(command: String, arguments: List[String]): Unit =
commands.get(command).map(_(arguments)) match {
case Some(_) => ()
case _ => scriptError("Unknown command " + command); ()
}
def scriptError(message: String): Unit = sys.error("Test script error: " + message)
def spaced[T](l: Seq[T]) = l.mkString(" ")
def fromStrings(paths: List[String]) = paths.map(fromString)
def fromString(path: String) = new File(baseDirectory, path)
def touch(paths: List[String]): Unit = IO.touch(fromStrings(paths))
def delete(paths: List[String]): Unit = IO.delete(fromStrings(paths))
/*def sync(from: String, to: String) =
IO.sync(fromString(from), fromString(to), log)*/
def copyFile(from: String, to: String): Unit =
IO.copyFile(fromString(from), fromString(to))
def makeDirectories(paths: List[String]) =
IO.createDirectories(fromStrings(paths))
def diffFiles(file1: String, file2: String): Unit = {
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")
)
}
def newer(a: String, b: String): Unit = {
val pathA = fromString(a)
val pathB = fromString(b)
val isNewer = pathA.exists &&
(!pathB.exists || IO.getModifiedTimeOrZero(pathA) > IO.getModifiedTimeOrZero(pathB))
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)
scriptError("File(s) did not exist: " + notPresent.mkString("[ ", " , ", " ]"))
}
def absent(paths: List[String]): Unit = {
val present = fromStrings(paths).filter(_.exists)
if (present.nonEmpty)
scriptError("File(s) existed: " + present.mkString("[ ", " , ", " ]"))
}
def execute(command: List[String]): Unit = execute0(command.head, command.tail)
def execute0(command: String, args: List[String]): Unit = {
if (command.trim.isEmpty)
scriptError("Command was empty.")
else {
val exitValue = sys.process.Process(command :: args, baseDirectory).!
if (exitValue != 0)
sys.error("Nonzero exit value (" + exitValue + ")")
}
}
// these are for readability of the command list
implicit def commandBuilder(s: String): CommandBuilder = new CommandBuilder(s)
final class CommandBuilder(commandName: String) {
type NamedCommand = (String, List[String] => Unit)
def nonEmpty(action: List[String] => Unit): NamedCommand =
commandName -> { paths =>
if (paths.isEmpty)
scriptError("No arguments specified for " + commandName + " command.")
else
action(paths)
}
def twoArg(requiredArgs: String, action: (String, String) => Unit): NamedCommand =
commandName -> {
case List(from, to) => action(from, to)
case other => wrongArguments(requiredArgs, other)
}
def noArg(action: => Unit): NamedCommand =
commandName -> {
case Nil => action
case other => wrongArguments(other)
}
def oneArg(requiredArgs: String, action: String => Unit): NamedCommand =
commandName -> {
case List(single) => action(single)
case other => wrongArguments(requiredArgs, other)
}
def copy(mapper: File => FileMap): NamedCommand =
commandName -> {
case Nil => scriptError("No paths specified for " + commandName + " command.")
case path :: Nil => scriptError("No destination specified for " + commandName + " command.")
case paths =>
val mapped = fromStrings(paths)
val map = mapper(mapped.last)
IO.copy(mapped.init pair map)
()
}
def wrongArguments(args: List[String]): Unit =
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) + "'."
)
}
}

View File

@ -0,0 +1,22 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
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 def getResources(name: String) = null
override def getResource(name: String) = null
}

View File

@ -0,0 +1,12 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.scripted
trait HandlersProvider {
def getHandlers(config: ScriptConfig): Map[Char, StatementHandler]
}

View File

@ -0,0 +1,57 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package scripted
final class TestException(statement: Statement, msg: String, exception: Throwable)
extends RuntimeException(statement.linePrefix + " " + msg, exception)
class ScriptRunner {
import scala.collection.mutable.HashMap
def apply(statements: List[(StatementHandler, Statement)]): Unit = {
val states = new HashMap[StatementHandler, Any]
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)
}
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)
}
} else
()
case Right(s) =>
if (statement.successExpected)
states(handler) = s
else
throw new TestException(statement, "Command succeeded but failure was expected", null)
}
}
val handlers = Set() ++ statements.map(_._1)
try {
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 => () }
}
}
}
}

View File

@ -0,0 +1,212 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package scripted
import java.io.File
import sbt.util.{ Logger, LogExchange, Level }
import sbt.internal.util.{ ManagedLogger, ConsoleAppender, BufferedAppender }
import sbt.io.IO.wrapNull
import sbt.io.{ DirectoryFilter, HiddenFileFilter }
import sbt.io.syntax._
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 = {
val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, handlersProvider)
val logger = newLogger
val allTests = get(tests, resourceBaseDirectory, logger) flatMap {
case ScriptedTest(group, name) =>
runner.scriptedTest(group, name, logger)
}
runAll(allTests)
}
def runAll(tests: Seq[() => Option[String]]): Unit = {
val errors = for (test <- tests; err <- test()) yield err
if (errors.nonEmpty)
sys.error(errors.mkString("Failed tests:\n\t", "\n\t", "\n"))
}
def get(tests: Seq[String], baseDirectory: File, log: ManagedLogger): Seq[ScriptedTest] =
if (tests.isEmpty) listTests(baseDirectory, log) else parseTests(tests)
def listTests(baseDirectory: File, log: ManagedLogger): Seq[ScriptedTest] =
(new ListTests(baseDirectory, _ => true, log)).listTests
def parseTests(in: Seq[String]): Seq[ScriptedTest] =
for (testString <- in) yield {
val Array(group, name) = testString.split("/").map(_.trim)
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
}
}
final class ScriptedTests(
resourceBaseDirectory: File,
bufferLog: Boolean,
handlersProvider: HandlersProvider,
stripQuotes: Boolean
) {
def this(resourceBaseDirectory: File, bufferLog: Boolean, handlersProvider: HandlersProvider) =
this(resourceBaseDirectory, bufferLog, handlersProvider, true)
private val testResources = new Resources(resourceBaseDirectory)
private val consoleAppender: ConsoleAppender = ConsoleAppender()
val ScriptFilename = "test"
val PendingScriptFilename = "pending"
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]] = {
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)
}
}
}
}
}
}
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(): Unit = {
val run = new ScriptRunner
val parser = createParser()
run(parser.parse(file, stripQuotes))
}
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 {
// val emptyCallback: File => Unit = { _ => () }
// }
final case class ScriptedTest(group: String, name: String) {
override def toString = group + "/" + name
}
object ListTests {
def list(directory: File, filter: java.io.FileFilter) = wrapNull(directory.listFiles(filter))
}
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] = {
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 {
override def getMessage: String =
s"The pending test $label succeeded. Mark this test as passing to remove this failure."
}

View File

@ -0,0 +1,33 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package scripted
trait StatementHandler {
type State
def initialState: State
def apply(command: String, arguments: List[String], state: State): State
def finish(state: State): Unit
}
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)
def apply(command: String, arguments: List[String]): Unit
def finish(state: Unit) = ()
}
/** Use when a stack trace is not useful */
final class TestFailed(msg: String) extends RuntimeException(msg) {
override def fillInStackTrace = this
}

View File

@ -0,0 +1,118 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package scripted
import java.io.File
import scala.util.parsing.combinator._
import scala.util.parsing.input.Positional
import Character.isWhitespace
import sbt.io.IO
/*
statement*
statement ::= startChar successChar word+ nl
startChar ::= <single character>
successChar ::= '+' | '-'
word ::= [^ \[\]]+
comment ::= '#' \S* nl
nl ::= '\r' \'n' | '\n' | '\r' | eof
*/
final case class Statement(
command: String,
arguments: List[String],
successExpected: Boolean,
line: Int
) {
def linePrefix = "{line " + line + "} "
}
private object TestScriptParser {
val SuccessLiteral = "success"
val FailureLiteral = "failure"
val WordRegex = """[^ \[\]\s'\"][^ \[\]\s]*""".r
}
import TestScriptParser._
class TestScriptParser(handlers: Map[Char, StatementHandler]) extends RegexParsers {
require(handlers.nonEmpty)
override def skipWhitespace = false
import IO.read
if (handlers.keys.exists(isWhitespace))
sys.error("Start characters cannot be whitespace")
if (handlers.keys.exists(key => key == '+' || key == '-'))
sys.error("Start characters cannot be '+' or '-'")
@deprecated("Use variant that specifies whether to strip quotes or not", "1.4.0")
def parse(scriptFile: File): List[(StatementHandler, Statement)] =
parse(scriptFile, stripQuotes = true)
def parse(scriptFile: File, stripQuotes: Boolean): List[(StatementHandler, Statement)] =
parse(read(scriptFile), Some(scriptFile.getAbsolutePath), stripQuotes)
@deprecated("Use variant that specifies whether to strip quotes or not", "1.4.0")
def parse(script: String): List[(StatementHandler, Statement)] =
parse(script, None, stripQuotes = true)
def parse(script: String, stripQuotes: Boolean): List[(StatementHandler, Statement)] =
parse(script, None, stripQuotes)
private def parse(
script: String,
label: Option[String],
stripQuotes: Boolean
): List[(StatementHandler, Statement)] = {
parseAll(statements(stripQuotes), 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)
}
}
}
@deprecated("Use variant that specifies whether to strip quotes or not", "1.4.0")
lazy val statements = rep1(space ~> statement <~ newline)
def statements(stripQuotes: Boolean): Parser[List[(StatementHandler, Statement)]] =
rep1(space ~> statement(stripQuotes) <~ newline)
@deprecated("Use variant that specifies whether to strip quotes or not", "1.4.0")
def statement: Parser[(StatementHandler, Statement)] = statement(stripQuotes = true)
def statement(stripQuotes: Boolean): Parser[(StatementHandler, Statement)] = {
trait PositionalStatement extends Positional {
def tuple: (StatementHandler, Statement)
}
positioned {
val w = if (stripQuotes) word else rawWord
val command = w | err("expected command")
val arguments = rep(space ~> w | 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
private lazy val rawWord: 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
}

View File

@ -18,6 +18,7 @@ import sbt.util.Logger
import sbt.ConcurrentRestrictions.Tag
import sbt.protocol.testing._
import sbt.internal.util.ConsoleAppender
import sbt.internal.util.Util.{ AnyOps, none }
private[sbt] object ForkTests {
def apply(
@ -56,8 +57,8 @@ private[sbt] object ForkTests {
std.TaskExtra.task {
val server = new ServerSocket(0)
val testListeners = opts.testListeners flatMap {
case tl: TestsListener => Some(tl)
case _ => None
case tl: TestsListener => tl.some
case _ => none[TestsListener]
}
object Acceptor extends Runnable {
@ -124,7 +125,7 @@ private[sbt] object ForkTests {
val acceptorThread = new Thread(Acceptor)
acceptorThread.start()
val fullCp = classpath ++: Seq(
val fullCp = classpath ++ Seq(
IO.classLocationPath[ForkMain].toFile,
IO.classLocationPath[Framework].toFile
)

View File

@ -51,8 +51,8 @@ object Package {
val entryMap = manifest.getEntries.asScala
for ((key, value) <- mergeManifest.getEntries.asScala) {
entryMap.get(key) match {
case Some(attributes) => mergeAttributes(attributes, value)
case None => entryMap put (key, value)
case Some(attributes) => mergeAttributes(attributes, value); ()
case None => entryMap put (key, value); ()
}
}
}
@ -70,20 +70,30 @@ object Package {
val options: Seq[PackageOption]
)
@deprecated("Please specify whether to use a static timestamp", "1.4.0")
def apply(conf: Configuration, cacheStoreFactory: CacheStoreFactory, log: Logger): Unit =
apply(conf, cacheStoreFactory, log, None)
/**
*
* @param conf the package configuration that should be build
* @param cacheStoreFactory used for jar caching. We try to avoid rebuilds as much as possible
* @param log feedback for the user
* @param time static timestamp to use for all entries, if any.
*/
def apply(conf: Configuration, cacheStoreFactory: CacheStoreFactory, log: Logger): Unit = {
def apply(
conf: Configuration,
cacheStoreFactory: CacheStoreFactory,
log: Logger,
time: Option[Long]
): Unit = {
val manifest = new Manifest
val main = manifest.getMainAttributes
for (option <- conf.options) {
option match {
case JarManifest(mergeManifest) => mergeManifests(manifest, mergeManifest)
case MainClass(mainClassName) => main.put(Attributes.Name.MAIN_CLASS, mainClassName)
case ManifestAttributes(attributes @ _*) => main.asScala ++= attributes
case JarManifest(mergeManifest) => mergeManifests(manifest, mergeManifest); ()
case MainClass(mainClassName) => main.put(Attributes.Name.MAIN_CLASS, mainClassName); ()
case ManifestAttributes(attributes @ _*) => main.asScala ++= attributes; ()
case _ => log.warn("Ignored unknown package option " + option)
}
}
@ -96,8 +106,9 @@ object Package {
val sources :+: _ :+: manifest :+: HNil = inputs
outputChanged(cacheStoreFactory make "output") { (outChanged, jar: PlainFileInfo) =>
if (inChanged || outChanged) {
makeJar(sources, jar.file, manifest, log)
makeJar(sources, jar.file, manifest, log, time)
jar.file
()
} else
log.debug("Jar uptodate: " + jar.file)
}
@ -152,7 +163,17 @@ object Package {
homepage map (h => (IMPLEMENTATION_URL, h.toString))
}: _*)
}
def makeJar(sources: Seq[(File, String)], jar: File, manifest: Manifest, log: Logger): Unit = {
@deprecated("Please specify whether to use a static timestamp", "1.4.0")
def makeJar(sources: Seq[(File, String)], jar: File, manifest: Manifest, log: Logger): Unit =
makeJar(sources, jar, manifest, log, None)
def makeJar(
sources: Seq[(File, String)],
jar: File,
manifest: Manifest,
log: Logger,
time: Option[Long]
): Unit = {
val path = jar.getAbsolutePath
log.debug("Packaging " + path + " ...")
if (jar.exists)
@ -161,7 +182,7 @@ object Package {
else
sys.error(path + " exists, but is not a regular file")
log.debug(sourcesDebugString(sources))
IO.jar(sources, jar, manifest)
IO.jar(sources, jar, manifest, time)
log.debug("Done packaging.")
}
def sourcesDebugString(sources: Seq[(File, String)]): String =

Some files were not shown because too many files have changed in this diff Show More