mirror of https://github.com/sbt/sbt.git
Merge pull request #3125 from eed3si9n/wip/format
[sbt 1.0] Reformat using Scalafmt
This commit is contained in:
commit
edf7e566ba
|
|
@ -0,0 +1,18 @@
|
|||
maxColumn = 100
|
||||
project.git = true
|
||||
project.excludeFilters = [ /sbt-test/, /input_sources/, /contraband-scala/ ]
|
||||
|
||||
# http://docs.scala-lang.org/style/scaladoc.html recommends the JavaDoc style.
|
||||
# scala/scala is written that way too https://github.com/scala/scala/blob/v2.12.2/src/library/scala/Predef.scala
|
||||
docstrings = JavaDoc
|
||||
|
||||
# This also seems more idiomatic to include whitespace in import x.{ yyy }
|
||||
spaces.inImportCurlyBraces = true
|
||||
|
||||
# This works around sequence wildcard (`_*`) turning into `_ *`
|
||||
spaces.beforeSeqWildcard = true
|
||||
|
||||
# Vertical alignment only => for pattern matching
|
||||
align.tokens.add = [
|
||||
{ code = "=>", owner = "Case" }
|
||||
]
|
||||
|
|
@ -16,7 +16,7 @@ matrix:
|
|||
|
||||
env:
|
||||
matrix:
|
||||
- SBT_CMD=";test:compile;scalariformCheck"
|
||||
- SBT_CMD=";test:compile;scalafmtCheck"
|
||||
- SBT_CMD="mimaReportBinaryIssues"
|
||||
- SBT_CMD="safeUnitTests"
|
||||
- SBT_CMD="otherUnitTests"
|
||||
|
|
|
|||
448
build.sbt
448
build.sbt
|
|
@ -3,71 +3,77 @@ import Dependencies._
|
|||
import Sxr.sxr
|
||||
|
||||
import com.typesafe.tools.mima.core._, ProblemFilters._
|
||||
import com.typesafe.tools.mima.plugin.MimaKeys.{ binaryIssueFilters, previousArtifact}
|
||||
import com.typesafe.tools.mima.plugin.MimaKeys.{ binaryIssueFilters, previousArtifact }
|
||||
import com.typesafe.tools.mima.plugin.MimaPlugin.mimaDefaultSettings
|
||||
|
||||
// ThisBuild settings take lower precedence,
|
||||
// but can be shared across the multi projects.
|
||||
def buildLevelSettings: Seq[Setting[_]] = inThisBuild(Seq(
|
||||
organization := "org.scala-sbt",
|
||||
version := "1.0.0-SNAPSHOT",
|
||||
description := "sbt is an interactive build tool",
|
||||
bintrayOrganization := Some("sbt"),
|
||||
bintrayRepository := {
|
||||
if (publishStatus.value == "releases") "maven-releases"
|
||||
else "maven-snapshots"
|
||||
},
|
||||
bintrayPackage := "sbt",
|
||||
bintrayReleaseOnPublish := false,
|
||||
licenses := List("BSD New" -> url("https://github.com/sbt/sbt/blob/0.13/LICENSE")),
|
||||
developers := List(
|
||||
Developer("harrah", "Mark Harrah", "@harrah", url("https://github.com/harrah")),
|
||||
Developer("eed3si9n", "Eugene Yokota", "@eed3si9n", url("https://github.com/eed3si9n")),
|
||||
Developer("jsuereth", "Josh Suereth", "@jsuereth", url("https://github.com/jsuereth")),
|
||||
Developer("dwijnand", "Dale Wijnand", "@dwijnand", url("https://github.com/dwijnand")),
|
||||
Developer("gkossakowski", "Grzegorz Kossakowski", "@gkossakowski", url("https://github.com/gkossakowski")),
|
||||
Developer("Duhemm", "Martin Duhem", "@Duhemm", url("https://github.com/Duhemm"))
|
||||
),
|
||||
homepage := Some(url("https://github.com/sbt/sbt")),
|
||||
scmInfo := Some(ScmInfo(url("https://github.com/sbt/sbt"), "git@github.com:sbt/sbt.git")),
|
||||
resolvers += Resolver.mavenLocal
|
||||
))
|
||||
def buildLevelSettings: Seq[Setting[_]] =
|
||||
inThisBuild(
|
||||
Seq(
|
||||
organization := "org.scala-sbt",
|
||||
version := "1.0.0-SNAPSHOT",
|
||||
description := "sbt is an interactive build tool",
|
||||
bintrayOrganization := Some("sbt"),
|
||||
bintrayRepository := {
|
||||
if (publishStatus.value == "releases") "maven-releases"
|
||||
else "maven-snapshots"
|
||||
},
|
||||
bintrayPackage := "sbt",
|
||||
bintrayReleaseOnPublish := false,
|
||||
licenses := List("BSD New" -> url("https://github.com/sbt/sbt/blob/0.13/LICENSE")),
|
||||
developers := List(
|
||||
Developer("harrah", "Mark Harrah", "@harrah", url("https://github.com/harrah")),
|
||||
Developer("eed3si9n", "Eugene Yokota", "@eed3si9n", url("https://github.com/eed3si9n")),
|
||||
Developer("jsuereth", "Josh Suereth", "@jsuereth", url("https://github.com/jsuereth")),
|
||||
Developer("dwijnand", "Dale Wijnand", "@dwijnand", url("https://github.com/dwijnand")),
|
||||
Developer("gkossakowski",
|
||||
"Grzegorz Kossakowski",
|
||||
"@gkossakowski",
|
||||
url("https://github.com/gkossakowski")),
|
||||
Developer("Duhemm", "Martin Duhem", "@Duhemm", url("https://github.com/Duhemm"))
|
||||
),
|
||||
homepage := Some(url("https://github.com/sbt/sbt")),
|
||||
scmInfo := Some(ScmInfo(url("https://github.com/sbt/sbt"), "git@github.com:sbt/sbt.git")),
|
||||
resolvers += Resolver.mavenLocal
|
||||
))
|
||||
|
||||
def commonSettings: Seq[Setting[_]] = Seq[SettingsDefinition](
|
||||
scalaVersion := baseScalaVersion,
|
||||
componentID := None,
|
||||
resolvers += Resolver.typesafeIvyRepo("releases"),
|
||||
resolvers += Resolver.sonatypeRepo("snapshots"),
|
||||
resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/",
|
||||
concurrentRestrictions in Global += Util.testExclusiveRestriction,
|
||||
testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
|
||||
javacOptions in compile ++= Seq("-target", "6", "-source", "6", "-Xlint", "-Xlint:-serial"),
|
||||
incOptions := incOptions.value.withNameHashing(true),
|
||||
crossScalaVersions := Seq(baseScalaVersion),
|
||||
bintrayPackage := (bintrayPackage in ThisBuild).value,
|
||||
bintrayRepository := (bintrayRepository in ThisBuild).value,
|
||||
mimaDefaultSettings,
|
||||
publishArtifact in Test := false,
|
||||
mimaPreviousArtifacts := Set.empty, // Set(organization.value % moduleName.value % "1.0.0"),
|
||||
mimaBinaryIssueFilters ++= Seq(
|
||||
)
|
||||
) flatMap (_.settings)
|
||||
def commonSettings: Seq[Setting[_]] =
|
||||
Seq[SettingsDefinition](
|
||||
scalaVersion := baseScalaVersion,
|
||||
componentID := None,
|
||||
resolvers += Resolver.typesafeIvyRepo("releases"),
|
||||
resolvers += Resolver.sonatypeRepo("snapshots"),
|
||||
resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/",
|
||||
concurrentRestrictions in Global += Util.testExclusiveRestriction,
|
||||
testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
|
||||
javacOptions in compile ++= Seq("-target", "6", "-source", "6", "-Xlint", "-Xlint:-serial"),
|
||||
incOptions := incOptions.value.withNameHashing(true),
|
||||
crossScalaVersions := Seq(baseScalaVersion),
|
||||
bintrayPackage := (bintrayPackage in ThisBuild).value,
|
||||
bintrayRepository := (bintrayRepository in ThisBuild).value,
|
||||
mimaDefaultSettings,
|
||||
publishArtifact in Test := false,
|
||||
mimaPreviousArtifacts := Set.empty, // Set(organization.value % moduleName.value % "1.0.0"),
|
||||
mimaBinaryIssueFilters ++= Seq(
|
||||
)
|
||||
) flatMap (_.settings)
|
||||
|
||||
def minimalSettings: Seq[Setting[_]] =
|
||||
commonSettings ++ customCommands ++
|
||||
publishPomSettings ++ Release.javaVersionCheckSettings
|
||||
publishPomSettings ++ Release.javaVersionCheckSettings
|
||||
|
||||
def baseSettings: Seq[Setting[_]] =
|
||||
minimalSettings ++ Seq(projectComponent) ++ baseScalacOptions ++ Licensed.settings ++ Formatting.settings
|
||||
minimalSettings ++ Seq(projectComponent) ++ baseScalacOptions ++ Licensed.settings
|
||||
|
||||
def testedBaseSettings: Seq[Setting[_]] =
|
||||
baseSettings ++ testDependencies
|
||||
|
||||
lazy val sbtRoot: Project = (project in file(".")).
|
||||
enablePlugins(ScriptedPlugin).
|
||||
configs(Sxr.sxrConf).
|
||||
aggregate(nonRoots: _*).
|
||||
settings(
|
||||
lazy val sbtRoot: Project = (project in file("."))
|
||||
.enablePlugins(ScriptedPlugin)
|
||||
.configs(Sxr.sxrConf)
|
||||
.aggregate(nonRoots: _*)
|
||||
.settings(
|
||||
buildLevelSettings,
|
||||
minimalSettings,
|
||||
rootSettings,
|
||||
|
|
@ -77,112 +83,121 @@ lazy val sbtRoot: Project = (project in file(".")).
|
|||
|
||||
// This is used to configure an sbt-launcher for this version of sbt.
|
||||
lazy val bundledLauncherProj =
|
||||
(project in file("launch")).
|
||||
settings(
|
||||
minimalSettings,
|
||||
inConfig(Compile)(Transform.configSettings),
|
||||
Release.launcherSettings(sbtLaunchJar)
|
||||
).
|
||||
enablePlugins(SbtLauncherPlugin).
|
||||
settings(
|
||||
name := "sbt-launch",
|
||||
moduleName := "sbt-launch",
|
||||
description := "sbt application launcher",
|
||||
autoScalaLibrary := false,
|
||||
crossPaths := false,
|
||||
publish := Release.deployLauncher.value,
|
||||
publishLauncher := Release.deployLauncher.value,
|
||||
packageBin in Compile := sbtLaunchJar.value
|
||||
)
|
||||
(project in file("launch"))
|
||||
.settings(
|
||||
minimalSettings,
|
||||
inConfig(Compile)(Transform.configSettings),
|
||||
Release.launcherSettings(sbtLaunchJar)
|
||||
)
|
||||
.enablePlugins(SbtLauncherPlugin)
|
||||
.settings(
|
||||
name := "sbt-launch",
|
||||
moduleName := "sbt-launch",
|
||||
description := "sbt application launcher",
|
||||
autoScalaLibrary := false,
|
||||
crossPaths := false,
|
||||
publish := Release.deployLauncher.value,
|
||||
publishLauncher := Release.deployLauncher.value,
|
||||
packageBin in Compile := sbtLaunchJar.value
|
||||
)
|
||||
|
||||
/* ** subproject declarations ** */
|
||||
|
||||
/* **** Intermediate-level Modules **** */
|
||||
|
||||
// Runner for uniform test interface
|
||||
lazy val testingProj = (project in file("testing")).
|
||||
enablePlugins(ContrabandPlugin, JsonCodecPlugin).
|
||||
dependsOn(testAgentProj).
|
||||
settings(
|
||||
lazy val testingProj = (project in file("testing"))
|
||||
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
|
||||
.dependsOn(testAgentProj)
|
||||
.settings(
|
||||
baseSettings,
|
||||
name := "Testing",
|
||||
libraryDependencies ++= Seq(testInterface,launcherInterface, sjsonNewScalaJson),
|
||||
libraryDependencies ++= Seq(testInterface, launcherInterface, sjsonNewScalaJson),
|
||||
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
|
||||
).
|
||||
configure(addSbtIO, addSbtCompilerClasspath, addSbtUtilLogging)
|
||||
)
|
||||
.configure(addSbtIO, addSbtCompilerClasspath, addSbtUtilLogging)
|
||||
|
||||
// Testing agent for running tests in a separate process.
|
||||
lazy val testAgentProj = (project in file("testing") / "agent").
|
||||
settings(
|
||||
minimalSettings,
|
||||
crossScalaVersions := Seq(baseScalaVersion),
|
||||
crossPaths := false,
|
||||
autoScalaLibrary := false,
|
||||
name := "Test Agent",
|
||||
libraryDependencies += testInterface
|
||||
)
|
||||
lazy val testAgentProj = (project in file("testing") / "agent").settings(
|
||||
minimalSettings,
|
||||
crossScalaVersions := Seq(baseScalaVersion),
|
||||
crossPaths := false,
|
||||
autoScalaLibrary := false,
|
||||
name := "Test Agent",
|
||||
libraryDependencies += testInterface
|
||||
)
|
||||
|
||||
// Basic task engine
|
||||
lazy val taskProj = (project in file("tasks")).
|
||||
settings(
|
||||
lazy val taskProj = (project in file("tasks"))
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Tasks"
|
||||
).
|
||||
configure(addSbtUtilControl, addSbtUtilCollection)
|
||||
)
|
||||
.configure(addSbtUtilControl, addSbtUtilCollection)
|
||||
|
||||
// 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 (taskProj % "compile;test->test").
|
||||
settings(
|
||||
lazy val stdTaskProj = (project in file("tasks-standard"))
|
||||
.dependsOn(taskProj % "compile;test->test")
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Task System",
|
||||
testExclusive
|
||||
).
|
||||
configure(addSbtUtilCollection, addSbtUtilLogging, addSbtUtilCache, addSbtIO)
|
||||
)
|
||||
.configure(addSbtUtilCollection, addSbtUtilLogging, addSbtUtilCache, addSbtIO)
|
||||
|
||||
// Embedded Scala code runner
|
||||
lazy val runProj = (project in file("run")).
|
||||
settings(
|
||||
lazy val runProj = (project in file("run"))
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Run"
|
||||
).
|
||||
configure(addSbtIO, addSbtUtilLogging, addSbtCompilerClasspath)
|
||||
)
|
||||
.configure(addSbtIO, addSbtUtilLogging, addSbtCompilerClasspath)
|
||||
|
||||
lazy val scriptedSbtProj = (project in scriptedPath / "sbt").
|
||||
dependsOn(commandProj).
|
||||
settings(
|
||||
lazy val scriptedSbtProj = (project in scriptedPath / "sbt")
|
||||
.dependsOn(commandProj)
|
||||
.settings(
|
||||
baseSettings,
|
||||
name := "Scripted sbt",
|
||||
libraryDependencies ++= Seq(launcherInterface % "provided")
|
||||
).
|
||||
configure(addSbtIO, addSbtUtilLogging, addSbtCompilerInterface, addSbtUtilScripted)
|
||||
)
|
||||
.configure(addSbtIO, addSbtUtilLogging, addSbtCompilerInterface, addSbtUtilScripted)
|
||||
|
||||
lazy val scriptedPluginProj = (project in scriptedPath / "plugin").
|
||||
dependsOn(sbtProj).
|
||||
settings(
|
||||
lazy val scriptedPluginProj = (project in scriptedPath / "plugin")
|
||||
.dependsOn(sbtProj)
|
||||
.settings(
|
||||
baseSettings,
|
||||
name := "Scripted Plugin"
|
||||
).
|
||||
configure(addSbtCompilerClasspath)
|
||||
)
|
||||
.configure(addSbtCompilerClasspath)
|
||||
|
||||
// Implementation and support code for defining actions.
|
||||
lazy val actionsProj = (project in file("main-actions")).
|
||||
dependsOn(runProj, stdTaskProj, taskProj, testingProj).
|
||||
settings(
|
||||
lazy val actionsProj = (project in file("main-actions"))
|
||||
.dependsOn(runProj, stdTaskProj, taskProj, testingProj)
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Actions",
|
||||
libraryDependencies += sjsonNewScalaJson
|
||||
).
|
||||
configure(addSbtCompilerClasspath, addSbtUtilCompletion, addSbtCompilerApiInfo,
|
||||
addSbtZinc, addSbtCompilerIvyIntegration, addSbtCompilerInterface,
|
||||
addSbtIO, addSbtUtilLogging, addSbtUtilRelation, addSbtLm, addSbtUtilTracking)
|
||||
)
|
||||
.configure(
|
||||
addSbtCompilerClasspath,
|
||||
addSbtUtilCompletion,
|
||||
addSbtCompilerApiInfo,
|
||||
addSbtZinc,
|
||||
addSbtCompilerIvyIntegration,
|
||||
addSbtCompilerInterface,
|
||||
addSbtIO,
|
||||
addSbtUtilLogging,
|
||||
addSbtUtilRelation,
|
||||
addSbtLm,
|
||||
addSbtUtilTracking
|
||||
)
|
||||
|
||||
lazy val protocolProj = (project in file("protocol")).
|
||||
enablePlugins(ContrabandPlugin, JsonCodecPlugin).
|
||||
settings(
|
||||
lazy val protocolProj = (project in file("protocol"))
|
||||
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Protocol",
|
||||
libraryDependencies ++= Seq(sjsonNewScalaJson),
|
||||
|
|
@ -190,14 +205,14 @@ lazy val protocolProj = (project in file("protocol")).
|
|||
baseDirectory.value / "src" / "main" / "contraband-scala",
|
||||
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
|
||||
contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats
|
||||
).
|
||||
configure(addSbtUtilLogging)
|
||||
)
|
||||
.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).
|
||||
settings(
|
||||
lazy val commandProj = (project in file("main-command"))
|
||||
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
|
||||
.dependsOn(protocolProj)
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Command",
|
||||
libraryDependencies ++= Seq(launcherInterface, sjsonNewScalaJson, templateResolverApi),
|
||||
|
|
@ -205,44 +220,61 @@ lazy val commandProj = (project in file("main-command")).
|
|||
baseDirectory.value / "src" / "main" / "contraband-scala",
|
||||
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
|
||||
contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats
|
||||
).
|
||||
configure(addSbtCompilerInterface, addSbtIO, addSbtUtilLogging, addSbtUtilCompletion, addSbtCompilerClasspath, addSbtLm)
|
||||
)
|
||||
.configure(addSbtCompilerInterface,
|
||||
addSbtIO,
|
||||
addSbtUtilLogging,
|
||||
addSbtUtilCompletion,
|
||||
addSbtCompilerClasspath,
|
||||
addSbtLm)
|
||||
|
||||
// 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(commandProj, stdTaskProj).
|
||||
settings(
|
||||
lazy val mainSettingsProj = (project in file("main-settings"))
|
||||
.dependsOn(commandProj, stdTaskProj)
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Main Settings"
|
||||
).
|
||||
configure(addSbtUtilCache, addSbtUtilApplyMacro, addSbtCompilerInterface, addSbtUtilRelation,
|
||||
addSbtUtilLogging, addSbtIO, addSbtUtilCompletion, addSbtCompilerClasspath, addSbtLm)
|
||||
)
|
||||
.configure(
|
||||
addSbtUtilCache,
|
||||
addSbtUtilApplyMacro,
|
||||
addSbtCompilerInterface,
|
||||
addSbtUtilRelation,
|
||||
addSbtUtilLogging,
|
||||
addSbtIO,
|
||||
addSbtUtilCompletion,
|
||||
addSbtCompilerClasspath,
|
||||
addSbtLm
|
||||
)
|
||||
|
||||
// The main integration project for sbt. It brings all of the projects together, configures them, and provides for overriding conventions.
|
||||
lazy val mainProj = (project in file("main")).
|
||||
dependsOn(actionsProj, mainSettingsProj, runProj, commandProj).
|
||||
disablePlugins(SbtScalariform).
|
||||
settings(
|
||||
lazy val mainProj = (project in file("main"))
|
||||
.dependsOn(actionsProj, mainSettingsProj, runProj, commandProj)
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Main",
|
||||
libraryDependencies ++= scalaXml.value ++ Seq(launcherInterface)
|
||||
).
|
||||
configure(addSbtCompilerInterface,
|
||||
addSbtIO, addSbtUtilLogging, addSbtUtilLogic, addSbtLm, addSbtZincCompile)
|
||||
)
|
||||
.configure(addSbtCompilerInterface,
|
||||
addSbtIO,
|
||||
addSbtUtilLogging,
|
||||
addSbtUtilLogic,
|
||||
addSbtLm,
|
||||
addSbtZincCompile)
|
||||
|
||||
// Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object
|
||||
// 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, scriptedSbtProj % "test->test").
|
||||
settings(
|
||||
lazy val sbtProj = (project in file("sbt"))
|
||||
.dependsOn(mainProj, scriptedSbtProj % "test->test")
|
||||
.settings(
|
||||
baseSettings,
|
||||
name := "sbt",
|
||||
normalizedName := "sbt",
|
||||
crossScalaVersions := Seq(baseScalaVersion),
|
||||
crossPaths := false
|
||||
).
|
||||
configure(addSbtCompilerBridge)
|
||||
)
|
||||
.configure(addSbtCompilerBridge)
|
||||
|
||||
def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
|
||||
val result = scriptedSource(dir => (s: State) => Scripted.scriptedParser(dir)).parsed
|
||||
|
|
@ -252,52 +284,88 @@ def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
|
|||
// that alternate repo to the running scripted test (in Scripted.scriptedpreScripted).
|
||||
// (altLocalPublish in interfaceProj).value
|
||||
// (altLocalPublish in compileInterfaceProj).value
|
||||
Scripted.doScripted((sbtLaunchJar in bundledLauncherProj).value, (fullClasspath in scriptedSbtProj in Test).value,
|
||||
Scripted.doScripted(
|
||||
(sbtLaunchJar in bundledLauncherProj).value,
|
||||
(fullClasspath in scriptedSbtProj in Test).value,
|
||||
(scalaInstance in scriptedSbtProj).value,
|
||||
scriptedSource.value, scriptedBufferLog.value, result, scriptedPrescripted.value, scriptedLaunchOpts.value)
|
||||
scriptedSource.value,
|
||||
scriptedBufferLog.value,
|
||||
result,
|
||||
scriptedPrescripted.value,
|
||||
scriptedLaunchOpts.value
|
||||
)
|
||||
}
|
||||
|
||||
def scriptedUnpublishedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
|
||||
val result = scriptedSource(dir => (s: State) => Scripted.scriptedParser(dir)).parsed
|
||||
Scripted.doScripted((sbtLaunchJar in bundledLauncherProj).value, (fullClasspath in scriptedSbtProj in Test).value,
|
||||
Scripted.doScripted(
|
||||
(sbtLaunchJar in bundledLauncherProj).value,
|
||||
(fullClasspath in scriptedSbtProj in Test).value,
|
||||
(scalaInstance in scriptedSbtProj).value,
|
||||
scriptedSource.value, scriptedBufferLog.value, result, scriptedPrescripted.value, scriptedLaunchOpts.value)
|
||||
scriptedSource.value,
|
||||
scriptedBufferLog.value,
|
||||
result,
|
||||
scriptedPrescripted.value,
|
||||
scriptedLaunchOpts.value
|
||||
)
|
||||
}
|
||||
|
||||
lazy val publishLauncher = TaskKey[Unit]("publish-launcher")
|
||||
|
||||
lazy val myProvided = config("provided") intransitive
|
||||
|
||||
def allProjects = Seq(
|
||||
testingProj, testAgentProj, taskProj, stdTaskProj, runProj,
|
||||
scriptedSbtProj, scriptedPluginProj, protocolProj,
|
||||
actionsProj, commandProj, mainSettingsProj, mainProj, sbtProj, bundledLauncherProj)
|
||||
def allProjects =
|
||||
Seq(
|
||||
testingProj,
|
||||
testAgentProj,
|
||||
taskProj,
|
||||
stdTaskProj,
|
||||
runProj,
|
||||
scriptedSbtProj,
|
||||
scriptedPluginProj,
|
||||
protocolProj,
|
||||
actionsProj,
|
||||
commandProj,
|
||||
mainSettingsProj,
|
||||
mainProj,
|
||||
sbtProj,
|
||||
bundledLauncherProj
|
||||
)
|
||||
|
||||
def projectsWithMyProvided = allProjects.map(p => p.copy(configurations = (p.configurations.filter(_ != Provided)) :+ myProvided))
|
||||
def projectsWithMyProvided =
|
||||
allProjects.map(p =>
|
||||
p.copy(configurations = (p.configurations.filter(_ != Provided)) :+ myProvided))
|
||||
lazy val nonRoots = projectsWithMyProvided.map(p => LocalProject(p.id))
|
||||
|
||||
def rootSettings = fullDocSettings ++
|
||||
Util.publishPomSettings ++ otherRootSettings ++ Formatting.sbtFilesSettings ++
|
||||
Transform.conscriptSettings(bundledLauncherProj)
|
||||
def otherRootSettings = Seq(
|
||||
scripted := scriptedTask.evaluated,
|
||||
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
|
||||
scriptedSource := (sourceDirectory in sbtProj).value / "sbt-test",
|
||||
// scriptedPrescripted := { addSbtAlternateResolver _ },
|
||||
scriptedLaunchOpts := List("-XX:MaxPermSize=256M", "-Xmx1G"),
|
||||
publishAll := { val _ = (publishLocal).all(ScopeFilter(inAnyProject)).value },
|
||||
publishLocalBinAll := { val _ = (publishLocalBin).all(ScopeFilter(inAnyProject)).value },
|
||||
aggregate in bintrayRelease := false
|
||||
) ++ inConfig(Scripted.RepoOverrideTest)(Seq(
|
||||
scriptedPrescripted := { _ => () },
|
||||
scriptedLaunchOpts := {
|
||||
List("-XX:MaxPermSize=256M", "-Xmx1G", "-Dsbt.override.build.repos=true",
|
||||
s"""-Dsbt.repository.config=${ scriptedSource.value / "repo.config" }""")
|
||||
},
|
||||
scripted := scriptedTask.evaluated,
|
||||
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
|
||||
scriptedSource := (sourceDirectory in sbtProj).value / "repo-override-test"
|
||||
))
|
||||
def rootSettings =
|
||||
fullDocSettings ++
|
||||
Util.publishPomSettings ++ otherRootSettings ++
|
||||
Transform.conscriptSettings(bundledLauncherProj)
|
||||
def otherRootSettings =
|
||||
Seq(
|
||||
scripted := scriptedTask.evaluated,
|
||||
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
|
||||
scriptedSource := (sourceDirectory in sbtProj).value / "sbt-test",
|
||||
// scriptedPrescripted := { addSbtAlternateResolver _ },
|
||||
scriptedLaunchOpts := List("-XX:MaxPermSize=256M", "-Xmx1G"),
|
||||
publishAll := { val _ = (publishLocal).all(ScopeFilter(inAnyProject)).value },
|
||||
publishLocalBinAll := { val _ = (publishLocalBin).all(ScopeFilter(inAnyProject)).value },
|
||||
aggregate in bintrayRelease := false
|
||||
) ++ inConfig(Scripted.RepoOverrideTest)(
|
||||
Seq(
|
||||
scriptedPrescripted := { _ =>
|
||||
()
|
||||
},
|
||||
scriptedLaunchOpts := {
|
||||
List("-XX:MaxPermSize=256M",
|
||||
"-Xmx1G",
|
||||
"-Dsbt.override.build.repos=true",
|
||||
s"""-Dsbt.repository.config=${scriptedSource.value / "repo.config"}""")
|
||||
},
|
||||
scripted := scriptedTask.evaluated,
|
||||
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
|
||||
scriptedSource := (sourceDirectory in sbtProj).value / "repo-override-test"
|
||||
))
|
||||
|
||||
// def addSbtAlternateResolver(scriptedRoot: File) = {
|
||||
// val resolver = scriptedRoot / "project" / "AddResolverPlugin.scala"
|
||||
|
|
@ -337,17 +405,20 @@ def fullDocSettings = Util.baseScalacOptions ++ Docs.settings ++ Sxr.settings ++
|
|||
|
||||
lazy val safeUnitTests = taskKey[Unit]("Known working tests (for both 2.10 and 2.11)")
|
||||
lazy val safeProjects: ScopeFilter = ScopeFilter(
|
||||
inProjects(mainSettingsProj, mainProj,
|
||||
actionsProj, runProj, stdTaskProj),
|
||||
inProjects(mainSettingsProj, mainProj, actionsProj, runProj, stdTaskProj),
|
||||
inConfigurations(Test)
|
||||
)
|
||||
lazy val otherUnitTests = taskKey[Unit]("Unit test other projects")
|
||||
lazy val otherProjects: ScopeFilter = ScopeFilter(
|
||||
inProjects(
|
||||
testingProj, testAgentProj, taskProj,
|
||||
scriptedSbtProj, scriptedPluginProj,
|
||||
commandProj, mainSettingsProj, mainProj,
|
||||
sbtProj),
|
||||
inProjects(testingProj,
|
||||
testAgentProj,
|
||||
taskProj,
|
||||
scriptedSbtProj,
|
||||
scriptedPluginProj,
|
||||
commandProj,
|
||||
mainSettingsProj,
|
||||
mainProj,
|
||||
sbtProj),
|
||||
inConfigurations(Test)
|
||||
)
|
||||
|
||||
|
|
@ -362,12 +433,19 @@ def customCommands: Seq[Setting[_]] = Seq(
|
|||
otherUnitTests := {
|
||||
test.all(otherProjects).value
|
||||
},
|
||||
commands += Command.command("scalafmtCheck") { state =>
|
||||
sys.process.Process("git diff --name-only --exit-code").! match {
|
||||
case 0 => // ok
|
||||
case x => sys.error("git diff detected! Did you compile before committing?")
|
||||
}
|
||||
state
|
||||
},
|
||||
commands += Command.command("release-sbt-local") { state =>
|
||||
"clean" ::
|
||||
"so compile" ::
|
||||
"so publishLocal" ::
|
||||
"reload" ::
|
||||
state
|
||||
"so compile" ::
|
||||
"so publishLocal" ::
|
||||
"reload" ::
|
||||
state
|
||||
},
|
||||
/** There are several complications with sbt's build.
|
||||
* First is the fact that interface project is a Java-only project
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@
|
|||
package sbt
|
||||
|
||||
import sbt.internal.inc.javac.JavaTools
|
||||
import sbt.internal.inc.{ AnalyzingCompiler, ComponentCompiler, ScalaInstance, ZincComponentManager, IncrementalCompilerImpl }
|
||||
import sbt.internal.inc.{
|
||||
AnalyzingCompiler,
|
||||
ComponentCompiler,
|
||||
ScalaInstance,
|
||||
ZincComponentManager,
|
||||
IncrementalCompilerImpl
|
||||
}
|
||||
import xsbti.{ Logger => _, _ }
|
||||
import xsbti.compile.{ ClasspathOptions, Compilers, CompileResult, Inputs }
|
||||
import java.io.File
|
||||
|
|
@ -24,7 +30,9 @@ object Compiler {
|
|||
}
|
||||
|
||||
private[this] def scalaCompilerBridgeSource(suffix: String): ModuleID =
|
||||
ModuleID(xsbti.ArtifactInfo.SbtOrganization, s"compiler-bridge_$suffix", ComponentCompiler.incrementalVersion)
|
||||
ModuleID(xsbti.ArtifactInfo.SbtOrganization,
|
||||
s"compiler-bridge_$suffix",
|
||||
ComponentCompiler.incrementalVersion)
|
||||
.withConfigurations(Some("component"))
|
||||
.sources()
|
||||
|
||||
|
|
@ -33,7 +41,8 @@ object Compiler {
|
|||
private[sbt] def scalaCompilerBridgeSource2_12: ModuleID = scalaCompilerBridgeSource("2.12")
|
||||
|
||||
def compilers(
|
||||
cpOptions: ClasspathOptions, ivyConfiguration: IvyConfiguration
|
||||
cpOptions: ClasspathOptions,
|
||||
ivyConfiguration: IvyConfiguration
|
||||
)(implicit app: AppConfiguration, log: Logger): Compilers = {
|
||||
val scalaProvider = app.provider.scalaProvider
|
||||
val instance = ScalaInstance(scalaProvider.version, scalaProvider.launcher)
|
||||
|
|
@ -43,8 +52,11 @@ object Compiler {
|
|||
|
||||
// TODO: Get java compiler
|
||||
def compilers(
|
||||
instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File],
|
||||
ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID
|
||||
instance: ScalaInstance,
|
||||
cpOptions: ClasspathOptions,
|
||||
javaHome: Option[File],
|
||||
ivyConfiguration: IvyConfiguration,
|
||||
sourcesModule: ModuleID
|
||||
)(implicit app: AppConfiguration, log: Logger): Compilers = {
|
||||
val scalac = scalaCompiler(instance, cpOptions, javaHome, ivyConfiguration, sourcesModule)
|
||||
val javac = JavaTools.directOrFork(instance, cpOptions, javaHome)
|
||||
|
|
@ -52,12 +64,19 @@ object Compiler {
|
|||
}
|
||||
|
||||
def scalaCompiler(
|
||||
instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File],
|
||||
ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID
|
||||
instance: ScalaInstance,
|
||||
cpOptions: ClasspathOptions,
|
||||
javaHome: Option[File],
|
||||
ivyConfiguration: IvyConfiguration,
|
||||
sourcesModule: ModuleID
|
||||
)(implicit app: AppConfiguration, log: Logger): AnalyzingCompiler = {
|
||||
val launcher = app.provider.scalaProvider.launcher
|
||||
val componentManager = new ZincComponentManager(launcher.globalLock, app.provider.components, Option(launcher.ivyHome), log)
|
||||
val provider = ComponentCompiler.interfaceProvider(componentManager, ivyConfiguration, sourcesModule)
|
||||
val componentManager = new ZincComponentManager(launcher.globalLock,
|
||||
app.provider.components,
|
||||
Option(launcher.ivyHome),
|
||||
log)
|
||||
val provider =
|
||||
ComponentCompiler.interfaceProvider(componentManager, ivyConfiguration, sourcesModule)
|
||||
new AnalyzingCompiler(instance, provider, cpOptions, _ => (), None)
|
||||
}
|
||||
|
||||
|
|
@ -66,5 +85,11 @@ object Compiler {
|
|||
def compile(in: Inputs, log: Logger): CompileResult = compiler.compile(in, log)
|
||||
|
||||
private[sbt] def foldMappers[A](mappers: Seq[A => Option[A]]) =
|
||||
mappers.foldRight({ p: A => p }) { (mapper, mappers) => { p: A => mapper(p).getOrElse(mappers(p)) } }
|
||||
mappers.foldRight({ p: A =>
|
||||
p
|
||||
}) { (mapper, mappers) =>
|
||||
{ p: A =>
|
||||
mapper(p).getOrElse(mappers(p))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,23 +11,36 @@ import xsbti.compile.{ Inputs, Compilers }
|
|||
import scala.util.Try
|
||||
|
||||
final class Console(compiler: AnalyzingCompiler) {
|
||||
|
||||
/** Starts an interactive scala interpreter session with the given classpath.*/
|
||||
def apply(classpath: Seq[File], log: Logger): Try[Unit] =
|
||||
apply(classpath, Nil, "", "", log)
|
||||
|
||||
def apply(classpath: Seq[File], options: Seq[String], initialCommands: String, cleanupCommands: String, log: Logger): Try[Unit] =
|
||||
def apply(classpath: Seq[File],
|
||||
options: Seq[String],
|
||||
initialCommands: String,
|
||||
cleanupCommands: String,
|
||||
log: Logger): Try[Unit] =
|
||||
apply(classpath, options, initialCommands, cleanupCommands)(None, Nil)(log)
|
||||
|
||||
def apply(classpath: Seq[File], options: Seq[String], loader: ClassLoader, initialCommands: String, cleanupCommands: String)(bindings: (String, Any)*)(implicit log: Logger): Try[Unit] =
|
||||
def apply(classpath: Seq[File],
|
||||
options: Seq[String],
|
||||
loader: ClassLoader,
|
||||
initialCommands: String,
|
||||
cleanupCommands: String)(bindings: (String, Any)*)(implicit log: Logger): Try[Unit] =
|
||||
apply(classpath, options, initialCommands, cleanupCommands)(Some(loader), bindings)
|
||||
|
||||
def apply(classpath: Seq[File], options: Seq[String], initialCommands: String, cleanupCommands: String)(loader: Option[ClassLoader], bindings: Seq[(String, Any)])(implicit log: Logger): Try[Unit] =
|
||||
{
|
||||
def console0() = compiler.console(classpath, options, initialCommands, cleanupCommands, log)(loader, bindings)
|
||||
// TODO: Fix JLine
|
||||
//JLine.withJLine(Run.executeTrapExit(console0, log))
|
||||
Run.executeTrapExit(console0, log)
|
||||
}
|
||||
def apply(classpath: Seq[File],
|
||||
options: Seq[String],
|
||||
initialCommands: String,
|
||||
cleanupCommands: String)(loader: Option[ClassLoader], bindings: Seq[(String, Any)])(
|
||||
implicit log: Logger): Try[Unit] = {
|
||||
def console0() =
|
||||
compiler.console(classpath, options, initialCommands, cleanupCommands, log)(loader, bindings)
|
||||
// TODO: Fix JLine
|
||||
//JLine.withJLine(Run.executeTrapExit(console0, log))
|
||||
Run.executeTrapExit(console0, log)
|
||||
}
|
||||
}
|
||||
object Console {
|
||||
def apply(conf: Inputs): Console =
|
||||
|
|
|
|||
|
|
@ -19,26 +19,67 @@ import sbt.internal.util.ManagedLogger
|
|||
|
||||
object Doc {
|
||||
import RawCompileLike._
|
||||
def scaladoc(label: String, cacheStoreFactory: CacheStoreFactory, compiler: AnalyzingCompiler): Gen =
|
||||
def scaladoc(label: String,
|
||||
cacheStoreFactory: CacheStoreFactory,
|
||||
compiler: AnalyzingCompiler): Gen =
|
||||
scaladoc(label, cacheStoreFactory, compiler, Seq())
|
||||
def scaladoc(label: String, cacheStoreFactory: CacheStoreFactory, compiler: AnalyzingCompiler, fileInputOptions: Seq[String]): Gen =
|
||||
cached(cacheStoreFactory, fileInputOptions, prepare(label + " Scala API documentation", compiler.doc))
|
||||
def javadoc(label: String, cacheStoreFactory: CacheStoreFactory, doc: JavaTools, log: Logger, reporter: Reporter): Gen =
|
||||
def scaladoc(label: String,
|
||||
cacheStoreFactory: CacheStoreFactory,
|
||||
compiler: AnalyzingCompiler,
|
||||
fileInputOptions: Seq[String]): Gen =
|
||||
cached(cacheStoreFactory,
|
||||
fileInputOptions,
|
||||
prepare(label + " Scala API documentation", compiler.doc))
|
||||
def javadoc(label: String,
|
||||
cacheStoreFactory: CacheStoreFactory,
|
||||
doc: JavaTools,
|
||||
log: Logger,
|
||||
reporter: Reporter): Gen =
|
||||
javadoc(label, cacheStoreFactory, doc, log, reporter, Seq())
|
||||
def javadoc(label: String, cacheStoreFactory: CacheStoreFactory, doc: JavaTools, log: Logger, reporter: Reporter, fileInputOptions: Seq[String]): Gen =
|
||||
cached(cacheStoreFactory, fileInputOptions, prepare(label + " Java API documentation", filterSources(
|
||||
javaSourcesOnly,
|
||||
(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maxErrors: Int, log: Logger) => {
|
||||
// doc.doc
|
||||
???
|
||||
}
|
||||
)))
|
||||
def javadoc(label: String,
|
||||
cacheStoreFactory: CacheStoreFactory,
|
||||
doc: JavaTools,
|
||||
log: Logger,
|
||||
reporter: Reporter,
|
||||
fileInputOptions: Seq[String]): Gen =
|
||||
cached(
|
||||
cacheStoreFactory,
|
||||
fileInputOptions,
|
||||
prepare(
|
||||
label + " Java API documentation",
|
||||
filterSources(
|
||||
javaSourcesOnly,
|
||||
(sources: Seq[File],
|
||||
classpath: Seq[File],
|
||||
outputDirectory: File,
|
||||
options: Seq[String],
|
||||
maxErrors: Int,
|
||||
log: Logger) => {
|
||||
// doc.doc
|
||||
???
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val javaSourcesOnly: File => Boolean = _.getName.endsWith(".java")
|
||||
|
||||
private[sbt] final class Scaladoc(maximumErrors: Int, compiler: AnalyzingCompiler) extends Doc {
|
||||
def apply(label: String, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], log: ManagedLogger): Unit = {
|
||||
generate("Scala", label, compiler.doc, sources, classpath, outputDirectory, options, maximumErrors, log)
|
||||
def apply(label: String,
|
||||
sources: Seq[File],
|
||||
classpath: Seq[File],
|
||||
outputDirectory: File,
|
||||
options: Seq[String],
|
||||
log: ManagedLogger): Unit = {
|
||||
generate("Scala",
|
||||
label,
|
||||
compiler.doc,
|
||||
sources,
|
||||
classpath,
|
||||
outputDirectory,
|
||||
options,
|
||||
maximumErrors,
|
||||
log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,12 +87,21 @@ object Doc {
|
|||
sealed trait Doc {
|
||||
type Gen = (Seq[File], Seq[File], File, Seq[String], Int, ManagedLogger) => Unit
|
||||
|
||||
private[sbt] final def generate(variant: String, label: String, docf: Gen, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maxErrors: Int, log: ManagedLogger): Unit = {
|
||||
private[sbt] final def generate(variant: String,
|
||||
label: String,
|
||||
docf: Gen,
|
||||
sources: Seq[File],
|
||||
classpath: Seq[File],
|
||||
outputDirectory: File,
|
||||
options: Seq[String],
|
||||
maxErrors: Int,
|
||||
log: ManagedLogger): Unit = {
|
||||
val logSnip = variant + " API documentation"
|
||||
if (sources.isEmpty)
|
||||
log.info("No sources available, skipping " + logSnip + "...")
|
||||
else {
|
||||
log.info("Generating " + logSnip + " for " + label + " sources to " + outputDirectory.absolutePath + "...")
|
||||
log.info(
|
||||
"Generating " + logSnip + " for " + label + " sources to " + outputDirectory.absolutePath + "...")
|
||||
IO.delete(outputDirectory)
|
||||
IO.createDirectory(outputDirectory)
|
||||
docf(sources, classpath, outputDirectory, options, maxErrors, log)
|
||||
|
|
|
|||
|
|
@ -18,24 +18,37 @@ object DotGraph {
|
|||
apply(relations, outputDirectory, toString, toString)
|
||||
}
|
||||
def packages(relations: Relations, outputDirectory: File, sourceRoots: Iterable[File]): Unit = {
|
||||
val packageOnly = (path: String) =>
|
||||
{
|
||||
val last = path.lastIndexOf(File.separatorChar.toInt)
|
||||
val packagePath = (if (last > 0) path.substring(0, last) else path).trim
|
||||
if (packagePath.isEmpty) "" else packagePath.replace(File.separatorChar, '.')
|
||||
}
|
||||
val packageOnly = (path: String) => {
|
||||
val last = path.lastIndexOf(File.separatorChar.toInt)
|
||||
val packagePath = (if (last > 0) path.substring(0, last) else path).trim
|
||||
if (packagePath.isEmpty) "" else packagePath.replace(File.separatorChar, '.')
|
||||
}
|
||||
val toString = packageOnly compose fToString(sourceRoots)
|
||||
apply(relations, outputDirectory, toString, toString)
|
||||
}
|
||||
def apply(relations: Relations, outputDir: File, sourceToString: File => String, externalToString: File => String): Unit = {
|
||||
def apply(relations: Relations,
|
||||
outputDir: File,
|
||||
sourceToString: File => String,
|
||||
externalToString: File => String): Unit = {
|
||||
def file(name: String) = new File(outputDir, name)
|
||||
IO.createDirectory(outputDir)
|
||||
generateGraph(file("int-class-deps"), "dependencies", relations.internalClassDep, identity[String], identity[String])
|
||||
generateGraph(file("binary-dependencies"), "externalDependencies", relations.libraryDep, externalToString, sourceToString)
|
||||
generateGraph(file("int-class-deps"),
|
||||
"dependencies",
|
||||
relations.internalClassDep,
|
||||
identity[String],
|
||||
identity[String])
|
||||
generateGraph(file("binary-dependencies"),
|
||||
"externalDependencies",
|
||||
relations.libraryDep,
|
||||
externalToString,
|
||||
sourceToString)
|
||||
}
|
||||
|
||||
def generateGraph[K, V](file: File, graphName: String, relation: Relation[K, V],
|
||||
keyToString: K => String, valueToString: V => String): Unit = {
|
||||
def generateGraph[K, V](file: File,
|
||||
graphName: String,
|
||||
relation: Relation[K, V],
|
||||
keyToString: K => String,
|
||||
valueToString: V => String): Unit = {
|
||||
import scala.collection.mutable.{ HashMap, HashSet }
|
||||
val mappedGraph = new HashMap[String, HashSet[String]]
|
||||
for ((key, values) <- relation.forwardMap; keyString = keyToString(key); value <- values)
|
||||
|
|
@ -58,10 +71,9 @@ object DotGraph {
|
|||
def sourceToString(roots: Iterable[File], source: File) =
|
||||
relativized(roots, source).trim.stripSuffix(".scala").stripSuffix(".java")
|
||||
|
||||
private def relativized(roots: Iterable[File], path: File): String =
|
||||
{
|
||||
val relativized = roots.flatMap(root => IO.relativize(root, path))
|
||||
val shortest = (Int.MaxValue /: relativized)(_ min _.length)
|
||||
relativized.find(_.length == shortest).getOrElse(path.getName)
|
||||
}
|
||||
private def relativized(roots: Iterable[File], path: File): String = {
|
||||
val relativized = roots.flatMap(root => IO.relativize(root, path))
|
||||
val shortest = (Int.MaxValue /: relativized)(_ min _.length)
|
||||
relativized.find(_.length == shortest).getOrElse(path.getName)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,13 @@ import sbt.ConcurrentRestrictions.Tag
|
|||
import sbt.protocol.testing._
|
||||
|
||||
private[sbt] object ForkTests {
|
||||
def apply(runners: Map[TestFramework, Runner], tests: List[TestDefinition], config: Execution, classpath: Seq[File], fork: ForkOptions, log: Logger, tag: Tag): Task[TestOutput] = {
|
||||
def apply(runners: Map[TestFramework, Runner],
|
||||
tests: List[TestDefinition],
|
||||
config: Execution,
|
||||
classpath: Seq[File],
|
||||
fork: ForkOptions,
|
||||
log: Logger,
|
||||
tag: Tag): Task[TestOutput] = {
|
||||
val opts = processOptions(config, tests, log)
|
||||
|
||||
import std.TaskExtra._
|
||||
|
|
@ -32,7 +38,12 @@ private[sbt] object ForkTests {
|
|||
}
|
||||
}
|
||||
|
||||
private[this] def mainTestTask(runners: Map[TestFramework, Runner], opts: ProcessedOptions, classpath: Seq[File], fork: ForkOptions, log: Logger, parallel: Boolean): Task[TestOutput] =
|
||||
private[this] def mainTestTask(runners: Map[TestFramework, Runner],
|
||||
opts: ProcessedOptions,
|
||||
classpath: Seq[File],
|
||||
fork: ForkOptions,
|
||||
log: Logger,
|
||||
parallel: Boolean): Task[TestOutput] =
|
||||
std.TaskExtra.task {
|
||||
val server = new ServerSocket(0)
|
||||
val testListeners = opts.testListeners flatMap {
|
||||
|
|
@ -42,7 +53,8 @@ private[sbt] object ForkTests {
|
|||
|
||||
object Acceptor extends Runnable {
|
||||
val resultsAcc = mutable.Map.empty[String, SuiteResult]
|
||||
lazy val result = TestOutput(overall(resultsAcc.values.map(_.result)), resultsAcc.toMap, Iterable.empty)
|
||||
lazy val result =
|
||||
TestOutput(overall(resultsAcc.values.map(_.result)), resultsAcc.toMap, Iterable.empty)
|
||||
|
||||
def run(): Unit = {
|
||||
val socket =
|
||||
|
|
@ -50,7 +62,8 @@ private[sbt] object ForkTests {
|
|||
server.accept()
|
||||
} catch {
|
||||
case e: java.net.SocketException =>
|
||||
log.error("Could not accept connection from test agent: " + e.getClass + ": " + e.getMessage)
|
||||
log.error(
|
||||
"Could not accept connection from test agent: " + e.getClass + ": " + e.getMessage)
|
||||
log.trace(e)
|
||||
server.close()
|
||||
return
|
||||
|
|
@ -64,7 +77,12 @@ private[sbt] object ForkTests {
|
|||
val config = new ForkConfiguration(log.ansiCodesSupported, parallel)
|
||||
os.writeObject(config)
|
||||
|
||||
val taskdefs = opts.tests.map(t => new TaskDef(t.name, forkFingerprint(t.fingerprint), t.explicitlySpecified, t.selectors))
|
||||
val taskdefs = opts.tests.map(
|
||||
t =>
|
||||
new TaskDef(t.name,
|
||||
forkFingerprint(t.fingerprint),
|
||||
t.explicitlySpecified,
|
||||
t.selectors))
|
||||
os.writeObject(taskdefs.toArray)
|
||||
|
||||
os.writeInt(runners.size)
|
||||
|
|
@ -79,7 +97,8 @@ private[sbt] object ForkTests {
|
|||
} catch {
|
||||
case NonFatal(e) =>
|
||||
def throwableToString(t: Throwable) = {
|
||||
import java.io._; val sw = new StringWriter; t.printStackTrace(new PrintWriter(sw)); sw.toString
|
||||
import java.io._; val sw = new StringWriter; t.printStackTrace(new PrintWriter(sw));
|
||||
sw.toString
|
||||
}
|
||||
resultsAcc("Forked test harness failed: " + throwableToString(e)) = SuiteResult.Error
|
||||
} finally {
|
||||
|
|
@ -93,12 +112,20 @@ private[sbt] object ForkTests {
|
|||
val acceptorThread = new Thread(Acceptor)
|
||||
acceptorThread.start()
|
||||
|
||||
val fullCp = classpath ++: Seq(IO.classLocationFile[ForkMain], IO.classLocationFile[Framework])
|
||||
val options = Seq("-classpath", fullCp mkString File.pathSeparator, classOf[ForkMain].getCanonicalName, server.getLocalPort.toString)
|
||||
val fullCp = classpath ++: Seq(IO.classLocationFile[ForkMain],
|
||||
IO.classLocationFile[Framework])
|
||||
val options = Seq("-classpath",
|
||||
fullCp mkString File.pathSeparator,
|
||||
classOf[ForkMain].getCanonicalName,
|
||||
server.getLocalPort.toString)
|
||||
val ec = Fork.java(fork, options)
|
||||
val result =
|
||||
if (ec != 0)
|
||||
TestOutput(TestResult.Error, Map("Running java with options " + options.mkString(" ") + " failed with exit code " + ec -> SuiteResult.Error), Iterable.empty)
|
||||
TestOutput(TestResult.Error,
|
||||
Map(
|
||||
"Running java with options " + options
|
||||
.mkString(" ") + " failed with exit code " + ec -> SuiteResult.Error),
|
||||
Iterable.empty)
|
||||
else {
|
||||
// Need to wait acceptor thread to finish its business
|
||||
acceptorThread.join()
|
||||
|
|
@ -119,9 +146,14 @@ private[sbt] object ForkTests {
|
|||
case _ => sys.error("Unknown fingerprint type: " + f.getClass)
|
||||
}
|
||||
}
|
||||
private final class React(is: ObjectInputStream, os: ObjectOutputStream, log: Logger, listeners: Seq[TestReportListener], results: mutable.Map[String, SuiteResult]) {
|
||||
private final class React(is: ObjectInputStream,
|
||||
os: ObjectOutputStream,
|
||||
log: Logger,
|
||||
listeners: Seq[TestReportListener],
|
||||
results: mutable.Map[String, SuiteResult]) {
|
||||
import ForkTags._
|
||||
@annotation.tailrec def react(): Unit = is.readObject match {
|
||||
@annotation.tailrec
|
||||
def react(): Unit = is.readObject match {
|
||||
case `Done` =>
|
||||
os.writeObject(Done); os.flush()
|
||||
case Array(`Error`, s: String) =>
|
||||
|
|
|
|||
|
|
@ -27,11 +27,10 @@ object Package {
|
|||
}
|
||||
final case class MainClass(mainClassName: String) extends PackageOption
|
||||
final case class ManifestAttributes(attributes: (Attributes.Name, String)*) extends PackageOption
|
||||
def ManifestAttributes(attributes: (String, String)*): ManifestAttributes =
|
||||
{
|
||||
val converted = for ((name, value) <- attributes) yield (new Attributes.Name(name), value)
|
||||
new ManifestAttributes(converted: _*)
|
||||
}
|
||||
def ManifestAttributes(attributes: (String, String)*): ManifestAttributes = {
|
||||
val converted = for ((name, value) <- attributes) yield (new Attributes.Name(name), value)
|
||||
new ManifestAttributes(converted: _*)
|
||||
}
|
||||
|
||||
def mergeAttributes(a1: Attributes, a2: Attributes) = a1.asScala ++= a2.asScala
|
||||
// merges `mergeManifest` into `manifest` (mutating `manifest` in the process)
|
||||
|
|
@ -46,7 +45,9 @@ object Package {
|
|||
}
|
||||
}
|
||||
|
||||
final class Configuration(val sources: Seq[(File, String)], val jar: File, val options: Seq[PackageOption])
|
||||
final class Configuration(val sources: Seq[(File, String)],
|
||||
val jar: File,
|
||||
val options: Seq[PackageOption])
|
||||
def apply(conf: Configuration, cacheStoreFactory: CacheStoreFactory, log: Logger): Unit = {
|
||||
val manifest = new Manifest
|
||||
val main = manifest.getMainAttributes
|
||||
|
|
@ -60,15 +61,17 @@ object Package {
|
|||
}
|
||||
setVersion(main)
|
||||
|
||||
val cachedMakeJar = inputChanged(cacheStoreFactory make "inputs") { (inChanged, inputs: Map[File, String] :+: FilesInfo[ModifiedFileInfo] :+: Manifest :+: HNil) =>
|
||||
import exists.format
|
||||
val sources :+: _ :+: manifest :+: HNil = inputs
|
||||
inputChanged(cacheStoreFactory make "output") { (outChanged, jar: PlainFileInfo) =>
|
||||
if (inChanged || outChanged)
|
||||
makeJar(sources.toSeq, jar.file, manifest, log)
|
||||
else
|
||||
log.debug("Jar uptodate: " + jar.file)
|
||||
}
|
||||
val cachedMakeJar = inputChanged(cacheStoreFactory make "inputs") {
|
||||
(inChanged,
|
||||
inputs: Map[File, String] :+: FilesInfo[ModifiedFileInfo] :+: Manifest :+: HNil) =>
|
||||
import exists.format
|
||||
val sources :+: _ :+: manifest :+: HNil = inputs
|
||||
inputChanged(cacheStoreFactory make "output") { (outChanged, jar: PlainFileInfo) =>
|
||||
if (inChanged || outChanged)
|
||||
makeJar(sources.toSeq, jar.file, manifest, log)
|
||||
else
|
||||
log.debug("Jar uptodate: " + jar.file)
|
||||
}
|
||||
}
|
||||
|
||||
val map = conf.sources.toMap
|
||||
|
|
@ -80,20 +83,27 @@ object Package {
|
|||
if (main.getValue(version) eq null)
|
||||
main.put(version, "1.0")
|
||||
}
|
||||
def addSpecManifestAttributes(name: String, version: String, orgName: String): PackageOption =
|
||||
{
|
||||
import Attributes.Name._
|
||||
val attribKeys = Seq(SPECIFICATION_TITLE, SPECIFICATION_VERSION, SPECIFICATION_VENDOR)
|
||||
val attribVals = Seq(name, version, orgName)
|
||||
ManifestAttributes(attribKeys zip attribVals: _*)
|
||||
}
|
||||
def addImplManifestAttributes(name: String, version: String, homepage: Option[java.net.URL], org: String, orgName: String): PackageOption =
|
||||
{
|
||||
import Attributes.Name._
|
||||
val attribKeys = Seq(IMPLEMENTATION_TITLE, IMPLEMENTATION_VERSION, IMPLEMENTATION_VENDOR, IMPLEMENTATION_VENDOR_ID)
|
||||
val attribVals = Seq(name, version, orgName, org)
|
||||
ManifestAttributes((attribKeys zip attribVals) ++ { homepage map (h => (IMPLEMENTATION_URL, h.toString)) }: _*)
|
||||
}
|
||||
def addSpecManifestAttributes(name: String, version: String, orgName: String): PackageOption = {
|
||||
import Attributes.Name._
|
||||
val attribKeys = Seq(SPECIFICATION_TITLE, SPECIFICATION_VERSION, SPECIFICATION_VENDOR)
|
||||
val attribVals = Seq(name, version, orgName)
|
||||
ManifestAttributes(attribKeys zip attribVals: _*)
|
||||
}
|
||||
def addImplManifestAttributes(name: String,
|
||||
version: String,
|
||||
homepage: Option[java.net.URL],
|
||||
org: String,
|
||||
orgName: String): PackageOption = {
|
||||
import Attributes.Name._
|
||||
val attribKeys = Seq(IMPLEMENTATION_TITLE,
|
||||
IMPLEMENTATION_VERSION,
|
||||
IMPLEMENTATION_VENDOR,
|
||||
IMPLEMENTATION_VENDOR_ID)
|
||||
val attribVals = Seq(name, version, orgName, org)
|
||||
ManifestAttributes((attribKeys zip attribVals) ++ {
|
||||
homepage map (h => (IMPLEMENTATION_URL, h.toString))
|
||||
}: _*)
|
||||
}
|
||||
def makeJar(sources: Seq[(File, String)], jar: File, manifest: Manifest, log: Logger): Unit = {
|
||||
val path = jar.getAbsolutePath
|
||||
log.info("Packaging " + path + " ...")
|
||||
|
|
|
|||
|
|
@ -23,43 +23,49 @@ import sbt.internal.util.ManagedLogger
|
|||
object RawCompileLike {
|
||||
type Gen = (Seq[File], Seq[File], File, Seq[String], Int, ManagedLogger) => Unit
|
||||
|
||||
private def optionFiles(options: Seq[String], fileInputOpts: Seq[String]): List[File] =
|
||||
{
|
||||
@annotation.tailrec
|
||||
def loop(opt: List[String], result: List[File]): List[File] = {
|
||||
opt.dropWhile(!fileInputOpts.contains(_)) match {
|
||||
case List(_, fileOpt, tail @ _*) =>
|
||||
{
|
||||
val file = new File(fileOpt)
|
||||
if (file.isFile) loop(tail.toList, file :: result)
|
||||
else loop(tail.toList, result)
|
||||
}
|
||||
case Nil | List(_) => result
|
||||
private def optionFiles(options: Seq[String], fileInputOpts: Seq[String]): List[File] = {
|
||||
@annotation.tailrec
|
||||
def loop(opt: List[String], result: List[File]): List[File] = {
|
||||
opt.dropWhile(!fileInputOpts.contains(_)) match {
|
||||
case List(_, fileOpt, tail @ _*) => {
|
||||
val file = new File(fileOpt)
|
||||
if (file.isFile) loop(tail.toList, file :: result)
|
||||
else loop(tail.toList, result)
|
||||
}
|
||||
case Nil | List(_) => result
|
||||
}
|
||||
loop(options.toList, Nil)
|
||||
}
|
||||
loop(options.toList, Nil)
|
||||
}
|
||||
|
||||
def cached(cacheStoreFactory: CacheStoreFactory, doCompile: Gen): Gen = cached(cacheStoreFactory, Seq(), doCompile)
|
||||
def cached(cacheStoreFactory: CacheStoreFactory, fileInputOpts: Seq[String], doCompile: Gen): Gen = (sources, classpath, outputDirectory, options, maxErrors, log) =>
|
||||
{
|
||||
type Inputs = FilesInfo[HashFileInfo] :+: FilesInfo[ModifiedFileInfo] :+: Seq[File] :+: File :+: Seq[String] :+: Int :+: HNil
|
||||
val inputs: Inputs = hash(sources.toSet ++ optionFiles(options, fileInputOpts)) :+: lastModified(classpath.toSet) :+: classpath :+: outputDirectory :+: options :+: maxErrors :+: HNil
|
||||
def cached(cacheStoreFactory: CacheStoreFactory, doCompile: Gen): Gen =
|
||||
cached(cacheStoreFactory, Seq(), doCompile)
|
||||
def cached(cacheStoreFactory: CacheStoreFactory,
|
||||
fileInputOpts: Seq[String],
|
||||
doCompile: Gen): Gen =
|
||||
(sources, classpath, outputDirectory, options, maxErrors, log) => {
|
||||
type Inputs =
|
||||
FilesInfo[HashFileInfo] :+: FilesInfo[ModifiedFileInfo] :+: Seq[File] :+: File :+: Seq[
|
||||
String] :+: Int :+: HNil
|
||||
val inputs
|
||||
: Inputs = hash(sources.toSet ++ optionFiles(options, fileInputOpts)) :+: lastModified(
|
||||
classpath.toSet) :+: classpath :+: outputDirectory :+: options :+: maxErrors :+: HNil
|
||||
implicit val stringEquiv: Equiv[String] = defaultEquiv
|
||||
implicit val fileEquiv: Equiv[File] = defaultEquiv
|
||||
implicit val intEquiv: Equiv[Int] = defaultEquiv
|
||||
val cachedComp = inputChanged(cacheStoreFactory make "inputs") { (inChanged, in: Inputs) =>
|
||||
inputChanged(cacheStoreFactory make "output") { (outChanged, outputs: FilesInfo[PlainFileInfo]) =>
|
||||
if (inChanged || outChanged)
|
||||
doCompile(sources, classpath, outputDirectory, options, maxErrors, log)
|
||||
else
|
||||
log.debug("Uptodate: " + outputDirectory.getAbsolutePath)
|
||||
inputChanged(cacheStoreFactory make "output") {
|
||||
(outChanged, outputs: FilesInfo[PlainFileInfo]) =>
|
||||
if (inChanged || outChanged)
|
||||
doCompile(sources, classpath, outputDirectory, options, maxErrors, log)
|
||||
else
|
||||
log.debug("Uptodate: " + outputDirectory.getAbsolutePath)
|
||||
}
|
||||
}
|
||||
cachedComp(inputs)(exists(outputDirectory.allPaths.get.toSet))
|
||||
}
|
||||
def prepare(description: String, doCompile: Gen): Gen = (sources, classpath, outputDirectory, options, maxErrors, log) =>
|
||||
{
|
||||
def prepare(description: String, doCompile: Gen): Gen =
|
||||
(sources, classpath, outputDirectory, options, maxErrors, log) => {
|
||||
if (sources.isEmpty)
|
||||
log.info("No sources available, skipping " + description + "...")
|
||||
else {
|
||||
|
|
@ -70,15 +76,19 @@ object RawCompileLike {
|
|||
log.info(description.capitalize + " successful.")
|
||||
}
|
||||
}
|
||||
def filterSources(f: File => Boolean, doCompile: Gen): Gen = (sources, classpath, outputDirectory, options, maxErrors, log) =>
|
||||
doCompile(sources filter f, classpath, outputDirectory, options, maxErrors, log)
|
||||
def filterSources(f: File => Boolean, doCompile: Gen): Gen =
|
||||
(sources, classpath, outputDirectory, options, maxErrors, log) =>
|
||||
doCompile(sources filter f, classpath, outputDirectory, options, maxErrors, log)
|
||||
|
||||
def rawCompile(instance: ScalaInstance, cpOptions: ClasspathOptions): Gen = (sources, classpath, outputDirectory, options, maxErrors, log) =>
|
||||
{
|
||||
def rawCompile(instance: ScalaInstance, cpOptions: ClasspathOptions): Gen =
|
||||
(sources, classpath, outputDirectory, options, maxErrors, log) => {
|
||||
val compiler = new RawCompiler(instance, cpOptions, log)
|
||||
compiler(sources, classpath, outputDirectory, options)
|
||||
}
|
||||
def compile(label: String, cacheStoreFactory: CacheStoreFactory, instance: ScalaInstance, cpOptions: ClasspathOptions): Gen =
|
||||
def compile(label: String,
|
||||
cacheStoreFactory: CacheStoreFactory,
|
||||
instance: ScalaInstance,
|
||||
cpOptions: ClasspathOptions): Gen =
|
||||
cached(cacheStoreFactory, prepare(label + " sources", rawCompile(instance, cpOptions)))
|
||||
|
||||
val nop: Gen = (sources, classpath, outputDirectory, options, maxErrors, log) => ()
|
||||
|
|
|
|||
|
|
@ -26,42 +26,44 @@ import sjsonnew.{ Builder, JsonFormat, Unbuilder, deserializationError }
|
|||
* It is safe to use for its intended purpose: copying resources to a class output directory.
|
||||
*/
|
||||
object Sync {
|
||||
def apply(store: CacheStore, inStyle: FileInfo.Style = FileInfo.lastModified, outStyle: FileInfo.Style = FileInfo.exists): Traversable[(File, File)] => Relation[File, File] =
|
||||
mappings =>
|
||||
{
|
||||
val relation = Relation.empty ++ mappings
|
||||
noDuplicateTargets(relation)
|
||||
val currentInfo = relation._1s.map(s => (s, inStyle(s))).toMap
|
||||
def apply(store: CacheStore,
|
||||
inStyle: FileInfo.Style = FileInfo.lastModified,
|
||||
outStyle: FileInfo.Style = FileInfo.exists)
|
||||
: Traversable[(File, File)] => Relation[File, File] =
|
||||
mappings => {
|
||||
val relation = Relation.empty ++ mappings
|
||||
noDuplicateTargets(relation)
|
||||
val currentInfo = relation._1s.map(s => (s, inStyle(s))).toMap
|
||||
|
||||
val (previousRelation, previousInfo) = readInfo(store)(inStyle.format)
|
||||
val removeTargets = previousRelation._2s -- relation._2s
|
||||
val (previousRelation, previousInfo) = readInfo(store)(inStyle.format)
|
||||
val removeTargets = previousRelation._2s -- relation._2s
|
||||
|
||||
def outofdate(source: File, target: File): Boolean =
|
||||
!previousRelation.contains(source, target) ||
|
||||
(previousInfo get source) != (currentInfo get source) ||
|
||||
!target.exists ||
|
||||
target.isDirectory != source.isDirectory
|
||||
def outofdate(source: File, target: File): Boolean =
|
||||
!previousRelation.contains(source, target) ||
|
||||
(previousInfo get source) != (currentInfo get source) ||
|
||||
!target.exists ||
|
||||
target.isDirectory != source.isDirectory
|
||||
|
||||
val updates = relation filter outofdate
|
||||
val updates = relation filter outofdate
|
||||
|
||||
val (cleanDirs, cleanFiles) = (updates._2s ++ removeTargets).partition(_.isDirectory)
|
||||
val (cleanDirs, cleanFiles) = (updates._2s ++ removeTargets).partition(_.isDirectory)
|
||||
|
||||
IO.delete(cleanFiles)
|
||||
IO.deleteIfEmpty(cleanDirs)
|
||||
updates.all.foreach((copy _).tupled)
|
||||
IO.delete(cleanFiles)
|
||||
IO.deleteIfEmpty(cleanDirs)
|
||||
updates.all.foreach((copy _).tupled)
|
||||
|
||||
writeInfo(store, relation, currentInfo)(inStyle.format)
|
||||
relation
|
||||
}
|
||||
writeInfo(store, relation, currentInfo)(inStyle.format)
|
||||
relation
|
||||
}
|
||||
|
||||
def copy(source: File, target: File): Unit =
|
||||
if (source.isFile)
|
||||
IO.copyFile(source, target, true)
|
||||
else if (!target.exists) // we don't want to update the last modified time of an existing directory
|
||||
{
|
||||
IO.createDirectory(target)
|
||||
IO.copyLastModified(source, target)
|
||||
}
|
||||
{
|
||||
IO.createDirectory(target)
|
||||
IO.copyLastModified(source, target)
|
||||
}
|
||||
|
||||
def noDuplicateTargets(relation: Relation[File, File]): Unit = {
|
||||
val dups = relation.reverseMap.filter {
|
||||
|
|
@ -75,7 +77,8 @@ object Sync {
|
|||
sys.error("Duplicate mappings:" + dups.mkString)
|
||||
}
|
||||
|
||||
implicit def relationFormat[A, B](implicit af: JsonFormat[Map[A, Set[B]]], bf: JsonFormat[Map[B, Set[A]]]): JsonFormat[Relation[A, B]] =
|
||||
implicit def relationFormat[A, B](implicit af: JsonFormat[Map[A, Set[B]]],
|
||||
bf: JsonFormat[Map[B, Set[A]]]): JsonFormat[Relation[A, B]] =
|
||||
new JsonFormat[Relation[A, B]] {
|
||||
def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Relation[A, B] =
|
||||
jsOpt match {
|
||||
|
|
@ -98,12 +101,15 @@ object Sync {
|
|||
|
||||
}
|
||||
|
||||
def writeInfo[F <: FileInfo](store: CacheStore, relation: Relation[File, File], info: Map[File, F])(implicit infoFormat: JsonFormat[F]): Unit =
|
||||
def writeInfo[F <: FileInfo](store: CacheStore,
|
||||
relation: Relation[File, File],
|
||||
info: Map[File, F])(implicit infoFormat: JsonFormat[F]): Unit =
|
||||
store.write((relation, info))
|
||||
|
||||
type RelationInfo[F] = (Relation[File, File], Map[File, F])
|
||||
|
||||
def readInfo[F <: FileInfo](store: CacheStore)(implicit infoFormat: JsonFormat[F]): RelationInfo[F] =
|
||||
def readInfo[F <: FileInfo](store: CacheStore)(
|
||||
implicit infoFormat: JsonFormat[F]): RelationInfo[F] =
|
||||
store.read(default = (Relation.empty[File, File], Map.empty[File, F]))
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,13 @@ trait TestResultLogger {
|
|||
def run(log: Logger, results: Output, taskName: String): Unit
|
||||
|
||||
/** Only allow invocation if certain criteria is met, else use another `TestResultLogger` (defaulting to nothing) . */
|
||||
final def onlyIf(f: (Output, String) => Boolean, otherwise: TestResultLogger = TestResultLogger.Null) =
|
||||
final def onlyIf(f: (Output, String) => Boolean,
|
||||
otherwise: TestResultLogger = TestResultLogger.Null) =
|
||||
TestResultLogger.choose(f, this, otherwise)
|
||||
|
||||
/** Allow invocation unless a certain predicate passes, in which case use another `TestResultLogger` (defaulting to nothing) . */
|
||||
final def unless(f: (Output, String) => Boolean, otherwise: TestResultLogger = TestResultLogger.Null) =
|
||||
final def unless(f: (Output, String) => Boolean,
|
||||
otherwise: TestResultLogger = TestResultLogger.Null) =
|
||||
TestResultLogger.choose(f, otherwise, this)
|
||||
}
|
||||
|
||||
|
|
@ -118,16 +120,32 @@ object TestResultLogger {
|
|||
results.summaries.size > 1 || results.summaries.headOption.forall(_.summaryText.isEmpty)
|
||||
|
||||
val printStandard = TestResultLogger((log, results, _) => {
|
||||
val (skippedCount, errorsCount, passedCount, failuresCount, ignoredCount, canceledCount, pendingCount) =
|
||||
val (skippedCount,
|
||||
errorsCount,
|
||||
passedCount,
|
||||
failuresCount,
|
||||
ignoredCount,
|
||||
canceledCount,
|
||||
pendingCount) =
|
||||
results.events.foldLeft((0, 0, 0, 0, 0, 0, 0)) {
|
||||
case ((skippedAcc, errorAcc, passedAcc, failureAcc, ignoredAcc, canceledAcc, pendingAcc), (name, testEvent)) =>
|
||||
(skippedAcc + testEvent.skippedCount, errorAcc + testEvent.errorCount, passedAcc + testEvent.passedCount, failureAcc + testEvent.failureCount,
|
||||
ignoredAcc + testEvent.ignoredCount, canceledAcc + testEvent.canceledCount, pendingAcc + testEvent.pendingCount)
|
||||
case ((skippedAcc, errorAcc, passedAcc, failureAcc, ignoredAcc, canceledAcc, pendingAcc),
|
||||
(name, testEvent)) =>
|
||||
(skippedAcc + testEvent.skippedCount,
|
||||
errorAcc + testEvent.errorCount,
|
||||
passedAcc + testEvent.passedCount,
|
||||
failureAcc + testEvent.failureCount,
|
||||
ignoredAcc + testEvent.ignoredCount,
|
||||
canceledAcc + testEvent.canceledCount,
|
||||
pendingAcc + testEvent.pendingCount)
|
||||
}
|
||||
val totalCount = failuresCount + errorsCount + skippedCount + passedCount
|
||||
val base = s"Total $totalCount, Failed $failuresCount, Errors $errorsCount, Passed $passedCount"
|
||||
val base =
|
||||
s"Total $totalCount, Failed $failuresCount, Errors $errorsCount, Passed $passedCount"
|
||||
|
||||
val otherCounts = Seq("Skipped" -> skippedCount, "Ignored" -> ignoredCount, "Canceled" -> canceledCount, "Pending" -> pendingCount)
|
||||
val otherCounts = Seq("Skipped" -> skippedCount,
|
||||
"Ignored" -> ignoredCount,
|
||||
"Canceled" -> canceledCount,
|
||||
"Pending" -> pendingCount)
|
||||
val extra = otherCounts.filter(_._2 > 0).map { case (label, count) => s", $label $count" }
|
||||
|
||||
val postfix = base + extra.mkString
|
||||
|
|
@ -155,7 +173,7 @@ object TestResultLogger {
|
|||
show("Error during tests:", Level.Error, select(TestResult.Error))
|
||||
})
|
||||
|
||||
val printNoTests = TestResultLogger((log, results, taskName) =>
|
||||
log.info("No tests to run for " + taskName))
|
||||
val printNoTests = TestResultLogger(
|
||||
(log, results, taskName) => log.info("No tests to run for " + taskName))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,16 @@ import xsbti.api.Definition
|
|||
import xsbti.compile.CompileAnalysis
|
||||
import ConcurrentRestrictions.Tag
|
||||
|
||||
import testing.{ AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint, Runner, TaskDef, SuiteSelector, Task => TestTask }
|
||||
import testing.{
|
||||
AnnotatedFingerprint,
|
||||
Fingerprint,
|
||||
Framework,
|
||||
SubclassFingerprint,
|
||||
Runner,
|
||||
TaskDef,
|
||||
SuiteSelector,
|
||||
Task => TestTask
|
||||
}
|
||||
import scala.annotation.tailrec
|
||||
|
||||
import sbt.internal.util.ManagedLogger
|
||||
|
|
@ -21,6 +30,7 @@ import sbt.protocol.testing.TestResult
|
|||
|
||||
sealed trait TestOption
|
||||
object Tests {
|
||||
|
||||
/**
|
||||
* The result of a test run.
|
||||
*
|
||||
|
|
@ -28,7 +38,9 @@ object Tests {
|
|||
* @param events The result of each test group (suite) executed during this test run.
|
||||
* @param summaries Explicit summaries directly provided by test frameworks. This may be empty, in which case a default summary will be generated.
|
||||
*/
|
||||
final case class Output(overall: TestResult, events: Map[String, SuiteResult], summaries: Iterable[Summary])
|
||||
final case class Output(overall: TestResult,
|
||||
events: Map[String, SuiteResult],
|
||||
summaries: Iterable[Summary])
|
||||
|
||||
/**
|
||||
* Summarizes a test run.
|
||||
|
|
@ -88,7 +100,8 @@ object Tests {
|
|||
* If None, the arguments will apply to all test frameworks.
|
||||
* @param args The list of arguments to pass to the selected framework(s).
|
||||
*/
|
||||
final case class Argument(framework: Option[TestFramework], args: List[String]) extends TestOption
|
||||
final case class Argument(framework: Option[TestFramework], args: List[String])
|
||||
extends TestOption
|
||||
|
||||
/**
|
||||
* Configures test execution.
|
||||
|
|
@ -115,92 +128,134 @@ object Tests {
|
|||
final case class Group(name: String, tests: Seq[TestDefinition], runPolicy: TestRunPolicy)
|
||||
|
||||
private[sbt] final class ProcessedOptions(
|
||||
val tests: Seq[TestDefinition],
|
||||
val setup: Seq[ClassLoader => Unit],
|
||||
val cleanup: Seq[ClassLoader => Unit],
|
||||
val testListeners: Seq[TestReportListener]
|
||||
val tests: Seq[TestDefinition],
|
||||
val setup: Seq[ClassLoader => Unit],
|
||||
val cleanup: Seq[ClassLoader => Unit],
|
||||
val testListeners: Seq[TestReportListener]
|
||||
)
|
||||
private[sbt] def processOptions(config: Execution, discovered: Seq[TestDefinition], log: Logger): ProcessedOptions =
|
||||
{
|
||||
import collection.mutable.{ HashSet, ListBuffer }
|
||||
val testFilters = new ListBuffer[String => Boolean]
|
||||
var orderedFilters = Seq[String => Boolean]()
|
||||
val excludeTestsSet = new HashSet[String]
|
||||
val setup, cleanup = new ListBuffer[ClassLoader => Unit]
|
||||
val testListeners = new ListBuffer[TestReportListener]
|
||||
val undefinedFrameworks = new ListBuffer[String]
|
||||
private[sbt] def processOptions(config: Execution,
|
||||
discovered: Seq[TestDefinition],
|
||||
log: Logger): ProcessedOptions = {
|
||||
import collection.mutable.{ HashSet, ListBuffer }
|
||||
val testFilters = new ListBuffer[String => Boolean]
|
||||
var orderedFilters = Seq[String => Boolean]()
|
||||
val excludeTestsSet = new HashSet[String]
|
||||
val setup, cleanup = new ListBuffer[ClassLoader => Unit]
|
||||
val testListeners = new ListBuffer[TestReportListener]
|
||||
val undefinedFrameworks = new ListBuffer[String]
|
||||
|
||||
for (option <- config.options) {
|
||||
option match {
|
||||
case Filter(include) => testFilters += include
|
||||
case Filters(includes) => if (orderedFilters.nonEmpty) sys.error("Cannot define multiple ordered test filters.") else orderedFilters = includes
|
||||
case Exclude(exclude) => excludeTestsSet ++= exclude
|
||||
case Listeners(listeners) => testListeners ++= listeners
|
||||
case Setup(setupFunction) => setup += setupFunction
|
||||
case Cleanup(cleanupFunction) => cleanup += cleanupFunction
|
||||
case a: Argument => // now handled by whatever constructs `runners`
|
||||
}
|
||||
}
|
||||
|
||||
if (excludeTestsSet.nonEmpty)
|
||||
log.debug(excludeTestsSet.mkString("Excluding tests: \n\t", "\n\t", ""))
|
||||
if (undefinedFrameworks.nonEmpty)
|
||||
log.warn("Arguments defined for test frameworks that are not present:\n\t" + undefinedFrameworks.mkString("\n\t"))
|
||||
|
||||
def includeTest(test: TestDefinition) = !excludeTestsSet.contains(test.name) && testFilters.forall(filter => filter(test.name))
|
||||
val filtered0 = discovered.filter(includeTest).toList.distinct
|
||||
val tests = if (orderedFilters.isEmpty) filtered0 else orderedFilters.flatMap(f => filtered0.filter(d => f(d.name))).toList.distinct
|
||||
val uniqueTests = distinctBy(tests)(_.name)
|
||||
new ProcessedOptions(uniqueTests, setup.toList, cleanup.toList, testListeners.toList)
|
||||
}
|
||||
|
||||
private[this] def distinctBy[T, K](in: Seq[T])(f: T => K): Seq[T] =
|
||||
{
|
||||
val seen = new collection.mutable.HashSet[K]
|
||||
in.filter(t => seen.add(f(t)))
|
||||
}
|
||||
|
||||
def apply(frameworks: Map[TestFramework, Framework], testLoader: ClassLoader, runners: Map[TestFramework, Runner], discovered: Seq[TestDefinition], config: Execution, log: ManagedLogger): Task[Output] =
|
||||
{
|
||||
val o = processOptions(config, discovered, log)
|
||||
testTask(testLoader, frameworks, runners, o.tests, o.setup, o.cleanup, log, o.testListeners, config)
|
||||
}
|
||||
|
||||
def testTask(loader: ClassLoader, frameworks: Map[TestFramework, Framework], runners: Map[TestFramework, Runner], tests: Seq[TestDefinition],
|
||||
userSetup: Iterable[ClassLoader => Unit], userCleanup: Iterable[ClassLoader => Unit],
|
||||
log: ManagedLogger, testListeners: Seq[TestReportListener], config: Execution): Task[Output] =
|
||||
{
|
||||
def fj(actions: Iterable[() => Unit]): Task[Unit] = nop.dependsOn(actions.toSeq.fork(_()): _*)
|
||||
def partApp(actions: Iterable[ClassLoader => Unit]) = actions.toSeq map { a => () => a(loader) }
|
||||
|
||||
val (frameworkSetup, runnables, frameworkCleanup) =
|
||||
TestFramework.testTasks(frameworks, runners, loader, tests, log, testListeners)
|
||||
|
||||
val setupTasks = fj(partApp(userSetup) :+ frameworkSetup)
|
||||
val mainTasks =
|
||||
if (config.parallel)
|
||||
makeParallel(loader, runnables, setupTasks, config.tags) //.toSeq.join
|
||||
else
|
||||
makeSerial(loader, runnables, setupTasks, config.tags)
|
||||
val taggedMainTasks = mainTasks.tagw(config.tags: _*)
|
||||
taggedMainTasks map processResults flatMap { results =>
|
||||
val cleanupTasks = fj(partApp(userCleanup) :+ frameworkCleanup(results.overall))
|
||||
cleanupTasks map { _ => results }
|
||||
for (option <- config.options) {
|
||||
option match {
|
||||
case Filter(include) => testFilters += include
|
||||
case Filters(includes) =>
|
||||
if (orderedFilters.nonEmpty) sys.error("Cannot define multiple ordered test filters.")
|
||||
else orderedFilters = includes
|
||||
case Exclude(exclude) => excludeTestsSet ++= exclude
|
||||
case Listeners(listeners) => testListeners ++= listeners
|
||||
case Setup(setupFunction) => setup += setupFunction
|
||||
case Cleanup(cleanupFunction) => cleanup += cleanupFunction
|
||||
case a: Argument => // now handled by whatever constructs `runners`
|
||||
}
|
||||
}
|
||||
|
||||
if (excludeTestsSet.nonEmpty)
|
||||
log.debug(excludeTestsSet.mkString("Excluding tests: \n\t", "\n\t", ""))
|
||||
if (undefinedFrameworks.nonEmpty)
|
||||
log.warn(
|
||||
"Arguments defined for test frameworks that are not present:\n\t" + undefinedFrameworks
|
||||
.mkString("\n\t"))
|
||||
|
||||
def includeTest(test: TestDefinition) =
|
||||
!excludeTestsSet.contains(test.name) && testFilters.forall(filter => filter(test.name))
|
||||
val filtered0 = discovered.filter(includeTest).toList.distinct
|
||||
val tests =
|
||||
if (orderedFilters.isEmpty) filtered0
|
||||
else orderedFilters.flatMap(f => filtered0.filter(d => f(d.name))).toList.distinct
|
||||
val uniqueTests = distinctBy(tests)(_.name)
|
||||
new ProcessedOptions(uniqueTests, setup.toList, cleanup.toList, testListeners.toList)
|
||||
}
|
||||
|
||||
private[this] def distinctBy[T, K](in: Seq[T])(f: T => K): Seq[T] = {
|
||||
val seen = new collection.mutable.HashSet[K]
|
||||
in.filter(t => seen.add(f(t)))
|
||||
}
|
||||
|
||||
def apply(frameworks: Map[TestFramework, Framework],
|
||||
testLoader: ClassLoader,
|
||||
runners: Map[TestFramework, Runner],
|
||||
discovered: Seq[TestDefinition],
|
||||
config: Execution,
|
||||
log: ManagedLogger): Task[Output] = {
|
||||
val o = processOptions(config, discovered, log)
|
||||
testTask(testLoader,
|
||||
frameworks,
|
||||
runners,
|
||||
o.tests,
|
||||
o.setup,
|
||||
o.cleanup,
|
||||
log,
|
||||
o.testListeners,
|
||||
config)
|
||||
}
|
||||
|
||||
def testTask(loader: ClassLoader,
|
||||
frameworks: Map[TestFramework, Framework],
|
||||
runners: Map[TestFramework, Runner],
|
||||
tests: Seq[TestDefinition],
|
||||
userSetup: Iterable[ClassLoader => Unit],
|
||||
userCleanup: Iterable[ClassLoader => Unit],
|
||||
log: ManagedLogger,
|
||||
testListeners: Seq[TestReportListener],
|
||||
config: Execution): Task[Output] = {
|
||||
def fj(actions: Iterable[() => Unit]): Task[Unit] = nop.dependsOn(actions.toSeq.fork(_()): _*)
|
||||
def partApp(actions: Iterable[ClassLoader => Unit]) = actions.toSeq map { a => () =>
|
||||
a(loader)
|
||||
}
|
||||
|
||||
val (frameworkSetup, runnables, frameworkCleanup) =
|
||||
TestFramework.testTasks(frameworks, runners, loader, tests, log, testListeners)
|
||||
|
||||
val setupTasks = fj(partApp(userSetup) :+ frameworkSetup)
|
||||
val mainTasks =
|
||||
if (config.parallel)
|
||||
makeParallel(loader, runnables, setupTasks, config.tags) //.toSeq.join
|
||||
else
|
||||
makeSerial(loader, runnables, setupTasks, config.tags)
|
||||
val taggedMainTasks = mainTasks.tagw(config.tags: _*)
|
||||
taggedMainTasks map processResults flatMap { results =>
|
||||
val cleanupTasks = fj(partApp(userCleanup) :+ frameworkCleanup(results.overall))
|
||||
cleanupTasks map { _ =>
|
||||
results
|
||||
}
|
||||
}
|
||||
}
|
||||
type TestRunnable = (String, TestFunction)
|
||||
|
||||
private def createNestedRunnables(loader: ClassLoader, testFun: TestFunction, nestedTasks: Seq[TestTask]): Seq[(String, TestFunction)] =
|
||||
private def createNestedRunnables(loader: ClassLoader,
|
||||
testFun: TestFunction,
|
||||
nestedTasks: Seq[TestTask]): Seq[(String, TestFunction)] =
|
||||
nestedTasks.view.zipWithIndex map {
|
||||
case (nt, idx) =>
|
||||
val testFunDef = testFun.taskDef
|
||||
(testFunDef.fullyQualifiedName, TestFramework.createTestFunction(loader, new TaskDef(testFunDef.fullyQualifiedName + "-" + idx, testFunDef.fingerprint, testFunDef.explicitlySpecified, testFunDef.selectors), testFun.runner, nt))
|
||||
(testFunDef.fullyQualifiedName,
|
||||
TestFramework.createTestFunction(loader,
|
||||
new TaskDef(testFunDef.fullyQualifiedName + "-" + idx,
|
||||
testFunDef.fingerprint,
|
||||
testFunDef.explicitlySpecified,
|
||||
testFunDef.selectors),
|
||||
testFun.runner,
|
||||
nt))
|
||||
}
|
||||
|
||||
def makeParallel(loader: ClassLoader, runnables: Iterable[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag, Int)]): Task[Map[String, SuiteResult]] =
|
||||
def makeParallel(loader: ClassLoader,
|
||||
runnables: Iterable[TestRunnable],
|
||||
setupTasks: Task[Unit],
|
||||
tags: Seq[(Tag, Int)]): Task[Map[String, SuiteResult]] =
|
||||
toTasks(loader, runnables.toSeq, tags).dependsOn(setupTasks)
|
||||
|
||||
def toTasks(loader: ClassLoader, runnables: Seq[TestRunnable], tags: Seq[(Tag, Int)]): Task[Map[String, SuiteResult]] = {
|
||||
def toTasks(loader: ClassLoader,
|
||||
runnables: Seq[TestRunnable],
|
||||
tags: Seq[(Tag, Int)]): Task[Map[String, SuiteResult]] = {
|
||||
val tasks = runnables.map { case (name, test) => toTask(loader, name, test, tags) }
|
||||
tasks.join.map(_.foldLeft(Map.empty[String, SuiteResult]) {
|
||||
case (sum, e) =>
|
||||
|
|
@ -212,7 +267,10 @@ object Tests {
|
|||
})
|
||||
}
|
||||
|
||||
def toTask(loader: ClassLoader, name: String, fun: TestFunction, tags: Seq[(Tag, Int)]): Task[Map[String, SuiteResult]] = {
|
||||
def toTask(loader: ClassLoader,
|
||||
name: String,
|
||||
fun: TestFunction,
|
||||
tags: Seq[(Tag, Int)]): Task[Map[String, SuiteResult]] = {
|
||||
val base = task { (name, fun.apply()) }
|
||||
val taggedBase = base.tagw(tags: _*).tag(fun.tags.map(ConcurrentRestrictions.Tag(_)): _*)
|
||||
taggedBase flatMap {
|
||||
|
|
@ -229,21 +287,24 @@ object Tests {
|
|||
}
|
||||
}
|
||||
|
||||
def makeSerial(loader: ClassLoader, runnables: Seq[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag, Int)]): Task[List[(String, SuiteResult)]] =
|
||||
{
|
||||
@tailrec
|
||||
def processRunnable(runnableList: List[TestRunnable], acc: List[(String, SuiteResult)]): List[(String, SuiteResult)] =
|
||||
runnableList match {
|
||||
case hd :: rst =>
|
||||
val testFun = hd._2
|
||||
val (result, nestedTasks) = testFun.apply()
|
||||
val nestedRunnables = createNestedRunnables(loader, testFun, nestedTasks)
|
||||
processRunnable(nestedRunnables.toList ::: rst, (hd._1, result) :: acc)
|
||||
case Nil => acc
|
||||
}
|
||||
def makeSerial(loader: ClassLoader,
|
||||
runnables: Seq[TestRunnable],
|
||||
setupTasks: Task[Unit],
|
||||
tags: Seq[(Tag, Int)]): Task[List[(String, SuiteResult)]] = {
|
||||
@tailrec
|
||||
def processRunnable(runnableList: List[TestRunnable],
|
||||
acc: List[(String, SuiteResult)]): List[(String, SuiteResult)] =
|
||||
runnableList match {
|
||||
case hd :: rst =>
|
||||
val testFun = hd._2
|
||||
val (result, nestedTasks) = testFun.apply()
|
||||
val nestedRunnables = createNestedRunnables(loader, testFun, nestedTasks)
|
||||
processRunnable(nestedRunnables.toList ::: rst, (hd._1, result) :: acc)
|
||||
case Nil => acc
|
||||
}
|
||||
|
||||
task { processRunnable(runnables.toList, List.empty) } dependsOn (setupTasks)
|
||||
}
|
||||
task { processRunnable(runnables.toList, List.empty) } dependsOn (setupTasks)
|
||||
}
|
||||
|
||||
def processResults(results: Iterable[(String, SuiteResult)]): Output =
|
||||
Output(overall(results.map(_._2.result)), results.toMap, Iterable.empty)
|
||||
|
|
@ -257,24 +318,34 @@ object Tests {
|
|||
|
||||
def foldTasks(results: Seq[Task[Output]], parallel: Boolean): Task[Output] =
|
||||
if (results.isEmpty)
|
||||
task { Output(TestResult.Passed, Map.empty, Nil) }
|
||||
else if (parallel)
|
||||
task { Output(TestResult.Passed, Map.empty, Nil) } else if (parallel)
|
||||
reduced(results.toIndexedSeq, {
|
||||
case (Output(v1, m1, _), Output(v2, m2, _)) => Output(if (severity(v1) < severity(v2)) v2 else v1, m1 ++ m2, Iterable.empty)
|
||||
case (Output(v1, m1, _), Output(v2, m2, _)) =>
|
||||
Output(if (severity(v1) < severity(v2)) v2 else v1, m1 ++ m2, Iterable.empty)
|
||||
})
|
||||
else {
|
||||
def sequence(tasks: List[Task[Output]], acc: List[Output]): Task[List[Output]] = tasks match {
|
||||
case Nil => task(acc.reverse)
|
||||
case hd :: tl => hd flatMap { out => sequence(tl, out :: acc) }
|
||||
}
|
||||
def sequence(tasks: List[Task[Output]], acc: List[Output]): Task[List[Output]] =
|
||||
tasks match {
|
||||
case Nil => task(acc.reverse)
|
||||
case hd :: tl =>
|
||||
hd flatMap { out =>
|
||||
sequence(tl, out :: acc)
|
||||
}
|
||||
}
|
||||
sequence(results.toList, List()) map { ress =>
|
||||
val (rs, ms) = ress.unzip { e => (e.overall, e.events) }
|
||||
val (rs, ms) = ress.unzip { e =>
|
||||
(e.overall, e.events)
|
||||
}
|
||||
Output(overall(rs), ms reduce (_ ++ _), Iterable.empty)
|
||||
}
|
||||
}
|
||||
def overall(results: Iterable[TestResult]): TestResult =
|
||||
((TestResult.Passed: TestResult) /: results) { (acc, result) => if (severity(acc) < severity(result)) result else acc }
|
||||
def discover(frameworks: Seq[Framework], analysis: CompileAnalysis, log: Logger): (Seq[TestDefinition], Set[String]) =
|
||||
((TestResult.Passed: TestResult) /: results) { (acc, result) =>
|
||||
if (severity(acc) < severity(result)) result else acc
|
||||
}
|
||||
def discover(frameworks: Seq[Framework],
|
||||
analysis: CompileAnalysis,
|
||||
log: Logger): (Seq[TestDefinition], Set[String]) =
|
||||
discover(frameworks flatMap TestFramework.getFingerprints, allDefs(analysis), log)
|
||||
|
||||
def allDefs(analysis: CompileAnalysis) = analysis match {
|
||||
|
|
@ -290,27 +361,37 @@ object Tests {
|
|||
all
|
||||
}.toSeq
|
||||
}
|
||||
def discover(fingerprints: Seq[Fingerprint], definitions: Seq[Definition], log: Logger): (Seq[TestDefinition], Set[String]) =
|
||||
{
|
||||
val subclasses = fingerprints collect { case sub: SubclassFingerprint => (sub.superclassName, sub.isModule, sub) };
|
||||
val annotations = fingerprints collect { case ann: AnnotatedFingerprint => (ann.annotationName, ann.isModule, ann) };
|
||||
log.debug("Subclass fingerprints: " + subclasses)
|
||||
log.debug("Annotation fingerprints: " + annotations)
|
||||
def discover(fingerprints: Seq[Fingerprint],
|
||||
definitions: Seq[Definition],
|
||||
log: Logger): (Seq[TestDefinition], Set[String]) = {
|
||||
val subclasses = fingerprints collect {
|
||||
case sub: SubclassFingerprint => (sub.superclassName, sub.isModule, sub)
|
||||
};
|
||||
val annotations = fingerprints collect {
|
||||
case ann: AnnotatedFingerprint => (ann.annotationName, ann.isModule, ann)
|
||||
};
|
||||
log.debug("Subclass fingerprints: " + subclasses)
|
||||
log.debug("Annotation fingerprints: " + annotations)
|
||||
|
||||
def firsts[A, B, C](s: Seq[(A, B, C)]): Set[A] = s.map(_._1).toSet
|
||||
def defined(in: Seq[(String, Boolean, Fingerprint)], names: Set[String], IsModule: Boolean): Seq[Fingerprint] =
|
||||
in collect { case (name, IsModule, print) if names(name) => print }
|
||||
def firsts[A, B, C](s: Seq[(A, B, C)]): Set[A] = s.map(_._1).toSet
|
||||
def defined(in: Seq[(String, Boolean, Fingerprint)],
|
||||
names: Set[String],
|
||||
IsModule: Boolean): Seq[Fingerprint] =
|
||||
in collect { case (name, IsModule, print) if names(name) => print }
|
||||
|
||||
def toFingerprints(d: Discovered): Seq[Fingerprint] =
|
||||
defined(subclasses, d.baseClasses, d.isModule) ++
|
||||
defined(annotations, d.annotations, d.isModule)
|
||||
def toFingerprints(d: Discovered): Seq[Fingerprint] =
|
||||
defined(subclasses, d.baseClasses, d.isModule) ++
|
||||
defined(annotations, d.annotations, d.isModule)
|
||||
|
||||
val discovered = Discovery(firsts(subclasses), firsts(annotations))(definitions)
|
||||
// TODO: To pass in correct explicitlySpecified and selectors
|
||||
val tests = for ((df, di) <- discovered; fingerprint <- toFingerprints(di)) yield new TestDefinition(df.name, fingerprint, false, Array(new SuiteSelector))
|
||||
val mains = discovered collect { case (df, di) if di.hasMain => df.name }
|
||||
(tests, mains.toSet)
|
||||
}
|
||||
val discovered = Discovery(firsts(subclasses), firsts(annotations))(definitions)
|
||||
// TODO: To pass in correct explicitlySpecified and selectors
|
||||
val tests = for ((df, di) <- discovered; fingerprint <- toFingerprints(di))
|
||||
yield new TestDefinition(df.name, fingerprint, false, Array(new SuiteSelector))
|
||||
val mains = discovered collect { case (df, di) if di.hasMain => df.name }
|
||||
(tests, mains.toSet)
|
||||
}
|
||||
}
|
||||
|
||||
final class TestsFailedException extends RuntimeException("Tests unsuccessful") with FeedbackProvidedException
|
||||
final class TestsFailedException
|
||||
extends RuntimeException("Tests unsuccessful")
|
||||
with FeedbackProvidedException
|
||||
|
|
|
|||
|
|
@ -26,7 +26,10 @@ final class EvalImports(val strings: Seq[(String, Int)], val srcName: String)
|
|||
* the module from that class loader. `generated` contains the compiled classes and cache files related
|
||||
* to the expression. The name of the auto-generated module wrapping the expression is `enclosingModule`.
|
||||
*/
|
||||
final class EvalResult(val tpe: String, val getValue: ClassLoader => Any, val generated: Seq[File], val enclosingModule: String)
|
||||
final class EvalResult(val tpe: String,
|
||||
val getValue: ClassLoader => Any,
|
||||
val generated: Seq[File],
|
||||
val enclosingModule: String)
|
||||
|
||||
/**
|
||||
* The result of evaluating a group of Scala definitions. The definitions are wrapped in an auto-generated,
|
||||
|
|
@ -35,7 +38,10 @@ final class EvalResult(val tpe: String, val getValue: ClassLoader => Any, val ge
|
|||
* from the classpath that the definitions were compiled against. The list of vals with the requested types is `valNames`.
|
||||
* The values for these may be obtained by providing the parent class loader to `values` as is done with `loader`.
|
||||
*/
|
||||
final class EvalDefinitions(val loader: ClassLoader => ClassLoader, val generated: Seq[File], val enclosingModule: String, val valNames: Seq[String]) {
|
||||
final class EvalDefinitions(val loader: ClassLoader => ClassLoader,
|
||||
val generated: Seq[File],
|
||||
val enclosingModule: String,
|
||||
val valNames: Seq[String]) {
|
||||
def values(parent: ClassLoader): Seq[Any] = {
|
||||
val module = getModule(enclosingModule, loader(parent))
|
||||
for (n <- valNames) yield module.getClass.getMethod(n).invoke(module)
|
||||
|
|
@ -44,26 +50,31 @@ final class EvalDefinitions(val loader: ClassLoader => ClassLoader, val generate
|
|||
|
||||
final class EvalException(msg: String) extends RuntimeException(msg)
|
||||
// not thread safe, since it reuses a Global instance
|
||||
final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Settings => Reporter, backing: Option[File]) {
|
||||
def this(mkReporter: Settings => Reporter, backing: Option[File]) = this(Nil, IO.classLocationFile[Product] :: Nil, mkReporter, backing)
|
||||
final class Eval(optionsNoncp: Seq[String],
|
||||
classpath: Seq[File],
|
||||
mkReporter: Settings => Reporter,
|
||||
backing: Option[File]) {
|
||||
def this(mkReporter: Settings => Reporter, backing: Option[File]) =
|
||||
this(Nil, IO.classLocationFile[Product] :: Nil, mkReporter, backing)
|
||||
def this() = this(s => new ConsoleReporter(s), None)
|
||||
|
||||
backing.foreach(IO.createDirectory)
|
||||
val classpathString = Path.makeString(classpath ++ backing.toList)
|
||||
val options = "-cp" +: classpathString +: optionsNoncp
|
||||
|
||||
lazy val settings =
|
||||
{
|
||||
val s = new Settings(println)
|
||||
new CompilerCommand(options.toList, s) // this side-effects on Settings..
|
||||
s
|
||||
}
|
||||
lazy val settings = {
|
||||
val s = new Settings(println)
|
||||
new CompilerCommand(options.toList, s) // this side-effects on Settings..
|
||||
s
|
||||
}
|
||||
lazy val reporter = mkReporter(settings)
|
||||
|
||||
/**
|
||||
* Subclass of Global which allows us to mutate currentRun from outside.
|
||||
* See for rationale https://issues.scala-lang.org/browse/SI-8794
|
||||
*/
|
||||
final class EvalGlobal(settings: Settings, reporter: Reporter) extends Global(settings, reporter) {
|
||||
final class EvalGlobal(settings: Settings, reporter: Reporter)
|
||||
extends Global(settings, reporter) {
|
||||
override def currentRun: Run = curRun
|
||||
var curRun: Run = null
|
||||
}
|
||||
|
|
@ -78,116 +89,135 @@ final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Se
|
|||
private[this] var toUnlinkLater = List[Symbol]()
|
||||
private[this] def unlink(sym: Symbol) = sym.owner.info.decls.unlink(sym)
|
||||
|
||||
def eval(expression: String, imports: EvalImports = noImports, tpeName: Option[String] = None, srcName: String = "<setting>", line: Int = DefaultStartLine): EvalResult =
|
||||
{
|
||||
val ev = new EvalType[String] {
|
||||
def makeUnit = mkUnit(srcName, line, expression)
|
||||
def unlink = true
|
||||
def unitBody(unit: CompilationUnit, importTrees: Seq[Tree], moduleName: String): Tree = {
|
||||
val (parser, tree) = parse(unit, settingErrorStrings, _.expr())
|
||||
val tpt: Tree = expectedType(tpeName)
|
||||
augment(parser, importTrees, tree, tpt, moduleName)
|
||||
}
|
||||
def extra(run: Run, unit: CompilationUnit) = enteringPhase(run.typerPhase.next) { (new TypeExtractor).getType(unit.body) }
|
||||
def read(file: File) = IO.read(file)
|
||||
def write(value: String, f: File) = IO.write(f, value)
|
||||
def extraHash = ""
|
||||
def eval(expression: String,
|
||||
imports: EvalImports = noImports,
|
||||
tpeName: Option[String] = None,
|
||||
srcName: String = "<setting>",
|
||||
line: Int = DefaultStartLine): EvalResult = {
|
||||
val ev = new EvalType[String] {
|
||||
def makeUnit = mkUnit(srcName, line, expression)
|
||||
def unlink = true
|
||||
def unitBody(unit: CompilationUnit, importTrees: Seq[Tree], moduleName: String): Tree = {
|
||||
val (parser, tree) = parse(unit, settingErrorStrings, _.expr())
|
||||
val tpt: Tree = expectedType(tpeName)
|
||||
augment(parser, importTrees, tree, tpt, moduleName)
|
||||
}
|
||||
val i = evalCommon(expression :: Nil, imports, tpeName, ev)
|
||||
val value = (cl: ClassLoader) => getValue[Any](i.enclosingModule, i.loader(cl))
|
||||
new EvalResult(i.extra, value, i.generated, i.enclosingModule)
|
||||
def extra(run: Run, unit: CompilationUnit) = enteringPhase(run.typerPhase.next) {
|
||||
(new TypeExtractor).getType(unit.body)
|
||||
}
|
||||
def read(file: File) = IO.read(file)
|
||||
def write(value: String, f: File) = IO.write(f, value)
|
||||
def extraHash = ""
|
||||
}
|
||||
def evalDefinitions(definitions: Seq[(String, scala.Range)], imports: EvalImports, srcName: String, file: Option[File], valTypes: Seq[String]): EvalDefinitions =
|
||||
{
|
||||
require(definitions.nonEmpty, "Definitions to evaluate cannot be empty.")
|
||||
val ev = new EvalType[Seq[String]] {
|
||||
lazy val (fullUnit, defUnits) = mkDefsUnit(srcName, definitions)
|
||||
def makeUnit = fullUnit
|
||||
def unlink = false
|
||||
def unitBody(unit: CompilationUnit, importTrees: Seq[Tree], moduleName: String): Tree = {
|
||||
val fullParser = new syntaxAnalyzer.UnitParser(unit)
|
||||
val trees = defUnits flatMap parseDefinitions
|
||||
syntheticModule(fullParser, importTrees, trees.toList, moduleName)
|
||||
}
|
||||
def extra(run: Run, unit: CompilationUnit) = {
|
||||
enteringPhase(run.typerPhase.next) { (new ValExtractor(valTypes.toSet)).getVals(unit.body) }
|
||||
}
|
||||
def read(file: File) = IO.readLines(file)
|
||||
def write(value: Seq[String], file: File) = IO.writeLines(file, value)
|
||||
def extraHash = file match {
|
||||
case Some(f) => f.getAbsolutePath
|
||||
case None => ""
|
||||
val i = evalCommon(expression :: Nil, imports, tpeName, ev)
|
||||
val value = (cl: ClassLoader) => getValue[Any](i.enclosingModule, i.loader(cl))
|
||||
new EvalResult(i.extra, value, i.generated, i.enclosingModule)
|
||||
}
|
||||
def evalDefinitions(definitions: Seq[(String, scala.Range)],
|
||||
imports: EvalImports,
|
||||
srcName: String,
|
||||
file: Option[File],
|
||||
valTypes: Seq[String]): EvalDefinitions = {
|
||||
require(definitions.nonEmpty, "Definitions to evaluate cannot be empty.")
|
||||
val ev = new EvalType[Seq[String]] {
|
||||
lazy val (fullUnit, defUnits) = mkDefsUnit(srcName, definitions)
|
||||
def makeUnit = fullUnit
|
||||
def unlink = false
|
||||
def unitBody(unit: CompilationUnit, importTrees: Seq[Tree], moduleName: String): Tree = {
|
||||
val fullParser = new syntaxAnalyzer.UnitParser(unit)
|
||||
val trees = defUnits flatMap parseDefinitions
|
||||
syntheticModule(fullParser, importTrees, trees.toList, moduleName)
|
||||
}
|
||||
def extra(run: Run, unit: CompilationUnit) = {
|
||||
enteringPhase(run.typerPhase.next) {
|
||||
(new ValExtractor(valTypes.toSet)).getVals(unit.body)
|
||||
}
|
||||
}
|
||||
val i = evalCommon(definitions.map(_._1), imports, Some(""), ev)
|
||||
new EvalDefinitions(i.loader, i.generated, i.enclosingModule, i.extra)
|
||||
def read(file: File) = IO.readLines(file)
|
||||
def write(value: Seq[String], file: File) = IO.writeLines(file, value)
|
||||
def extraHash = file match {
|
||||
case Some(f) => f.getAbsolutePath
|
||||
case None => ""
|
||||
}
|
||||
}
|
||||
val i = evalCommon(definitions.map(_._1), imports, Some(""), ev)
|
||||
new EvalDefinitions(i.loader, i.generated, i.enclosingModule, i.extra)
|
||||
}
|
||||
|
||||
private[this] def evalCommon[T](content: Seq[String],
|
||||
imports: EvalImports,
|
||||
tpeName: Option[String],
|
||||
ev: EvalType[T]): EvalIntermediate[T] = {
|
||||
import Eval._
|
||||
// TODO - We also encode the source of the setting into the hash to avoid conflicts where the exact SAME setting
|
||||
// is defined in multiple evaluated instances with a backing. This leads to issues with finding a previous
|
||||
// value on the classpath when compiling.
|
||||
val hash = Hash.toHex(
|
||||
Hash(bytes(
|
||||
stringSeqBytes(content) :: optBytes(backing)(fileExistsBytes) :: stringSeqBytes(options) ::
|
||||
seqBytes(classpath)(fileModifiedBytes) :: stringSeqBytes(imports.strings.map(_._1)) :: optBytes(
|
||||
tpeName)(bytes) ::
|
||||
bytes(ev.extraHash) :: Nil)))
|
||||
val moduleName = makeModuleName(hash)
|
||||
|
||||
lazy val unit = {
|
||||
reporter.reset
|
||||
ev.makeUnit
|
||||
}
|
||||
lazy val run = new Run {
|
||||
override def units = (unit :: Nil).iterator
|
||||
}
|
||||
def unlinkAll(): Unit =
|
||||
for ((sym, _) <- run.symSource) if (ev.unlink) unlink(sym) else toUnlinkLater ::= sym
|
||||
|
||||
val (extra, loader) = backing match {
|
||||
case Some(back) if classExists(back, moduleName) =>
|
||||
val loader = (parent: ClassLoader) => new URLClassLoader(Array(back.toURI.toURL), parent)
|
||||
val extra = ev.read(cacheFile(back, moduleName))
|
||||
(extra, loader)
|
||||
case _ =>
|
||||
try { compileAndLoad(run, unit, imports, backing, moduleName, ev) } finally { unlinkAll() }
|
||||
}
|
||||
|
||||
private[this] def evalCommon[T](content: Seq[String], imports: EvalImports, tpeName: Option[String], ev: EvalType[T]): EvalIntermediate[T] =
|
||||
{
|
||||
import Eval._
|
||||
// TODO - We also encode the source of the setting into the hash to avoid conflicts where the exact SAME setting
|
||||
// is defined in multiple evaluated instances with a backing. This leads to issues with finding a previous
|
||||
// value on the classpath when compiling.
|
||||
val hash = Hash.toHex(Hash(bytes(stringSeqBytes(content) :: optBytes(backing)(fileExistsBytes) :: stringSeqBytes(options) ::
|
||||
seqBytes(classpath)(fileModifiedBytes) :: stringSeqBytes(imports.strings.map(_._1)) :: optBytes(tpeName)(bytes) ::
|
||||
bytes(ev.extraHash) :: Nil)))
|
||||
val moduleName = makeModuleName(hash)
|
||||
|
||||
lazy val unit = {
|
||||
reporter.reset
|
||||
ev.makeUnit
|
||||
}
|
||||
lazy val run = new Run {
|
||||
override def units = (unit :: Nil).iterator
|
||||
}
|
||||
def unlinkAll(): Unit = for ((sym, _) <- run.symSource) if (ev.unlink) unlink(sym) else toUnlinkLater ::= sym
|
||||
|
||||
val (extra, loader) = backing match {
|
||||
case Some(back) if classExists(back, moduleName) =>
|
||||
val loader = (parent: ClassLoader) => new URLClassLoader(Array(back.toURI.toURL), parent)
|
||||
val extra = ev.read(cacheFile(back, moduleName))
|
||||
(extra, loader)
|
||||
case _ =>
|
||||
try { compileAndLoad(run, unit, imports, backing, moduleName, ev) }
|
||||
finally { unlinkAll() }
|
||||
}
|
||||
|
||||
val generatedFiles = getGeneratedFiles(backing, moduleName)
|
||||
new EvalIntermediate(extra, loader, generatedFiles, moduleName)
|
||||
}
|
||||
val generatedFiles = getGeneratedFiles(backing, moduleName)
|
||||
new EvalIntermediate(extra, loader, generatedFiles, moduleName)
|
||||
}
|
||||
// location of the cached type or definition information
|
||||
private[this] def cacheFile(base: File, moduleName: String): File = new File(base, moduleName + ".cache")
|
||||
private[this] def compileAndLoad[T](run: Run, unit: CompilationUnit, imports: EvalImports, backing: Option[File], moduleName: String, ev: EvalType[T]): (T, ClassLoader => ClassLoader) =
|
||||
{
|
||||
global.curRun = run
|
||||
run.currentUnit = unit
|
||||
val dir = outputDirectory(backing)
|
||||
settings.outputDirs setSingleOutput dir
|
||||
private[this] def cacheFile(base: File, moduleName: String): File =
|
||||
new File(base, moduleName + ".cache")
|
||||
private[this] def compileAndLoad[T](run: Run,
|
||||
unit: CompilationUnit,
|
||||
imports: EvalImports,
|
||||
backing: Option[File],
|
||||
moduleName: String,
|
||||
ev: EvalType[T]): (T, ClassLoader => ClassLoader) = {
|
||||
global.curRun = run
|
||||
run.currentUnit = unit
|
||||
val dir = outputDirectory(backing)
|
||||
settings.outputDirs setSingleOutput dir
|
||||
|
||||
val importTrees = parseImports(imports)
|
||||
unit.body = ev.unitBody(unit, importTrees, moduleName)
|
||||
val importTrees = parseImports(imports)
|
||||
unit.body = ev.unitBody(unit, importTrees, moduleName)
|
||||
|
||||
def compile(phase: Phase): Unit =
|
||||
{
|
||||
globalPhase = phase
|
||||
if (phase == null || phase == phase.next || reporter.hasErrors)
|
||||
()
|
||||
else {
|
||||
enteringPhase(phase) { phase.run }
|
||||
compile(phase.next)
|
||||
}
|
||||
}
|
||||
|
||||
compile(run.namerPhase)
|
||||
checkError("Type error in expression")
|
||||
|
||||
val extra = ev.extra(run, unit)
|
||||
for (f <- backing) ev.write(extra, cacheFile(f, moduleName))
|
||||
val loader = (parent: ClassLoader) => new AbstractFileClassLoader(dir, parent)
|
||||
(extra, loader)
|
||||
def compile(phase: Phase): Unit = {
|
||||
globalPhase = phase
|
||||
if (phase == null || phase == phase.next || reporter.hasErrors)
|
||||
()
|
||||
else {
|
||||
enteringPhase(phase) { phase.run }
|
||||
compile(phase.next)
|
||||
}
|
||||
}
|
||||
|
||||
compile(run.namerPhase)
|
||||
checkError("Type error in expression")
|
||||
|
||||
val extra = ev.extra(run, unit)
|
||||
for (f <- backing) ev.write(extra, cacheFile(f, moduleName))
|
||||
val loader = (parent: ClassLoader) => new AbstractFileClassLoader(dir, parent)
|
||||
(extra, loader)
|
||||
}
|
||||
|
||||
private[this] def expectedType(tpeName: Option[String]): Tree =
|
||||
tpeName match {
|
||||
case Some(tpe) => parseType(tpe)
|
||||
|
|
@ -195,43 +225,55 @@ final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Se
|
|||
}
|
||||
|
||||
private[this] def outputDirectory(backing: Option[File]): AbstractFile =
|
||||
backing match { case None => new VirtualDirectory("<virtual>", None); case Some(dir) => new PlainFile(dir) }
|
||||
backing match {
|
||||
case None => new VirtualDirectory("<virtual>", None); case Some(dir) => new PlainFile(dir)
|
||||
}
|
||||
|
||||
def load(dir: AbstractFile, moduleName: String): ClassLoader => Any = parent => getValue[Any](moduleName, new AbstractFileClassLoader(dir, parent))
|
||||
def loadPlain(dir: File, moduleName: String): ClassLoader => Any = parent => getValue[Any](moduleName, new URLClassLoader(Array(dir.toURI.toURL), parent))
|
||||
def load(dir: AbstractFile, moduleName: String): ClassLoader => Any =
|
||||
parent => getValue[Any](moduleName, new AbstractFileClassLoader(dir, parent))
|
||||
def loadPlain(dir: File, moduleName: String): ClassLoader => Any =
|
||||
parent => getValue[Any](moduleName, new URLClassLoader(Array(dir.toURI.toURL), parent))
|
||||
|
||||
//wrap tree in object objectName { def WrapValName = <tree> }
|
||||
def augment(parser: global.syntaxAnalyzer.UnitParser, imports: Seq[Tree], tree: Tree, tpt: Tree, objectName: String): Tree =
|
||||
{
|
||||
val method = DefDef(NoMods, newTermName(WrapValName), Nil, Nil, tpt, tree)
|
||||
syntheticModule(parser, imports, method :: Nil, objectName)
|
||||
}
|
||||
private[this] def syntheticModule(parser: global.syntaxAnalyzer.UnitParser, imports: Seq[Tree], definitions: List[Tree], objectName: String): Tree =
|
||||
{
|
||||
val emptyTypeName = nme.EMPTY.toTypeName
|
||||
def emptyPkg = parser.atPos(0, 0, 0) { Ident(nme.EMPTY_PACKAGE_NAME) }
|
||||
def emptyInit = DefDef(
|
||||
NoMods,
|
||||
nme.CONSTRUCTOR,
|
||||
Nil,
|
||||
List(Nil),
|
||||
TypeTree(),
|
||||
Block(List(Apply(Select(Super(This(emptyTypeName), emptyTypeName), nme.CONSTRUCTOR), Nil)), Literal(Constant(())))
|
||||
)
|
||||
def augment(parser: global.syntaxAnalyzer.UnitParser,
|
||||
imports: Seq[Tree],
|
||||
tree: Tree,
|
||||
tpt: Tree,
|
||||
objectName: String): Tree = {
|
||||
val method = DefDef(NoMods, newTermName(WrapValName), Nil, Nil, tpt, tree)
|
||||
syntheticModule(parser, imports, method :: Nil, objectName)
|
||||
}
|
||||
private[this] def syntheticModule(parser: global.syntaxAnalyzer.UnitParser,
|
||||
imports: Seq[Tree],
|
||||
definitions: List[Tree],
|
||||
objectName: String): Tree = {
|
||||
val emptyTypeName = nme.EMPTY.toTypeName
|
||||
def emptyPkg = parser.atPos(0, 0, 0) { Ident(nme.EMPTY_PACKAGE_NAME) }
|
||||
def emptyInit = DefDef(
|
||||
NoMods,
|
||||
nme.CONSTRUCTOR,
|
||||
Nil,
|
||||
List(Nil),
|
||||
TypeTree(),
|
||||
Block(List(Apply(Select(Super(This(emptyTypeName), emptyTypeName), nme.CONSTRUCTOR), Nil)),
|
||||
Literal(Constant(())))
|
||||
)
|
||||
|
||||
def moduleBody = Template(List(gen.scalaAnyRefConstr), noSelfType, emptyInit :: definitions)
|
||||
def moduleDef = ModuleDef(NoMods, newTermName(objectName), moduleBody)
|
||||
parser.makePackaging(0, emptyPkg, (imports :+ moduleDef).toList)
|
||||
}
|
||||
def moduleBody = Template(List(gen.scalaAnyRefConstr), noSelfType, emptyInit :: definitions)
|
||||
def moduleDef = ModuleDef(NoMods, newTermName(objectName), moduleBody)
|
||||
parser.makePackaging(0, emptyPkg, (imports :+ moduleDef).toList)
|
||||
}
|
||||
|
||||
private[this] final class TypeExtractor extends Traverser {
|
||||
private[this] var result = ""
|
||||
def getType(t: Tree) = { result = ""; traverse(t); result }
|
||||
override def traverse(tree: Tree): Unit = tree match {
|
||||
case d: DefDef if d.symbol.nameString == WrapValName => result = d.symbol.tpe.finalResultType.toString
|
||||
case _ => super.traverse(tree)
|
||||
case d: DefDef if d.symbol.nameString == WrapValName =>
|
||||
result = d.symbol.tpe.finalResultType.toString
|
||||
case _ => super.traverse(tree)
|
||||
}
|
||||
}
|
||||
|
||||
/** Tree traverser that obtains the names of vals in a top-level module whose type is a subtype of one of `types`.*/
|
||||
private[this] final class ValExtractor(tpes: Set[String]) extends Traverser {
|
||||
private[this] var vals = List[String]()
|
||||
|
|
@ -242,15 +284,20 @@ final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Se
|
|||
}
|
||||
}
|
||||
override def traverse(tree: Tree): Unit = tree match {
|
||||
case ValDef(_, n, actualTpe, _) if isTopLevelModule(tree.symbol.owner) && isAcceptableType(actualTpe.tpe) =>
|
||||
case ValDef(_, n, actualTpe, _)
|
||||
if isTopLevelModule(tree.symbol.owner) && isAcceptableType(actualTpe.tpe) =>
|
||||
vals ::= n.dropLocal.encoded
|
||||
case _ => super.traverse(tree)
|
||||
}
|
||||
}
|
||||
// inlined implemented of Symbol.isTopLevelModule that was removed in e5b050814deb2e7e1d6d05511d3a6cb6b013b549
|
||||
private[this] def isTopLevelModule(s: Symbol): Boolean = s.hasFlag(reflect.internal.Flags.MODULE) && s.owner.isPackageClass
|
||||
private[this] def isTopLevelModule(s: Symbol): Boolean =
|
||||
s.hasFlag(reflect.internal.Flags.MODULE) && s.owner.isPackageClass
|
||||
|
||||
private[this] final class EvalIntermediate[T](val extra: T, val loader: ClassLoader => ClassLoader, val generated: Seq[File], val enclosingModule: String)
|
||||
private[this] final class EvalIntermediate[T](val extra: T,
|
||||
val loader: ClassLoader => ClassLoader,
|
||||
val generated: Seq[File],
|
||||
val enclosingModule: String)
|
||||
|
||||
private[this] def classExists(dir: File, name: String) = (new File(dir, name + ".class")).exists
|
||||
// TODO: use the code from Analyzer
|
||||
|
|
@ -264,7 +311,10 @@ final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Se
|
|||
(s contains moduleName)
|
||||
}
|
||||
|
||||
private[this] class ParseErrorStrings(val base: String, val extraBlank: String, val missingBlank: String, val extraSemi: String)
|
||||
private[this] class ParseErrorStrings(val base: String,
|
||||
val extraBlank: String,
|
||||
val missingBlank: String,
|
||||
val extraSemi: String)
|
||||
private[this] def definitionErrorStrings = new ParseErrorStrings(
|
||||
base = "Error parsing definition.",
|
||||
extraBlank = " Ensure that there are no blank lines within a definition.",
|
||||
|
|
@ -275,67 +325,67 @@ final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Se
|
|||
base = "Error parsing expression.",
|
||||
extraBlank = " Ensure that there are no blank lines within a setting.",
|
||||
missingBlank = " Ensure that settings are separated by blank lines.",
|
||||
extraSemi = " Note that settings are expressions and do not end with semicolons. (Semicolons are fine within {} blocks, however.)"
|
||||
extraSemi =
|
||||
" Note that settings are expressions and do not end with semicolons. (Semicolons are fine within {} blocks, however.)"
|
||||
)
|
||||
|
||||
/**
|
||||
* Parses the provided compilation `unit` according to `f` and then performs checks on the final parser state
|
||||
* to catch errors that are common when the content is embedded in a blank-line-delimited format.
|
||||
*/
|
||||
private[this] def parse[T](unit: CompilationUnit, errors: ParseErrorStrings, f: syntaxAnalyzer.UnitParser => T): (syntaxAnalyzer.UnitParser, T) =
|
||||
{
|
||||
val parser = new syntaxAnalyzer.UnitParser(unit)
|
||||
private[this] def parse[T](unit: CompilationUnit,
|
||||
errors: ParseErrorStrings,
|
||||
f: syntaxAnalyzer.UnitParser => T): (syntaxAnalyzer.UnitParser, T) = {
|
||||
val parser = new syntaxAnalyzer.UnitParser(unit)
|
||||
|
||||
val tree = f(parser)
|
||||
val extra = parser.in.token match {
|
||||
case EOF => errors.extraBlank
|
||||
case _ => ""
|
||||
}
|
||||
checkError(errors.base + extra)
|
||||
|
||||
parser.accept(EOF)
|
||||
val extra2 = parser.in.token match {
|
||||
case SEMI => errors.extraSemi
|
||||
case NEWLINE | NEWLINES => errors.missingBlank
|
||||
case _ => ""
|
||||
}
|
||||
checkError(errors.base + extra2)
|
||||
|
||||
(parser, tree)
|
||||
val tree = f(parser)
|
||||
val extra = parser.in.token match {
|
||||
case EOF => errors.extraBlank
|
||||
case _ => ""
|
||||
}
|
||||
private[this] def parseType(tpe: String): Tree =
|
||||
{
|
||||
val tpeParser = new syntaxAnalyzer.UnitParser(mkUnit("<expected-type>", DefaultStartLine, tpe))
|
||||
val tpt0: Tree = tpeParser.typ()
|
||||
tpeParser.accept(EOF)
|
||||
checkError("Error parsing expression type.")
|
||||
tpt0
|
||||
checkError(errors.base + extra)
|
||||
|
||||
parser.accept(EOF)
|
||||
val extra2 = parser.in.token match {
|
||||
case SEMI => errors.extraSemi
|
||||
case NEWLINE | NEWLINES => errors.missingBlank
|
||||
case _ => ""
|
||||
}
|
||||
checkError(errors.base + extra2)
|
||||
|
||||
(parser, tree)
|
||||
}
|
||||
private[this] def parseType(tpe: String): Tree = {
|
||||
val tpeParser = new syntaxAnalyzer.UnitParser(mkUnit("<expected-type>", DefaultStartLine, tpe))
|
||||
val tpt0: Tree = tpeParser.typ()
|
||||
tpeParser.accept(EOF)
|
||||
checkError("Error parsing expression type.")
|
||||
tpt0
|
||||
}
|
||||
private[this] def parseImports(imports: EvalImports): Seq[Tree] =
|
||||
imports.strings flatMap { case (s, line) => parseImport(mkUnit(imports.srcName, line, s)) }
|
||||
private[this] def parseImport(importUnit: CompilationUnit): Seq[Tree] =
|
||||
{
|
||||
val parser = new syntaxAnalyzer.UnitParser(importUnit)
|
||||
val trees: Seq[Tree] = parser.importClause()
|
||||
parser.accept(EOF)
|
||||
checkError("Error parsing imports for expression.")
|
||||
trees
|
||||
}
|
||||
private[this] def parseImport(importUnit: CompilationUnit): Seq[Tree] = {
|
||||
val parser = new syntaxAnalyzer.UnitParser(importUnit)
|
||||
val trees: Seq[Tree] = parser.importClause()
|
||||
parser.accept(EOF)
|
||||
checkError("Error parsing imports for expression.")
|
||||
trees
|
||||
}
|
||||
private[this] def parseDefinitions(du: CompilationUnit): Seq[Tree] =
|
||||
parse(du, definitionErrorStrings, parseDefinitions)._2
|
||||
|
||||
/** Parses one or more definitions (defs, vals, lazy vals, classes, traits, modules). */
|
||||
private[this] def parseDefinitions(parser: syntaxAnalyzer.UnitParser): Seq[Tree] =
|
||||
{
|
||||
val defs = ListBuffer[Tree]()
|
||||
do {
|
||||
defs ++= parser.nonLocalDefOrDcl
|
||||
parser.acceptStatSepOpt()
|
||||
} while (!parser.isStatSeqEnd)
|
||||
defs.toList
|
||||
}
|
||||
private[this] def parseDefinitions(parser: syntaxAnalyzer.UnitParser): Seq[Tree] = {
|
||||
val defs = ListBuffer[Tree]()
|
||||
do {
|
||||
defs ++= parser.nonLocalDefOrDcl
|
||||
parser.acceptStatSepOpt()
|
||||
} while (!parser.isStatSeqEnd)
|
||||
defs.toList
|
||||
}
|
||||
|
||||
private[this] trait EvalType[T] {
|
||||
|
||||
/** Extracts additional information after the compilation unit is evaluated.*/
|
||||
def extra(run: Run, unit: CompilationUnit): T
|
||||
|
||||
|
|
@ -369,47 +419,55 @@ final class Eval(optionsNoncp: Seq[String], classpath: Seq[File], mkReporter: Se
|
|||
val DefaultStartLine = 0
|
||||
private[this] def makeModuleName(hash: String): String = "$" + Hash.halve(hash)
|
||||
private[this] def noImports = new EvalImports(Nil, "")
|
||||
private[this] def mkUnit(srcName: String, firstLine: Int, s: String) = new CompilationUnit(new EvalSourceFile(srcName, firstLine, s))
|
||||
private[this] def checkError(label: String) = if (reporter.hasErrors) throw new EvalException(label)
|
||||
private[this] def mkUnit(srcName: String, firstLine: Int, s: String) =
|
||||
new CompilationUnit(new EvalSourceFile(srcName, firstLine, s))
|
||||
private[this] def checkError(label: String) =
|
||||
if (reporter.hasErrors) throw new EvalException(label)
|
||||
|
||||
private[this] final class EvalSourceFile(name: String, startLine: Int, contents: String) extends BatchSourceFile(name, contents) {
|
||||
private[this] final class EvalSourceFile(name: String, startLine: Int, contents: String)
|
||||
extends BatchSourceFile(name, contents) {
|
||||
override def lineToOffset(line: Int): Int = super.lineToOffset((line - startLine) max 0)
|
||||
override def offsetToLine(offset: Int): Int = super.offsetToLine(offset) + startLine
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a CompilationUnit for each definition, which can be used to independently parse the definition into a Tree.
|
||||
* Additionally, a CompilationUnit for the combined definitions is constructed for use by combined compilation after parsing.
|
||||
*/
|
||||
private[this] def mkDefsUnit(srcName: String, definitions: Seq[(String, scala.Range)]): (CompilationUnit, Seq[CompilationUnit]) =
|
||||
{
|
||||
def fragmentUnit(content: String, lineMap: Array[Int]) = new CompilationUnit(fragmentSourceFile(srcName, content, lineMap))
|
||||
private[this] def mkDefsUnit(
|
||||
srcName: String,
|
||||
definitions: Seq[(String, scala.Range)]): (CompilationUnit, Seq[CompilationUnit]) = {
|
||||
def fragmentUnit(content: String, lineMap: Array[Int]) =
|
||||
new CompilationUnit(fragmentSourceFile(srcName, content, lineMap))
|
||||
|
||||
import collection.mutable.ListBuffer
|
||||
val lines = new ListBuffer[Int]()
|
||||
val defs = new ListBuffer[CompilationUnit]()
|
||||
val fullContent = new java.lang.StringBuilder()
|
||||
for ((defString, range) <- definitions) {
|
||||
defs += fragmentUnit(defString, range.toArray)
|
||||
fullContent.append(defString)
|
||||
lines ++= range
|
||||
fullContent.append("\n\n")
|
||||
lines ++= (range.end :: range.end :: Nil)
|
||||
}
|
||||
val fullUnit = fragmentUnit(fullContent.toString, lines.toArray)
|
||||
(fullUnit, defs.toSeq)
|
||||
import collection.mutable.ListBuffer
|
||||
val lines = new ListBuffer[Int]()
|
||||
val defs = new ListBuffer[CompilationUnit]()
|
||||
val fullContent = new java.lang.StringBuilder()
|
||||
for ((defString, range) <- definitions) {
|
||||
defs += fragmentUnit(defString, range.toArray)
|
||||
fullContent.append(defString)
|
||||
lines ++= range
|
||||
fullContent.append("\n\n")
|
||||
lines ++= (range.end :: range.end :: Nil)
|
||||
}
|
||||
val fullUnit = fragmentUnit(fullContent.toString, lines.toArray)
|
||||
(fullUnit, defs.toSeq)
|
||||
}
|
||||
|
||||
/**
|
||||
* Source file that can map the offset in the file to and from line numbers that may discontinuous.
|
||||
* The values in `lineMap` must be ordered, but need not be consecutive.
|
||||
*/
|
||||
private[this] def fragmentSourceFile(srcName: String, content: String, lineMap: Array[Int]) = new BatchSourceFile(srcName, content) {
|
||||
override def lineToOffset(line: Int): Int = super.lineToOffset(lineMap.indexWhere(_ == line) max 0)
|
||||
override def offsetToLine(offset: Int): Int = index(lineMap, super.offsetToLine(offset))
|
||||
// the SourceFile attribute is populated from this method, so we are required to only return the name
|
||||
override def toString = new File(srcName).getName
|
||||
private[this] def index(a: Array[Int], i: Int): Int = if (i < 0 || i >= a.length) 0 else a(i)
|
||||
}
|
||||
private[this] def fragmentSourceFile(srcName: String, content: String, lineMap: Array[Int]) =
|
||||
new BatchSourceFile(srcName, content) {
|
||||
override def lineToOffset(line: Int): Int =
|
||||
super.lineToOffset(lineMap.indexWhere(_ == line) max 0)
|
||||
override def offsetToLine(offset: Int): Int = index(lineMap, super.offsetToLine(offset))
|
||||
// the SourceFile attribute is populated from this method, so we are required to only return the name
|
||||
override def toString = new File(srcName).getName
|
||||
private[this] def index(a: Array[Int], i: Int): Int = if (i < 0 || i >= a.length) 0 else a(i)
|
||||
}
|
||||
}
|
||||
private[sbt] object Eval {
|
||||
def optBytes[T](o: Option[T])(f: T => Array[Byte]): Array[Byte] = seqBytes(o.toSeq)(f)
|
||||
|
|
@ -417,7 +475,8 @@ private[sbt] object Eval {
|
|||
def seqBytes[T](s: Seq[T])(f: T => Array[Byte]): Array[Byte] = bytes(s map f)
|
||||
def bytes(b: Seq[Array[Byte]]): Array[Byte] = bytes(b.length) ++ b.flatten.toArray[Byte]
|
||||
def bytes(b: Boolean): Array[Byte] = Array[Byte](if (b) 1 else 0)
|
||||
def filesModifiedBytes(fs: Array[File]): Array[Byte] = if (fs eq null) filesModifiedBytes(Array[File]()) else seqBytes(fs)(fileModifiedBytes)
|
||||
def filesModifiedBytes(fs: Array[File]): Array[Byte] =
|
||||
if (fs eq null) filesModifiedBytes(Array[File]()) else seqBytes(fs)(fileModifiedBytes)
|
||||
def fileModifiedBytes(f: File): Array[Byte] =
|
||||
(if (f.isDirectory) filesModifiedBytes(f listFiles classDirFilter) else bytes(f.lastModified)) ++
|
||||
bytes(f.getAbsolutePath)
|
||||
|
|
@ -426,18 +485,16 @@ private[sbt] object Eval {
|
|||
bytes(f.getAbsolutePath)
|
||||
|
||||
def bytes(s: String): Array[Byte] = s getBytes "UTF-8"
|
||||
def bytes(l: Long): Array[Byte] =
|
||||
{
|
||||
val buffer = ByteBuffer.allocate(8)
|
||||
buffer.putLong(l)
|
||||
buffer.array
|
||||
}
|
||||
def bytes(i: Int): Array[Byte] =
|
||||
{
|
||||
val buffer = ByteBuffer.allocate(4)
|
||||
buffer.putInt(i)
|
||||
buffer.array
|
||||
}
|
||||
def bytes(l: Long): Array[Byte] = {
|
||||
val buffer = ByteBuffer.allocate(8)
|
||||
buffer.putLong(l)
|
||||
buffer.array
|
||||
}
|
||||
def bytes(i: Int): Array[Byte] = {
|
||||
val buffer = ByteBuffer.allocate(4)
|
||||
buffer.putInt(i)
|
||||
buffer.array
|
||||
}
|
||||
|
||||
/** The name of the synthetic val in the synthetic module that an expression is assigned to. */
|
||||
final val WrapValName = "$sbtdef"
|
||||
|
|
@ -446,20 +503,18 @@ private[sbt] object Eval {
|
|||
* Gets the value of the expression wrapped in module `objectName`, which is accessible via `loader`.
|
||||
* The module name should not include the trailing `$`.
|
||||
*/
|
||||
def getValue[T](objectName: String, loader: ClassLoader): T =
|
||||
{
|
||||
val module = getModule(objectName, loader)
|
||||
val accessor = module.getClass.getMethod(WrapValName)
|
||||
val value = accessor.invoke(module)
|
||||
value.asInstanceOf[T]
|
||||
}
|
||||
def getValue[T](objectName: String, loader: ClassLoader): T = {
|
||||
val module = getModule(objectName, loader)
|
||||
val accessor = module.getClass.getMethod(WrapValName)
|
||||
val value = accessor.invoke(module)
|
||||
value.asInstanceOf[T]
|
||||
}
|
||||
|
||||
/** Gets the top-level module `moduleName` from the provided class `loader`. The module name should not include the trailing `$`.*/
|
||||
def getModule(moduleName: String, loader: ClassLoader): Any =
|
||||
{
|
||||
val clazz = Class.forName(moduleName + "$", true, loader)
|
||||
clazz.getField("MODULE$").get(null)
|
||||
}
|
||||
def getModule(moduleName: String, loader: ClassLoader): Any = {
|
||||
val clazz = Class.forName(moduleName + "$", true, loader)
|
||||
clazz.getField("MODULE$").get(null)
|
||||
}
|
||||
|
||||
private val classDirFilter: FileFilter = DirectoryFilter || GlobFilter("*.class")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,15 +31,19 @@ class CacheIvyTest extends Properties("CacheIvy") {
|
|||
content = converter.toJsonUnsafe(value)
|
||||
}
|
||||
|
||||
private def testCache[T: JsonFormat, U](f: (SingletonCache[T], CacheStore) => U)(implicit cache: SingletonCache[T]): U = {
|
||||
private def testCache[T: JsonFormat, U](f: (SingletonCache[T], CacheStore) => U)(
|
||||
implicit cache: SingletonCache[T]): U = {
|
||||
val store = new InMemoryStore(Converter)
|
||||
f(cache, store)
|
||||
}
|
||||
|
||||
private def cachePreservesEquality[T: JsonFormat](m: T, eq: (T, T) => Prop, str: T => String): Prop = testCache[T, Prop] { (cache, store) =>
|
||||
cache.write(store, m)
|
||||
val out = cache.read(store)
|
||||
eq(out, m) :| s"Expected: ${str(m)}" :| s"Got: ${str(out)}"
|
||||
private def cachePreservesEquality[T: JsonFormat](m: T,
|
||||
eq: (T, T) => Prop,
|
||||
str: T => String): Prop = testCache[T, Prop] {
|
||||
(cache, store) =>
|
||||
cache.write(store, m)
|
||||
val out = cache.read(store)
|
||||
eq(out, m) :| s"Expected: ${str(m)}" :| s"Got: ${str(out)}"
|
||||
}
|
||||
|
||||
implicit val arbExclusionRule: Arbitrary[ExclusionRule] = Arbitrary(
|
||||
|
|
@ -77,11 +81,22 @@ class CacheIvyTest extends Properties("CacheIvy") {
|
|||
inclusions <- Gen.listOf(arbitrary[InclusionRule])
|
||||
extraAttributes <- Gen.mapOf(arbitrary[(String, String)])
|
||||
crossVersion <- arbitrary[CrossVersion]
|
||||
} yield ModuleID(
|
||||
organization = o, name = n, revision = r, configurations = cs, isChanging = isChanging, isTransitive = isTransitive,
|
||||
isForce = isForce, explicitArtifacts = explicitArtifacts.toVector, inclusions = inclusions.toVector, exclusions = exclusions.toVector,
|
||||
extraAttributes = extraAttributes, crossVersion = crossVersion, branchName = branch
|
||||
)
|
||||
} yield
|
||||
ModuleID(
|
||||
organization = o,
|
||||
name = n,
|
||||
revision = r,
|
||||
configurations = cs,
|
||||
isChanging = isChanging,
|
||||
isTransitive = isTransitive,
|
||||
isForce = isForce,
|
||||
explicitArtifacts = explicitArtifacts.toVector,
|
||||
inclusions = inclusions.toVector,
|
||||
exclusions = exclusions.toVector,
|
||||
extraAttributes = extraAttributes,
|
||||
crossVersion = crossVersion,
|
||||
branchName = branch
|
||||
)
|
||||
}
|
||||
|
||||
property("moduleIDFormat") = forAll { (m: ModuleID) =>
|
||||
|
|
|
|||
|
|
@ -16,22 +16,23 @@ class EvalTest extends Properties("eval") {
|
|||
property("inferred integer") = forAll { (i: Int) =>
|
||||
val result = eval.eval(i.toString)
|
||||
(label("Value", value(result)) |: (value(result) == i)) &&
|
||||
(label("Type", value(result)) |: (result.tpe == IntType)) &&
|
||||
(label("Files", result.generated) |: (result.generated.isEmpty))
|
||||
(label("Type", value(result)) |: (result.tpe == IntType)) &&
|
||||
(label("Files", result.generated) |: (result.generated.isEmpty))
|
||||
}
|
||||
|
||||
property("explicit integer") = forAll { (i: Int) =>
|
||||
val result = eval.eval(i.toString, tpeName = Some(IntType))
|
||||
(label("Value", value(result)) |: (value(result) == i)) &&
|
||||
(label("Type", result.tpe) |: (result.tpe == IntType)) &&
|
||||
(label("Files", result.generated) |: (result.generated.isEmpty))
|
||||
(label("Type", result.tpe) |: (result.tpe == IntType)) &&
|
||||
(label("Files", result.generated) |: (result.generated.isEmpty))
|
||||
}
|
||||
|
||||
property("type mismatch") = forAll { (i: Int, l: Int) =>
|
||||
val line = math.abs(l)
|
||||
val src = "mismatch"
|
||||
throws(classOf[RuntimeException])(eval.eval(i.toString, tpeName = Some(BooleanType), line = line, srcName = src)) &&
|
||||
hasErrors(line + 1, src)
|
||||
throws(classOf[RuntimeException])(
|
||||
eval.eval(i.toString, tpeName = Some(BooleanType), line = line, srcName = src)) &&
|
||||
hasErrors(line + 1, src)
|
||||
}
|
||||
|
||||
property("backed local class") = forAll { (i: Int) =>
|
||||
|
|
@ -40,8 +41,8 @@ class EvalTest extends Properties("eval") {
|
|||
val result = eval.eval(local(i))
|
||||
val v = value(result).asInstanceOf[{ def i: Int }].i
|
||||
(label("Value", v) |: (v == i)) &&
|
||||
(label("Type", result.tpe) |: (result.tpe == LocalType)) &&
|
||||
(label("Files", result.generated) |: result.generated.nonEmpty)
|
||||
(label("Type", result.tpe) |: (result.tpe == LocalType)) &&
|
||||
(label("Files", result.generated) |: result.generated.nonEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,35 +63,38 @@ val p = {
|
|||
|
||||
property("val test") = secure {
|
||||
val defs = (ValTestContent, 1 to 7) :: Nil
|
||||
val res = eval.evalDefinitions(defs, new EvalImports(Nil, ""), "<defs>", None, "scala.Int" :: Nil)
|
||||
val res =
|
||||
eval.evalDefinitions(defs, new EvalImports(Nil, ""), "<defs>", None, "scala.Int" :: Nil)
|
||||
label("Val names", res.valNames) |: (res.valNames.toSet == ValTestNames)
|
||||
}
|
||||
|
||||
property("explicit import") = forAll(testImport("import math.abs" :: Nil))
|
||||
property("wildcard import") = forAll(testImport("import math._" :: Nil))
|
||||
property("comma-separated imports") = forAll(testImport("import annotation._, math._, meta._" :: Nil))
|
||||
property("multiple imports") = forAll(testImport("import annotation._" :: "import math._" :: "import meta._" :: Nil))
|
||||
property("comma-separated imports") = forAll(
|
||||
testImport("import annotation._, math._, meta._" :: Nil))
|
||||
property("multiple imports") = forAll(
|
||||
testImport("import annotation._" :: "import math._" :: "import meta._" :: Nil))
|
||||
|
||||
private[this] def testImport(imports: Seq[String]): Int => Prop = i =>
|
||||
value(eval.eval("abs(" + i + ")", new EvalImports(imports.zipWithIndex, "imp"))) == math.abs(i)
|
||||
private[this] def testImport(imports: Seq[String]): Int => Prop =
|
||||
i =>
|
||||
value(eval.eval("abs(" + i + ")", new EvalImports(imports.zipWithIndex, "imp"))) == math.abs(
|
||||
i)
|
||||
|
||||
private[this] def local(i: Int) = "{ class ETest(val i: Int); new ETest(" + i + ") }"
|
||||
val LocalType = "AnyRef{val i: Int}"
|
||||
|
||||
private[this] def value(r: EvalResult) = r.getValue(getClass.getClassLoader)
|
||||
private[this] def hasErrors(line: Int, src: String) =
|
||||
{
|
||||
val is = reporter.infos
|
||||
("Has errors" |: is.nonEmpty) &&
|
||||
all(is.toSeq.map(validPosition(line, src)): _*)
|
||||
}
|
||||
private[this] def validPosition(line: Int, src: String)(i: Info) =
|
||||
{
|
||||
val nme = i.pos.source.file.name
|
||||
(label("Severity", i.severity) |: (i.severity == ERROR)) &&
|
||||
(label("Line", i.pos.line) |: (i.pos.line == line)) &&
|
||||
(label("Name", nme) |: (nme == src))
|
||||
}
|
||||
private[this] def hasErrors(line: Int, src: String) = {
|
||||
val is = reporter.infos
|
||||
("Has errors" |: is.nonEmpty) &&
|
||||
all(is.toSeq.map(validPosition(line, src)): _*)
|
||||
}
|
||||
private[this] def validPosition(line: Int, src: String)(i: Info) = {
|
||||
val nme = i.pos.source.file.name
|
||||
(label("Severity", i.severity) |: (i.severity == ERROR)) &&
|
||||
(label("Line", i.pos.line) |: (i.pos.line == line)) &&
|
||||
(label("Name", nme) |: (nme == src))
|
||||
}
|
||||
val IntType = "Int"
|
||||
val BooleanType = "Boolean"
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ object BasicCommandStrings {
|
|||
/** The command name to terminate the program.*/
|
||||
val TerminateAction: String = Exit
|
||||
|
||||
def helpBrief = (HelpCommand, s"Displays this help message or prints detailed help on requested commands (run '$HelpCommand <command>').")
|
||||
def helpBrief =
|
||||
(HelpCommand,
|
||||
s"Displays this help message or prints detailed help on requested commands (run '$HelpCommand <command>').")
|
||||
def helpDetailed = s"""$HelpCommand
|
||||
|
||||
Prints a help summary.
|
||||
|
|
@ -30,26 +32,29 @@ $HelpCommand <regular expression>
|
|||
Searches the help according to the provided regular expression.
|
||||
"""
|
||||
|
||||
def CompletionsDetailed = "Displays a list of completions for the given argument string (run 'completions <string>')."
|
||||
def CompletionsDetailed =
|
||||
"Displays a list of completions for the given argument string (run 'completions <string>')."
|
||||
def CompletionsBrief = (CompletionsCommand, CompletionsDetailed)
|
||||
|
||||
def templateBrief = (TemplateCommand, "Creates a new sbt build.")
|
||||
def templateDetailed = TemplateCommand + """ [--options] <template>
|
||||
Create a new sbt build based on the given template."""
|
||||
|
||||
def HistoryHelpBrief = (HistoryCommands.Start -> "History command help. Lists and describes all history commands.")
|
||||
def historyHelp = Help(Nil, (HistoryHelpBrief +: HistoryCommands.descriptions).toMap, Set(HistoryCommands.Start))
|
||||
def HistoryHelpBrief =
|
||||
(HistoryCommands.Start -> "History command help. Lists and describes all history commands.")
|
||||
def historyHelp =
|
||||
Help(Nil, (HistoryHelpBrief +: HistoryCommands.descriptions).toMap, Set(HistoryCommands.Start))
|
||||
|
||||
def exitBrief = "Terminates the build."
|
||||
|
||||
def logLevelHelp =
|
||||
{
|
||||
val levels = Level.values.toSeq
|
||||
val levelList = levels.mkString(", ")
|
||||
val brief = ("<log-level>", "Sets the logging level to 'log-level'. Valid levels: " + levelList)
|
||||
val detailed = levels.map(l => (l.toString, logLevelDetail(l))).toMap
|
||||
Help(brief, detailed)
|
||||
}
|
||||
def logLevelHelp = {
|
||||
val levels = Level.values.toSeq
|
||||
val levelList = levels.mkString(", ")
|
||||
val brief =
|
||||
("<log-level>", "Sets the logging level to 'log-level'. Valid levels: " + levelList)
|
||||
val detailed = levels.map(l => (l.toString, logLevelDetail(l))).toMap
|
||||
Help(brief, detailed)
|
||||
}
|
||||
|
||||
private[this] def logLevelDetail(level: Level.Value): String =
|
||||
s"""$level
|
||||
|
|
@ -70,11 +75,12 @@ $HelpCommand <regular expression>
|
|||
private[sbt] def isEarlyCommand(s: String): Boolean = {
|
||||
val levelOptions = Level.values.toSeq map { "-" + _ }
|
||||
(s.startsWith(EarlyCommand + "(") && s.endsWith(")")) ||
|
||||
(levelOptions contains s)
|
||||
(levelOptions contains s)
|
||||
}
|
||||
|
||||
val EarlyCommand = "early"
|
||||
val EarlyCommandBrief = (s"$EarlyCommand(<command>)", "Schedules a command to run before other commands on startup.")
|
||||
val EarlyCommandBrief =
|
||||
(s"$EarlyCommand(<command>)", "Schedules a command to run before other commands on startup.")
|
||||
val EarlyCommandDetailed =
|
||||
s"""$EarlyCommand(<command>)
|
||||
|
||||
|
|
@ -117,7 +123,9 @@ $HelpCommand <regular expression>
|
|||
and is useful when working with development versions of sbt or Scala."""
|
||||
|
||||
def Multi = ";"
|
||||
def MultiBrief = (Multi + " <command> (" + Multi + " <command>)*", "Runs the provided semicolon-separated commands.")
|
||||
def MultiBrief =
|
||||
(Multi + " <command> (" + Multi + " <command>)*",
|
||||
"Runs the provided semicolon-separated commands.")
|
||||
def MultiDetailed =
|
||||
Multi + " command1 " + Multi + """ command2 ...
|
||||
|
||||
|
|
@ -150,7 +158,8 @@ $AliasCommand name=
|
|||
Removes the alias for `name`."""
|
||||
|
||||
def Shell = "shell"
|
||||
def ShellDetailed = "Provides an interactive prompt and network server from which commands can be run."
|
||||
def ShellDetailed =
|
||||
"Provides an interactive prompt and network server from which commands can be run."
|
||||
|
||||
def OldShell = "oldshell"
|
||||
def OldShellDetailed = "Provides an interactive prompt from which commands can be run."
|
||||
|
|
@ -168,7 +177,8 @@ $AliasCommand name=
|
|||
def ClearOnFailure = "--"
|
||||
def FailureWall = "---"
|
||||
def OnFailureDeprecated = deprecatedAlias(OnFailure, BasicCommandStrings.OnFailure)
|
||||
def ClearOnFailureDeprecated = deprecatedAlias(ClearOnFailure, BasicCommandStrings.ClearOnFailure)
|
||||
def ClearOnFailureDeprecated =
|
||||
deprecatedAlias(ClearOnFailure, BasicCommandStrings.ClearOnFailure)
|
||||
def FailureWallDeprecated = deprecatedAlias(FailureWall, BasicCommandStrings.FailureWall)
|
||||
private[this] def deprecatedAlias(oldName: String, newName: String): String =
|
||||
s"The `$oldName` command is deprecated in favor of `$newName` and will be removed in 0.14.0"
|
||||
|
|
|
|||
|
|
@ -28,9 +28,27 @@ import scala.util.control.NonFatal
|
|||
|
||||
object BasicCommands {
|
||||
lazy val allBasicCommands: Seq[Command] = Seq(
|
||||
nop, ignore, help, completionsCommand, multi, ifLast, append, setOnFailure, clearOnFailure,
|
||||
stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, oldshell, client,
|
||||
read, alias
|
||||
nop,
|
||||
ignore,
|
||||
help,
|
||||
completionsCommand,
|
||||
multi,
|
||||
ifLast,
|
||||
append,
|
||||
setOnFailure,
|
||||
clearOnFailure,
|
||||
stashOnFailure,
|
||||
popOnFailure,
|
||||
reboot,
|
||||
call,
|
||||
early,
|
||||
exit,
|
||||
continuous,
|
||||
history,
|
||||
oldshell,
|
||||
client,
|
||||
read,
|
||||
alias
|
||||
) ++ compatCommands
|
||||
|
||||
def nop: Command = Command.custom(s => success(() => s))
|
||||
|
|
@ -51,24 +69,26 @@ object BasicCommands {
|
|||
|
||||
def help: Command = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser)
|
||||
|
||||
def helpParser(s: State): Parser[() => State] =
|
||||
{
|
||||
val h = (Help.empty /: s.definedCommands)((a, b) =>
|
||||
a ++ (try b.help(s) catch { case NonFatal(_) => Help.empty }))
|
||||
val helpCommands = h.detail.keySet
|
||||
val spacedArg = singleArgument(helpCommands).?
|
||||
applyEffect(spacedArg)(runHelp(s, h))
|
||||
}
|
||||
def helpParser(s: State): Parser[() => State] = {
|
||||
val h = (Help.empty /: s.definedCommands)(
|
||||
(a, b) =>
|
||||
a ++ (try b.help(s)
|
||||
catch { case NonFatal(_) => Help.empty }))
|
||||
val helpCommands = h.detail.keySet
|
||||
val spacedArg = singleArgument(helpCommands).?
|
||||
applyEffect(spacedArg)(runHelp(s, h))
|
||||
}
|
||||
|
||||
def runHelp(s: State, h: Help)(arg: Option[String]): State =
|
||||
{
|
||||
val message = try Help.message(h, arg) catch { case NonFatal(ex) => ex.toString }
|
||||
System.out.println(message)
|
||||
s
|
||||
}
|
||||
def runHelp(s: State, h: Help)(arg: Option[String]): State = {
|
||||
val message = try Help.message(h, arg)
|
||||
catch { case NonFatal(ex) => ex.toString }
|
||||
System.out.println(message)
|
||||
s
|
||||
}
|
||||
|
||||
def completionsCommand: Command =
|
||||
Command(CompletionsCommand, CompletionsBrief, CompletionsDetailed)(completionsParser)(runCompletions(_)(_))
|
||||
Command(CompletionsCommand, CompletionsBrief, CompletionsDetailed)(completionsParser)(
|
||||
runCompletions(_)(_))
|
||||
|
||||
def completionsParser(state: State): Parser[String] = {
|
||||
val notQuoted = (NotQuoted ~ any.*) map { case (nq, s) => nq ++ s }
|
||||
|
|
@ -77,21 +97,21 @@ object BasicCommands {
|
|||
}
|
||||
|
||||
def runCompletions(state: State)(input: String): State = {
|
||||
Parser.completions(state.combinedParser, input, 9).get map {
|
||||
c => if (c.isEmpty) input else input + c.append
|
||||
Parser.completions(state.combinedParser, input, 9).get map { c =>
|
||||
if (c.isEmpty) input else input + c.append
|
||||
} foreach { c =>
|
||||
System.out.println("[completions] " + c.replaceAll("\n", " "))
|
||||
}
|
||||
state
|
||||
}
|
||||
|
||||
def multiParser(s: State): Parser[List[String]] =
|
||||
{
|
||||
val nonSemi = token(charClass(_ != ';').+, hide = const(true))
|
||||
val semi = token(';' ~> OptSpace)
|
||||
val part = semi flatMap (_ => matched((s.combinedParser & nonSemi) | nonSemi) <~ token(OptSpace))
|
||||
(part map (_.trim)).+ map (_.toList)
|
||||
}
|
||||
def multiParser(s: State): Parser[List[String]] = {
|
||||
val nonSemi = token(charClass(_ != ';').+, hide = const(true))
|
||||
val semi = token(';' ~> OptSpace)
|
||||
val part = semi flatMap (_ =>
|
||||
matched((s.combinedParser & nonSemi) | nonSemi) <~ token(OptSpace))
|
||||
(part map (_.trim)).+ map (_.toList)
|
||||
}
|
||||
|
||||
def multiApplied(s: State): Parser[() => State] =
|
||||
Command.applyEffect(multiParser(s))(_ ::: s)
|
||||
|
|
@ -104,12 +124,13 @@ object BasicCommands {
|
|||
def combinedLax(s: State, any: Parser[_]): Parser[String] =
|
||||
matched(s.combinedParser | token(any, hide = const(true)))
|
||||
|
||||
def ifLast: Command = Command(IfLast, Help.more(IfLast, IfLastDetailed))(otherCommandParser)((s, arg) =>
|
||||
if (s.remainingCommands.isEmpty) arg :: s else s)
|
||||
def ifLast: Command =
|
||||
Command(IfLast, Help.more(IfLast, IfLastDetailed))(otherCommandParser)((s, arg) =>
|
||||
if (s.remainingCommands.isEmpty) arg :: s else s)
|
||||
|
||||
def append: Command =
|
||||
Command(AppendCommand, Help.more(AppendCommand, AppendLastDetailed))(otherCommandParser)((s, arg) =>
|
||||
s.copy(remainingCommands = s.remainingCommands :+ Exec(arg, s.source)))
|
||||
Command(AppendCommand, Help.more(AppendCommand, AppendLastDetailed))(otherCommandParser)(
|
||||
(s, arg) => s.copy(remainingCommands = s.remainingCommands :+ Exec(arg, s.source)))
|
||||
|
||||
def setOnFailure: Command =
|
||||
Command(OnFailure, Help.more(OnFailure, OnFailureDetailed))(otherCommandParser)((s, arg) =>
|
||||
|
|
@ -120,8 +141,10 @@ object BasicCommands {
|
|||
s.log.warn(Compat.ClearOnFailureDeprecated)
|
||||
s.copy(onFailure = None)
|
||||
},
|
||||
Command.arb(s => token(Compat.OnFailure, hide = const(true))
|
||||
.flatMap(_ => otherCommandParser(s))) { (s, arg) =>
|
||||
Command.arb(
|
||||
s =>
|
||||
token(Compat.OnFailure, hide = const(true))
|
||||
.flatMap(_ => otherCommandParser(s))) { (s, arg) =>
|
||||
s.log.warn(Compat.OnFailureDeprecated)
|
||||
s.copy(onFailure = Some(Exec(arg, s.source)))
|
||||
},
|
||||
|
|
@ -133,43 +156,48 @@ object BasicCommands {
|
|||
|
||||
def clearOnFailure: Command = Command.command(ClearOnFailure)(s => s.copy(onFailure = None))
|
||||
|
||||
def stashOnFailure: Command = Command.command(StashOnFailure)(s =>
|
||||
s.copy(onFailure = None).update(OnFailureStack)(s.onFailure :: _.toList.flatten))
|
||||
def stashOnFailure: Command =
|
||||
Command.command(StashOnFailure)(s =>
|
||||
s.copy(onFailure = None).update(OnFailureStack)(s.onFailure :: _.toList.flatten))
|
||||
|
||||
def popOnFailure: Command = Command.command(PopOnFailure) { s =>
|
||||
val stack = s.get(OnFailureStack).getOrElse(Nil)
|
||||
val updated = if (stack.isEmpty) s.remove(OnFailureStack) else s.put(OnFailureStack, stack.tail)
|
||||
val updated =
|
||||
if (stack.isEmpty) s.remove(OnFailureStack) else s.put(OnFailureStack, stack.tail)
|
||||
updated.copy(onFailure = stack.headOption.flatten)
|
||||
}
|
||||
|
||||
def reboot: Command =
|
||||
Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(rebootParser)((s, full) => s reboot full)
|
||||
Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(rebootParser)((s, full) =>
|
||||
s reboot full)
|
||||
|
||||
def rebootParser(s: State): Parser[Boolean] = token(Space ~> "full" ^^^ true) ?? false
|
||||
|
||||
def call: Command = Command(ApplyCommand, Help.more(ApplyCommand, ApplyDetailed))(_ => callParser) {
|
||||
case (state, (cp, args)) =>
|
||||
val parentLoader = getClass.getClassLoader
|
||||
def argsStr = args mkString ", "
|
||||
def cpStr = cp mkString File.pathSeparator
|
||||
def fromCpStr = if (cp.isEmpty) "" else s" from $cpStr"
|
||||
state.log info s"Applying State transformations $argsStr$fromCpStr"
|
||||
val loader = if (cp.isEmpty) parentLoader else toLoader(cp.map(f => new File(f)), parentLoader)
|
||||
val loaded = args.map(arg => ModuleUtilities.getObject(arg, loader).asInstanceOf[State => State])
|
||||
(state /: loaded)((s, obj) => obj(s))
|
||||
}
|
||||
def call: Command =
|
||||
Command(ApplyCommand, Help.more(ApplyCommand, ApplyDetailed))(_ => callParser) {
|
||||
case (state, (cp, args)) =>
|
||||
val parentLoader = getClass.getClassLoader
|
||||
def argsStr = args mkString ", "
|
||||
def cpStr = cp mkString File.pathSeparator
|
||||
def fromCpStr = if (cp.isEmpty) "" else s" from $cpStr"
|
||||
state.log info s"Applying State transformations $argsStr$fromCpStr"
|
||||
val loader =
|
||||
if (cp.isEmpty) parentLoader else toLoader(cp.map(f => new File(f)), parentLoader)
|
||||
val loaded =
|
||||
args.map(arg => ModuleUtilities.getObject(arg, loader).asInstanceOf[State => State])
|
||||
(state /: loaded)((s, obj) => obj(s))
|
||||
}
|
||||
|
||||
def callParser: Parser[(Seq[String], Seq[String])] =
|
||||
token(Space) ~> ((classpathOptionParser ?? Nil) ~ rep1sep(className, token(Space)))
|
||||
|
||||
private[this] def className: Parser[String] =
|
||||
{
|
||||
val base = StringBasic & not('-' ~> any.*, "Class name cannot start with '-'.")
|
||||
def single(s: String) = Completions.single(Completion.displayOnly(s))
|
||||
val compl = TokenCompletions.fixed((seen, _) =>
|
||||
if (seen.startsWith("-")) Completions.nil else single("<class name>"))
|
||||
token(base, compl)
|
||||
}
|
||||
private[this] def className: Parser[String] = {
|
||||
val base = StringBasic & not('-' ~> any.*, "Class name cannot start with '-'.")
|
||||
def single(s: String) = Completions.single(Completion.displayOnly(s))
|
||||
val compl = TokenCompletions.fixed((seen, _) =>
|
||||
if (seen.startsWith("-")) Completions.nil else single("<class name>"))
|
||||
token(base, compl)
|
||||
}
|
||||
|
||||
private[this] def classpathOptionParser: Parser[Seq[String]] =
|
||||
token(("-cp" | "-classpath") ~> Space) ~> classpathStrings <~ token(Space)
|
||||
|
|
@ -180,11 +208,12 @@ object BasicCommands {
|
|||
def exit: Command = Command.command(TerminateAction, exitBrief, exitBrief)(_ exit true)
|
||||
|
||||
def continuous: Command =
|
||||
Command(ContinuousExecutePrefix, continuousBriefHelp, continuousDetail)(otherCommandParser) { (s, arg) =>
|
||||
withAttribute(s, Watched.Configuration, "Continuous execution not configured.") { w =>
|
||||
val repeat = ContinuousExecutePrefix + (if (arg.startsWith(" ")) arg else " " + arg)
|
||||
Watched.executeContinuously(w, s, arg, repeat)
|
||||
}
|
||||
Command(ContinuousExecutePrefix, continuousBriefHelp, continuousDetail)(otherCommandParser) {
|
||||
(s, arg) =>
|
||||
withAttribute(s, Watched.Configuration, "Continuous execution not configured.") { w =>
|
||||
val repeat = ContinuousExecutePrefix + (if (arg.startsWith(" ")) arg else " " + arg)
|
||||
Watched.executeContinuously(w, s, arg, repeat)
|
||||
}
|
||||
}
|
||||
|
||||
def history: Command = Command.custom(historyParser, BasicCommandStrings.historyHelp)
|
||||
|
|
@ -209,16 +238,19 @@ object BasicCommands {
|
|||
val line = reader.readLine(prompt)
|
||||
line match {
|
||||
case Some(line) =>
|
||||
val newState = s.copy(
|
||||
onFailure = Some(Exec(Shell, None)),
|
||||
remainingCommands = Exec(line, s.source) +: Exec(OldShell, None) +: s.remainingCommands
|
||||
).setInteractive(true)
|
||||
val newState = s
|
||||
.copy(
|
||||
onFailure = Some(Exec(Shell, None)),
|
||||
remainingCommands = Exec(line, s.source) +: Exec(OldShell, None) +: s.remainingCommands
|
||||
)
|
||||
.setInteractive(true)
|
||||
if (line.trim.isEmpty) newState else newState.clearGlobalLog
|
||||
case None => s.setInteractive(false)
|
||||
}
|
||||
}
|
||||
|
||||
def client: Command = Command(Client, Help.more(Client, ClientDetailed))(_ => clientParser)(runClient)
|
||||
def client: Command =
|
||||
Command(Client, Help.more(Client, ClientDetailed))(_ => clientParser)(runClient)
|
||||
|
||||
def clientParser: Parser[Seq[String]] =
|
||||
(token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF) map (_ => Nil))
|
||||
|
|
@ -233,14 +265,14 @@ object BasicCommands {
|
|||
"exit" :: s0.copy(remainingCommands = Nil)
|
||||
}
|
||||
|
||||
def read: Command = Command(ReadCommand, Help.more(ReadCommand, ReadDetailed))(readParser)(doRead(_)(_))
|
||||
def read: Command =
|
||||
Command(ReadCommand, Help.more(ReadCommand, ReadDetailed))(readParser)(doRead(_)(_))
|
||||
|
||||
def readParser(s: State): Parser[Either[Int, Seq[File]]] =
|
||||
{
|
||||
val files = (token(Space) ~> fileParser(s.baseDir)).+
|
||||
val portAndSuccess = token(OptSpace) ~> Port
|
||||
portAndSuccess || files
|
||||
}
|
||||
def readParser(s: State): Parser[Either[Int, Seq[File]]] = {
|
||||
val files = (token(Space) ~> fileParser(s.baseDir)).+
|
||||
val portAndSuccess = token(OptSpace) ~> Port
|
||||
portAndSuccess || files
|
||||
}
|
||||
|
||||
def doRead(s: State)(arg: Either[Int, Seq[File]]): State =
|
||||
arg match {
|
||||
|
|
@ -267,24 +299,24 @@ object BasicCommands {
|
|||
}
|
||||
}
|
||||
|
||||
private def readMessage(port: Int, previousSuccess: Boolean): Option[String] =
|
||||
{
|
||||
// split into two connections because this first connection ends the previous communication
|
||||
xsbt.IPC.client(port) { _.send(previousSuccess.toString) }
|
||||
// and this second connection starts the next communication
|
||||
xsbt.IPC.client(port) { ipc =>
|
||||
val message = ipc.receive
|
||||
if (message eq null) None else Some(message)
|
||||
}
|
||||
private def readMessage(port: Int, previousSuccess: Boolean): Option[String] = {
|
||||
// split into two connections because this first connection ends the previous communication
|
||||
xsbt.IPC.client(port) { _.send(previousSuccess.toString) }
|
||||
// and this second connection starts the next communication
|
||||
xsbt.IPC.client(port) { ipc =>
|
||||
val message = ipc.receive
|
||||
if (message eq null) None else Some(message)
|
||||
}
|
||||
}
|
||||
|
||||
def alias: Command = Command(AliasCommand, Help.more(AliasCommand, AliasDetailed)) { s =>
|
||||
val name = token(OpOrID.examples(aliasNames(s): _*))
|
||||
val assign = token(OptSpace ~ '=' ~ OptSpace)
|
||||
val sfree = removeAliases(s)
|
||||
val to = matched(sfree.combinedParser, partial = true).failOnException | any.+.string
|
||||
OptSpace ~> (name ~ (assign ~> to.?).?).?
|
||||
}(runAlias)
|
||||
def alias: Command =
|
||||
Command(AliasCommand, Help.more(AliasCommand, AliasDetailed)) { s =>
|
||||
val name = token(OpOrID.examples(aliasNames(s): _*))
|
||||
val assign = token(OptSpace ~ '=' ~ OptSpace)
|
||||
val sfree = removeAliases(s)
|
||||
val to = matched(sfree.combinedParser, partial = true).failOnException | any.+.string
|
||||
OptSpace ~> (name ~ (assign ~> to.?).?).?
|
||||
}(runAlias)
|
||||
|
||||
def runAlias(s: State, args: Option[(String, Option[Option[String]])]): State =
|
||||
args match {
|
||||
|
|
@ -336,20 +368,23 @@ object BasicCommands {
|
|||
s.definedCommands.flatMap(c => getAlias(c).filter(tupled(pred)))
|
||||
|
||||
def newAlias(name: String, value: String): Command =
|
||||
Command.make(name, (name, s"'$value'"), s"Alias of '$value'")(aliasBody(name, value))
|
||||
Command
|
||||
.make(name, (name, s"'$value'"), s"Alias of '$value'")(aliasBody(name, value))
|
||||
.tag(CommandAliasKey, (name, value))
|
||||
|
||||
def aliasBody(name: String, value: String)(state: State): Parser[() => State] = {
|
||||
val aliasRemoved = removeAlias(state, name)
|
||||
// apply the alias value to the commands of `state` except for the alias to avoid recursion (#933)
|
||||
val partiallyApplied = Parser(Command.combine(aliasRemoved.definedCommands)(aliasRemoved))(value)
|
||||
val partiallyApplied =
|
||||
Parser(Command.combine(aliasRemoved.definedCommands)(aliasRemoved))(value)
|
||||
val arg = matched(partiallyApplied & (success(()) | (SpaceClass ~ any.*)))
|
||||
// by scheduling the expanded alias instead of directly executing,
|
||||
// we get errors on the expanded string (#598)
|
||||
arg.map(str => () => (value + str) :: state)
|
||||
}
|
||||
|
||||
def delegateToAlias(name: String, orElse: Parser[() => State])(state: State): Parser[() => State] =
|
||||
def delegateToAlias(name: String, orElse: Parser[() => State])(
|
||||
state: State): Parser[() => State] =
|
||||
aliases(state, (nme, _) => nme == name).headOption match {
|
||||
case None => orElse
|
||||
case Some((n, v)) => aliasBody(n, v)(state)
|
||||
|
|
|
|||
|
|
@ -6,15 +6,37 @@ import sbt.internal.inc.classpath.ClassLoaderCache
|
|||
import sbt.librarymanagement.ModuleID
|
||||
|
||||
object BasicKeys {
|
||||
val historyPath = AttributeKey[Option[File]]("history", "The location where command line history is persisted.", 40)
|
||||
val shellPrompt = AttributeKey[State => String]("shell-prompt", "The function that constructs the command prompt from the current build state.", 10000)
|
||||
val historyPath = AttributeKey[Option[File]](
|
||||
"history",
|
||||
"The location where command line history is persisted.",
|
||||
40)
|
||||
val shellPrompt = AttributeKey[State => String](
|
||||
"shell-prompt",
|
||||
"The function that constructs the command prompt from the current build state.",
|
||||
10000)
|
||||
val watch = AttributeKey[Watched]("watch", "Continuous execution configuration.", 1000)
|
||||
val serverPort = AttributeKey[Int]("server-port", "The port number used by server command.", 10000)
|
||||
private[sbt] val interactive = AttributeKey[Boolean]("interactive", "True if commands are currently being entered from an interactive environment.", 10)
|
||||
private[sbt] val classLoaderCache = AttributeKey[ClassLoaderCache]("class-loader-cache", "Caches class loaders based on the classpath entries and last modified times.", 10)
|
||||
private[sbt] val OnFailureStack = AttributeKey[List[Option[Exec]]]("on-failure-stack", "Stack that remembers on-failure handlers.", 10)
|
||||
private[sbt] val explicitGlobalLogLevels = AttributeKey[Boolean]("explicit-global-log-levels", "True if the global logging levels were explicitly set by the user.", 10)
|
||||
private[sbt] val templateResolverInfos = AttributeKey[Seq[TemplateResolverInfo]]("templateResolverInfos", "List of template resolver infos.", 1000)
|
||||
val serverPort =
|
||||
AttributeKey[Int]("server-port", "The port number used by server command.", 10000)
|
||||
private[sbt] val interactive = AttributeKey[Boolean](
|
||||
"interactive",
|
||||
"True if commands are currently being entered from an interactive environment.",
|
||||
10)
|
||||
private[sbt] val classLoaderCache = AttributeKey[ClassLoaderCache](
|
||||
"class-loader-cache",
|
||||
"Caches class loaders based on the classpath entries and last modified times.",
|
||||
10)
|
||||
private[sbt] val OnFailureStack = AttributeKey[List[Option[Exec]]](
|
||||
"on-failure-stack",
|
||||
"Stack that remembers on-failure handlers.",
|
||||
10)
|
||||
private[sbt] val explicitGlobalLogLevels = AttributeKey[Boolean](
|
||||
"explicit-global-log-levels",
|
||||
"True if the global logging levels were explicitly set by the user.",
|
||||
10)
|
||||
private[sbt] val templateResolverInfos = AttributeKey[Seq[TemplateResolverInfo]](
|
||||
"templateResolverInfos",
|
||||
"List of template resolver infos.",
|
||||
1000)
|
||||
}
|
||||
|
||||
case class TemplateResolverInfo(module: ModuleID, implementationClass: String)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ private[sbt] final class SimpleCommand(
|
|||
val name: String,
|
||||
private[sbt] val help0: Help,
|
||||
val parser: State => Parser[() => State],
|
||||
|
||||
val tags: AttributeMap
|
||||
) extends Command {
|
||||
|
||||
|
|
@ -42,7 +41,9 @@ private[sbt] final class SimpleCommand(
|
|||
}
|
||||
|
||||
private[sbt] final class ArbitraryCommand(
|
||||
val parser: State => Parser[() => State], val help: State => Help, val tags: AttributeMap
|
||||
val parser: State => Parser[() => State],
|
||||
val help: State => Help,
|
||||
val tags: AttributeMap
|
||||
) extends Command {
|
||||
def tag[T](key: AttributeKey[T], value: T): ArbitraryCommand =
|
||||
new ArbitraryCommand(parser, help, tags.put(key, value))
|
||||
|
|
@ -56,16 +57,19 @@ object Command {
|
|||
def make(name: String, help: Help = Help.empty)(parser: State => Parser[() => State]): Command =
|
||||
new SimpleCommand(name, help, parser, AttributeMap.empty)
|
||||
|
||||
def make(name: String, briefHelp: (String, String), detail: String)(parser: State => Parser[() => State]): Command =
|
||||
def make(name: String, briefHelp: (String, String), detail: String)(
|
||||
parser: State => Parser[() => State]): Command =
|
||||
make(name, Help(name, briefHelp, detail))(parser)
|
||||
|
||||
// General command construction
|
||||
|
||||
/** Construct a command with the given name, parser and effect. */
|
||||
def apply[T](name: String, help: Help = Help.empty)(parser: State => Parser[T])(effect: (State, T) => State): Command =
|
||||
def apply[T](name: String, help: Help = Help.empty)(parser: State => Parser[T])(
|
||||
effect: (State, T) => State): Command =
|
||||
make(name, help)(applyEffect(parser)(effect))
|
||||
|
||||
def apply[T](name: String, briefHelp: (String, String), detail: String)(parser: State => Parser[T])(effect: (State, T) => State): Command =
|
||||
def apply[T](name: String, briefHelp: (String, String), detail: String)(
|
||||
parser: State => Parser[T])(effect: (State, T) => State): Command =
|
||||
apply(name, Help(name, briefHelp, detail))(parser)(effect)
|
||||
|
||||
// No-argument command construction
|
||||
|
|
@ -83,16 +87,19 @@ object Command {
|
|||
def single(name: String, help: Help = Help.empty)(f: (State, String) => State): Command =
|
||||
make(name, help)(state => token(trimmed(spacedAny(name)) map apply1(f, state)))
|
||||
|
||||
def single(name: String, briefHelp: (String, String), detail: String)(f: (State, String) => State): Command =
|
||||
def single(name: String, briefHelp: (String, String), detail: String)(
|
||||
f: (State, String) => State): Command =
|
||||
single(name, Help(name, briefHelp, detail))(f)
|
||||
|
||||
// Multi-argument command construction
|
||||
|
||||
/** Construct a multi-argument command with the given name, tab completion display and effect. */
|
||||
def args(name: String, display: String, help: Help = Help.empty)(f: (State, Seq[String]) => State): Command =
|
||||
def args(name: String, display: String, help: Help = Help.empty)(
|
||||
f: (State, Seq[String]) => State): Command =
|
||||
make(name, help)(state => spaceDelimited(display) map apply1(f, state))
|
||||
|
||||
def args(name: String, briefHelp: (String, String), detail: String, display: String)(f: (State, Seq[String]) => State): Command =
|
||||
def args(name: String, briefHelp: (String, String), detail: String, display: String)(
|
||||
f: (State, Seq[String]) => State): Command =
|
||||
args(name, display, Help(name, briefHelp, detail))(f)
|
||||
|
||||
// create ArbitraryCommand
|
||||
|
|
@ -103,7 +110,8 @@ object Command {
|
|||
def custom(parser: State => Parser[() => State], help: Help = Help.empty): Command =
|
||||
customHelp(parser, const(help))
|
||||
|
||||
def arb[T](parser: State => Parser[T], help: Help = Help.empty)(effect: (State, T) => State): Command =
|
||||
def arb[T](parser: State => Parser[T], help: Help = Help.empty)(
|
||||
effect: (State, T) => State): Command =
|
||||
custom(applyEffect(parser)(effect), help)
|
||||
|
||||
// misc Command object utilities
|
||||
|
|
@ -112,15 +120,18 @@ object Command {
|
|||
|
||||
def applyEffect[T](p: Parser[T])(f: T => State): Parser[() => State] = p map (t => () => f(t))
|
||||
|
||||
def applyEffect[T](parser: State => Parser[T])(effect: (State, T) => State): State => Parser[() => State] =
|
||||
def applyEffect[T](parser: State => Parser[T])(
|
||||
effect: (State, T) => State): State => Parser[() => State] =
|
||||
s => applyEffect(parser(s))(t => effect(s, t))
|
||||
|
||||
def combine(cmds: Seq[Command]): State => Parser[() => State] = {
|
||||
val (simple, arbs) = separateCommands(cmds)
|
||||
state => (simpleParser(simple)(state) /: arbs.map(_ parser state))(_ | _)
|
||||
state =>
|
||||
(simpleParser(simple)(state) /: arbs.map(_ parser state))(_ | _)
|
||||
}
|
||||
|
||||
private[this] def separateCommands(cmds: Seq[Command]): (Seq[SimpleCommand], Seq[ArbitraryCommand]) =
|
||||
private[this] def separateCommands(
|
||||
cmds: Seq[Command]): (Seq[SimpleCommand], Seq[ArbitraryCommand]) =
|
||||
Util.separate(cmds) { case s: SimpleCommand => Left(s); case a: ArbitraryCommand => Right(a) }
|
||||
|
||||
private[this] def apply1[A, B, C](f: (A, B) => C, a: A): B => () => C = b => () => f(a, b)
|
||||
|
|
@ -130,15 +141,18 @@ object Command {
|
|||
|
||||
private[this] def argParser(sc: SimpleCommand): State => Parser[() => State] = {
|
||||
def usageError = s"${sc.name} usage:" + Help.message(sc.help0, None)
|
||||
s => (Parser.softFailure(usageError, definitive = true): Parser[() => State]) | sc.parser(s)
|
||||
s =>
|
||||
(Parser.softFailure(usageError, definitive = true): Parser[() => State]) | sc.parser(s)
|
||||
}
|
||||
|
||||
def simpleParser(commandMap: Map[String, State => Parser[() => State]]): State => Parser[() => State] =
|
||||
state => token(OpOrID examples commandMap.keys.toSet) flatMap (id =>
|
||||
(commandMap get id) match {
|
||||
case None => failure(invalidValue("command", commandMap.keys)(id))
|
||||
case Some(c) => c(state)
|
||||
})
|
||||
def simpleParser(
|
||||
commandMap: Map[String, State => Parser[() => State]]): State => Parser[() => State] =
|
||||
state =>
|
||||
token(OpOrID examples commandMap.keys.toSet) flatMap (id =>
|
||||
(commandMap get id) match {
|
||||
case None => failure(invalidValue("command", commandMap.keys)(id))
|
||||
case Some(c) => c(state)
|
||||
})
|
||||
|
||||
def invalidValue(label: String, allowed: Iterable[String])(value: String): String =
|
||||
s"Not a valid $label: $value" + similar(value, allowed)
|
||||
|
|
@ -148,12 +162,22 @@ object Command {
|
|||
if (suggested.isEmpty) "" else suggested.mkString(" (similar: ", ", ", ")")
|
||||
}
|
||||
|
||||
def suggestions(a: String, bs: Seq[String], maxDistance: Int = 3, maxSuggestions: Int = 3): Seq[String] =
|
||||
def suggestions(a: String,
|
||||
bs: Seq[String],
|
||||
maxDistance: Int = 3,
|
||||
maxSuggestions: Int = 3): Seq[String] =
|
||||
bs map (b => (b, distance(a, b))) filter (_._2 <= maxDistance) sortBy (_._2) take (maxSuggestions) map (_._1)
|
||||
|
||||
def distance(a: String, b: String): Int =
|
||||
EditDistance.levenshtein(a, b, insertCost = 1, deleteCost = 1, subCost = 2, transposeCost = 1,
|
||||
matchCost = -1, caseCost = 1, transpositions = true)
|
||||
EditDistance.levenshtein(a,
|
||||
b,
|
||||
insertCost = 1,
|
||||
deleteCost = 1,
|
||||
subCost = 2,
|
||||
transposeCost = 1,
|
||||
matchCost = -1,
|
||||
caseCost = 1,
|
||||
transpositions = true)
|
||||
|
||||
def spacedAny(name: String): Parser[String] = spacedC(name, any)
|
||||
|
||||
|
|
@ -169,9 +193,12 @@ trait Help {
|
|||
}
|
||||
|
||||
private final class Help0(
|
||||
val brief: Seq[(String, String)], val detail: Map[String, String], val more: Set[String]
|
||||
val brief: Seq[(String, String)],
|
||||
val detail: Map[String, String],
|
||||
val more: Set[String]
|
||||
) extends Help {
|
||||
def ++(h: Help): Help = new Help0(Help0.this.brief ++ h.brief, Help0.this.detail ++ h.detail, more ++ h.more)
|
||||
def ++(h: Help): Help =
|
||||
new Help0(Help0.this.brief ++ h.brief, Help0.this.detail ++ h.detail, more ++ h.more)
|
||||
}
|
||||
|
||||
object Help {
|
||||
|
|
@ -186,10 +213,13 @@ object Help {
|
|||
def apply(briefHelp: Seq[(String, String)], detailedHelp: Map[String, String]): Help =
|
||||
apply(briefHelp, detailedHelp, Set.empty[String])
|
||||
|
||||
def apply(briefHelp: Seq[(String, String)], detailedHelp: Map[String, String], more: Set[String]): Help =
|
||||
def apply(briefHelp: Seq[(String, String)],
|
||||
detailedHelp: Map[String, String],
|
||||
more: Set[String]): Help =
|
||||
new Help0(briefHelp, detailedHelp, more)
|
||||
|
||||
def more(name: String, detailedHelp: String): Help = apply(Nil, Map(name -> detailedHelp), Set(name))
|
||||
def more(name: String, detailedHelp: String): Help =
|
||||
apply(Nil, Map(name -> detailedHelp), Set(name))
|
||||
def briefDetail(help: Seq[(String, String)]): Help = apply(help, help.toMap)
|
||||
def briefOnly(help: Seq[(String, String)]): Help = apply(help, Map.empty[String, String])
|
||||
def detailOnly(help: Seq[(String, String)]): Help = apply(Nil, help.toMap)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,11 @@ import sbt.internal.util.complete.DefaultParsers._
|
|||
import sbt.io.IO
|
||||
|
||||
object CommandUtil {
|
||||
def readLines(files: Seq[File]): Seq[String] = files flatMap (IO.readLines(_)) flatMap processLine
|
||||
def processLine(s: String): Option[String] = { val s2 = s.trim; if (ignoreLine(s2)) None else Some(s2) }
|
||||
def readLines(files: Seq[File]): Seq[String] =
|
||||
files flatMap (IO.readLines(_)) flatMap processLine
|
||||
def processLine(s: String): Option[String] = {
|
||||
val s2 = s.trim; if (ignoreLine(s2)) None else Some(s2)
|
||||
}
|
||||
def ignoreLine(s: String): Boolean = s.isEmpty || s.startsWith("#")
|
||||
|
||||
private def canRead = (_: File).canRead
|
||||
|
|
@ -23,10 +26,12 @@ object CommandUtil {
|
|||
try state.configuration.provider.scalaProvider.launcher.bootDirectory
|
||||
catch { case _: NoSuchMethodError => new File(".").getAbsoluteFile }
|
||||
|
||||
def aligned(pre: String, sep: String, in: Seq[(String, String)]): Seq[String] = if (in.isEmpty) Nil else {
|
||||
val width = in.iterator.map(_._1.length).max
|
||||
for ((a, b) <- in) yield pre + fill(a, width) + sep + b
|
||||
}
|
||||
def aligned(pre: String, sep: String, in: Seq[(String, String)]): Seq[String] =
|
||||
if (in.isEmpty) Nil
|
||||
else {
|
||||
val width = in.iterator.map(_._1.length).max
|
||||
for ((a, b) <- in) yield pre + fill(a, width) + sep + b
|
||||
}
|
||||
|
||||
def fill(s: String, size: Int): String = s + " " * math.max(size - s.length, 0)
|
||||
|
||||
|
|
@ -37,42 +42,41 @@ object CommandUtil {
|
|||
case Some(nav) => f(nav)
|
||||
}
|
||||
|
||||
def singleArgument(exampleStrings: Set[String]): Parser[String] =
|
||||
{
|
||||
val arg = (NotSpaceClass ~ any.*) map { case (ns, s) => (ns +: s).mkString }
|
||||
token(Space) ~> token(arg examples exampleStrings)
|
||||
}
|
||||
def singleArgument(exampleStrings: Set[String]): Parser[String] = {
|
||||
val arg = (NotSpaceClass ~ any.*) map { case (ns, s) => (ns +: s).mkString }
|
||||
token(Space) ~> token(arg examples exampleStrings)
|
||||
}
|
||||
|
||||
def detail(selected: String, detailMap: Map[String, String]): String =
|
||||
detailMap.get(selected) match {
|
||||
case Some(exactDetail) => exactDetail
|
||||
case None => try {
|
||||
val details = searchHelp(selected, detailMap)
|
||||
if (details.isEmpty)
|
||||
"No matches for regular expression '" + selected + "'."
|
||||
else
|
||||
layoutDetails(details)
|
||||
} catch {
|
||||
case pse: PatternSyntaxException =>
|
||||
sys.error("Invalid regular expression (java.util.regex syntax).\n" + pse.getMessage)
|
||||
}
|
||||
case None =>
|
||||
try {
|
||||
val details = searchHelp(selected, detailMap)
|
||||
if (details.isEmpty)
|
||||
"No matches for regular expression '" + selected + "'."
|
||||
else
|
||||
layoutDetails(details)
|
||||
} catch {
|
||||
case pse: PatternSyntaxException =>
|
||||
sys.error("Invalid regular expression (java.util.regex syntax).\n" + pse.getMessage)
|
||||
}
|
||||
}
|
||||
|
||||
def searchHelp(selected: String, detailMap: Map[String, String]): Map[String, String] =
|
||||
{
|
||||
val pattern = Pattern.compile(selected, HelpPatternFlags)
|
||||
detailMap flatMap {
|
||||
case (k, v) =>
|
||||
val contentMatches = Highlight.showMatches(pattern)(v)
|
||||
val keyMatches = Highlight.showMatches(pattern)(k)
|
||||
val keyString = Highlight.bold(keyMatches getOrElse k)
|
||||
val contentString = contentMatches getOrElse v
|
||||
if (keyMatches.isDefined || contentMatches.isDefined)
|
||||
(keyString, contentString) :: Nil
|
||||
else
|
||||
Nil
|
||||
}
|
||||
def searchHelp(selected: String, detailMap: Map[String, String]): Map[String, String] = {
|
||||
val pattern = Pattern.compile(selected, HelpPatternFlags)
|
||||
detailMap flatMap {
|
||||
case (k, v) =>
|
||||
val contentMatches = Highlight.showMatches(pattern)(v)
|
||||
val keyMatches = Highlight.showMatches(pattern)(k)
|
||||
val keyString = Highlight.bold(keyMatches getOrElse k)
|
||||
val contentString = contentMatches getOrElse v
|
||||
if (keyMatches.isDefined || contentMatches.isDefined)
|
||||
(keyString, contentString) :: Nil
|
||||
else
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
def layoutDetails(details: Map[String, String]): String =
|
||||
details.map { case (k, v) => k + "\n\n " + v } mkString ("\n", "\n\n", "\n")
|
||||
|
|
|
|||
|
|
@ -7,17 +7,17 @@ import sbt.internal.util.ConsoleAppender
|
|||
|
||||
object Highlight {
|
||||
|
||||
def showMatches(pattern: Pattern)(line: String): Option[String] =
|
||||
{
|
||||
val matcher = pattern.matcher(line)
|
||||
if (ConsoleAppender.formatEnabled) {
|
||||
// ANSI codes like \033[39m (normal text color) don't work on Windows
|
||||
val highlighted = matcher.replaceAll(scala.Console.RED + "$0" + RESET)
|
||||
if (highlighted == line) None else Some(highlighted)
|
||||
} else if (matcher.find)
|
||||
Some(line)
|
||||
else
|
||||
None
|
||||
}
|
||||
def bold(s: String) = if (ConsoleAppender.formatEnabled) BOLD + s.replace(RESET, RESET + BOLD) + RESET else s
|
||||
def showMatches(pattern: Pattern)(line: String): Option[String] = {
|
||||
val matcher = pattern.matcher(line)
|
||||
if (ConsoleAppender.formatEnabled) {
|
||||
// ANSI codes like \033[39m (normal text color) don't work on Windows
|
||||
val highlighted = matcher.replaceAll(scala.Console.RED + "$0" + RESET)
|
||||
if (highlighted == line) None else Some(highlighted)
|
||||
} else if (matcher.find)
|
||||
Some(line)
|
||||
else
|
||||
None
|
||||
}
|
||||
def bold(s: String) =
|
||||
if (ConsoleAppender.formatEnabled) BOLD + s.replace(RESET, RESET + BOLD) + RESET else s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,21 @@ import java.io.File
|
|||
final case class Exit(code: Int) extends xsbti.Exit {
|
||||
require(code >= 0)
|
||||
}
|
||||
final case class Reboot(scalaVersion: String, argsList: Seq[String], app: xsbti.ApplicationID, baseDirectory: File) extends xsbti.Reboot {
|
||||
final case class Reboot(scalaVersion: String,
|
||||
argsList: Seq[String],
|
||||
app: xsbti.ApplicationID,
|
||||
baseDirectory: File)
|
||||
extends xsbti.Reboot {
|
||||
def arguments = argsList.toArray
|
||||
}
|
||||
final case class ApplicationID(groupID: String, name: String, version: String, mainClass: String, components: Seq[String], crossVersionedValue: xsbti.CrossValue, extra: Seq[File]) extends xsbti.ApplicationID {
|
||||
final case class ApplicationID(groupID: String,
|
||||
name: String,
|
||||
version: String,
|
||||
mainClass: String,
|
||||
components: Seq[String],
|
||||
crossVersionedValue: xsbti.CrossValue,
|
||||
extra: Seq[File])
|
||||
extends xsbti.ApplicationID {
|
||||
def mainComponents = components.toArray
|
||||
def classpathExtra = extra.toArray
|
||||
def crossVersioned = crossVersionedValue != xsbti.CrossValue.Disabled
|
||||
|
|
@ -20,5 +31,11 @@ object ApplicationID {
|
|||
def apply(delegate: xsbti.ApplicationID, newVersion: String): ApplicationID =
|
||||
apply(delegate).copy(version = newVersion)
|
||||
def apply(delegate: xsbti.ApplicationID): ApplicationID =
|
||||
ApplicationID(delegate.groupID, delegate.name, delegate.version, delegate.mainClass, delegate.mainComponents, delegate.crossVersionedValue, delegate.classpathExtra)
|
||||
}
|
||||
ApplicationID(delegate.groupID,
|
||||
delegate.name,
|
||||
delegate.version,
|
||||
delegate.mainClass,
|
||||
delegate.mainComponents,
|
||||
delegate.crossVersionedValue,
|
||||
delegate.classpathExtra)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,14 @@ package sbt
|
|||
import java.io.File
|
||||
import java.util.concurrent.Callable
|
||||
import sbt.util.Logger
|
||||
import sbt.internal.util.{ AttributeKey, AttributeMap, ErrorHandling, ExitHook, ExitHooks, GlobalLogging }
|
||||
import sbt.internal.util.{
|
||||
AttributeKey,
|
||||
AttributeMap,
|
||||
ErrorHandling,
|
||||
ExitHook,
|
||||
ExitHooks,
|
||||
GlobalLogging
|
||||
}
|
||||
import sbt.internal.util.complete.HistoryCommands
|
||||
import sbt.internal.inc.classpath.ClassLoaderCache
|
||||
import sbt.BasicCommandStrings.Shell
|
||||
|
|
@ -91,11 +98,13 @@ trait StateOps {
|
|||
|
||||
/** Sets the next command processing action to be to rotate the global log and continue executing commands.*/
|
||||
def clearGlobalLog: State
|
||||
|
||||
/** Sets the next command processing action to be to keep the previous log and continue executing commands. */
|
||||
def keepLastLog: State
|
||||
|
||||
/** Sets the next command processing action to be to exit with a zero exit code if `ok` is true and a nonzero exit code if `ok` if false.*/
|
||||
def exit(ok: Boolean): State
|
||||
|
||||
/** Marks the currently executing command as failing. This triggers failure handling by the command processor. See also `State.onFailure`*/
|
||||
def fail: State
|
||||
|
||||
|
|
@ -109,17 +118,22 @@ trait StateOps {
|
|||
|
||||
/** Registers `newCommands` as available commands. */
|
||||
def ++(newCommands: Seq[Command]): State
|
||||
|
||||
/** Registers `newCommand` as an available command. */
|
||||
def +(newCommand: Command): State
|
||||
|
||||
/** Gets the value associated with `key` from the custom attributes map.*/
|
||||
def get[T](key: AttributeKey[T]): Option[T]
|
||||
|
||||
/** Sets the value associated with `key` in the custom attributes map.*/
|
||||
def put[T](key: AttributeKey[T], value: T): State
|
||||
|
||||
/** Removes the `key` and any associated value from the custom attributes map.*/
|
||||
def remove(key: AttributeKey[_]): State
|
||||
|
||||
/** Sets the value associated with `key` in the custom attributes map by transforming the current value.*/
|
||||
def update[T](key: AttributeKey[T])(f: Option[T] => T): State
|
||||
|
||||
/** Returns true if `key` exists in the custom attributes map, false if it does not exist.*/
|
||||
def has(key: AttributeKey[_]): Boolean
|
||||
|
||||
|
|
@ -134,11 +148,13 @@ trait StateOps {
|
|||
|
||||
/** Runs any defined exitHooks and then clears them.*/
|
||||
def runExitHooks(): State
|
||||
|
||||
/** Registers a new exit hook, which will run when sbt exits or restarts.*/
|
||||
def addExitHook(f: => Unit): State
|
||||
|
||||
/** An advisory flag that is `true` if this application will execute commands based on user input.*/
|
||||
def interactive: Boolean
|
||||
|
||||
/** Changes the advisory `interactive` flag. */
|
||||
def setInteractive(flag: Boolean): State
|
||||
|
||||
|
|
@ -150,17 +166,22 @@ trait StateOps {
|
|||
}
|
||||
|
||||
object State {
|
||||
|
||||
/** Indicates where command execution should resume after a failure.*/
|
||||
val FailureWall = BasicCommandStrings.FailureWall
|
||||
|
||||
/** Represents the next action for the command processor.*/
|
||||
sealed trait Next
|
||||
|
||||
/** Indicates that the command processor should process the next command.*/
|
||||
object Continue extends Next
|
||||
|
||||
/** Indicates that the application should exit with the given result.*/
|
||||
final class Return(val result: xsbti.MainResult) extends Next
|
||||
|
||||
/** Indicates that global logging should be rotated.*/
|
||||
final object ClearGlobalLog extends Next
|
||||
|
||||
/** Indicates that the previous log file should be preserved instead of discarded.*/
|
||||
final object KeepLastLog extends Next
|
||||
|
||||
|
|
@ -170,30 +191,33 @@ object State {
|
|||
* @param maxSize the maximum number of commands to keep, or 0 to keep an unlimited number.
|
||||
*/
|
||||
final class History private[State] (val executed: Seq[Exec], val maxSize: Int) {
|
||||
|
||||
/** Adds `command` as the most recently executed command.*/
|
||||
def ::(command: Exec): History =
|
||||
{
|
||||
val prependTo = if (maxSize > 0 && executed.size >= maxSize) executed.take(maxSize - 1) else executed
|
||||
new History(command +: prependTo, maxSize)
|
||||
}
|
||||
def ::(command: Exec): History = {
|
||||
val prependTo =
|
||||
if (maxSize > 0 && executed.size >= maxSize) executed.take(maxSize - 1) else executed
|
||||
new History(command +: prependTo, maxSize)
|
||||
}
|
||||
|
||||
/** Changes the maximum number of commands kept, adjusting the current history if necessary.*/
|
||||
def setMaxSize(size: Int): History =
|
||||
new History(if (size <= 0) executed else executed.take(size), size)
|
||||
def currentOption: Option[Exec] = executed.headOption
|
||||
def previous: Option[Exec] = executed.drop(1).headOption
|
||||
}
|
||||
|
||||
/** Constructs an empty command History with a default, finite command limit.*/
|
||||
def newHistory = new History(Vector.empty, HistoryCommands.MaxLines)
|
||||
|
||||
def defaultReload(state: State): Reboot =
|
||||
{
|
||||
val app = state.configuration.provider
|
||||
new Reboot(
|
||||
app.scalaProvider.version,
|
||||
state.remainingCommands map { case e: Exec => e.commandLine },
|
||||
app.id, state.configuration.baseDirectory
|
||||
)
|
||||
}
|
||||
def defaultReload(state: State): Reboot = {
|
||||
val app = state.configuration.provider
|
||||
new Reboot(
|
||||
app.scalaProvider.version,
|
||||
state.remainingCommands map { case e: Exec => e.commandLine },
|
||||
app.id,
|
||||
state.configuration.baseDirectory
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides operations and transformations on State. */
|
||||
implicit def stateOps(s: State): StateOps = new StateOps {
|
||||
|
|
@ -204,22 +228,33 @@ object State {
|
|||
}
|
||||
def isInteractive = System.console() != null
|
||||
def hasInput = System.console().reader().ready()
|
||||
def hasShellCmd = s.definedCommands exists { case c: SimpleCommand => c.name == Shell; case _ => false }
|
||||
def hasShellCmd = s.definedCommands exists {
|
||||
case c: SimpleCommand => c.name == Shell; case _ => false
|
||||
}
|
||||
s.remainingCommands match {
|
||||
case List() => if (isInteractive && hasInput && hasShellCmd) doX(Exec(Shell, s.source), Nil) else exit(true)
|
||||
case List() =>
|
||||
if (isInteractive && hasInput && hasShellCmd) doX(Exec(Shell, s.source), Nil)
|
||||
else exit(true)
|
||||
case List(x, xs @ _*) => doX(x, xs.toList)
|
||||
}
|
||||
}
|
||||
def :::(newCommands: List[String]): State = ++:(newCommands map { Exec(_, s.source) })
|
||||
def ++:(newCommands: List[Exec]): State = s.copy(remainingCommands = newCommands ::: s.remainingCommands)
|
||||
def ++:(newCommands: List[Exec]): State =
|
||||
s.copy(remainingCommands = newCommands ::: s.remainingCommands)
|
||||
def ::(command: String): State = +:(Exec(command, s.source))
|
||||
def +:(command: Exec): State = (command :: Nil) ++: this
|
||||
def ++(newCommands: Seq[Command]): State = s.copy(definedCommands = (s.definedCommands ++ newCommands).distinct)
|
||||
def ++(newCommands: Seq[Command]): State =
|
||||
s.copy(definedCommands = (s.definedCommands ++ newCommands).distinct)
|
||||
def +(newCommand: Command): State = this ++ (newCommand :: Nil)
|
||||
def baseDir: File = s.configuration.baseDirectory
|
||||
def setNext(n: Next) = s.copy(next = n)
|
||||
def continue = setNext(Continue)
|
||||
def reboot(full: Boolean) = { runExitHooks(); throw new xsbti.FullReload((s.remainingCommands map { case e: Exec => e.commandLine }).toArray, full) }
|
||||
def reboot(full: Boolean) = {
|
||||
runExitHooks();
|
||||
throw new xsbti.FullReload(
|
||||
(s.remainingCommands map { case e: Exec => e.commandLine }).toArray,
|
||||
full)
|
||||
}
|
||||
def reload = runExitHooks().setNext(new Return(defaultReload(s)))
|
||||
def clearGlobalLog = setNext(ClearGlobalLog)
|
||||
def keepLastLog = setNext(KeepLastLog)
|
||||
|
|
@ -231,16 +266,16 @@ object State {
|
|||
def remove(key: AttributeKey[_]) = s.copy(attributes = s.attributes remove key)
|
||||
def log = s.globalLogging.full
|
||||
def handleError(t: Throwable): State = handleException(t, s, log)
|
||||
def fail =
|
||||
{
|
||||
import BasicCommandStrings.Compat.{ FailureWall => CompatFailureWall }
|
||||
val remaining =
|
||||
s.remainingCommands.dropWhile(c => c.commandLine != FailureWall && c.commandLine != CompatFailureWall)
|
||||
if (remaining.isEmpty)
|
||||
applyOnFailure(s, Nil, exit(ok = false))
|
||||
else
|
||||
applyOnFailure(s, remaining, s.copy(remainingCommands = remaining))
|
||||
}
|
||||
def fail = {
|
||||
import BasicCommandStrings.Compat.{ FailureWall => CompatFailureWall }
|
||||
val remaining =
|
||||
s.remainingCommands.dropWhile(c =>
|
||||
c.commandLine != FailureWall && c.commandLine != CompatFailureWall)
|
||||
if (remaining.isEmpty)
|
||||
applyOnFailure(s, Nil, exit(ok = false))
|
||||
else
|
||||
applyOnFailure(s, remaining, s.copy(remainingCommands = remaining))
|
||||
}
|
||||
private[this] def applyOnFailure(s: State, remaining: List[Exec], noHandler: => State): State =
|
||||
s.onFailure match {
|
||||
case Some(c) => s.copy(remainingCommands = c +: remaining, onFailure = None)
|
||||
|
|
@ -254,27 +289,30 @@ object State {
|
|||
s.copy(exitHooks = Set.empty)
|
||||
}
|
||||
def locked[T](file: File)(t: => T): T =
|
||||
s.configuration.provider.scalaProvider.launcher.globalLock.apply(file, new Callable[T] { def call = t })
|
||||
s.configuration.provider.scalaProvider.launcher.globalLock.apply(file, new Callable[T] {
|
||||
def call = t
|
||||
})
|
||||
|
||||
def interactive = getBoolean(s, BasicKeys.interactive, false)
|
||||
def setInteractive(i: Boolean) = s.put(BasicKeys.interactive, i)
|
||||
|
||||
def classLoaderCache: ClassLoaderCache = s get BasicKeys.classLoaderCache getOrElse newClassLoaderCache
|
||||
def classLoaderCache: ClassLoaderCache =
|
||||
s get BasicKeys.classLoaderCache getOrElse newClassLoaderCache
|
||||
def initializeClassLoaderCache = s.put(BasicKeys.classLoaderCache, newClassLoaderCache)
|
||||
private[this] def newClassLoaderCache = new ClassLoaderCache(s.configuration.provider.scalaProvider.launcher.topLoader)
|
||||
private[this] def newClassLoaderCache =
|
||||
new ClassLoaderCache(s.configuration.provider.scalaProvider.launcher.topLoader)
|
||||
}
|
||||
|
||||
import ExceptionCategory._
|
||||
|
||||
private[sbt] def handleException(t: Throwable, s: State, log: Logger): State =
|
||||
{
|
||||
ExceptionCategory(t) match {
|
||||
case AlreadyHandled => ()
|
||||
case m: MessageOnly => log.error(m.message)
|
||||
case f: Full => logFullException(f.exception, log)
|
||||
}
|
||||
s.fail
|
||||
private[sbt] def handleException(t: Throwable, s: State, log: Logger): State = {
|
||||
ExceptionCategory(t) match {
|
||||
case AlreadyHandled => ()
|
||||
case m: MessageOnly => log.error(m.message)
|
||||
case f: Full => logFullException(f.exception, log)
|
||||
}
|
||||
s.fail
|
||||
}
|
||||
private[sbt] def logFullException(e: Throwable, log: Logger): Unit = {
|
||||
log.trace(e)
|
||||
log.error(ErrorHandling reducedToString e)
|
||||
|
|
|
|||
|
|
@ -14,22 +14,27 @@ import sbt.internal.util.AttributeKey
|
|||
import sbt.internal.util.Types.const
|
||||
|
||||
trait Watched {
|
||||
|
||||
/** The files watched when an action is run with a preceeding ~ */
|
||||
def watchPaths(s: State): Seq[File] = Nil
|
||||
def terminateWatch(key: Int): Boolean = Watched.isEnter(key)
|
||||
|
||||
/**
|
||||
* The time in milliseconds between checking for changes. The actual time between the last change made to a file and the
|
||||
* execution time is between `pollInterval` and `pollInterval*2`.
|
||||
*/
|
||||
def pollInterval: Int = Watched.PollDelayMillis
|
||||
|
||||
/** The message to show when triggered execution waits for sources to change.*/
|
||||
private[sbt] def watchingMessage(s: WatchState): String = Watched.defaultWatchingMessage(s)
|
||||
|
||||
/** The message to show before an action is run. */
|
||||
private[sbt] def triggeredMessage(s: WatchState): String = Watched.defaultTriggeredMessage(s)
|
||||
}
|
||||
|
||||
object Watched {
|
||||
val defaultWatchingMessage: WatchState => String = _.count + ". Waiting for source changes... (press enter to interrupt)"
|
||||
val defaultWatchingMessage
|
||||
: WatchState => String = _.count + ". Waiting for source changes... (press enter to interrupt)"
|
||||
val defaultTriggeredMessage: WatchState => String = const("")
|
||||
val clearWhenTriggered: WatchState => String = const(clearScreen)
|
||||
def clearScreen: String = "\u001b[2J\u001b[0;0H"
|
||||
|
|
@ -50,36 +55,40 @@ object Watched {
|
|||
def isEnter(key: Int): Boolean = key == 10 || key == 13
|
||||
def printIfDefined(msg: String) = if (!msg.isEmpty) System.out.println(msg)
|
||||
|
||||
def executeContinuously(watched: Watched, s: State, next: String, repeat: String): State =
|
||||
{
|
||||
@tailrec def shouldTerminate: Boolean =
|
||||
(System.in.available > 0) && (watched.terminateWatch(System.in.read()) || shouldTerminate)
|
||||
val sourcesFinder = PathFinder { watched watchPaths s }
|
||||
val watchState = s get ContinuousState getOrElse WatchState.empty
|
||||
def executeContinuously(watched: Watched, s: State, next: String, repeat: String): State = {
|
||||
@tailrec def shouldTerminate: Boolean =
|
||||
(System.in.available > 0) && (watched.terminateWatch(System.in.read()) || shouldTerminate)
|
||||
val sourcesFinder = PathFinder { watched watchPaths s }
|
||||
val watchState = s get ContinuousState getOrElse WatchState.empty
|
||||
|
||||
if (watchState.count > 0)
|
||||
printIfDefined(watched watchingMessage watchState)
|
||||
if (watchState.count > 0)
|
||||
printIfDefined(watched watchingMessage watchState)
|
||||
|
||||
val (triggered, newWatchState) =
|
||||
try {
|
||||
val (triggered, newWatchState) = SourceModificationWatch.watch(sourcesFinder, watched.pollInterval, watchState)(shouldTerminate)
|
||||
(triggered, newWatchState)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
val log = s.log
|
||||
log.error("Error occurred obtaining files to watch. Terminating continuous execution...")
|
||||
State.handleException(e, s, log)
|
||||
(false, watchState)
|
||||
}
|
||||
|
||||
if (triggered) {
|
||||
printIfDefined(watched triggeredMessage newWatchState)
|
||||
(ClearOnFailure :: next :: FailureWall :: repeat :: s).put(ContinuousState, newWatchState)
|
||||
} else {
|
||||
while (System.in.available() > 0) System.in.read()
|
||||
s.put(ContinuousState, WatchState.empty)
|
||||
val (triggered, newWatchState) =
|
||||
try {
|
||||
val (triggered, newWatchState) =
|
||||
SourceModificationWatch.watch(sourcesFinder, watched.pollInterval, watchState)(
|
||||
shouldTerminate)
|
||||
(triggered, newWatchState)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
val log = s.log
|
||||
log.error(
|
||||
"Error occurred obtaining files to watch. Terminating continuous execution...")
|
||||
State.handleException(e, s, log)
|
||||
(false, watchState)
|
||||
}
|
||||
|
||||
if (triggered) {
|
||||
printIfDefined(watched triggeredMessage newWatchState)
|
||||
(ClearOnFailure :: next :: FailureWall :: repeat :: s).put(ContinuousState, newWatchState)
|
||||
} else {
|
||||
while (System.in.available() > 0) System.in.read()
|
||||
s.put(ContinuousState, WatchState.empty)
|
||||
}
|
||||
val ContinuousState = AttributeKey[WatchState]("watch state", "Internal: tracks state for continuous execution.")
|
||||
val Configuration = AttributeKey[Watched]("watched-configuration", "Configures continuous execution.")
|
||||
}
|
||||
val ContinuousState =
|
||||
AttributeKey[WatchState]("watch state", "Internal: tracks state for continuous execution.")
|
||||
val Configuration =
|
||||
AttributeKey[Watched]("watched-configuration", "Configures continuous execution.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,23 +48,21 @@ class NetworkClient(arguments: List[String]) { self =>
|
|||
new ServerConnection(socket) {
|
||||
override def onEvent(event: EventMessage): Unit = self.onEvent(event)
|
||||
override def onLogEntry(event: StringEvent): Unit = self.onLogEntry(event)
|
||||
override def onShutdown(): Unit =
|
||||
{
|
||||
running.set(false)
|
||||
}
|
||||
override def onShutdown(): Unit = {
|
||||
running.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def onLogEntry(event: StringEvent): Unit =
|
||||
{
|
||||
val level = event.level match {
|
||||
case "debug" => Level.Debug
|
||||
case "info" => Level.Info
|
||||
case "warn" => Level.Warn
|
||||
case "error" => Level.Error
|
||||
}
|
||||
console.appendLog(level, event.message)
|
||||
def onLogEntry(event: StringEvent): Unit = {
|
||||
val level = event.level match {
|
||||
case "debug" => Level.Debug
|
||||
case "info" => Level.Info
|
||||
case "warn" => Level.Warn
|
||||
case "error" => Level.Error
|
||||
}
|
||||
console.appendLog(level, event.message)
|
||||
}
|
||||
|
||||
def onEvent(event: EventMessage): Unit =
|
||||
event match {
|
||||
|
|
@ -84,41 +82,39 @@ class NetworkClient(arguments: List[String]) { self =>
|
|||
case e => println(e.toString)
|
||||
}
|
||||
|
||||
def start(): Unit =
|
||||
{
|
||||
val reader = JLine.simple(None, JLine.HandleCONT, injectThreadSleep = true)
|
||||
while (running.get) {
|
||||
reader.readLine("> ", None) match {
|
||||
case Some("exit") =>
|
||||
running.set(false)
|
||||
case Some(s) =>
|
||||
val execId = UUID.randomUUID.toString
|
||||
publishCommand(ExecCommand(s, execId))
|
||||
lock.synchronized {
|
||||
pendingExecIds += execId
|
||||
}
|
||||
while (pendingExecIds contains execId) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
case _ => //
|
||||
}
|
||||
def start(): Unit = {
|
||||
val reader = JLine.simple(None, JLine.HandleCONT, injectThreadSleep = true)
|
||||
while (running.get) {
|
||||
reader.readLine("> ", None) match {
|
||||
case Some("exit") =>
|
||||
running.set(false)
|
||||
case Some(s) =>
|
||||
val execId = UUID.randomUUID.toString
|
||||
publishCommand(ExecCommand(s, execId))
|
||||
lock.synchronized {
|
||||
pendingExecIds += execId
|
||||
}
|
||||
while (pendingExecIds contains execId) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
case _ => //
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def publishCommand(command: CommandMessage): Unit =
|
||||
{
|
||||
val bytes = Serialization.serializeCommand(command)
|
||||
try {
|
||||
connection.publish(bytes)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
// log.debug(e.getMessage)
|
||||
// toDel += client
|
||||
}
|
||||
lock.synchronized {
|
||||
status.set("Processing")
|
||||
}
|
||||
def publishCommand(command: CommandMessage): Unit = {
|
||||
val bytes = Serialization.serializeCommand(command)
|
||||
try {
|
||||
connection.publish(bytes)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
// log.debug(e.getMessage)
|
||||
// toDel += client
|
||||
}
|
||||
lock.synchronized {
|
||||
status.set("Processing")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object NetworkClient {
|
||||
|
|
|
|||
|
|
@ -35,16 +35,18 @@ abstract class ServerConnection(connection: Socket) {
|
|||
val chunk = buffer.take(delimPos)
|
||||
buffer = buffer.drop(delimPos + 1)
|
||||
|
||||
Serialization.deserializeEvent(chunk).fold(
|
||||
{ errorDesc =>
|
||||
val s = new String(chunk.toArray, "UTF-8")
|
||||
println(s"Got invalid chunk from server: $s \n" + errorDesc)
|
||||
},
|
||||
_ match {
|
||||
case event: EventMessage => onEvent(event)
|
||||
case event: StringEvent => onLogEntry(event)
|
||||
}
|
||||
)
|
||||
Serialization
|
||||
.deserializeEvent(chunk)
|
||||
.fold(
|
||||
{ errorDesc =>
|
||||
val s = new String(chunk.toArray, "UTF-8")
|
||||
println(s"Got invalid chunk from server: $s \n" + errorDesc)
|
||||
},
|
||||
_ match {
|
||||
case event: EventMessage => onEvent(event)
|
||||
case event: StringEvent => onLogEntry(event)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ private[sbt] sealed trait ServerInstance {
|
|||
}
|
||||
|
||||
private[sbt] object Server {
|
||||
def start(host: String, port: Int, onIncomingSocket: Socket => Unit,
|
||||
/*onIncomingCommand: CommandMessage => Unit,*/ log: Logger): ServerInstance =
|
||||
def start(host: String,
|
||||
port: Int,
|
||||
onIncomingSocket: Socket => Unit,
|
||||
/*onIncomingCommand: CommandMessage => Unit,*/ log: Logger): ServerInstance =
|
||||
new ServerInstance {
|
||||
|
||||
// val lock = new AnyRef {}
|
||||
|
|
|
|||
|
|
@ -16,44 +16,37 @@ object IPC {
|
|||
def client[T](port: Int)(f: IPC => T): T =
|
||||
ipc(new Socket(loopback, port))(f)
|
||||
|
||||
def pullServer[T](f: Server => T): T =
|
||||
{
|
||||
val server = makeServer
|
||||
try { f(new Server(server)) }
|
||||
finally { server.close() }
|
||||
}
|
||||
def pullServer[T](f: Server => T): T = {
|
||||
val server = makeServer
|
||||
try { f(new Server(server)) } finally { server.close() }
|
||||
}
|
||||
def unmanagedServer: Server = new Server(makeServer)
|
||||
def makeServer: ServerSocket =
|
||||
{
|
||||
val random = new java.util.Random
|
||||
def nextPort = random.nextInt(portMax - portMin + 1) + portMin
|
||||
def createServer(attempts: Int): ServerSocket =
|
||||
if (attempts > 0)
|
||||
try { new ServerSocket(nextPort, 1, loopback) }
|
||||
catch { case NonFatal(e) => createServer(attempts - 1) }
|
||||
else
|
||||
sys.error("Could not connect to socket: maximum attempts exceeded")
|
||||
createServer(10)
|
||||
}
|
||||
def makeServer: ServerSocket = {
|
||||
val random = new java.util.Random
|
||||
def nextPort = random.nextInt(portMax - portMin + 1) + portMin
|
||||
def createServer(attempts: Int): ServerSocket =
|
||||
if (attempts > 0)
|
||||
try { new ServerSocket(nextPort, 1, loopback) } catch {
|
||||
case NonFatal(e) => createServer(attempts - 1)
|
||||
} else
|
||||
sys.error("Could not connect to socket: maximum attempts exceeded")
|
||||
createServer(10)
|
||||
}
|
||||
def server[T](f: IPC => Option[T]): T = serverImpl(makeServer, f)
|
||||
def server[T](port: Int)(f: IPC => Option[T]): T =
|
||||
serverImpl(new ServerSocket(port, 1, loopback), f)
|
||||
private def serverImpl[T](server: ServerSocket, f: IPC => Option[T]): T =
|
||||
{
|
||||
def listen(): T =
|
||||
{
|
||||
ipc(server.accept())(f) match {
|
||||
case Some(done) => done
|
||||
case None => listen()
|
||||
}
|
||||
}
|
||||
|
||||
try { listen() }
|
||||
finally { server.close() }
|
||||
private def serverImpl[T](server: ServerSocket, f: IPC => Option[T]): T = {
|
||||
def listen(): T = {
|
||||
ipc(server.accept())(f) match {
|
||||
case Some(done) => done
|
||||
case None => listen()
|
||||
}
|
||||
}
|
||||
|
||||
try { listen() } finally { server.close() }
|
||||
}
|
||||
private def ipc[T](s: Socket)(f: IPC => T): T =
|
||||
try { f(new IPC(s)) }
|
||||
finally { s.close() }
|
||||
try { f(new IPC(s)) } finally { s.close() }
|
||||
|
||||
final class Server private[IPC] (s: ServerSocket) {
|
||||
def port = s.getLocalPort
|
||||
|
|
|
|||
|
|
@ -8,40 +8,54 @@ import Def.Initialize
|
|||
import reflect.internal.annotations.compileTimeOnly
|
||||
|
||||
object Append {
|
||||
@implicitNotFound(msg = "No implicit for Append.Value[${A}, ${B}] found,\n so ${B} cannot be appended to ${A}")
|
||||
@implicitNotFound(
|
||||
msg = "No implicit for Append.Value[${A}, ${B}] found,\n so ${B} cannot be appended to ${A}")
|
||||
trait Value[A, B] {
|
||||
def appendValue(a: A, b: B): A
|
||||
}
|
||||
@implicitNotFound(msg = "No implicit for Append.Values[${A}, ${B}] found,\n so ${B} cannot be appended to ${A}")
|
||||
@implicitNotFound(
|
||||
msg = "No implicit for Append.Values[${A}, ${B}] found,\n so ${B} cannot be appended to ${A}")
|
||||
trait Values[A, -B] {
|
||||
def appendValues(a: A, b: B): A
|
||||
}
|
||||
trait Sequence[A, -B, T] extends Value[A, T] with Values[A, B]
|
||||
|
||||
implicit def appendSeq[T, V <: T]: Sequence[Seq[T], Seq[V], V] = new Sequence[Seq[T], Seq[V], V] {
|
||||
def appendValues(a: Seq[T], b: Seq[V]): Seq[T] = a ++ b
|
||||
def appendValue(a: Seq[T], b: V): Seq[T] = a :+ b
|
||||
}
|
||||
implicit def appendSeqImplicit[T, V](implicit ev: V => T): Sequence[Seq[T], Seq[V], V] = new Sequence[Seq[T], Seq[V], V] {
|
||||
def appendValues(a: Seq[T], b: Seq[V]): Seq[T] = a ++ (b map { x => (x: T) })
|
||||
def appendValue(a: Seq[T], b: V): Seq[T] = a :+ (b: T)
|
||||
}
|
||||
implicit def appendSeq[T, V <: T]: Sequence[Seq[T], Seq[V], V] =
|
||||
new Sequence[Seq[T], Seq[V], V] {
|
||||
def appendValues(a: Seq[T], b: Seq[V]): Seq[T] = a ++ b
|
||||
def appendValue(a: Seq[T], b: V): Seq[T] = a :+ b
|
||||
}
|
||||
implicit def appendSeqImplicit[T, V](implicit ev: V => T): Sequence[Seq[T], Seq[V], V] =
|
||||
new Sequence[Seq[T], Seq[V], V] {
|
||||
def appendValues(a: Seq[T], b: Seq[V]): Seq[T] =
|
||||
a ++ (b map { x =>
|
||||
(x: T)
|
||||
})
|
||||
def appendValue(a: Seq[T], b: V): Seq[T] = a :+ (b: T)
|
||||
}
|
||||
@compileTimeOnly("This can be used in += only.")
|
||||
implicit def appendTaskValueSeq[T, V <: T]: Value[Seq[Task[T]], Initialize[Task[V]]] = new Value[Seq[Task[T]], Initialize[Task[V]]] {
|
||||
def appendValue(a: Seq[Task[T]], b: Initialize[Task[V]]): Seq[Task[T]] = ???
|
||||
}
|
||||
implicit def appendTaskValueSeq[T, V <: T]: Value[Seq[Task[T]], Initialize[Task[V]]] =
|
||||
new Value[Seq[Task[T]], Initialize[Task[V]]] {
|
||||
def appendValue(a: Seq[Task[T]], b: Initialize[Task[V]]): Seq[Task[T]] = ???
|
||||
}
|
||||
@compileTimeOnly("This can be used in += only.")
|
||||
implicit def appendTaskKeySeq[T, V <: T]: Value[Seq[Task[T]], TaskKey[V]] = new Value[Seq[Task[T]], TaskKey[V]] {
|
||||
def appendValue(a: Seq[Task[T]], b: TaskKey[V]): Seq[Task[T]] = ???
|
||||
}
|
||||
implicit def appendList[T, V <: T]: Sequence[List[T], List[V], V] = new Sequence[List[T], List[V], V] {
|
||||
def appendValues(a: List[T], b: List[V]): List[T] = a ::: b
|
||||
def appendValue(a: List[T], b: V): List[T] = a :+ b
|
||||
}
|
||||
implicit def appendListImplicit[T, V](implicit ev: V => T): Sequence[List[T], List[V], V] = new Sequence[List[T], List[V], V] {
|
||||
def appendValues(a: List[T], b: List[V]): List[T] = a ::: (b map { x => (x: T) })
|
||||
def appendValue(a: List[T], b: V): List[T] = a :+ (b: T)
|
||||
}
|
||||
implicit def appendTaskKeySeq[T, V <: T]: Value[Seq[Task[T]], TaskKey[V]] =
|
||||
new Value[Seq[Task[T]], TaskKey[V]] {
|
||||
def appendValue(a: Seq[Task[T]], b: TaskKey[V]): Seq[Task[T]] = ???
|
||||
}
|
||||
implicit def appendList[T, V <: T]: Sequence[List[T], List[V], V] =
|
||||
new Sequence[List[T], List[V], V] {
|
||||
def appendValues(a: List[T], b: List[V]): List[T] = a ::: b
|
||||
def appendValue(a: List[T], b: V): List[T] = a :+ b
|
||||
}
|
||||
implicit def appendListImplicit[T, V](implicit ev: V => T): Sequence[List[T], List[V], V] =
|
||||
new Sequence[List[T], List[V], V] {
|
||||
def appendValues(a: List[T], b: List[V]): List[T] =
|
||||
a ::: (b map { x =>
|
||||
(x: T)
|
||||
})
|
||||
def appendValue(a: List[T], b: V): List[T] = a :+ (b: T)
|
||||
}
|
||||
implicit def appendString: Value[String, String] = new Value[String, String] {
|
||||
def appendValue(a: String, b: String) = a + b
|
||||
}
|
||||
|
|
@ -54,18 +68,21 @@ object Append {
|
|||
implicit def appendDouble = new Value[Double, Double] {
|
||||
def appendValue(a: Double, b: Double) = a + b
|
||||
}
|
||||
implicit def appendClasspath: Sequence[Classpath, Seq[File], File] = new Sequence[Classpath, Seq[File], File] {
|
||||
def appendValues(a: Classpath, b: Seq[File]): Classpath = a ++ Attributed.blankSeq(b)
|
||||
def appendValue(a: Classpath, b: File): Classpath = a :+ Attributed.blank(b)
|
||||
}
|
||||
implicit def appendSet[T, V <: T]: Sequence[Set[T], Set[V], V] = new Sequence[Set[T], Set[V], V] {
|
||||
def appendValues(a: Set[T], b: Set[V]): Set[T] = a ++ b
|
||||
def appendValue(a: Set[T], b: V): Set[T] = a + b
|
||||
}
|
||||
implicit def appendMap[A, B, X <: A, Y <: B]: Sequence[Map[A, B], Map[X, Y], (X, Y)] = new Sequence[Map[A, B], Map[X, Y], (X, Y)] {
|
||||
def appendValues(a: Map[A, B], b: Map[X, Y]): Map[A, B] = a ++ b
|
||||
def appendValue(a: Map[A, B], b: (X, Y)): Map[A, B] = a + b
|
||||
}
|
||||
implicit def appendClasspath: Sequence[Classpath, Seq[File], File] =
|
||||
new Sequence[Classpath, Seq[File], File] {
|
||||
def appendValues(a: Classpath, b: Seq[File]): Classpath = a ++ Attributed.blankSeq(b)
|
||||
def appendValue(a: Classpath, b: File): Classpath = a :+ Attributed.blank(b)
|
||||
}
|
||||
implicit def appendSet[T, V <: T]: Sequence[Set[T], Set[V], V] =
|
||||
new Sequence[Set[T], Set[V], V] {
|
||||
def appendValues(a: Set[T], b: Set[V]): Set[T] = a ++ b
|
||||
def appendValue(a: Set[T], b: V): Set[T] = a + b
|
||||
}
|
||||
implicit def appendMap[A, B, X <: A, Y <: B]: Sequence[Map[A, B], Map[X, Y], (X, Y)] =
|
||||
new Sequence[Map[A, B], Map[X, Y], (X, Y)] {
|
||||
def appendValues(a: Map[A, B], b: Map[X, Y]): Map[A, B] = a ++ b
|
||||
def appendValue(a: Map[A, B], b: (X, Y)): Map[A, B] = a + b
|
||||
}
|
||||
implicit def appendOption[T]: Sequence[Seq[T], Option[T], Option[T]] =
|
||||
new Sequence[Seq[T], Option[T], Option[T]] {
|
||||
def appendValue(a: Seq[T], b: Option[T]): Seq[T] = b.fold(a)(a :+ _)
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@ package sbt
|
|||
|
||||
final case class ConfigKey(name: String)
|
||||
object ConfigKey {
|
||||
implicit def configurationToKey(c: sbt.librarymanagement.Configuration): ConfigKey = ConfigKey(c.name)
|
||||
implicit def configurationToKey(c: sbt.librarymanagement.Configuration): ConfigKey =
|
||||
ConfigKey(c.name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,35 +17,55 @@ object Def extends Init[Scope] with TaskMacroExtra {
|
|||
|
||||
val triggeredBy = AttributeKey[Seq[Task[_]]]("triggered-by")
|
||||
val runBefore = AttributeKey[Seq[Task[_]]]("run-before")
|
||||
val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped", "The ScopedKey for the referencing setting or task.", KeyRanks.DSetting)
|
||||
private[sbt] val taskDefinitionKey = AttributeKey[ScopedKey[_]]("task-definition-key", "Internal: used to map a task back to its ScopedKey.", Invisible)
|
||||
val resolvedScoped = SettingKey[ScopedKey[_]](
|
||||
"resolved-scoped",
|
||||
"The ScopedKey for the referencing setting or task.",
|
||||
KeyRanks.DSetting)
|
||||
private[sbt] val taskDefinitionKey = AttributeKey[ScopedKey[_]](
|
||||
"task-definition-key",
|
||||
"Internal: used to map a task back to its ScopedKey.",
|
||||
Invisible)
|
||||
|
||||
lazy val showFullKey: Show[ScopedKey[_]] = showFullKey(None)
|
||||
def showFullKey(keyNameColor: Option[String]): Show[ScopedKey[_]] =
|
||||
Show[ScopedKey[_]]((key: ScopedKey[_]) => displayFull(key, keyNameColor))
|
||||
|
||||
def showRelativeKey(current: ProjectRef, multi: Boolean, keyNameColor: Option[String] = None): Show[ScopedKey[_]] =
|
||||
Show[ScopedKey[_]]((key: ScopedKey[_]) =>
|
||||
Scope.display(key.scope, colored(key.key.label, keyNameColor), ref => displayRelative(current, multi, ref)))
|
||||
def showRelativeKey(current: ProjectRef,
|
||||
multi: Boolean,
|
||||
keyNameColor: Option[String] = None): Show[ScopedKey[_]] =
|
||||
Show[ScopedKey[_]](
|
||||
(key: ScopedKey[_]) =>
|
||||
Scope.display(key.scope,
|
||||
colored(key.key.label, keyNameColor),
|
||||
ref => displayRelative(current, multi, ref)))
|
||||
|
||||
def showBuildRelativeKey(currentBuild: URI, multi: Boolean, keyNameColor: Option[String] = None): Show[ScopedKey[_]] =
|
||||
Show[ScopedKey[_]]((key: ScopedKey[_]) =>
|
||||
Scope.display(key.scope, colored(key.key.label, keyNameColor), ref => displayBuildRelative(currentBuild, multi, ref)))
|
||||
def showBuildRelativeKey(currentBuild: URI,
|
||||
multi: Boolean,
|
||||
keyNameColor: Option[String] = None): Show[ScopedKey[_]] =
|
||||
Show[ScopedKey[_]](
|
||||
(key: ScopedKey[_]) =>
|
||||
Scope.display(key.scope,
|
||||
colored(key.key.label, keyNameColor),
|
||||
ref => displayBuildRelative(currentBuild, multi, ref)))
|
||||
|
||||
def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String = project match {
|
||||
case BuildRef(current.build) => "{.}/"
|
||||
case `current` => if (multi) current.project + "/" else ""
|
||||
case ProjectRef(current.build, x) => x + "/"
|
||||
case _ => Reference.display(project) + "/"
|
||||
}
|
||||
def displayBuildRelative(currentBuild: URI, multi: Boolean, project: Reference): String = project match {
|
||||
case BuildRef(`currentBuild`) => "{.}/"
|
||||
case ProjectRef(`currentBuild`, x) => x + "/"
|
||||
case _ => Reference.display(project) + "/"
|
||||
}
|
||||
def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String =
|
||||
project match {
|
||||
case BuildRef(current.build) => "{.}/"
|
||||
case `current` => if (multi) current.project + "/" else ""
|
||||
case ProjectRef(current.build, x) => x + "/"
|
||||
case _ => Reference.display(project) + "/"
|
||||
}
|
||||
def displayBuildRelative(currentBuild: URI, multi: Boolean, project: Reference): String =
|
||||
project match {
|
||||
case BuildRef(`currentBuild`) => "{.}/"
|
||||
case ProjectRef(`currentBuild`, x) => x + "/"
|
||||
case _ => Reference.display(project) + "/"
|
||||
}
|
||||
def displayFull(scoped: ScopedKey[_]): String = displayFull(scoped, None)
|
||||
def displayFull(scoped: ScopedKey[_], keyNameColor: Option[String]): String = Scope.display(scoped.scope, colored(scoped.key.label, keyNameColor))
|
||||
def displayMasked(scoped: ScopedKey[_], mask: ScopeMask): String = Scope.displayMasked(scoped.scope, scoped.key.label, mask)
|
||||
def displayFull(scoped: ScopedKey[_], keyNameColor: Option[String]): String =
|
||||
Scope.display(scoped.scope, colored(scoped.key.label, keyNameColor))
|
||||
def displayMasked(scoped: ScopedKey[_], mask: ScopeMask): String =
|
||||
Scope.displayMasked(scoped.scope, scoped.key.label, mask)
|
||||
|
||||
def colored(s: String, color: Option[String]): String = color match {
|
||||
case Some(c) => c + s + scala.Console.RESET
|
||||
|
|
@ -54,10 +74,16 @@ object Def extends Init[Scope] with TaskMacroExtra {
|
|||
|
||||
override def deriveAllowed[T](s: Setting[T], allowDynamic: Boolean): Option[String] =
|
||||
super.deriveAllowed(s, allowDynamic) orElse
|
||||
(if (s.key.scope != ThisScope) Some(s"Scope cannot be defined for ${definedSettingString(s)}") else None) orElse
|
||||
s.dependencies.find(k => k.scope != ThisScope).map(k => s"Scope cannot be defined for dependency ${k.key.label} of ${definedSettingString(s)}")
|
||||
(if (s.key.scope != ThisScope)
|
||||
Some(s"Scope cannot be defined for ${definedSettingString(s)}")
|
||||
else None) orElse
|
||||
s.dependencies
|
||||
.find(k => k.scope != ThisScope)
|
||||
.map(k =>
|
||||
s"Scope cannot be defined for dependency ${k.key.label} of ${definedSettingString(s)}")
|
||||
|
||||
override def intersect(s1: Scope, s2: Scope)(implicit delegates: Scope => Seq[Scope]): Option[Scope] =
|
||||
override def intersect(s1: Scope, s2: Scope)(
|
||||
implicit delegates: Scope => Seq[Scope]): Option[Scope] =
|
||||
if (s2 == GlobalScope) Some(s1) // s1 is more specific
|
||||
else if (s1 == GlobalScope) Some(s2) // s2 is more specific
|
||||
else super.intersect(s1, s2)
|
||||
|
|
@ -71,7 +97,8 @@ object Def extends Init[Scope] with TaskMacroExtra {
|
|||
* A default Parser for splitting input into space-separated arguments.
|
||||
* `argLabel` is an optional, fixed label shown for an argument during tab completion.
|
||||
*/
|
||||
def spaceDelimited(argLabel: String = "<arg>"): Parser[Seq[String]] = sbt.internal.util.complete.Parsers.spaceDelimited(argLabel)
|
||||
def spaceDelimited(argLabel: String = "<arg>"): Parser[Seq[String]] =
|
||||
sbt.internal.util.complete.Parsers.spaceDelimited(argLabel)
|
||||
|
||||
/** Lifts the result of a setting initialization into a Task. */
|
||||
def toITask[T](i: Initialize[T]): Initialize[Task[T]] = map(i)(std.TaskExtra.inlineTask)
|
||||
|
|
@ -81,7 +108,12 @@ object Def extends Init[Scope] with TaskMacroExtra {
|
|||
def toIParser[T](p: Initialize[InputTask[T]]): Initialize[State => Parser[Task[T]]] = p(_.parser)
|
||||
|
||||
import language.experimental.macros
|
||||
import std.TaskMacro.{ inputTaskMacroImpl, inputTaskDynMacroImpl, taskDynMacroImpl, taskMacroImpl }
|
||||
import std.TaskMacro.{
|
||||
inputTaskMacroImpl,
|
||||
inputTaskDynMacroImpl,
|
||||
taskDynMacroImpl,
|
||||
taskMacroImpl
|
||||
}
|
||||
import std.SettingMacro.{ settingDynMacroImpl, settingMacroImpl }
|
||||
import std.{ InputEvaluated, MacroPrevious, MacroValue, MacroTaskValue, ParserInput }
|
||||
|
||||
|
|
@ -90,7 +122,8 @@ object Def extends Init[Scope] with TaskMacroExtra {
|
|||
def setting[T](t: T): Def.Initialize[T] = macro settingMacroImpl[T]
|
||||
def settingDyn[T](t: Def.Initialize[T]): Def.Initialize[T] = macro settingDynMacroImpl[T]
|
||||
def inputTask[T](t: T): Def.Initialize[InputTask[T]] = macro inputTaskMacroImpl[T]
|
||||
def inputTaskDyn[T](t: Def.Initialize[Task[T]]): Def.Initialize[InputTask[T]] = macro inputTaskDynMacroImpl[T]
|
||||
def inputTaskDyn[T](t: Def.Initialize[Task[T]]): Def.Initialize[InputTask[T]] =
|
||||
macro inputTaskDynMacroImpl[T]
|
||||
|
||||
// The following conversions enable the types Initialize[T], Initialize[Task[T]], and Task[T] to
|
||||
// be used in task and setting macros as inputs with an ultimate result of type T
|
||||
|
|
@ -110,17 +143,24 @@ object Def extends Init[Scope] with TaskMacroExtra {
|
|||
def taskKey[T](description: String): TaskKey[T] = macro std.KeyMacro.taskKeyImpl[T]
|
||||
def inputKey[T](description: String): InputKey[T] = macro std.KeyMacro.inputKeyImpl[T]
|
||||
|
||||
private[sbt] def dummy[T: Manifest](name: String, description: String): (TaskKey[T], Task[T]) = (TaskKey[T](name, description, DTask), dummyTask(name))
|
||||
private[sbt] def dummyTask[T](name: String): Task[T] =
|
||||
{
|
||||
import std.TaskExtra.{ task => newTask, _ }
|
||||
val base: Task[T] = newTask(sys.error("Dummy task '" + name + "' did not get converted to a full task.")) named name
|
||||
base.copy(info = base.info.set(isDummyTask, true))
|
||||
}
|
||||
private[sbt] def isDummy(t: Task[_]): Boolean = t.info.attributes.get(isDummyTask) getOrElse false
|
||||
private[sbt] val isDummyTask = AttributeKey[Boolean]("is-dummy-task", "Internal: used to identify dummy tasks. sbt injects values for these tasks at the start of task execution.", Invisible)
|
||||
private[sbt] def dummy[T: Manifest](name: String, description: String): (TaskKey[T], Task[T]) =
|
||||
(TaskKey[T](name, description, DTask), dummyTask(name))
|
||||
private[sbt] def dummyTask[T](name: String): Task[T] = {
|
||||
import std.TaskExtra.{ task => newTask, _ }
|
||||
val base: Task[T] = newTask(
|
||||
sys.error("Dummy task '" + name + "' did not get converted to a full task.")) named name
|
||||
base.copy(info = base.info.set(isDummyTask, true))
|
||||
}
|
||||
private[sbt] def isDummy(t: Task[_]): Boolean =
|
||||
t.info.attributes.get(isDummyTask) getOrElse false
|
||||
private[sbt] val isDummyTask = AttributeKey[Boolean](
|
||||
"is-dummy-task",
|
||||
"Internal: used to identify dummy tasks. sbt injects values for these tasks at the start of task execution.",
|
||||
Invisible)
|
||||
private[sbt] val (stateKey, dummyState) = dummy[State]("state", "Current build state.")
|
||||
private[sbt] val (streamsManagerKey, dummyStreamsManager) = Def.dummy[std.Streams[ScopedKey[_]]]("streams-manager", "Streams manager, which provides streams for different contexts.")
|
||||
private[sbt] val (streamsManagerKey, dummyStreamsManager) = Def.dummy[std.Streams[ScopedKey[_]]](
|
||||
"streams-manager",
|
||||
"Streams manager, which provides streams for different contexts.")
|
||||
}
|
||||
// these need to be mixed into the sbt package object because the target doesn't involve Initialize or anything in Def
|
||||
trait TaskMacroExtra {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,18 @@ sealed trait DelegateIndex {
|
|||
// def extra(ref: ProjectRef, e: AttributeMap): Seq[ScopeAxis[AttributeMap]]
|
||||
}
|
||||
private final class DelegateIndex0(refs: Map[ProjectRef, ProjectDelegates]) extends DelegateIndex {
|
||||
def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] = refs.get(ref) match { case Some(pd) => pd.refs; case None => Nil }
|
||||
def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] = refs.get(ref) match {
|
||||
case Some(pd) => pd.refs; case None => Nil
|
||||
}
|
||||
def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] =
|
||||
refs.get(ref) match {
|
||||
case Some(pd) => pd.confs.get(conf) match { case Some(cs) => cs; case None => Select(conf) :: Global :: Nil }
|
||||
case None => Select(conf) :: Global :: Nil
|
||||
case Some(pd) =>
|
||||
pd.confs.get(conf) match {
|
||||
case Some(cs) => cs; case None => Select(conf) :: Global :: Nil
|
||||
}
|
||||
case None => Select(conf) :: Global :: Nil
|
||||
}
|
||||
}
|
||||
private final class ProjectDelegates(val ref: ProjectRef, val refs: Seq[ScopeAxis[ResolvedReference]], val confs: Map[ConfigKey, Seq[ScopeAxis[ConfigKey]]])
|
||||
private final class ProjectDelegates(val ref: ProjectRef,
|
||||
val refs: Seq[ScopeAxis[ResolvedReference]],
|
||||
val confs: Map[ConfigKey, Seq[ScopeAxis[ConfigKey]]])
|
||||
|
|
|
|||
|
|
@ -14,12 +14,13 @@ final class InputTask[T] private (val parser: State => Parser[Task[T]]) {
|
|||
def partialInput(in: String): InputTask[T] =
|
||||
new InputTask[T](s => Parser(parser(s))(in))
|
||||
|
||||
def fullInput(in: String): InputTask[T] = new InputTask[T](s =>
|
||||
Parser.parse(in, parser(s)) match {
|
||||
case Right(v) => Parser.success(v)
|
||||
case Left(msg) =>
|
||||
val indented = msg.lines.map(" " + _).mkString("\n")
|
||||
Parser.failure(s"Invalid programmatic input:\n$indented")
|
||||
def fullInput(in: String): InputTask[T] =
|
||||
new InputTask[T](s =>
|
||||
Parser.parse(in, parser(s)) match {
|
||||
case Right(v) => Parser.success(v)
|
||||
case Left(msg) =>
|
||||
val indented = msg.lines.map(" " + _).mkString("\n")
|
||||
Parser.failure(s"Invalid programmatic input:\n$indented")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -54,49 +55,60 @@ object InputTask {
|
|||
|
||||
def free[I, T](p: State => Parser[I])(c: I => Task[T]): InputTask[T] = free(s => p(s) map c)
|
||||
|
||||
def separate[I, T](p: State => Parser[I])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] =
|
||||
def separate[I, T](p: State => Parser[I])(
|
||||
action: Initialize[I => Task[T]]): Initialize[InputTask[T]] =
|
||||
separate(Def value p)(action)
|
||||
|
||||
def separate[I, T](p: Initialize[State => Parser[I]])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] =
|
||||
def separate[I, T](p: Initialize[State => Parser[I]])(
|
||||
action: Initialize[I => Task[T]]): Initialize[InputTask[T]] =
|
||||
p.zipWith(action)((parser, act) => free(parser)(act))
|
||||
|
||||
/** Constructs an InputTask that accepts no user input. */
|
||||
def createFree[T](action: Initialize[Task[T]]): Initialize[InputTask[T]] =
|
||||
action { tsk => free(emptyParser)(const(tsk)) }
|
||||
action { tsk =>
|
||||
free(emptyParser)(const(tsk))
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an InputTask from:
|
||||
* a) a Parser constructed using other Settings, but not Tasks
|
||||
* b) a dynamically constructed Task that uses Settings, Tasks, and the result of parsing.
|
||||
*/
|
||||
def createDyn[I, T](p: Initialize[State => Parser[I]])(action: Initialize[Task[I => Initialize[Task[T]]]]): Initialize[InputTask[T]] =
|
||||
def createDyn[I, T](p: Initialize[State => Parser[I]])(
|
||||
action: Initialize[Task[I => Initialize[Task[T]]]]): Initialize[InputTask[T]] =
|
||||
separate(p)(std.FullInstance.flattenFun[I, T](action))
|
||||
|
||||
/** A dummy parser that consumes no input and produces nothing useful (unit).*/
|
||||
def emptyParser: State => Parser[Unit] = Types.const(sbt.internal.util.complete.DefaultParsers.success(()))
|
||||
def emptyParser: State => Parser[Unit] =
|
||||
Types.const(sbt.internal.util.complete.DefaultParsers.success(()))
|
||||
|
||||
/** Implementation detail that is public because it is used by a macro.*/
|
||||
def parserAsInput[T](p: Parser[T]): Initialize[State => Parser[T]] = Def.valueStrict(Types.const(p))
|
||||
def parserAsInput[T](p: Parser[T]): Initialize[State => Parser[T]] =
|
||||
Def.valueStrict(Types.const(p))
|
||||
|
||||
/** Implementation detail that is public because it is used by a macro.*/
|
||||
def initParserAsInput[T](i: Initialize[Parser[T]]): Initialize[State => Parser[T]] = i(Types.const)
|
||||
def initParserAsInput[T](i: Initialize[Parser[T]]): Initialize[State => Parser[T]] =
|
||||
i(Types.const)
|
||||
|
||||
@deprecated("Use another InputTask constructor or the `Def.inputTask` macro.", "0.13.0")
|
||||
def apply[I, T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
|
||||
{
|
||||
val dummyKey = localKey[Task[I]]
|
||||
val (marker, dummy) = dummyTask[I]
|
||||
val it = action(TaskKey(dummyKey)) mapConstant subResultForDummy(dummyKey, dummy)
|
||||
val act = it { tsk => (value: I) => subForDummy(marker, value, tsk) }
|
||||
separate(p)(act)
|
||||
def apply[I, T](p: Initialize[State => Parser[I]])(
|
||||
action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] = {
|
||||
val dummyKey = localKey[Task[I]]
|
||||
val (marker, dummy) = dummyTask[I]
|
||||
val it = action(TaskKey(dummyKey)) mapConstant subResultForDummy(dummyKey, dummy)
|
||||
val act = it { tsk => (value: I) =>
|
||||
subForDummy(marker, value, tsk)
|
||||
}
|
||||
separate(p)(act)
|
||||
}
|
||||
|
||||
/**
|
||||
* The proper solution is to have a Manifest context bound and accept slight source incompatibility,
|
||||
* The affected InputTask construction methods are all deprecated and so it is better to keep complete
|
||||
* compatibility. Because the AttributeKey is local, it uses object equality and the manifest is not used.
|
||||
*/
|
||||
private[this] def localKey[T]: AttributeKey[T] = AttributeKey.local[Unit].asInstanceOf[AttributeKey[T]]
|
||||
private[this] def localKey[T]: AttributeKey[T] =
|
||||
AttributeKey.local[Unit].asInstanceOf[AttributeKey[T]]
|
||||
|
||||
private[this] def subResultForDummy[I](dummyKey: AttributeKey[Task[I]], dummyTask: Task[I]) =
|
||||
new (ScopedKey ~> Option) {
|
||||
|
|
@ -109,35 +121,34 @@ object InputTask {
|
|||
None
|
||||
}
|
||||
|
||||
private[this] def dummyTask[I]: (AttributeKey[Option[I]], Task[I]) =
|
||||
{
|
||||
val key = localKey[Option[I]]
|
||||
val f: () => I = () => sys.error(s"Internal sbt error: InputTask stub was not substituted properly.")
|
||||
val t: Task[I] = Task(Info[I]().set(key, None), Pure(f, false))
|
||||
(key, t)
|
||||
}
|
||||
private[this] def dummyTask[I]: (AttributeKey[Option[I]], Task[I]) = {
|
||||
val key = localKey[Option[I]]
|
||||
val f: () => I = () =>
|
||||
sys.error(s"Internal sbt error: InputTask stub was not substituted properly.")
|
||||
val t: Task[I] = Task(Info[I]().set(key, None), Pure(f, false))
|
||||
(key, t)
|
||||
}
|
||||
|
||||
private[this] def subForDummy[I, T](marker: AttributeKey[Option[I]], value: I, task: Task[T]): Task[T] =
|
||||
{
|
||||
val seen = new java.util.IdentityHashMap[Task[_], Task[_]]
|
||||
lazy val f: Task ~> Task = new (Task ~> Task) {
|
||||
def apply[A](t: Task[A]): Task[A] =
|
||||
{
|
||||
val t0 = seen.get(t)
|
||||
if (t0 == null) {
|
||||
val newAction =
|
||||
if (t.info.get(marker).isDefined)
|
||||
Pure(() => value.asInstanceOf[A], inline = true)
|
||||
else
|
||||
t.work.mapTask(f)
|
||||
val newTask = Task(t.info, newAction)
|
||||
seen.put(t, newTask)
|
||||
newTask
|
||||
} else
|
||||
t0.asInstanceOf[Task[A]]
|
||||
}
|
||||
private[this] def subForDummy[I, T](marker: AttributeKey[Option[I]],
|
||||
value: I,
|
||||
task: Task[T]): Task[T] = {
|
||||
val seen = new java.util.IdentityHashMap[Task[_], Task[_]]
|
||||
lazy val f: Task ~> Task = new (Task ~> Task) {
|
||||
def apply[A](t: Task[A]): Task[A] = {
|
||||
val t0 = seen.get(t)
|
||||
if (t0 == null) {
|
||||
val newAction =
|
||||
if (t.info.get(marker).isDefined)
|
||||
Pure(() => value.asInstanceOf[A], inline = true)
|
||||
else
|
||||
t.work.mapTask(f)
|
||||
val newTask = Task(t.info, newAction)
|
||||
seen.put(t, newTask)
|
||||
newTask
|
||||
} else
|
||||
t0.asInstanceOf[Task[A]]
|
||||
}
|
||||
f(task)
|
||||
}
|
||||
f(task)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,16 @@ import sjsonnew.JsonFormat
|
|||
*/
|
||||
private[sbt] final class Previous(streams: Streams, referenced: IMap[ScopedTaskKey, Referenced]) {
|
||||
private[this] val map = referenced.mapValues(toValue)
|
||||
private[this] def toValue = new (Referenced ~> ReferencedValue) { def apply[T](x: Referenced[T]) = new ReferencedValue(x) }
|
||||
private[this] def toValue = new (Referenced ~> ReferencedValue) {
|
||||
def apply[T](x: Referenced[T]) = new ReferencedValue(x)
|
||||
}
|
||||
|
||||
private[this] final class ReferencedValue[T](referenced: Referenced[T]) {
|
||||
import referenced.{ stamped, task }
|
||||
lazy val previousValue: Option[T] = {
|
||||
val in = streams(task).getInput(task, StreamName)
|
||||
try read(in, stamped) finally in.close()
|
||||
try read(in, stamped)
|
||||
finally in.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +31,7 @@ private[sbt] final class Previous(streams: Streams, referenced: IMap[ScopedTaskK
|
|||
}
|
||||
object Previous {
|
||||
import sjsonnew.BasicJsonProtocol.StringJsonFormat
|
||||
private[sbt]type ScopedTaskKey[T] = ScopedKey[Task[T]]
|
||||
private[sbt] type ScopedTaskKey[T] = ScopedKey[Task[T]]
|
||||
private type Streams = sbt.std.Streams[ScopedKey[_]]
|
||||
|
||||
/** The stream where the task value is persisted. */
|
||||
|
|
@ -40,8 +43,14 @@ object Previous {
|
|||
def setTask(newTask: ScopedKey[Task[T]]) = new Referenced(newTask, format)
|
||||
}
|
||||
|
||||
private[sbt] val references = SettingKey[References]("previous-references", "Collects all static references to previous values of tasks.", KeyRanks.Invisible)
|
||||
private[sbt] val cache = TaskKey[Previous]("previous-cache", "Caches previous values of tasks read from disk for the duration of a task execution.", KeyRanks.Invisible)
|
||||
private[sbt] val references = SettingKey[References](
|
||||
"previous-references",
|
||||
"Collects all static references to previous values of tasks.",
|
||||
KeyRanks.Invisible)
|
||||
private[sbt] val cache = TaskKey[Previous](
|
||||
"previous-cache",
|
||||
"Caches previous values of tasks read from disk for the duration of a task execution.",
|
||||
KeyRanks.Invisible)
|
||||
|
||||
/** Records references to previous task value. This should be completely populated after settings finish loading. */
|
||||
private[sbt] final class References {
|
||||
|
|
@ -56,20 +65,22 @@ object Previous {
|
|||
}
|
||||
|
||||
/** Persists values of tasks t where there is some task referencing it via t.previous. */
|
||||
private[sbt] def complete(referenced: References, results: RMap[Task, Result], streams: Streams): Unit =
|
||||
{
|
||||
val map = referenced.getReferences
|
||||
def impl[T](key: ScopedKey[_], result: T): Unit =
|
||||
for (i <- map.get(key.asInstanceOf[ScopedTaskKey[T]])) {
|
||||
val out = streams.apply(i.task).getOutput(StreamName)
|
||||
try write(out, i.stamped, result) finally out.close()
|
||||
}
|
||||
private[sbt] def complete(referenced: References,
|
||||
results: RMap[Task, Result],
|
||||
streams: Streams): Unit = {
|
||||
val map = referenced.getReferences
|
||||
def impl[T](key: ScopedKey[_], result: T): Unit =
|
||||
for (i <- map.get(key.asInstanceOf[ScopedTaskKey[T]])) {
|
||||
val out = streams.apply(i.task).getOutput(StreamName)
|
||||
try write(out, i.stamped, result)
|
||||
finally out.close()
|
||||
}
|
||||
|
||||
for {
|
||||
results.TPair(Task(info, _), Value(result)) <- results.toTypedSeq
|
||||
key <- info.attributes get Def.taskDefinitionKey
|
||||
} impl(key, result)
|
||||
}
|
||||
for {
|
||||
results.TPair(Task(info, _), Value(result)) <- results.toTypedSeq
|
||||
key <- info.attributes get Def.taskDefinitionKey
|
||||
} impl(key, result)
|
||||
}
|
||||
|
||||
private def read[T](input: Input, format: JsonFormat[T]): Option[T] =
|
||||
try Some(input.read()(format))
|
||||
|
|
@ -80,14 +91,13 @@ object Previous {
|
|||
catch { case e: Exception => () }
|
||||
|
||||
/** Public as a macro implementation detail. Do not call directly. */
|
||||
def runtime[T](skey: TaskKey[T])(implicit format: JsonFormat[T]): Initialize[Task[Option[T]]] =
|
||||
{
|
||||
val inputs = (cache in Global) zip Def.validated(skey, selfRefOk = true) zip (references in Global)
|
||||
inputs {
|
||||
case ((prevTask, resolved), refs) =>
|
||||
refs.recordReference(resolved, format) // always evaluated on project load
|
||||
import std.TaskExtra._
|
||||
prevTask.map(_ get resolved) // evaluated if this task is evaluated
|
||||
}
|
||||
def runtime[T](skey: TaskKey[T])(implicit format: JsonFormat[T]): Initialize[Task[Option[T]]] = {
|
||||
val inputs = (cache in Global) zip Def.validated(skey, selfRefOk = true) zip (references in Global)
|
||||
inputs {
|
||||
case ((prevTask, resolved), refs) =>
|
||||
refs.recordReference(resolved, format) // always evaluated on project load
|
||||
import std.TaskExtra._
|
||||
prevTask.map(_ get resolved) // evaluated if this task is evaluated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,21 +18,30 @@ sealed trait ResolvedReference extends Reference
|
|||
|
||||
/** Identifies a build. */
|
||||
sealed trait BuildReference extends Reference
|
||||
|
||||
/** Identifies the build for the current context. */
|
||||
final case object ThisBuild extends BuildReference
|
||||
|
||||
/** Uniquely identifies a build by a URI. */
|
||||
final case class BuildRef(build: URI) extends BuildReference with ResolvedReference
|
||||
|
||||
/** Identifies a project. */
|
||||
sealed trait ProjectReference extends Reference
|
||||
|
||||
/** Uniquely references a project by a URI and a project identifier String. */
|
||||
final case class ProjectRef(build: URI, project: String) extends ProjectReference with ResolvedReference
|
||||
final case class ProjectRef(build: URI, project: String)
|
||||
extends ProjectReference
|
||||
with ResolvedReference
|
||||
|
||||
/** Identifies a project in the current build context. */
|
||||
final case class LocalProject(project: String) extends ProjectReference
|
||||
|
||||
/** Identifies the root project in the specified build. */
|
||||
final case class RootProject(build: URI) extends ProjectReference
|
||||
|
||||
/** Identifies the root project in the current build context. */
|
||||
final case object LocalRootProject extends ProjectReference
|
||||
|
||||
/** Identifies the project for the current context. */
|
||||
final case object ThisProject extends ProjectReference
|
||||
|
||||
|
|
@ -40,18 +49,20 @@ object ProjectRef {
|
|||
def apply(base: File, id: String): ProjectRef = ProjectRef(IO toURI base, id)
|
||||
}
|
||||
object RootProject {
|
||||
|
||||
/** Reference to the root project at 'base'.*/
|
||||
def apply(base: File): RootProject = RootProject(IO toURI base)
|
||||
}
|
||||
object Reference {
|
||||
implicit val resolvedReferenceOrdering: Ordering[ResolvedReference] = new Ordering[ResolvedReference] {
|
||||
def compare(a: ResolvedReference, b: ResolvedReference): Int = (a, b) match {
|
||||
case (ba: BuildRef, bb: BuildRef) => buildRefOrdering.compare(ba, bb)
|
||||
case (pa: ProjectRef, pb: ProjectRef) => projectRefOrdering.compare(pa, pb)
|
||||
case (_: BuildRef, _: ProjectRef) => -1
|
||||
case (_: ProjectRef, _: BuildRef) => 1
|
||||
implicit val resolvedReferenceOrdering: Ordering[ResolvedReference] =
|
||||
new Ordering[ResolvedReference] {
|
||||
def compare(a: ResolvedReference, b: ResolvedReference): Int = (a, b) match {
|
||||
case (ba: BuildRef, bb: BuildRef) => buildRefOrdering.compare(ba, bb)
|
||||
case (pa: ProjectRef, pb: ProjectRef) => projectRefOrdering.compare(pa, pb)
|
||||
case (_: BuildRef, _: ProjectRef) => -1
|
||||
case (_: ProjectRef, _: BuildRef) => 1
|
||||
}
|
||||
}
|
||||
}
|
||||
implicit val buildRefOrdering: Ordering[BuildRef] = new Ordering[BuildRef] {
|
||||
def compare(a: BuildRef, b: BuildRef): Int = a.build compareTo b.build
|
||||
}
|
||||
|
|
@ -87,6 +98,7 @@ object Reference {
|
|||
case BuildRef(b) => b
|
||||
case ProjectRef(b, _) => b
|
||||
}
|
||||
|
||||
/** Extracts the build URI from a Reference if one has been explicitly defined.*/
|
||||
def uri(ref: Reference): Option[URI] = ref match {
|
||||
case RootProject(b) => Some(b)
|
||||
|
|
|
|||
|
|
@ -3,20 +3,23 @@ package sbt
|
|||
import scala.annotation.implicitNotFound
|
||||
|
||||
object Remove {
|
||||
@implicitNotFound(msg = "No implicit for Remove.Value[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}")
|
||||
@implicitNotFound(
|
||||
msg = "No implicit for Remove.Value[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}")
|
||||
trait Value[A, B] extends Any {
|
||||
def removeValue(a: A, b: B): A
|
||||
}
|
||||
@implicitNotFound(msg = "No implicit for Remove.Values[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}")
|
||||
@implicitNotFound(msg =
|
||||
"No implicit for Remove.Values[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}")
|
||||
trait Values[A, -B] extends Any {
|
||||
def removeValues(a: A, b: B): A
|
||||
}
|
||||
trait Sequence[A, -B, T] extends Value[A, T] with Values[A, B]
|
||||
|
||||
implicit def removeSeq[T, V <: T]: Sequence[Seq[T], Seq[V], V] = new Sequence[Seq[T], Seq[V], V] {
|
||||
def removeValue(a: Seq[T], b: V): Seq[T] = a filterNot b.==
|
||||
def removeValues(a: Seq[T], b: Seq[V]): Seq[T] = a diff b
|
||||
}
|
||||
implicit def removeSeq[T, V <: T]: Sequence[Seq[T], Seq[V], V] =
|
||||
new Sequence[Seq[T], Seq[V], V] {
|
||||
def removeValue(a: Seq[T], b: V): Seq[T] = a filterNot b.==
|
||||
def removeValues(a: Seq[T], b: Seq[V]): Seq[T] = a diff b
|
||||
}
|
||||
implicit def removeOption[T]: Sequence[Seq[T], Option[T], Option[T]] =
|
||||
new Sequence[Seq[T], Option[T], Option[T]] {
|
||||
def removeValue(a: Seq[T], b: Option[T]): Seq[T] = b.fold(a)(a filterNot _.==)
|
||||
|
|
|
|||
|
|
@ -9,11 +9,18 @@ import sbt.internal.util.{ AttributeKey, AttributeMap, Dag }
|
|||
|
||||
import sbt.io.IO
|
||||
|
||||
final case class Scope(project: ScopeAxis[Reference], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], extra: ScopeAxis[AttributeMap]) {
|
||||
def in(project: Reference, config: ConfigKey): Scope = copy(project = Select(project), config = Select(config))
|
||||
def in(config: ConfigKey, task: AttributeKey[_]): Scope = copy(config = Select(config), task = Select(task))
|
||||
def in(project: Reference, task: AttributeKey[_]): Scope = copy(project = Select(project), task = Select(task))
|
||||
def in(project: Reference, config: ConfigKey, task: AttributeKey[_]): Scope = copy(project = Select(project), config = Select(config), task = Select(task))
|
||||
final case class Scope(project: ScopeAxis[Reference],
|
||||
config: ScopeAxis[ConfigKey],
|
||||
task: ScopeAxis[AttributeKey[_]],
|
||||
extra: ScopeAxis[AttributeMap]) {
|
||||
def in(project: Reference, config: ConfigKey): Scope =
|
||||
copy(project = Select(project), config = Select(config))
|
||||
def in(config: ConfigKey, task: AttributeKey[_]): Scope =
|
||||
copy(config = Select(config), task = Select(task))
|
||||
def in(project: Reference, task: AttributeKey[_]): Scope =
|
||||
copy(project = Select(project), task = Select(task))
|
||||
def in(project: Reference, config: ConfigKey, task: AttributeKey[_]): Scope =
|
||||
copy(project = Select(project), config = Select(config), task = Select(task))
|
||||
def in(project: Reference): Scope = copy(project = Select(project))
|
||||
def in(config: ConfigKey): Scope = copy(config = Select(config))
|
||||
def in(task: AttributeKey[_]): Scope = copy(task = Select(task))
|
||||
|
|
@ -28,8 +35,14 @@ object Scope {
|
|||
def resolveBuildScope(thisScope: Scope, current: URI): Scope => Scope =
|
||||
buildResolve(current) compose replaceThis(thisScope) compose subThisProject
|
||||
|
||||
def replaceThis(thisScope: Scope): Scope => Scope = (scope: Scope) =>
|
||||
Scope(subThis(thisScope.project, scope.project), subThis(thisScope.config, scope.config), subThis(thisScope.task, scope.task), subThis(thisScope.extra, scope.extra))
|
||||
def replaceThis(thisScope: Scope): Scope => Scope =
|
||||
(scope: Scope) =>
|
||||
Scope(
|
||||
subThis(thisScope.project, scope.project),
|
||||
subThis(thisScope.config, scope.config),
|
||||
subThis(thisScope.task, scope.task),
|
||||
subThis(thisScope.extra, scope.extra)
|
||||
)
|
||||
|
||||
def subThis[T](sub: ScopeAxis[T], into: ScopeAxis[T]): ScopeAxis[T] =
|
||||
if (into == This) sub else into
|
||||
|
|
@ -49,11 +62,10 @@ object Scope {
|
|||
case _ => scope.copy(task = Select(key))
|
||||
}
|
||||
|
||||
def mapReference(f: Reference => Reference): Scope => Scope =
|
||||
{
|
||||
case Scope(Select(ref), a, b, c) => Scope(Select(f(ref)), a, b, c)
|
||||
case x => x
|
||||
}
|
||||
def mapReference(f: Reference => Reference): Scope => Scope = {
|
||||
case Scope(Select(ref), a, b, c) => Scope(Select(f(ref)), a, b, c)
|
||||
case x => x
|
||||
}
|
||||
def resolveProject(uri: URI, rootProject: URI => String): Scope => Scope =
|
||||
mapReference(ref => resolveReference(uri, rootProject, ref))
|
||||
def buildResolve(uri: URI): Scope => Scope =
|
||||
|
|
@ -75,7 +87,8 @@ object Scope {
|
|||
case LocalProject(id) => ProjectRef(current, id)
|
||||
case RootProject(uri) => RootProject(resolveBuild(current, uri))
|
||||
case ProjectRef(uri, id) => ProjectRef(resolveBuild(current, uri), id)
|
||||
case ThisProject => RootProject(current) // Is this right? It was an inexhaustive match before..
|
||||
case ThisProject =>
|
||||
RootProject(current) // Is this right? It was an inexhaustive match before..
|
||||
}
|
||||
def resolveBuild(current: URI, uri: URI): URI =
|
||||
if (!uri.isAbsolute && current.isOpaque && uri.getSchemeSpecificPart == ".")
|
||||
|
|
@ -83,20 +96,25 @@ object Scope {
|
|||
else
|
||||
IO.directoryURI(current resolve uri)
|
||||
|
||||
def resolveReference(current: URI, rootProject: URI => String, ref: Reference): ResolvedReference =
|
||||
def resolveReference(current: URI,
|
||||
rootProject: URI => String,
|
||||
ref: Reference): ResolvedReference =
|
||||
ref match {
|
||||
case br: BuildReference => resolveBuildRef(current, br)
|
||||
case pr: ProjectReference => resolveProjectRef(current, rootProject, pr)
|
||||
}
|
||||
|
||||
def resolveProjectRef(current: URI, rootProject: URI => String, ref: ProjectReference): ProjectRef =
|
||||
def resolveProjectRef(current: URI,
|
||||
rootProject: URI => String,
|
||||
ref: ProjectReference): ProjectRef =
|
||||
ref match {
|
||||
case LocalRootProject => ProjectRef(current, rootProject(current))
|
||||
case LocalProject(id) => ProjectRef(current, id)
|
||||
case RootProject(uri) =>
|
||||
val res = resolveBuild(current, uri); ProjectRef(res, rootProject(res))
|
||||
case ProjectRef(uri, id) => ProjectRef(resolveBuild(current, uri), id)
|
||||
case ThisProject => ProjectRef(current, rootProject(current)) // Is this right? It was an inexhaustive match before..
|
||||
case ThisProject =>
|
||||
ProjectRef(current, rootProject(current)) // Is this right? It was an inexhaustive match before..
|
||||
}
|
||||
def resolveBuildRef(current: URI, ref: BuildReference): BuildRef =
|
||||
ref match {
|
||||
|
|
@ -105,18 +123,23 @@ object Scope {
|
|||
}
|
||||
|
||||
def display(config: ConfigKey): String = config.name + ":"
|
||||
def display(scope: Scope, sep: String): String = displayMasked(scope, sep, showProject, ScopeMask())
|
||||
def displayMasked(scope: Scope, sep: String, mask: ScopeMask): String = displayMasked(scope, sep, showProject, mask)
|
||||
def display(scope: Scope, sep: String, showProject: Reference => String): String = displayMasked(scope, sep, showProject, ScopeMask())
|
||||
def displayMasked(scope: Scope, sep: String, showProject: Reference => String, mask: ScopeMask): String =
|
||||
{
|
||||
import scope.{ project, config, task, extra }
|
||||
val configPrefix = config.foldStrict(display, "*:", ".:")
|
||||
val taskPrefix = task.foldStrict(_.label + "::", "", ".::")
|
||||
val extras = extra.foldStrict(_.entries.map(_.toString).toList, Nil, Nil)
|
||||
val postfix = if (extras.isEmpty) "" else extras.mkString("(", ", ", ")")
|
||||
mask.concatShow(projectPrefix(project, showProject), configPrefix, taskPrefix, sep, postfix)
|
||||
}
|
||||
def display(scope: Scope, sep: String): String =
|
||||
displayMasked(scope, sep, showProject, ScopeMask())
|
||||
def displayMasked(scope: Scope, sep: String, mask: ScopeMask): String =
|
||||
displayMasked(scope, sep, showProject, mask)
|
||||
def display(scope: Scope, sep: String, showProject: Reference => String): String =
|
||||
displayMasked(scope, sep, showProject, ScopeMask())
|
||||
def displayMasked(scope: Scope,
|
||||
sep: String,
|
||||
showProject: Reference => String,
|
||||
mask: ScopeMask): String = {
|
||||
import scope.{ project, config, task, extra }
|
||||
val configPrefix = config.foldStrict(display, "*:", ".:")
|
||||
val taskPrefix = task.foldStrict(_.label + "::", "", ".::")
|
||||
val extras = extra.foldStrict(_.entries.map(_.toString).toList, Nil, Nil)
|
||||
val postfix = if (extras.isEmpty) "" else extras.mkString("(", ", ", ")")
|
||||
mask.concatShow(projectPrefix(project, showProject), configPrefix, taskPrefix, sep, postfix)
|
||||
}
|
||||
|
||||
def equal(a: Scope, b: Scope, mask: ScopeMask): Boolean =
|
||||
(!mask.project || a.project == b.project) &&
|
||||
|
|
@ -124,106 +147,121 @@ object Scope {
|
|||
(!mask.task || a.task == b.task) &&
|
||||
(!mask.extra || a.extra == b.extra)
|
||||
|
||||
def projectPrefix(project: ScopeAxis[Reference], show: Reference => String = showProject): String = project.foldStrict(show, "*/", "./")
|
||||
def projectPrefix(project: ScopeAxis[Reference],
|
||||
show: Reference => String = showProject): String =
|
||||
project.foldStrict(show, "*/", "./")
|
||||
def showProject = (ref: Reference) => Reference.display(ref) + "/"
|
||||
|
||||
def transformTaskName(s: String) =
|
||||
{
|
||||
val parts = s.split("-+")
|
||||
(parts.take(1) ++ parts.drop(1).map(_.capitalize)).mkString
|
||||
}
|
||||
def transformTaskName(s: String) = {
|
||||
val parts = s.split("-+")
|
||||
(parts.take(1) ++ parts.drop(1).map(_.capitalize)).mkString
|
||||
}
|
||||
|
||||
// *Inherit functions should be immediate delegates and not include argument itself. Transitivity will be provided by this method
|
||||
def delegates[Proj](
|
||||
refs: Seq[(ProjectRef, Proj)],
|
||||
configurations: Proj => Seq[ConfigKey],
|
||||
resolve: Reference => ResolvedReference,
|
||||
rootProject: URI => String,
|
||||
projectInherit: ProjectRef => Seq[ProjectRef],
|
||||
configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey],
|
||||
taskInherit: AttributeKey[_] => Seq[AttributeKey[_]],
|
||||
extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap]
|
||||
): Scope => Seq[Scope] =
|
||||
{
|
||||
val index = delegates(refs, configurations, projectInherit, configInherit)
|
||||
scope => indexedDelegates(resolve, index, rootProject, taskInherit, extraInherit)(scope)
|
||||
}
|
||||
refs: Seq[(ProjectRef, Proj)],
|
||||
configurations: Proj => Seq[ConfigKey],
|
||||
resolve: Reference => ResolvedReference,
|
||||
rootProject: URI => String,
|
||||
projectInherit: ProjectRef => Seq[ProjectRef],
|
||||
configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey],
|
||||
taskInherit: AttributeKey[_] => Seq[AttributeKey[_]],
|
||||
extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap]
|
||||
): Scope => Seq[Scope] = {
|
||||
val index = delegates(refs, configurations, projectInherit, configInherit)
|
||||
scope =>
|
||||
indexedDelegates(resolve, index, rootProject, taskInherit, extraInherit)(scope)
|
||||
}
|
||||
|
||||
def indexedDelegates(
|
||||
resolve: Reference => ResolvedReference,
|
||||
index: DelegateIndex,
|
||||
rootProject: URI => String,
|
||||
taskInherit: AttributeKey[_] => Seq[AttributeKey[_]],
|
||||
extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap]
|
||||
)(rawScope: Scope): Seq[Scope] =
|
||||
{
|
||||
val scope = Scope.replaceThis(GlobalScope)(rawScope)
|
||||
resolve: Reference => ResolvedReference,
|
||||
index: DelegateIndex,
|
||||
rootProject: URI => String,
|
||||
taskInherit: AttributeKey[_] => Seq[AttributeKey[_]],
|
||||
extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap]
|
||||
)(rawScope: Scope): Seq[Scope] = {
|
||||
val scope = Scope.replaceThis(GlobalScope)(rawScope)
|
||||
|
||||
def nonProjectScopes(resolvedProj: ResolvedReference)(px: ScopeAxis[ResolvedReference]) =
|
||||
{
|
||||
val p = px.toOption getOrElse resolvedProj
|
||||
val configProj = p match { case pr: ProjectRef => pr; case br: BuildRef => ProjectRef(br.build, rootProject(br.build)) }
|
||||
val cLin = scope.config match { case Select(conf) => index.config(configProj, conf); case _ => withGlobalAxis(scope.config) }
|
||||
val tLin = scope.task match { case t @ Select(task) => linearize(t)(taskInherit); case _ => withGlobalAxis(scope.task) }
|
||||
val eLin = withGlobalAxis(scope.extra)
|
||||
for (c <- cLin; t <- tLin; e <- eLin) yield Scope(px, c, t, e)
|
||||
}
|
||||
scope.project match {
|
||||
case Global | This => globalProjectDelegates(scope)
|
||||
case Select(proj) =>
|
||||
val resolvedProj = resolve(proj)
|
||||
val projAxes: Seq[ScopeAxis[ResolvedReference]] =
|
||||
resolvedProj match {
|
||||
case pr: ProjectRef => index.project(pr)
|
||||
case br: BuildRef => Select(br) :: Global :: Nil
|
||||
}
|
||||
projAxes flatMap nonProjectScopes(resolvedProj)
|
||||
def nonProjectScopes(resolvedProj: ResolvedReference)(px: ScopeAxis[ResolvedReference]) = {
|
||||
val p = px.toOption getOrElse resolvedProj
|
||||
val configProj = p match {
|
||||
case pr: ProjectRef => pr; case br: BuildRef => ProjectRef(br.build, rootProject(br.build))
|
||||
}
|
||||
val cLin = scope.config match {
|
||||
case Select(conf) => index.config(configProj, conf); case _ => withGlobalAxis(scope.config)
|
||||
}
|
||||
val tLin = scope.task match {
|
||||
case t @ Select(task) => linearize(t)(taskInherit); case _ => withGlobalAxis(scope.task)
|
||||
}
|
||||
val eLin = withGlobalAxis(scope.extra)
|
||||
for (c <- cLin; t <- tLin; e <- eLin) yield Scope(px, c, t, e)
|
||||
}
|
||||
scope.project match {
|
||||
case Global | This => globalProjectDelegates(scope)
|
||||
case Select(proj) =>
|
||||
val resolvedProj = resolve(proj)
|
||||
val projAxes: Seq[ScopeAxis[ResolvedReference]] =
|
||||
resolvedProj match {
|
||||
case pr: ProjectRef => index.project(pr)
|
||||
case br: BuildRef => Select(br) :: Global :: Nil
|
||||
}
|
||||
projAxes flatMap nonProjectScopes(resolvedProj)
|
||||
}
|
||||
}
|
||||
|
||||
def withGlobalAxis[T](base: ScopeAxis[T]): Seq[ScopeAxis[T]] = if (base.isSelect) base :: Global :: Nil else Global :: Nil
|
||||
def withGlobalScope(base: Scope): Seq[Scope] = if (base == GlobalScope) GlobalScope :: Nil else base :: GlobalScope :: Nil
|
||||
def withGlobalAxis[T](base: ScopeAxis[T]): Seq[ScopeAxis[T]] =
|
||||
if (base.isSelect) base :: Global :: Nil else Global :: Nil
|
||||
def withGlobalScope(base: Scope): Seq[Scope] =
|
||||
if (base == GlobalScope) GlobalScope :: Nil else base :: GlobalScope :: Nil
|
||||
def withRawBuilds(ps: Seq[ScopeAxis[ProjectRef]]): Seq[ScopeAxis[ResolvedReference]] =
|
||||
ps ++ (ps flatMap rawBuild).distinct :+ Global
|
||||
|
||||
def rawBuild(ps: ScopeAxis[ProjectRef]): Seq[ScopeAxis[BuildRef]] = ps match { case Select(ref) => Select(BuildRef(ref.build)) :: Nil; case _ => Nil }
|
||||
def rawBuild(ps: ScopeAxis[ProjectRef]): Seq[ScopeAxis[BuildRef]] = ps match {
|
||||
case Select(ref) => Select(BuildRef(ref.build)) :: Nil; case _ => Nil
|
||||
}
|
||||
|
||||
def delegates[Proj](
|
||||
refs: Seq[(ProjectRef, Proj)],
|
||||
configurations: Proj => Seq[ConfigKey],
|
||||
projectInherit: ProjectRef => Seq[ProjectRef],
|
||||
configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey]
|
||||
): DelegateIndex =
|
||||
{
|
||||
val pDelegates = refs map {
|
||||
case (ref, project) =>
|
||||
(ref, delegateIndex(ref, configurations(project))(projectInherit, configInherit))
|
||||
} toMap;
|
||||
new DelegateIndex0(pDelegates)
|
||||
refs: Seq[(ProjectRef, Proj)],
|
||||
configurations: Proj => Seq[ConfigKey],
|
||||
projectInherit: ProjectRef => Seq[ProjectRef],
|
||||
configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey]
|
||||
): DelegateIndex = {
|
||||
val pDelegates = refs map {
|
||||
case (ref, project) =>
|
||||
(ref, delegateIndex(ref, configurations(project))(projectInherit, configInherit))
|
||||
} toMap;
|
||||
new DelegateIndex0(pDelegates)
|
||||
}
|
||||
private[this] def delegateIndex(ref: ProjectRef, confs: Seq[ConfigKey])(
|
||||
projectInherit: ProjectRef => Seq[ProjectRef],
|
||||
configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey]): ProjectDelegates = {
|
||||
val refDelegates = withRawBuilds(linearize(Select(ref), false)(projectInherit))
|
||||
val configs = confs map { c =>
|
||||
axisDelegates(configInherit, ref, c)
|
||||
}
|
||||
private[this] def delegateIndex(ref: ProjectRef, confs: Seq[ConfigKey])(projectInherit: ProjectRef => Seq[ProjectRef], configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey]): ProjectDelegates =
|
||||
{
|
||||
val refDelegates = withRawBuilds(linearize(Select(ref), false)(projectInherit))
|
||||
val configs = confs map { c => axisDelegates(configInherit, ref, c) }
|
||||
new ProjectDelegates(ref, refDelegates, configs.toMap)
|
||||
}
|
||||
def axisDelegates[T](direct: (ResolvedReference, T) => Seq[T], ref: ResolvedReference, init: T): (T, Seq[ScopeAxis[T]]) =
|
||||
new ProjectDelegates(ref, refDelegates, configs.toMap)
|
||||
}
|
||||
def axisDelegates[T](direct: (ResolvedReference, T) => Seq[T],
|
||||
ref: ResolvedReference,
|
||||
init: T): (T, Seq[ScopeAxis[T]]) =
|
||||
(init, linearize(Select(init))(direct(ref, _)))
|
||||
|
||||
def linearize[T](axis: ScopeAxis[T], appendGlobal: Boolean = true)(inherit: T => Seq[T]): Seq[ScopeAxis[T]] =
|
||||
def linearize[T](axis: ScopeAxis[T], appendGlobal: Boolean = true)(
|
||||
inherit: T => Seq[T]): Seq[ScopeAxis[T]] =
|
||||
axis match {
|
||||
case Select(x) => topologicalSort[T](x, appendGlobal)(inherit)
|
||||
case Global | This => if (appendGlobal) Global :: Nil else Nil
|
||||
}
|
||||
|
||||
def topologicalSort[T](node: T, appendGlobal: Boolean)(dependencies: T => Seq[T]): Seq[ScopeAxis[T]] =
|
||||
{
|
||||
val o = Dag.topologicalSortUnchecked(node)(dependencies).map(Select.apply)
|
||||
if (appendGlobal) o ::: Global :: Nil else o
|
||||
}
|
||||
def topologicalSort[T](node: T, appendGlobal: Boolean)(
|
||||
dependencies: T => Seq[T]): Seq[ScopeAxis[T]] = {
|
||||
val o = Dag.topologicalSortUnchecked(node)(dependencies).map(Select.apply)
|
||||
if (appendGlobal) o ::: Global :: Nil else o
|
||||
}
|
||||
def globalProjectDelegates(scope: Scope): Seq[Scope] =
|
||||
if (scope == GlobalScope)
|
||||
GlobalScope :: Nil
|
||||
else
|
||||
for (c <- withGlobalAxis(scope.config); t <- withGlobalAxis(scope.task); e <- withGlobalAxis(scope.extra)) yield Scope(Global, c, t, e)
|
||||
for (c <- withGlobalAxis(scope.config); t <- withGlobalAxis(scope.task);
|
||||
e <- withGlobalAxis(scope.extra)) yield Scope(Global, c, t, e)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
package sbt
|
||||
|
||||
/** Specifies the Scope axes that should be used for an operation. `true` indicates an axis should be used. */
|
||||
final case class ScopeMask(project: Boolean = true, config: Boolean = true, task: Boolean = true, extra: Boolean = true) {
|
||||
def concatShow(p: String, c: String, t: String, sep: String, x: String): String =
|
||||
{
|
||||
val sb = new StringBuilder
|
||||
if (project) sb.append(p)
|
||||
if (config) sb.append(c)
|
||||
if (task) sb.append(t)
|
||||
sb.append(sep)
|
||||
if (extra) sb.append(x)
|
||||
sb.toString
|
||||
}
|
||||
final case class ScopeMask(project: Boolean = true,
|
||||
config: Boolean = true,
|
||||
task: Boolean = true,
|
||||
extra: Boolean = true) {
|
||||
def concatShow(p: String, c: String, t: String, sep: String, x: String): String = {
|
||||
val sb = new StringBuilder
|
||||
if (project) sb.append(p)
|
||||
if (config) sb.append(c)
|
||||
if (task) sb.append(t)
|
||||
sb.append(sep)
|
||||
if (extra) sb.append(x)
|
||||
sb.toString
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import sbt.Def.{ Initialize, KeyedInitialize, ScopedKey, Setting, setting }
|
|||
import std.TaskExtra.{ task => mktask, _ }
|
||||
|
||||
/** An abstraction on top of Settings for build configuration and task definition. */
|
||||
|
||||
sealed trait Scoped { def scope: Scope; val key: AttributeKey[_] }
|
||||
|
||||
/** A common type for SettingKey and TaskKey so that both can be used as inputs to tasks.*/
|
||||
|
|
@ -27,30 +26,46 @@ sealed trait ScopedTaskable[T] extends Scoped {
|
|||
* The name and the type are represented by a value of type `AttributeKey[T]`.
|
||||
* Instances are constructed using the companion object.
|
||||
*/
|
||||
sealed abstract class SettingKey[T] extends ScopedTaskable[T] with KeyedInitialize[T] with Scoped.ScopingSetting[SettingKey[T]] with Scoped.DefinableSetting[T] {
|
||||
sealed abstract class SettingKey[T]
|
||||
extends ScopedTaskable[T]
|
||||
with KeyedInitialize[T]
|
||||
with Scoped.ScopingSetting[SettingKey[T]]
|
||||
with Scoped.DefinableSetting[T] {
|
||||
val key: AttributeKey[T]
|
||||
final def toTask: Initialize[Task[T]] = this apply inlineTask
|
||||
final def scopedKey: ScopedKey[T] = ScopedKey(scope, key)
|
||||
final def in(scope: Scope): SettingKey[T] = Scoped.scopedSetting(Scope.replaceThis(this.scope)(scope), this.key)
|
||||
final def in(scope: Scope): SettingKey[T] =
|
||||
Scoped.scopedSetting(Scope.replaceThis(this.scope)(scope), this.key)
|
||||
|
||||
final def :=(v: T): Setting[T] = macro std.TaskMacro.settingAssignMacroImpl[T]
|
||||
final def +=[U](v: U)(implicit a: Append.Value[T, U]): Setting[T] = macro std.TaskMacro.settingAppend1Impl[T, U]
|
||||
final def ++=[U](vs: U)(implicit a: Append.Values[T, U]): Setting[T] = macro std.TaskMacro.settingAppendNImpl[T, U]
|
||||
final def <+=[V](v: Initialize[V])(implicit a: Append.Value[T, V]): Setting[T] = macro std.TaskMacro.fakeSettingAppend1Position[T, V]
|
||||
final def <++=[V](vs: Initialize[V])(implicit a: Append.Values[T, V]): Setting[T] = macro std.TaskMacro.fakeSettingAppendNPosition[T, V]
|
||||
final def -=[U](v: U)(implicit r: Remove.Value[T, U]): Setting[T] = macro std.TaskMacro.settingRemove1Impl[T, U]
|
||||
final def --=[U](vs: U)(implicit r: Remove.Values[T, U]): Setting[T] = macro std.TaskMacro.settingRemoveNImpl[T, U]
|
||||
final def +=[U](v: U)(implicit a: Append.Value[T, U]): Setting[T] =
|
||||
macro std.TaskMacro.settingAppend1Impl[T, U]
|
||||
final def ++=[U](vs: U)(implicit a: Append.Values[T, U]): Setting[T] =
|
||||
macro std.TaskMacro.settingAppendNImpl[T, U]
|
||||
final def <+=[V](v: Initialize[V])(implicit a: Append.Value[T, V]): Setting[T] =
|
||||
macro std.TaskMacro.fakeSettingAppend1Position[T, V]
|
||||
final def <++=[V](vs: Initialize[V])(implicit a: Append.Values[T, V]): Setting[T] =
|
||||
macro std.TaskMacro.fakeSettingAppendNPosition[T, V]
|
||||
final def -=[U](v: U)(implicit r: Remove.Value[T, U]): Setting[T] =
|
||||
macro std.TaskMacro.settingRemove1Impl[T, U]
|
||||
final def --=[U](vs: U)(implicit r: Remove.Values[T, U]): Setting[T] =
|
||||
macro std.TaskMacro.settingRemoveNImpl[T, U]
|
||||
final def ~=(f: T => T): Setting[T] = macro std.TaskMacro.settingTransformPosition[T]
|
||||
|
||||
final def append1[V](v: Initialize[V], source: SourcePosition)(implicit a: Append.Value[T, V]): Setting[T] = make(v, source)(a.appendValue)
|
||||
final def appendN[V](vs: Initialize[V], source: SourcePosition)(implicit a: Append.Values[T, V]): Setting[T] = make(vs, source)(a.appendValues)
|
||||
final def append1[V](v: Initialize[V], source: SourcePosition)(
|
||||
implicit a: Append.Value[T, V]): Setting[T] = make(v, source)(a.appendValue)
|
||||
final def appendN[V](vs: Initialize[V], source: SourcePosition)(
|
||||
implicit a: Append.Values[T, V]): Setting[T] = make(vs, source)(a.appendValues)
|
||||
|
||||
final def remove1[V](v: Initialize[V], source: SourcePosition)(implicit r: Remove.Value[T, V]): Setting[T] = make(v, source)(r.removeValue)
|
||||
final def removeN[V](vs: Initialize[V], source: SourcePosition)(implicit r: Remove.Values[T, V]): Setting[T] = make(vs, source)(r.removeValues)
|
||||
final def remove1[V](v: Initialize[V], source: SourcePosition)(
|
||||
implicit r: Remove.Value[T, V]): Setting[T] = make(v, source)(r.removeValue)
|
||||
final def removeN[V](vs: Initialize[V], source: SourcePosition)(
|
||||
implicit r: Remove.Values[T, V]): Setting[T] = make(vs, source)(r.removeValues)
|
||||
|
||||
final def transform(f: T => T, source: SourcePosition): Setting[T] = set(scopedKey(f), source)
|
||||
|
||||
protected[this] def make[S](other: Initialize[S], source: SourcePosition)(f: (T, S) => T): Setting[T] =
|
||||
protected[this] def make[S](other: Initialize[S], source: SourcePosition)(
|
||||
f: (T, S) => T): Setting[T] =
|
||||
set((this, other)(f), source)
|
||||
}
|
||||
|
||||
|
|
@ -60,26 +75,42 @@ sealed abstract class SettingKey[T] extends ScopedTaskable[T] with KeyedInitiali
|
|||
* The name and the type are represented by a value of type `AttributeKey[Task[T]]`.
|
||||
* Instances are constructed using the companion object.
|
||||
*/
|
||||
sealed abstract class TaskKey[T] extends ScopedTaskable[T] with KeyedInitialize[Task[T]] with Scoped.ScopingSetting[TaskKey[T]] with Scoped.DefinableTask[T] {
|
||||
sealed abstract class TaskKey[T]
|
||||
extends ScopedTaskable[T]
|
||||
with KeyedInitialize[Task[T]]
|
||||
with Scoped.ScopingSetting[TaskKey[T]]
|
||||
with Scoped.DefinableTask[T] {
|
||||
val key: AttributeKey[Task[T]]
|
||||
def toTask: Initialize[Task[T]] = this
|
||||
def scopedKey: ScopedKey[Task[T]] = ScopedKey(scope, key)
|
||||
def in(scope: Scope): TaskKey[T] = Scoped.scopedTask(Scope.replaceThis(this.scope)(scope), this.key)
|
||||
def in(scope: Scope): TaskKey[T] =
|
||||
Scoped.scopedTask(Scope.replaceThis(this.scope)(scope), this.key)
|
||||
|
||||
def +=[U](v: U)(implicit a: Append.Value[T, U]): Setting[Task[T]] = macro std.TaskMacro.taskAppend1Impl[T, U]
|
||||
def ++=[U](vs: U)(implicit a: Append.Values[T, U]): Setting[Task[T]] = macro std.TaskMacro.taskAppendNImpl[T, U]
|
||||
def <+=[V](v: Initialize[Task[V]])(implicit a: Append.Value[T, V]): Setting[Task[T]] = macro std.TaskMacro.fakeTaskAppend1Position[T, V]
|
||||
def <++=[V](vs: Initialize[Task[V]])(implicit a: Append.Values[T, V]): Setting[Task[T]] = macro std.TaskMacro.fakeTaskAppendNPosition[T, V]
|
||||
final def -=[U](v: U)(implicit r: Remove.Value[T, U]): Setting[Task[T]] = macro std.TaskMacro.taskRemove1Impl[T, U]
|
||||
final def --=[U](vs: U)(implicit r: Remove.Values[T, U]): Setting[Task[T]] = macro std.TaskMacro.taskRemoveNImpl[T, U]
|
||||
def +=[U](v: U)(implicit a: Append.Value[T, U]): Setting[Task[T]] =
|
||||
macro std.TaskMacro.taskAppend1Impl[T, U]
|
||||
def ++=[U](vs: U)(implicit a: Append.Values[T, U]): Setting[Task[T]] =
|
||||
macro std.TaskMacro.taskAppendNImpl[T, U]
|
||||
def <+=[V](v: Initialize[Task[V]])(implicit a: Append.Value[T, V]): Setting[Task[T]] =
|
||||
macro std.TaskMacro.fakeTaskAppend1Position[T, V]
|
||||
def <++=[V](vs: Initialize[Task[V]])(implicit a: Append.Values[T, V]): Setting[Task[T]] =
|
||||
macro std.TaskMacro.fakeTaskAppendNPosition[T, V]
|
||||
final def -=[U](v: U)(implicit r: Remove.Value[T, U]): Setting[Task[T]] =
|
||||
macro std.TaskMacro.taskRemove1Impl[T, U]
|
||||
final def --=[U](vs: U)(implicit r: Remove.Values[T, U]): Setting[Task[T]] =
|
||||
macro std.TaskMacro.taskRemoveNImpl[T, U]
|
||||
|
||||
def append1[V](v: Initialize[Task[V]], source: SourcePosition)(implicit a: Append.Value[T, V]): Setting[Task[T]] = make(v, source)(a.appendValue)
|
||||
def appendN[V](vs: Initialize[Task[V]], source: SourcePosition)(implicit a: Append.Values[T, V]): Setting[Task[T]] = make(vs, source)(a.appendValues)
|
||||
def append1[V](v: Initialize[Task[V]], source: SourcePosition)(
|
||||
implicit a: Append.Value[T, V]): Setting[Task[T]] = make(v, source)(a.appendValue)
|
||||
def appendN[V](vs: Initialize[Task[V]], source: SourcePosition)(
|
||||
implicit a: Append.Values[T, V]): Setting[Task[T]] = make(vs, source)(a.appendValues)
|
||||
|
||||
final def remove1[V](v: Initialize[Task[V]], source: SourcePosition)(implicit r: Remove.Value[T, V]): Setting[Task[T]] = make(v, source)(r.removeValue)
|
||||
final def removeN[V](vs: Initialize[Task[V]], source: SourcePosition)(implicit r: Remove.Values[T, V]): Setting[Task[T]] = make(vs, source)(r.removeValues)
|
||||
final def remove1[V](v: Initialize[Task[V]], source: SourcePosition)(
|
||||
implicit r: Remove.Value[T, V]): Setting[Task[T]] = make(v, source)(r.removeValue)
|
||||
final def removeN[V](vs: Initialize[Task[V]], source: SourcePosition)(
|
||||
implicit r: Remove.Values[T, V]): Setting[Task[T]] = make(vs, source)(r.removeValues)
|
||||
|
||||
private[this] def make[S](other: Initialize[Task[S]], source: SourcePosition)(f: (T, S) => T): Setting[Task[T]] =
|
||||
private[this] def make[S](other: Initialize[Task[S]], source: SourcePosition)(
|
||||
f: (T, S) => T): Setting[Task[T]] =
|
||||
set((this, other)((a, b) => (a, b) map f.tupled), source)
|
||||
}
|
||||
|
||||
|
|
@ -90,20 +121,27 @@ sealed abstract class TaskKey[T] extends ScopedTaskable[T] with KeyedInitialize[
|
|||
* The name and the type are represented by a value of type `AttributeKey[InputTask[T]]`.
|
||||
* Instances are constructed using the companion object.
|
||||
*/
|
||||
sealed trait InputKey[T] extends Scoped with KeyedInitialize[InputTask[T]] with Scoped.ScopingSetting[InputKey[T]] with Scoped.DefinableSetting[InputTask[T]] {
|
||||
sealed trait InputKey[T]
|
||||
extends Scoped
|
||||
with KeyedInitialize[InputTask[T]]
|
||||
with Scoped.ScopingSetting[InputKey[T]]
|
||||
with Scoped.DefinableSetting[InputTask[T]] {
|
||||
val key: AttributeKey[InputTask[T]]
|
||||
def scopedKey: ScopedKey[InputTask[T]] = ScopedKey(scope, key)
|
||||
def in(scope: Scope): InputKey[T] = Scoped.scopedInput(Scope.replaceThis(this.scope)(scope), this.key)
|
||||
def in(scope: Scope): InputKey[T] =
|
||||
Scoped.scopedInput(Scope.replaceThis(this.scope)(scope), this.key)
|
||||
|
||||
final def :=(v: T): Setting[InputTask[T]] = macro std.TaskMacro.inputTaskAssignMacroImpl[T]
|
||||
final def ~=(f: T => T): Setting[InputTask[T]] = macro std.TaskMacro.itaskTransformPosition[T]
|
||||
final def transform(f: T => T, source: SourcePosition): Setting[InputTask[T]] = set(scopedKey(_ mapTask { _ map f }), source)
|
||||
final def transform(f: T => T, source: SourcePosition): Setting[InputTask[T]] =
|
||||
set(scopedKey(_ mapTask { _ map f }), source)
|
||||
}
|
||||
|
||||
/** Methods and types related to constructing settings, including keys, scopes, and initializations. */
|
||||
object Scoped {
|
||||
implicit def taskScopedToKey[T](s: TaskKey[T]): ScopedKey[Task[T]] = ScopedKey(s.scope, s.key)
|
||||
implicit def inputScopedToKey[T](s: InputKey[T]): ScopedKey[InputTask[T]] = ScopedKey(s.scope, s.key)
|
||||
implicit def inputScopedToKey[T](s: InputKey[T]): ScopedKey[InputTask[T]] =
|
||||
ScopedKey(s.scope, s.key)
|
||||
|
||||
/**
|
||||
* Mixin trait for adding convenience vocabulary associated with specifying the [[Scope]] of a setting.
|
||||
|
|
@ -129,13 +167,22 @@ object Scoped {
|
|||
def in(c: ConfigKey, t: Scoped): ResultType = in(This, Select(c), Select(t.key))
|
||||
def in(p: Reference, c: ConfigKey): ResultType = in(Select(p), Select(c), This)
|
||||
def in(p: Reference, t: Scoped): ResultType = in(Select(p), This, Select(t.key))
|
||||
def in(p: Reference, c: ConfigKey, t: Scoped): ResultType = in(Select(p), Select(c), Select(t.key))
|
||||
def in(p: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]]): ResultType = in(Scope(p, c, t, This))
|
||||
def in(p: Reference, c: ConfigKey, t: Scoped): ResultType =
|
||||
in(Select(p), Select(c), Select(t.key))
|
||||
def in(p: ScopeAxis[Reference],
|
||||
c: ScopeAxis[ConfigKey],
|
||||
t: ScopeAxis[AttributeKey[_]]): ResultType = in(Scope(p, c, t, This))
|
||||
}
|
||||
|
||||
def scopedSetting[T](s: Scope, k: AttributeKey[T]): SettingKey[T] = new SettingKey[T] { val scope = s; val key = k }
|
||||
def scopedInput[T](s: Scope, k: AttributeKey[InputTask[T]]): InputKey[T] = new InputKey[T] { val scope = s; val key = k }
|
||||
def scopedTask[T](s: Scope, k: AttributeKey[Task[T]]): TaskKey[T] = new TaskKey[T] { val scope = s; val key = k }
|
||||
def scopedSetting[T](s: Scope, k: AttributeKey[T]): SettingKey[T] = new SettingKey[T] {
|
||||
val scope = s; val key = k
|
||||
}
|
||||
def scopedInput[T](s: Scope, k: AttributeKey[InputTask[T]]): InputKey[T] = new InputKey[T] {
|
||||
val scope = s; val key = k
|
||||
}
|
||||
def scopedTask[T](s: Scope, k: AttributeKey[Task[T]]): TaskKey[T] = new TaskKey[T] {
|
||||
val scope = s; val key = k
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin trait for adding convenience vocabulary associated with applying a setting to a configuration item.
|
||||
|
|
@ -145,13 +192,16 @@ object Scoped {
|
|||
|
||||
private[sbt] final def :==(app: S): Setting[S] = macro std.TaskMacro.settingAssignPure[S]
|
||||
|
||||
final def <<=(app: Initialize[S]): Setting[S] = macro std.TaskMacro.fakeSettingAssignPosition[S]
|
||||
final def <<=(app: Initialize[S]): Setting[S] =
|
||||
macro std.TaskMacro.fakeSettingAssignPosition[S]
|
||||
|
||||
/** Internally used function for setting a value along with the `.sbt` file location where it is defined. */
|
||||
final def set(app: Initialize[S], source: SourcePosition): Setting[S] = setting(scopedKey, app, source)
|
||||
final def set(app: Initialize[S], source: SourcePosition): Setting[S] =
|
||||
setting(scopedKey, app, source)
|
||||
|
||||
/** From the given [[Settings]], extract the value bound to this key. */
|
||||
final def get(settings: Settings[Scope]): Option[S] = settings.get(scopedKey.scope, scopedKey.key)
|
||||
final def get(settings: Settings[Scope]): Option[S] =
|
||||
settings.get(scopedKey.scope, scopedKey.key)
|
||||
|
||||
/**
|
||||
* Creates an [[Def.Initialize]] with value [[scala.None]] if there was no previous definition of this key,
|
||||
|
|
@ -187,21 +237,29 @@ object Scoped {
|
|||
sealed trait DefinableTask[S] { self: TaskKey[S] =>
|
||||
|
||||
private[sbt] def :==(app: S): Setting[Task[S]] = macro std.TaskMacro.taskAssignPositionPure[S]
|
||||
private[sbt] def ::=(app: Task[S]): Setting[Task[S]] = macro std.TaskMacro.taskAssignPositionT[S]
|
||||
private[sbt] def ::=(app: Task[S]): Setting[Task[S]] =
|
||||
macro std.TaskMacro.taskAssignPositionT[S]
|
||||
def :=(v: S): Setting[Task[S]] = macro std.TaskMacro.taskAssignMacroImpl[S]
|
||||
def ~=(f: S => S): Setting[Task[S]] = macro std.TaskMacro.taskTransformPosition[S]
|
||||
|
||||
def <<=(app: Initialize[Task[S]]): Setting[Task[S]] = macro std.TaskMacro.fakeItaskAssignPosition[S]
|
||||
def set(app: Initialize[Task[S]], source: SourcePosition): Setting[Task[S]] = Def.setting(scopedKey, app, source)
|
||||
def transform(f: S => S, source: SourcePosition): Setting[Task[S]] = set(scopedKey(_ map f), source)
|
||||
def <<=(app: Initialize[Task[S]]): Setting[Task[S]] =
|
||||
macro std.TaskMacro.fakeItaskAssignPosition[S]
|
||||
def set(app: Initialize[Task[S]], source: SourcePosition): Setting[Task[S]] =
|
||||
Def.setting(scopedKey, app, source)
|
||||
def transform(f: S => S, source: SourcePosition): Setting[Task[S]] =
|
||||
set(scopedKey(_ map f), source)
|
||||
|
||||
@deprecated("No longer needed with new task syntax and SettingKey inheriting from Initialize.", "0.13.2")
|
||||
@deprecated("No longer needed with new task syntax and SettingKey inheriting from Initialize.",
|
||||
"0.13.2")
|
||||
def task: SettingKey[Task[S]] = scopedSetting(scope, key)
|
||||
def get(settings: Settings[Scope]): Option[Task[S]] = settings.get(scope, key)
|
||||
|
||||
def ? : Initialize[Task[Option[S]]] = Def.optional(scopedKey) { case None => mktask { None }; case Some(t) => t map some.fn }
|
||||
def ? : Initialize[Task[Option[S]]] = Def.optional(scopedKey) {
|
||||
case None => mktask { None }; case Some(t) => t map some.fn
|
||||
}
|
||||
def ??[T >: S](or: => T): Initialize[Task[T]] = Def.optional(scopedKey)(_ getOrElse mktask(or))
|
||||
def or[T >: S](i: Initialize[Task[T]]): Initialize[Task[T]] = (this.? zipWith i)((x, y) => (x, y) map { case (a, b) => a getOrElse b })
|
||||
def or[T >: S](i: Initialize[Task[T]]): Initialize[Task[T]] =
|
||||
(this.? zipWith i)((x, y) => (x, y) map { case (a, b) => a getOrElse b })
|
||||
}
|
||||
|
||||
final class RichInitializeTask[S](i: Initialize[Task[S]]) extends RichInitTaskBase[S, Task] {
|
||||
|
|
@ -213,19 +271,25 @@ object Scoped {
|
|||
def failure: Initialize[Task[Incomplete]] = i(_.failure)
|
||||
def result: Initialize[Task[Result[S]]] = i(_.result)
|
||||
|
||||
def xtriggeredBy[T](tasks: Initialize[Task[T]]*): Initialize[Task[S]] = nonLocal(tasks, Def.triggeredBy)
|
||||
def triggeredBy[T](tasks: Initialize[Task[T]]*): Initialize[Task[S]] = nonLocal(tasks, Def.triggeredBy)
|
||||
def runBefore[T](tasks: Initialize[Task[T]]*): Initialize[Task[S]] = nonLocal(tasks, Def.runBefore)
|
||||
def xtriggeredBy[T](tasks: Initialize[Task[T]]*): Initialize[Task[S]] =
|
||||
nonLocal(tasks, Def.triggeredBy)
|
||||
def triggeredBy[T](tasks: Initialize[Task[T]]*): Initialize[Task[S]] =
|
||||
nonLocal(tasks, Def.triggeredBy)
|
||||
def runBefore[T](tasks: Initialize[Task[T]]*): Initialize[Task[S]] =
|
||||
nonLocal(tasks, Def.runBefore)
|
||||
|
||||
private[this] def nonLocal(tasks: Seq[AnyInitTask], key: AttributeKey[Seq[Task[_]]]): Initialize[Task[S]] =
|
||||
private[this] def nonLocal(tasks: Seq[AnyInitTask],
|
||||
key: AttributeKey[Seq[Task[_]]]): Initialize[Task[S]] =
|
||||
(Initialize.joinAny[Task](tasks), i)((ts, i) => i.copy(info = i.info.set(key, ts)))
|
||||
}
|
||||
|
||||
final class RichInitializeInputTask[S](i: Initialize[InputTask[S]]) extends RichInitTaskBase[S, InputTask] {
|
||||
final class RichInitializeInputTask[S](i: Initialize[InputTask[S]])
|
||||
extends RichInitTaskBase[S, InputTask] {
|
||||
protected def onTask[T](f: Task[S] => Task[T]): Initialize[InputTask[T]] = i(_ mapTask f)
|
||||
|
||||
def dependsOn(tasks: AnyInitTask*): Initialize[InputTask[S]] =
|
||||
(i, Initialize.joinAny[Task](tasks))((thisTask, deps) => thisTask.mapTask(_.dependsOn(deps: _*)))
|
||||
(i, Initialize.joinAny[Task](tasks))((thisTask, deps) =>
|
||||
thisTask.mapTask(_.dependsOn(deps: _*)))
|
||||
}
|
||||
|
||||
sealed abstract class RichInitTaskBase[S, R[_]] {
|
||||
|
|
@ -242,16 +306,24 @@ object Scoped {
|
|||
def tag(tags: Tag*): Initialize[R[S]] = onTask(_.tag(tags: _*))
|
||||
def tagw(tags: (Tag, Int)*): Initialize[R[S]] = onTask(_.tagw(tags: _*))
|
||||
|
||||
@deprecated("Use the `result` method to create a task that returns the full Result of this task. Then, call `flatMap` on the new task.", "0.13.0")
|
||||
@deprecated(
|
||||
"Use the `result` method to create a task that returns the full Result of this task. Then, call `flatMap` on the new task.",
|
||||
"0.13.0")
|
||||
def flatMapR[T](f: Result[S] => Task[T]): Initialize[R[T]] = onTask(_ flatMapR f)
|
||||
|
||||
@deprecated("Use the `result` method to create a task that returns the full Result of this task. Then, call `map` on the new task.", "0.13.0")
|
||||
@deprecated(
|
||||
"Use the `result` method to create a task that returns the full Result of this task. Then, call `map` on the new task.",
|
||||
"0.13.0")
|
||||
def mapR[T](f: Result[S] => T): Initialize[R[T]] = onTask(_ mapR f)
|
||||
|
||||
@deprecated("Use the `failure` method to create a task that returns Incomplete when this task fails and then call `flatMap` on the new task.", "0.13.0")
|
||||
@deprecated(
|
||||
"Use the `failure` method to create a task that returns Incomplete when this task fails and then call `flatMap` on the new task.",
|
||||
"0.13.0")
|
||||
def flatFailure[T](f: Incomplete => Task[T]): Initialize[R[T]] = flatMapR(f compose failM)
|
||||
|
||||
@deprecated("Use the `failure` method to create a task that returns Incomplete when this task fails and then call `map` on the new task.", "0.13.0")
|
||||
@deprecated(
|
||||
"Use the `failure` method to create a task that returns Incomplete when this task fails and then call `map` on the new task.",
|
||||
"0.13.0")
|
||||
def mapFailure[T](f: Incomplete => T): Initialize[R[T]] = mapR(f compose failM)
|
||||
}
|
||||
|
||||
|
|
@ -264,9 +336,12 @@ object Scoped {
|
|||
}
|
||||
implicit def richAnyTaskSeq(in: Seq[AnyInitTask]): RichAnyTaskSeq = new RichAnyTaskSeq(in)
|
||||
final class RichAnyTaskSeq(keys: Seq[AnyInitTask]) {
|
||||
def dependOn: Initialize[Task[Unit]] = Initialize.joinAny[Task](keys).apply(deps => nop.dependsOn(deps: _*))
|
||||
def dependOn: Initialize[Task[Unit]] =
|
||||
Initialize.joinAny[Task](keys).apply(deps => nop.dependsOn(deps: _*))
|
||||
}
|
||||
|
||||
// format: off
|
||||
|
||||
// this is the least painful arrangement I came up with
|
||||
@deprecated("The sbt 0.10 style DSL is deprecated: '(k1, k2) map { (x, y) => ... }' should now be '{ val x = k1.value; val y = k2.value }'.\nSee http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html", "0.13.13") implicit def t2ToTable2[A, B](t2: (ScopedTaskable[A], ScopedTaskable[B])): RichTaskable2[A, B] = new RichTaskable2(t2)
|
||||
@deprecated("The sbt 0.10 style DSL is deprecated: '(k1, k2) map { (x, y) => ... }' should now be '{ val x = k1.value; val y = k2.value }'.\nSee http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html", "0.13.13") implicit def t3ToTable3[A, B, C](t3: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C])): RichTaskable3[A, B, C] = new RichTaskable3(t3)
|
||||
|
|
@ -279,12 +354,18 @@ object Scoped {
|
|||
@deprecated("The sbt 0.10 style DSL is deprecated: '(k1, k2) map { (x, y) => ... }' should now be '{ val x = k1.value; val y = k2.value }'.\nSee http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html", "0.13.13") implicit def t10ToTable10[A, B, C, D, E, F, G, H, I, J](t10: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I], ScopedTaskable[J])): RichTaskable10[A, B, C, D, E, F, G, H, I, J] = new RichTaskable10(t10)
|
||||
@deprecated("The sbt 0.10 style DSL is deprecated: '(k1, k2) map { (x, y) => ... }' should now be '{ val x = k1.value; val y = k2.value }'.\nSee http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html", "0.13.13") implicit def t11ToTable11[A, B, C, D, E, F, G, H, I, J, K](t11: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I], ScopedTaskable[J], ScopedTaskable[K])): RichTaskable11[A, B, C, D, E, F, G, H, I, J, K] = new RichTaskable11(t11)
|
||||
|
||||
sealed abstract class RichTaskables[K[L[x]]]( final val keys: K[ScopedTaskable])(implicit a: AList[K]) {
|
||||
// format: on
|
||||
|
||||
sealed abstract class RichTaskables[K[L[x]]](final val keys: K[ScopedTaskable])(
|
||||
implicit a: AList[K]) {
|
||||
type App[T] = Initialize[Task[T]]
|
||||
type Fun[M[_], Ret]
|
||||
protected def convert[M[_], Ret](f: Fun[M, Ret]): K[M] => Ret
|
||||
private[this] val inputs: K[App] = a.transform(keys, new (ScopedTaskable ~> App) { def apply[T](in: ScopedTaskable[T]): App[T] = in.toTask })
|
||||
private[this] def onTasks[T](f: K[Task] => Task[T]): App[T] = Def.app[({ type l[L[x]] = K[(L ∙ Task)#l] })#l, Task[T]](inputs)(f)(AList.asplit[K, Task](a))
|
||||
private[this] val inputs: K[App] = a.transform(keys, new (ScopedTaskable ~> App) {
|
||||
def apply[T](in: ScopedTaskable[T]): App[T] = in.toTask
|
||||
})
|
||||
private[this] def onTasks[T](f: K[Task] => Task[T]): App[T] =
|
||||
Def.app[({ type l[L[x]] = K[(L ∙ Task)#l] })#l, Task[T]](inputs)(f)(AList.asplit[K, Task](a))
|
||||
|
||||
def flatMap[T](f: Fun[Id, Task[T]]): App[T] = onTasks(_.flatMap(convert(f)))
|
||||
def flatMapR[T](f: Fun[Result, Task[T]]): App[T] = onTasks(_.flatMapR(convert(f)))
|
||||
|
|
@ -293,6 +374,9 @@ object Scoped {
|
|||
def flatFailure[T](f: Seq[Incomplete] => Task[T]): App[T] = onTasks(_ flatFailure f)
|
||||
def mapFailure[T](f: Seq[Incomplete] => T): App[T] = onTasks(_ mapFailure f)
|
||||
}
|
||||
|
||||
// format: off
|
||||
|
||||
type ST[X] = ScopedTaskable[X]
|
||||
final class RichTaskable2[A, B](t2: (ST[A], ST[B])) extends RichTaskables[AList.T2K[A, B]#l](t2)(AList.tuple2[A, B]) {
|
||||
type Fun[M[_], Ret] = (M[A], M[B]) => Ret
|
||||
|
|
@ -412,20 +496,32 @@ object Scoped {
|
|||
def identity = apply(mkTuple11)
|
||||
}
|
||||
|
||||
private[sbt] def extendScoped(s1: Scoped, ss: Seq[Scoped]): Seq[AttributeKey[_]] = s1.key +: ss.map(_.key)
|
||||
// format: on
|
||||
|
||||
private[sbt] def extendScoped(s1: Scoped, ss: Seq[Scoped]): Seq[AttributeKey[_]] =
|
||||
s1.key +: ss.map(_.key)
|
||||
}
|
||||
|
||||
import Scoped.extendScoped
|
||||
|
||||
/** Constructs InputKeys, which are associated with input tasks to define a setting.*/
|
||||
object InputKey {
|
||||
def apply[T: Manifest](label: String, description: String = "", rank: Int = KeyRanks.DefaultInputRank): InputKey[T] =
|
||||
def apply[T: Manifest](label: String,
|
||||
description: String = "",
|
||||
rank: Int = KeyRanks.DefaultInputRank): InputKey[T] =
|
||||
apply(AttributeKey[InputTask[T]](label, description, rank))
|
||||
|
||||
def apply[T: Manifest](label: String, description: String, extend1: Scoped, extendN: Scoped*): InputKey[T] =
|
||||
def apply[T: Manifest](label: String,
|
||||
description: String,
|
||||
extend1: Scoped,
|
||||
extendN: Scoped*): InputKey[T] =
|
||||
apply(label, description, KeyRanks.DefaultInputRank, extend1, extendN: _*)
|
||||
|
||||
def apply[T: Manifest](label: String, description: String, rank: Int, extend1: Scoped, extendN: Scoped*): InputKey[T] =
|
||||
def apply[T: Manifest](label: String,
|
||||
description: String,
|
||||
rank: Int,
|
||||
extend1: Scoped,
|
||||
extendN: Scoped*): InputKey[T] =
|
||||
apply(AttributeKey[InputTask[T]](label, description, extendScoped(extend1, extendN), rank))
|
||||
|
||||
def apply[T](akey: AttributeKey[InputTask[T]]): InputKey[T] =
|
||||
|
|
@ -434,13 +530,22 @@ object InputKey {
|
|||
|
||||
/** Constructs TaskKeys, which are associated with tasks to define a setting.*/
|
||||
object TaskKey {
|
||||
def apply[T: Manifest](label: String, description: String = "", rank: Int = KeyRanks.DefaultTaskRank): TaskKey[T] =
|
||||
def apply[T: Manifest](label: String,
|
||||
description: String = "",
|
||||
rank: Int = KeyRanks.DefaultTaskRank): TaskKey[T] =
|
||||
apply(AttributeKey[Task[T]](label, description, rank))
|
||||
|
||||
def apply[T: Manifest](label: String, description: String, extend1: Scoped, extendN: Scoped*): TaskKey[T] =
|
||||
def apply[T: Manifest](label: String,
|
||||
description: String,
|
||||
extend1: Scoped,
|
||||
extendN: Scoped*): TaskKey[T] =
|
||||
apply(AttributeKey[Task[T]](label, description, extendScoped(extend1, extendN)))
|
||||
|
||||
def apply[T: Manifest](label: String, description: String, rank: Int, extend1: Scoped, extendN: Scoped*): TaskKey[T] =
|
||||
def apply[T: Manifest](label: String,
|
||||
description: String,
|
||||
rank: Int,
|
||||
extend1: Scoped,
|
||||
extendN: Scoped*): TaskKey[T] =
|
||||
apply(AttributeKey[Task[T]](label, description, extendScoped(extend1, extendN), rank))
|
||||
|
||||
def apply[T](akey: AttributeKey[Task[T]]): TaskKey[T] =
|
||||
|
|
@ -451,13 +556,22 @@ object TaskKey {
|
|||
|
||||
/** Constructs SettingKeys, which are associated with a value to define a basic setting.*/
|
||||
object SettingKey {
|
||||
def apply[T: Manifest: OptJsonWriter](label: String, description: String = "", rank: Int = KeyRanks.DefaultSettingRank): SettingKey[T] =
|
||||
def apply[T: Manifest: OptJsonWriter](label: String,
|
||||
description: String = "",
|
||||
rank: Int = KeyRanks.DefaultSettingRank): SettingKey[T] =
|
||||
apply(AttributeKey[T](label, description, rank))
|
||||
|
||||
def apply[T: Manifest: OptJsonWriter](label: String, description: String, extend1: Scoped, extendN: Scoped*): SettingKey[T] =
|
||||
def apply[T: Manifest: OptJsonWriter](label: String,
|
||||
description: String,
|
||||
extend1: Scoped,
|
||||
extendN: Scoped*): SettingKey[T] =
|
||||
apply(AttributeKey[T](label, description, extendScoped(extend1, extendN)))
|
||||
|
||||
def apply[T: Manifest: OptJsonWriter](label: String, description: String, rank: Int, extend1: Scoped, extendN: Scoped*): SettingKey[T] =
|
||||
def apply[T: Manifest: OptJsonWriter](label: String,
|
||||
description: String,
|
||||
rank: Int,
|
||||
extend1: Scoped,
|
||||
extendN: Scoped*): SettingKey[T] =
|
||||
apply(AttributeKey[T](label, description, extendScoped(extend1, extendN), rank))
|
||||
|
||||
def apply[T](akey: AttributeKey[T]): SettingKey[T] =
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ object ParserConvert extends Convert {
|
|||
/** Convert instance for plain `Task`s not within the settings system. */
|
||||
object TaskConvert extends Convert {
|
||||
def apply[T: c.WeakTypeTag](c: blackbox.Context)(nme: String, in: c.Tree): Converted[c.type] =
|
||||
if (nme == InputWrapper.WrapTaskName) Converted.Success[c.type](in) else Converted.NotApplicable[c.type]
|
||||
if (nme == InputWrapper.WrapTaskName) Converted.Success[c.type](in)
|
||||
else Converted.NotApplicable[c.type]
|
||||
}
|
||||
|
||||
/** Converts an input `Tree` of type `Initialize[T]`, `Initialize[Task[T]]`, or `Task[T]` into a `Tree` of type `Initialize[Task[T]]`.*/
|
||||
|
|
|
|||
|
|
@ -22,39 +22,52 @@ object InputWrapper {
|
|||
private[std] final val WrapInputName = "wrapInputTask_\u2603\u2603"
|
||||
private[std] final val WrapPreviousName = "wrapPrevious_\u2603\u2603"
|
||||
|
||||
@compileTimeOnly("`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.")
|
||||
@compileTimeOnly(
|
||||
"`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.")
|
||||
def wrapTask_\u2603\u2603[T](in: Any): T = implDetailError
|
||||
|
||||
@compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.")
|
||||
@compileTimeOnly(
|
||||
"`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.")
|
||||
def wrapInit_\u2603\u2603[T](in: Any): T = implDetailError
|
||||
|
||||
@compileTimeOnly("`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.")
|
||||
@compileTimeOnly(
|
||||
"`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.")
|
||||
def wrapInitTask_\u2603\u2603[T](in: Any): T = implDetailError
|
||||
|
||||
@compileTimeOnly("`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask.")
|
||||
@compileTimeOnly(
|
||||
"`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask.")
|
||||
def wrapInputTask_\u2603\u2603[T](in: Any): T = implDetailError
|
||||
|
||||
@compileTimeOnly("`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask.")
|
||||
@compileTimeOnly(
|
||||
"`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask.")
|
||||
def wrapInitInputTask_\u2603\u2603[T](in: Any): T = implDetailError
|
||||
|
||||
@compileTimeOnly("`previous` can only be called on a task within a task or input task definition macro, such as :=, +=, ++=, Def.task, or Def.inputTask.")
|
||||
@compileTimeOnly(
|
||||
"`previous` can only be called on a task within a task or input task definition macro, such as :=, +=, ++=, Def.task, or Def.inputTask.")
|
||||
def wrapPrevious_\u2603\u2603[T](in: Any): T = implDetailError
|
||||
|
||||
private[this] def implDetailError = sys.error("This method is an implementation detail and should not be referenced.")
|
||||
private[this] def implDetailError =
|
||||
sys.error("This method is an implementation detail and should not be referenced.")
|
||||
|
||||
private[std] def wrapTask[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
private[std] def wrapTask[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any],
|
||||
pos: c.Position): c.Expr[T] =
|
||||
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapTaskName)(ts, pos)
|
||||
private[std] def wrapInit[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
private[std] def wrapInit[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any],
|
||||
pos: c.Position): c.Expr[T] =
|
||||
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapInitName)(ts, pos)
|
||||
private[std] def wrapInitTask[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
private[std] def wrapInitTask[T: c.WeakTypeTag](
|
||||
c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapInitTaskName)(ts, pos)
|
||||
|
||||
private[std] def wrapInitInputTask[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
private[std] def wrapInitInputTask[T: c.WeakTypeTag](
|
||||
c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapInitInputName)(ts, pos)
|
||||
private[std] def wrapInputTask[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
private[std] def wrapInputTask[T: c.WeakTypeTag](
|
||||
c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapInputName)(ts, pos)
|
||||
|
||||
private[std] def wrapPrevious[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[Option[T]] =
|
||||
private[std] def wrapPrevious[T: c.WeakTypeTag](
|
||||
c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[Option[T]] =
|
||||
wrapImpl[Option[T], InputWrapper.type](c, InputWrapper, WrapPreviousName)(ts, pos)
|
||||
|
||||
/**
|
||||
|
|
@ -63,44 +76,52 @@ object InputWrapper {
|
|||
*
|
||||
* `c.universe.reify { <s>.<wrapName>[T](ts.splice) }`
|
||||
*/
|
||||
def wrapImpl[T: c.WeakTypeTag, S <: AnyRef with Singleton](c: blackbox.Context, s: S, wrapName: String)(ts: c.Expr[Any], pos: c.Position)(implicit it: c.TypeTag[s.type]): c.Expr[T] =
|
||||
{
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
import internal.decorators._
|
||||
val util = new ContextUtil[c.type](c)
|
||||
val iw = util.singleton(s)
|
||||
val tpe = c.weakTypeOf[T]
|
||||
val nme = TermName(wrapName).encodedName
|
||||
val sel = Select(Ident(iw), nme)
|
||||
sel.setPos(pos) // need to set the position on Select, because that is where the compileTimeOnly check looks
|
||||
val tree = ApplyTree(TypeApply(sel, TypeTree(tpe) :: Nil), ts.tree :: Nil)
|
||||
tree.setPos(ts.tree.pos)
|
||||
// JZ: I'm not sure why we need to do this. Presumably a caller is wrapping this tree in a
|
||||
// typed tree *before* handing the whole thing back to the macro engine. One must never splice
|
||||
// untyped trees under typed trees, as the type checker doesn't descend if `tree.tpe == null`.
|
||||
//
|
||||
// #1031 The previous attempt to fix this just set the type on `tree`, which worked in cases when the
|
||||
// call to `.value` was inside a the task macro and eliminated before the end of the typer phase.
|
||||
// But, if a "naked" call to `.value` left the typer, the superaccessors phase would freak out when
|
||||
// if hit the untyped trees, before we could get to refchecks and the desired @compileTimeOnly warning.
|
||||
val typedTree = c.typecheck(tree)
|
||||
c.Expr[T](typedTree)
|
||||
}
|
||||
def wrapImpl[T: c.WeakTypeTag, S <: AnyRef with Singleton](
|
||||
c: blackbox.Context,
|
||||
s: S,
|
||||
wrapName: String)(ts: c.Expr[Any], pos: c.Position)(
|
||||
implicit it: c.TypeTag[s.type]): c.Expr[T] = {
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
import internal.decorators._
|
||||
val util = new ContextUtil[c.type](c)
|
||||
val iw = util.singleton(s)
|
||||
val tpe = c.weakTypeOf[T]
|
||||
val nme = TermName(wrapName).encodedName
|
||||
val sel = Select(Ident(iw), nme)
|
||||
sel.setPos(pos) // need to set the position on Select, because that is where the compileTimeOnly check looks
|
||||
val tree = ApplyTree(TypeApply(sel, TypeTree(tpe) :: Nil), ts.tree :: Nil)
|
||||
tree.setPos(ts.tree.pos)
|
||||
// JZ: I'm not sure why we need to do this. Presumably a caller is wrapping this tree in a
|
||||
// typed tree *before* handing the whole thing back to the macro engine. One must never splice
|
||||
// untyped trees under typed trees, as the type checker doesn't descend if `tree.tpe == null`.
|
||||
//
|
||||
// #1031 The previous attempt to fix this just set the type on `tree`, which worked in cases when the
|
||||
// call to `.value` was inside a the task macro and eliminated before the end of the typer phase.
|
||||
// But, if a "naked" call to `.value` left the typer, the superaccessors phase would freak out when
|
||||
// if hit the untyped trees, before we could get to refchecks and the desired @compileTimeOnly warning.
|
||||
val typedTree = c.typecheck(tree)
|
||||
c.Expr[T](typedTree)
|
||||
}
|
||||
|
||||
def valueMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[T] =
|
||||
ContextUtil.selectMacroImpl[T](c) { (ts, pos) =>
|
||||
ts.tree.tpe match {
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[T]] =>
|
||||
if (c.weakTypeOf[T] <:< c.weakTypeOf[InputTask[_]]) {
|
||||
c.abort(pos, """`value` is removed from input tasks. Use `evaluated` or `inputTaskValue`.
|
||||
|See http://www.scala-sbt.org/1.0/docs/Input-Tasks.html for more details.""".stripMargin)
|
||||
c.abort(
|
||||
pos,
|
||||
"""`value` is removed from input tasks. Use `evaluated` or `inputTaskValue`.
|
||||
|See http://www.scala-sbt.org/1.0/docs/Input-Tasks.html for more details.""".stripMargin
|
||||
)
|
||||
}
|
||||
InputWrapper.wrapInit[T](c)(ts, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[Task[T]]] => InputWrapper.wrapInitTask[T](c)(ts, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Task[T]] => InputWrapper.wrapTask[T](c)(ts, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[InputTask[T]] => InputWrapper.wrapInputTask[T](c)(ts, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[InputTask[T]]] => InputWrapper.wrapInitInputTask[T](c)(ts, pos)
|
||||
case tpe => unexpectedType(c)(pos, tpe)
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[Task[T]]] =>
|
||||
InputWrapper.wrapInitTask[T](c)(ts, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Task[T]] => InputWrapper.wrapTask[T](c)(ts, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[InputTask[T]] => InputWrapper.wrapInputTask[T](c)(ts, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[InputTask[T]]] =>
|
||||
InputWrapper.wrapInitInputTask[T](c)(ts, pos)
|
||||
case tpe => unexpectedType(c)(pos, tpe)
|
||||
}
|
||||
}
|
||||
def inputTaskValueMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[InputTask[T]] =
|
||||
|
|
@ -115,51 +136,60 @@ object InputWrapper {
|
|||
else
|
||||
unexpectedType(c)(pos, tpe)
|
||||
}
|
||||
|
||||
/** Translates <task: TaskKey[T]>.previous(format) to Previous.runtime(<task>)(format).value*/
|
||||
def previousMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(format: c.Expr[sjsonnew.JsonFormat[T]]): c.Expr[Option[T]] =
|
||||
{
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case a @ Apply(Select(Apply(_, t :: Nil), tp), fmt) =>
|
||||
if (t.tpe <:< c.weakTypeOf[TaskKey[T]]) {
|
||||
val tsTyped = c.Expr[TaskKey[T]](t)
|
||||
val newTree = c.universe.reify { Previous.runtime[T](tsTyped.splice)(format.splice) }
|
||||
wrapPrevious[T](c)(newTree, a.pos)
|
||||
} else
|
||||
unexpectedType(c)(a.pos, t.tpe)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
def previousMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
format: c.Expr[sjsonnew.JsonFormat[T]]): c.Expr[Option[T]] = {
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case a @ Apply(Select(Apply(_, t :: Nil), tp), fmt) =>
|
||||
if (t.tpe <:< c.weakTypeOf[TaskKey[T]]) {
|
||||
val tsTyped = c.Expr[TaskKey[T]](t)
|
||||
val newTree = c.universe.reify { Previous.runtime[T](tsTyped.splice)(format.splice) }
|
||||
wrapPrevious[T](c)(newTree, a.pos)
|
||||
} else
|
||||
unexpectedType(c)(a.pos, t.tpe)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
|
||||
private def unexpectedType(c: blackbox.Context)(pos: c.Position, tpe: c.Type) =
|
||||
c.abort(pos, s"Internal sbt error. Unexpected type ${tpe.widen}")
|
||||
}
|
||||
|
||||
sealed abstract class MacroTaskValue[T] {
|
||||
@compileTimeOnly("`taskValue` can only be used within a setting macro, such as :=, +=, ++=, or Def.setting.")
|
||||
@compileTimeOnly(
|
||||
"`taskValue` can only be used within a setting macro, such as :=, +=, ++=, or Def.setting.")
|
||||
def taskValue: Task[T] = macro InputWrapper.taskValueMacroImpl[T]
|
||||
}
|
||||
sealed abstract class MacroValue[T] {
|
||||
@compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.")
|
||||
@compileTimeOnly(
|
||||
"`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.")
|
||||
def value: T = macro InputWrapper.valueMacroImpl[T]
|
||||
}
|
||||
sealed abstract class ParserInput[T] {
|
||||
@compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
@compileTimeOnly(
|
||||
"`parsed` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
def parsed: T = macro ParserInput.parsedMacroImpl[T]
|
||||
}
|
||||
sealed abstract class InputEvaluated[T] {
|
||||
@compileTimeOnly("`evaluated` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
@compileTimeOnly(
|
||||
"`evaluated` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
def evaluated: T = macro InputWrapper.valueMacroImpl[T]
|
||||
@compileTimeOnly("`inputTaskValue` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
@compileTimeOnly(
|
||||
"`inputTaskValue` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
def inputTaskValue: InputTask[T] = macro InputWrapper.inputTaskValueMacroImpl[T]
|
||||
}
|
||||
sealed abstract class ParserInputTask[T] {
|
||||
@compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
@compileTimeOnly(
|
||||
"`parsed` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
def parsed: Task[T] = macro ParserInput.parsedInputMacroImpl[T]
|
||||
}
|
||||
sealed abstract class MacroPrevious[T] {
|
||||
@compileTimeOnly("`previous` can only be used within a task macro, such as :=, +=, ++=, or Def.task.")
|
||||
def previous(implicit format: sjsonnew.JsonFormat[T]): Option[T] = macro InputWrapper.previousMacroImpl[T]
|
||||
@compileTimeOnly(
|
||||
"`previous` can only be used within a task macro, such as :=, +=, ++=, or Def.task.")
|
||||
def previous(implicit format: sjsonnew.JsonFormat[T]): Option[T] =
|
||||
macro InputWrapper.previousMacroImpl[T]
|
||||
}
|
||||
|
||||
/** Implementation detail. The wrap method temporarily holds the input parser (as a Tree, at compile time) until the input task macro processes it. */
|
||||
|
|
@ -170,25 +200,34 @@ object ParserInput {
|
|||
private[std] val WrapName = "parser_\u2603\u2603"
|
||||
private[std] val WrapInitName = "initParser_\u2603\u2603"
|
||||
|
||||
@compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
def parser_\u2603\u2603[T](i: Any): T = sys.error("This method is an implementation detail and should not be referenced.")
|
||||
@compileTimeOnly(
|
||||
"`parsed` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
def parser_\u2603\u2603[T](i: Any): T =
|
||||
sys.error("This method is an implementation detail and should not be referenced.")
|
||||
|
||||
@compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
def initParser_\u2603\u2603[T](i: Any): T = sys.error("This method is an implementation detail and should not be referenced.")
|
||||
@compileTimeOnly(
|
||||
"`parsed` can only be used within an input task macro, such as := or Def.inputTask.")
|
||||
def initParser_\u2603\u2603[T](i: Any): T =
|
||||
sys.error("This method is an implementation detail and should not be referenced.")
|
||||
|
||||
private[std] def wrap[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
private[std] def wrap[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any],
|
||||
pos: c.Position): c.Expr[T] =
|
||||
InputWrapper.wrapImpl[T, ParserInput.type](c, ParserInput, WrapName)(ts, pos)
|
||||
private[std] def wrapInit[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
|
||||
private[std] def wrapInit[T: c.WeakTypeTag](c: blackbox.Context)(ts: c.Expr[Any],
|
||||
pos: c.Position): c.Expr[T] =
|
||||
InputWrapper.wrapImpl[T, ParserInput.type](c, ParserInput, WrapInitName)(ts, pos)
|
||||
|
||||
private[std] def inputParser[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[InputTask[T]]): c.Expr[State => Parser[Task[T]]] = c.universe.reify(t.splice.parser)
|
||||
private[std] def inputParser[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[InputTask[T]]): c.Expr[State => Parser[Task[T]]] =
|
||||
c.universe.reify(t.splice.parser)
|
||||
|
||||
def parsedInputMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Task[T]] =
|
||||
ContextUtil.selectMacroImpl[Task[T]](c) { (p, pos) =>
|
||||
p.tree.tpe match {
|
||||
case tpe if tpe <:< c.weakTypeOf[InputTask[T]] => wrapInputTask[T](c)(p.tree, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[InputTask[T]]] => wrapInitInputTask[T](c)(p.tree, pos)
|
||||
case tpe => unexpectedType(c)(pos, tpe, "parsedInputMacroImpl")
|
||||
case tpe if tpe <:< c.weakTypeOf[InputTask[T]] => wrapInputTask[T](c)(p.tree, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[InputTask[T]]] =>
|
||||
wrapInitInputTask[T](c)(p.tree, pos)
|
||||
case tpe => unexpectedType(c)(pos, tpe, "parsedInputMacroImpl")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +236,8 @@ object ParserInput {
|
|||
wrap[Task[T]](c)(inputParser(c)(e), pos)
|
||||
}
|
||||
|
||||
private def wrapInitInputTask[T: c.WeakTypeTag](c: blackbox.Context)(tree: c.Tree, pos: c.Position) = {
|
||||
private def wrapInitInputTask[T: c.WeakTypeTag](c: blackbox.Context)(tree: c.Tree,
|
||||
pos: c.Position) = {
|
||||
val e = c.Expr[Initialize[InputTask[T]]](tree)
|
||||
wrapInit[Task[T]](c)(c.universe.reify { Def.toIParser(e.splice) }, pos)
|
||||
}
|
||||
|
|
@ -206,9 +246,10 @@ object ParserInput {
|
|||
def parsedMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[T] =
|
||||
ContextUtil.selectMacroImpl[T](c) { (p, pos) =>
|
||||
p.tree.tpe match {
|
||||
case tpe if tpe <:< c.weakTypeOf[Parser[T]] => wrapParser[T](c)(p.tree, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[State => Parser[T]] => wrap[T](c)(p, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[Parser[T]]] => wrapInitParser[T](c)(p.tree, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Parser[T]] => wrapParser[T](c)(p.tree, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[State => Parser[T]] => wrap[T](c)(p, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[Parser[T]]] =>
|
||||
wrapInitParser[T](c)(p.tree, pos)
|
||||
case tpe if tpe <:< c.weakTypeOf[Initialize[State => Parser[T]]] => wrapInit[T](c)(p, pos)
|
||||
case tpe => unexpectedType(c)(pos, tpe, "parsedMacroImpl")
|
||||
}
|
||||
|
|
@ -219,7 +260,8 @@ object ParserInput {
|
|||
wrap[T](c)(c.universe.reify { Def.toSParser(e.splice) }, pos)
|
||||
}
|
||||
|
||||
private def wrapInitParser[T: c.WeakTypeTag](c: blackbox.Context)(tree: c.Tree, pos: c.Position) = {
|
||||
private def wrapInitParser[T: c.WeakTypeTag](c: blackbox.Context)(tree: c.Tree,
|
||||
pos: c.Position) = {
|
||||
val e = c.Expr[Initialize[Parser[T]]](tree)
|
||||
val es = c.universe.reify { Def.toISParser(e.splice) }
|
||||
wrapInit[T](c)(es, pos)
|
||||
|
|
|
|||
|
|
@ -7,32 +7,38 @@ import scala.reflect.macros._
|
|||
import sbt.util.OptJsonWriter
|
||||
|
||||
private[sbt] object KeyMacro {
|
||||
def settingKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(description: c.Expr[String]): c.Expr[SettingKey[T]] =
|
||||
def settingKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
description: c.Expr[String]): c.Expr[SettingKey[T]] =
|
||||
keyImpl2[T, SettingKey[T]](c) { (name, mf, ojw) =>
|
||||
c.universe.reify { SettingKey[T](name.splice, description.splice)(mf.splice, ojw.splice) }
|
||||
}
|
||||
def taskKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(description: c.Expr[String]): c.Expr[TaskKey[T]] =
|
||||
def taskKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
description: c.Expr[String]): c.Expr[TaskKey[T]] =
|
||||
keyImpl[T, TaskKey[T]](c) { (name, mf) =>
|
||||
c.universe.reify { TaskKey[T](name.splice, description.splice)(mf.splice) }
|
||||
}
|
||||
def inputKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(description: c.Expr[String]): c.Expr[InputKey[T]] =
|
||||
def inputKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
description: c.Expr[String]): c.Expr[InputKey[T]] =
|
||||
keyImpl[T, InputKey[T]](c) { (name, mf) =>
|
||||
c.universe.reify { InputKey[T](name.splice, description.splice)(mf.splice) }
|
||||
}
|
||||
|
||||
def keyImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: blackbox.Context)(
|
||||
f: (c.Expr[String], c.Expr[Manifest[T]]) => c.Expr[S]
|
||||
f: (c.Expr[String], c.Expr[Manifest[T]]) => c.Expr[S]
|
||||
): c.Expr[S] =
|
||||
f(getName(c), getImplicit[Manifest[T]](c))
|
||||
|
||||
private def keyImpl2[T: c.WeakTypeTag, S: c.WeakTypeTag](c: blackbox.Context)(
|
||||
f: (c.Expr[String], c.Expr[Manifest[T]], c.Expr[OptJsonWriter[T]]) => c.Expr[S]
|
||||
f: (c.Expr[String], c.Expr[Manifest[T]], c.Expr[OptJsonWriter[T]]) => c.Expr[S]
|
||||
): c.Expr[S] =
|
||||
f(getName(c), getImplicit[Manifest[T]](c), getImplicit[OptJsonWriter[T]](c))
|
||||
|
||||
private def getName[S: c.WeakTypeTag, T: c.WeakTypeTag](c: blackbox.Context): c.Expr[String] = {
|
||||
import c.universe._
|
||||
val enclosingValName = definingValName(c, methodName => s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""")
|
||||
val enclosingValName = definingValName(
|
||||
c,
|
||||
methodName =>
|
||||
s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""")
|
||||
c.Expr[String](Literal(Constant(enclosingValName)))
|
||||
}
|
||||
|
||||
|
|
@ -41,26 +47,30 @@ private[sbt] object KeyMacro {
|
|||
c.Expr[T](c.inferImplicitValue(weakTypeOf[T]))
|
||||
}
|
||||
|
||||
def definingValName(c: blackbox.Context, invalidEnclosingTree: String => String): String =
|
||||
{
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
val methodName = c.macroApplication.symbol.name
|
||||
def processName(n: Name): String = n.decodedName.toString.trim // trim is not strictly correct, but macros don't expose the API necessary
|
||||
@tailrec def enclosingVal(trees: List[c.Tree]): String =
|
||||
{
|
||||
trees match {
|
||||
case vd @ ValDef(_, name, _, _) :: ts => processName(name)
|
||||
case (_: ApplyTree | _: Select | _: TypeApply) :: xs => enclosingVal(xs)
|
||||
// lazy val x: X = <methodName> has this form for some reason (only when the explicit type is present, though)
|
||||
case Block(_, _) :: DefDef(mods, name, _, _, _, _) :: xs if mods.hasFlag(Flag.LAZY) => processName(name)
|
||||
case _ =>
|
||||
c.error(c.enclosingPosition, invalidEnclosingTree(methodName.decodedName.toString))
|
||||
"<error>"
|
||||
}
|
||||
}
|
||||
enclosingVal(enclosingTrees(c).toList)
|
||||
def definingValName(c: blackbox.Context, invalidEnclosingTree: String => String): String = {
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
val methodName = c.macroApplication.symbol.name
|
||||
def processName(n: Name): String =
|
||||
n.decodedName.toString.trim // trim is not strictly correct, but macros don't expose the API necessary
|
||||
@tailrec def enclosingVal(trees: List[c.Tree]): String = {
|
||||
trees match {
|
||||
case vd @ ValDef(_, name, _, _) :: ts => processName(name)
|
||||
case (_: ApplyTree | _: Select | _: TypeApply) :: xs => enclosingVal(xs)
|
||||
// lazy val x: X = <methodName> has this form for some reason (only when the explicit type is present, though)
|
||||
case Block(_, _) :: DefDef(mods, name, _, _, _, _) :: xs if mods.hasFlag(Flag.LAZY) =>
|
||||
processName(name)
|
||||
case _ =>
|
||||
c.error(c.enclosingPosition, invalidEnclosingTree(methodName.decodedName.toString))
|
||||
"<error>"
|
||||
}
|
||||
}
|
||||
enclosingVal(enclosingTrees(c).toList)
|
||||
}
|
||||
|
||||
def enclosingTrees(c: blackbox.Context): Seq[c.Tree] =
|
||||
c.asInstanceOf[reflect.macros.runtime.Context].callsiteTyper.context.enclosingContextChain.map(_.tree.asInstanceOf[c.Tree])
|
||||
c.asInstanceOf[reflect.macros.runtime.Context]
|
||||
.callsiteTyper
|
||||
.context
|
||||
.enclosingContextChain
|
||||
.map(_.tree.asInstanceOf[c.Tree])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import sbt.internal.util.appmacro.{ Convert, Converted, Instance, MixedBuilder,
|
|||
|
||||
object InitializeInstance extends MonadInstance {
|
||||
type M[x] = Initialize[x]
|
||||
def app[K[L[x]], Z](in: K[Initialize], f: K[Id] => Z)(implicit a: AList[K]): Initialize[Z] = Def.app[K, Z](in)(f)(a)
|
||||
def app[K[L[x]], Z](in: K[Initialize], f: K[Id] => Z)(implicit a: AList[K]): Initialize[Z] =
|
||||
Def.app[K, Z](in)(f)(a)
|
||||
def map[S, T](in: Initialize[S], f: S => T): Initialize[T] = Def.map(in)(f)
|
||||
def flatten[T](in: Initialize[Initialize[T]]): Initialize[T] = Def.bind(in)(idFun[Initialize[T]])
|
||||
def pure[T](t: () => T): Initialize[T] = Def.pure(t)
|
||||
|
|
@ -25,23 +26,30 @@ object InitializeConvert extends Convert {
|
|||
case _ => Converted.NotApplicable
|
||||
}
|
||||
|
||||
private def convert[T: c.WeakTypeTag](c: blackbox.Context)(in: c.Tree): Converted[c.type] =
|
||||
{
|
||||
val i = c.Expr[Initialize[T]](in)
|
||||
val t = c.universe.reify(i.splice).tree
|
||||
Converted.Success(t)
|
||||
}
|
||||
private def convert[T: c.WeakTypeTag](c: blackbox.Context)(in: c.Tree): Converted[c.type] = {
|
||||
val i = c.Expr[Initialize[T]](in)
|
||||
val t = c.universe.reify(i.splice).tree
|
||||
Converted.Success(t)
|
||||
}
|
||||
|
||||
private def failTask[C <: blackbox.Context with Singleton](c: C)(pos: c.Position): Converted[c.type] =
|
||||
private def failTask[C <: blackbox.Context with Singleton](c: C)(
|
||||
pos: c.Position): Converted[c.type] =
|
||||
Converted.Failure(pos, "A setting cannot depend on a task")
|
||||
private def failPrevious[C <: blackbox.Context with Singleton](c: C)(pos: c.Position): Converted[c.type] =
|
||||
private def failPrevious[C <: blackbox.Context with Singleton](c: C)(
|
||||
pos: c.Position): Converted[c.type] =
|
||||
Converted.Failure(pos, "A setting cannot depend on a task's previous value.")
|
||||
}
|
||||
|
||||
object SettingMacro {
|
||||
def settingMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[Initialize[T]] =
|
||||
Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder)(Left(t), Instance.idTransform[c.type])
|
||||
def settingMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[T]): c.Expr[Initialize[T]] =
|
||||
Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder)(
|
||||
Left(t),
|
||||
Instance.idTransform[c.type])
|
||||
|
||||
def settingDynMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[Initialize[T]]): c.Expr[Initialize[T]] =
|
||||
Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder)(Right(t), Instance.idTransform[c.type])
|
||||
def settingDynMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[Initialize[T]]): c.Expr[Initialize[T]] =
|
||||
Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder)(
|
||||
Right(t),
|
||||
Instance.idTransform[c.type])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,23 +38,29 @@ object ParserInstance extends Instance {
|
|||
}
|
||||
|
||||
/** Composes the Task and Initialize Instances to provide an Instance for [T] Initialize[Task[T]].*/
|
||||
object FullInstance extends Instance.Composed[Initialize, Task](InitializeInstance, TaskInstance) with MonadInstance {
|
||||
object FullInstance
|
||||
extends Instance.Composed[Initialize, Task](InitializeInstance, TaskInstance)
|
||||
with MonadInstance {
|
||||
type SS = sbt.internal.util.Settings[Scope]
|
||||
val settingsData = TaskKey[SS]("settings-data", "Provides access to the project data for the build.", KeyRanks.DTask)
|
||||
val settingsData = TaskKey[SS]("settings-data",
|
||||
"Provides access to the project data for the build.",
|
||||
KeyRanks.DTask)
|
||||
|
||||
def flatten[T](in: Initialize[Task[Initialize[Task[T]]]]): Initialize[Task[T]] = {
|
||||
import Scoped._
|
||||
(in, settingsData, Def.capturedTransformations) { (a: Task[Initialize[Task[T]]], data: Task[SS], f) =>
|
||||
import TaskExtra.multT2Task
|
||||
(a, data) flatMap { case (a, d) => f(a) evaluate d }
|
||||
(in, settingsData, Def.capturedTransformations) {
|
||||
(a: Task[Initialize[Task[T]]], data: Task[SS], f) =>
|
||||
import TaskExtra.multT2Task
|
||||
(a, data) flatMap { case (a, d) => f(a) evaluate d }
|
||||
}
|
||||
}
|
||||
|
||||
def flattenFun[S, T](in: Initialize[Task[S => Initialize[Task[T]]]]): Initialize[S => Task[T]] = {
|
||||
import Scoped._
|
||||
(in, settingsData, Def.capturedTransformations) { (a: Task[S => Initialize[Task[T]]], data: Task[SS], f) => (s: S) =>
|
||||
import TaskExtra.multT2Task
|
||||
(a, data) flatMap { case (af, d) => f(af(s)) evaluate d }
|
||||
(in, settingsData, Def.capturedTransformations) {
|
||||
(a: Task[S => Initialize[Task[T]]], data: Task[SS], f) => (s: S) =>
|
||||
import TaskExtra.multT2Task
|
||||
(a, data) flatMap { case (af, d) => f(af(s)) evaluate d }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -68,241 +74,273 @@ object TaskMacro {
|
|||
final val TransformInitName = "transform"
|
||||
final val InputTaskCreateDynName = "createDyn"
|
||||
final val InputTaskCreateFreeName = "createFree"
|
||||
final val append1Migration = "`<+=` operator is deprecated. Try `lhs += { x.value }`\n or see http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html."
|
||||
final val appendNMigration = "`<++=` operator is deprecated. Try `lhs ++= { x.value }`\n or see http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html."
|
||||
final val append1Migration =
|
||||
"`<+=` operator is deprecated. Try `lhs += { x.value }`\n or see http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html."
|
||||
final val appendNMigration =
|
||||
"`<++=` operator is deprecated. Try `lhs ++= { x.value }`\n or see http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html."
|
||||
final val assignMigration =
|
||||
"""`<<=` operator is deprecated. Use `key := { x.value }` or `key ~= (old => { newValue })`.
|
||||
|See http://www.scala-sbt.org/0.13/docs/Migrating-from-sbt-012x.html""".stripMargin
|
||||
|
||||
def taskMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[Initialize[Task[T]]] =
|
||||
Instance.contImpl[T, Id](c, FullInstance, FullConvert, MixedBuilder)(Left(t), Instance.idTransform[c.type])
|
||||
def taskMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[T]): c.Expr[Initialize[Task[T]]] =
|
||||
Instance.contImpl[T, Id](c, FullInstance, FullConvert, MixedBuilder)(
|
||||
Left(t),
|
||||
Instance.idTransform[c.type])
|
||||
|
||||
def taskDynMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[Task[T]]] =
|
||||
Instance.contImpl[T, Id](c, FullInstance, FullConvert, MixedBuilder)(Right(t), Instance.idTransform[c.type])
|
||||
def taskDynMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[Task[T]]] =
|
||||
Instance.contImpl[T, Id](c, FullInstance, FullConvert, MixedBuilder)(
|
||||
Right(t),
|
||||
Instance.idTransform[c.type])
|
||||
|
||||
/** Implementation of := macro for settings. */
|
||||
def settingAssignMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[T]): c.Expr[Setting[T]] =
|
||||
{
|
||||
val init = SettingMacro.settingMacroImpl[T](c)(v)
|
||||
val assign = transformMacroImpl(c)(init.tree)(AssignInitName)
|
||||
c.Expr[Setting[T]](assign)
|
||||
}
|
||||
def settingAssignMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
v: c.Expr[T]): c.Expr[Setting[T]] = {
|
||||
val init = SettingMacro.settingMacroImpl[T](c)(v)
|
||||
val assign = transformMacroImpl(c)(init.tree)(AssignInitName)
|
||||
c.Expr[Setting[T]](assign)
|
||||
}
|
||||
|
||||
/** Implementation of := macro for tasks. */
|
||||
def taskAssignMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[T]): c.Expr[Setting[Task[T]]] =
|
||||
{
|
||||
val init = taskMacroImpl[T](c)(v)
|
||||
val assign = transformMacroImpl(c)(init.tree)(AssignInitName)
|
||||
c.Expr[Setting[Task[T]]](assign)
|
||||
}
|
||||
def taskAssignMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
v: c.Expr[T]): c.Expr[Setting[Task[T]]] = {
|
||||
val init = taskMacroImpl[T](c)(v)
|
||||
val assign = transformMacroImpl(c)(init.tree)(AssignInitName)
|
||||
c.Expr[Setting[Task[T]]](assign)
|
||||
}
|
||||
|
||||
// Error macros (Restligeist)
|
||||
// These macros are there just so we can fail old operators like `<<=` and provide useful migration information.
|
||||
|
||||
def fakeSettingAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)(app: c.Expr[Initialize[T]]): c.Expr[Setting[T]] =
|
||||
def fakeSettingAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
app: c.Expr[Initialize[T]]): c.Expr[Setting[T]] =
|
||||
ContextUtil.selectMacroImpl[Setting[T]](c) { (ts, pos) =>
|
||||
c.abort(pos, assignMigration)
|
||||
}
|
||||
def fakeSettingAppend1Position[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[Initialize[V]])(a: c.Expr[Append.Value[S, V]]): c.Expr[Setting[S]] =
|
||||
def fakeSettingAppend1Position[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)(
|
||||
v: c.Expr[Initialize[V]])(a: c.Expr[Append.Value[S, V]]): c.Expr[Setting[S]] =
|
||||
ContextUtil.selectMacroImpl[Setting[S]](c) { (ts, pos) =>
|
||||
c.abort(pos, append1Migration)
|
||||
}
|
||||
def fakeSettingAppendNPosition[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[Initialize[V]])(a: c.Expr[Append.Values[S, V]]): c.Expr[Setting[S]] =
|
||||
def fakeSettingAppendNPosition[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)(
|
||||
vs: c.Expr[Initialize[V]])(a: c.Expr[Append.Values[S, V]]): c.Expr[Setting[S]] =
|
||||
ContextUtil.selectMacroImpl[Setting[S]](c) { (ts, pos) =>
|
||||
c.abort(pos, appendNMigration)
|
||||
}
|
||||
def fakeItaskAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)(app: c.Expr[Initialize[Task[T]]]): c.Expr[Setting[Task[T]]] =
|
||||
def fakeItaskAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
app: c.Expr[Initialize[Task[T]]]): c.Expr[Setting[Task[T]]] =
|
||||
ContextUtil.selectMacroImpl[Setting[Task[T]]](c) { (ts, pos) =>
|
||||
c.abort(pos, assignMigration)
|
||||
}
|
||||
def fakeTaskAppend1Position[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[Initialize[Task[V]]])(a: c.Expr[Append.Value[S, V]]): c.Expr[Setting[Task[S]]] =
|
||||
def fakeTaskAppend1Position[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)(
|
||||
v: c.Expr[Initialize[Task[V]]])(a: c.Expr[Append.Value[S, V]]): c.Expr[Setting[Task[S]]] =
|
||||
ContextUtil.selectMacroImpl[Setting[Task[S]]](c) { (ts, pos) =>
|
||||
c.abort(pos, append1Migration)
|
||||
}
|
||||
def fakeTaskAppendNPosition[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[Initialize[Task[V]]])(a: c.Expr[Append.Values[S, V]]): c.Expr[Setting[Task[S]]] =
|
||||
def fakeTaskAppendNPosition[S: c.WeakTypeTag, V: c.WeakTypeTag](c: blackbox.Context)(
|
||||
vs: c.Expr[Initialize[Task[V]]])(a: c.Expr[Append.Values[S, V]]): c.Expr[Setting[Task[S]]] =
|
||||
ContextUtil.selectMacroImpl[Setting[Task[S]]](c) { (ts, pos) =>
|
||||
c.abort(pos, appendNMigration)
|
||||
}
|
||||
|
||||
/* Implementations of <<= macro variations for tasks and settings. These just get the source position of the call site.*/
|
||||
|
||||
def itaskAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)(app: c.Expr[Initialize[Task[T]]]): c.Expr[Setting[Task[T]]] =
|
||||
def itaskAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
app: c.Expr[Initialize[Task[T]]]): c.Expr[Setting[Task[T]]] =
|
||||
settingAssignPosition(c)(app)
|
||||
|
||||
def taskAssignPositionT[T: c.WeakTypeTag](c: blackbox.Context)(app: c.Expr[Task[T]]): c.Expr[Setting[Task[T]]] =
|
||||
def taskAssignPositionT[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
app: c.Expr[Task[T]]): c.Expr[Setting[Task[T]]] =
|
||||
itaskAssignPosition(c)(c.universe.reify { Def.valueStrict(app.splice) })
|
||||
|
||||
def taskAssignPositionPure[T: c.WeakTypeTag](c: blackbox.Context)(app: c.Expr[T]): c.Expr[Setting[Task[T]]] =
|
||||
def taskAssignPositionPure[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
app: c.Expr[T]): c.Expr[Setting[Task[T]]] =
|
||||
taskAssignPositionT(c)(c.universe.reify { TaskExtra.constant(app.splice) })
|
||||
|
||||
def taskTransformPosition[S: c.WeakTypeTag](c: blackbox.Context)(f: c.Expr[S => S]): c.Expr[Setting[Task[S]]] =
|
||||
def taskTransformPosition[S: c.WeakTypeTag](c: blackbox.Context)(
|
||||
f: c.Expr[S => S]): c.Expr[Setting[Task[S]]] =
|
||||
c.Expr[Setting[Task[S]]](transformMacroImpl(c)(f.tree)(TransformInitName))
|
||||
|
||||
def settingTransformPosition[S: c.WeakTypeTag](c: blackbox.Context)(f: c.Expr[S => S]): c.Expr[Setting[S]] =
|
||||
def settingTransformPosition[S: c.WeakTypeTag](c: blackbox.Context)(
|
||||
f: c.Expr[S => S]): c.Expr[Setting[S]] =
|
||||
c.Expr[Setting[S]](transformMacroImpl(c)(f.tree)(TransformInitName))
|
||||
|
||||
def itaskTransformPosition[S: c.WeakTypeTag](c: blackbox.Context)(f: c.Expr[S => S]): c.Expr[Setting[S]] =
|
||||
def itaskTransformPosition[S: c.WeakTypeTag](c: blackbox.Context)(
|
||||
f: c.Expr[S => S]): c.Expr[Setting[S]] =
|
||||
c.Expr[Setting[S]](transformMacroImpl(c)(f.tree)(TransformInitName))
|
||||
|
||||
def settingAssignPure[T: c.WeakTypeTag](c: blackbox.Context)(app: c.Expr[T]): c.Expr[Setting[T]] =
|
||||
def settingAssignPure[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
app: c.Expr[T]): c.Expr[Setting[T]] =
|
||||
settingAssignPosition(c)(c.universe.reify { Def.valueStrict(app.splice) })
|
||||
|
||||
def settingAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)(app: c.Expr[Initialize[T]]): c.Expr[Setting[T]] =
|
||||
def settingAssignPosition[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
app: c.Expr[Initialize[T]]): c.Expr[Setting[T]] =
|
||||
c.Expr[Setting[T]](transformMacroImpl(c)(app.tree)(AssignInitName))
|
||||
|
||||
/** Implementation of := macro for tasks. */
|
||||
def inputTaskAssignMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[T]): c.Expr[Setting[InputTask[T]]] =
|
||||
{
|
||||
val init = inputTaskMacroImpl[T](c)(v)
|
||||
val assign = transformMacroImpl(c)(init.tree)(AssignInitName)
|
||||
c.Expr[Setting[InputTask[T]]](assign)
|
||||
}
|
||||
def inputTaskAssignMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
v: c.Expr[T]): c.Expr[Setting[InputTask[T]]] = {
|
||||
val init = inputTaskMacroImpl[T](c)(v)
|
||||
val assign = transformMacroImpl(c)(init.tree)(AssignInitName)
|
||||
c.Expr[Setting[InputTask[T]]](assign)
|
||||
}
|
||||
|
||||
/** Implementation of += macro for tasks. */
|
||||
def taskAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])(a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[Task[T]]] =
|
||||
{
|
||||
val init = taskMacroImpl[U](c)(v)
|
||||
val append = appendMacroImpl(c)(init.tree, a.tree)(Append1InitName)
|
||||
c.Expr[Setting[Task[T]]](append)
|
||||
}
|
||||
def taskAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])(
|
||||
a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[Task[T]]] = {
|
||||
val init = taskMacroImpl[U](c)(v)
|
||||
val append = appendMacroImpl(c)(init.tree, a.tree)(Append1InitName)
|
||||
c.Expr[Setting[Task[T]]](append)
|
||||
}
|
||||
|
||||
/** Implementation of += macro for settings. */
|
||||
def settingAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])(a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[T]] =
|
||||
{
|
||||
import c.universe._
|
||||
val ttpe = c.weakTypeOf[T]
|
||||
val typeArgs = ttpe.typeArgs
|
||||
v.tree.tpe match {
|
||||
// To allow Initialize[Task[A]] in the position of += RHS, we're going to call "taskValue" automatically.
|
||||
case tpe if typeArgs.nonEmpty && (tpe weak_<:< c.weakTypeOf[Initialize[_]]) =>
|
||||
c.macroApplication match {
|
||||
case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), _) =>
|
||||
val tree = Apply(TypeApply(Select(preT, TermName("+=").encodedName), TypeTree(typeArgs.head) :: Nil), Select(v.tree, TermName("taskValue").encodedName) :: Nil)
|
||||
c.Expr[Setting[T]](tree)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
case _ =>
|
||||
val init = SettingMacro.settingMacroImpl[U](c)(v)
|
||||
val append = appendMacroImpl(c)(init.tree, a.tree)(Append1InitName)
|
||||
c.Expr[Setting[T]](append)
|
||||
}
|
||||
def settingAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])(
|
||||
a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[T]] = {
|
||||
import c.universe._
|
||||
val ttpe = c.weakTypeOf[T]
|
||||
val typeArgs = ttpe.typeArgs
|
||||
v.tree.tpe match {
|
||||
// To allow Initialize[Task[A]] in the position of += RHS, we're going to call "taskValue" automatically.
|
||||
case tpe if typeArgs.nonEmpty && (tpe weak_<:< c.weakTypeOf[Initialize[_]]) =>
|
||||
c.macroApplication match {
|
||||
case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), _) =>
|
||||
val tree = Apply(
|
||||
TypeApply(Select(preT, TermName("+=").encodedName), TypeTree(typeArgs.head) :: Nil),
|
||||
Select(v.tree, TermName("taskValue").encodedName) :: Nil)
|
||||
c.Expr[Setting[T]](tree)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
case _ =>
|
||||
val init = SettingMacro.settingMacroImpl[U](c)(v)
|
||||
val append = appendMacroImpl(c)(init.tree, a.tree)(Append1InitName)
|
||||
c.Expr[Setting[T]](append)
|
||||
}
|
||||
}
|
||||
|
||||
/** Implementation of ++= macro for tasks. */
|
||||
def taskAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])(a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[Task[T]]] =
|
||||
{
|
||||
val init = taskMacroImpl[U](c)(vs)
|
||||
val append = appendMacroImpl(c)(init.tree, a.tree)(AppendNInitName)
|
||||
c.Expr[Setting[Task[T]]](append)
|
||||
}
|
||||
def taskAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])(
|
||||
a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[Task[T]]] = {
|
||||
val init = taskMacroImpl[U](c)(vs)
|
||||
val append = appendMacroImpl(c)(init.tree, a.tree)(AppendNInitName)
|
||||
c.Expr[Setting[Task[T]]](append)
|
||||
}
|
||||
|
||||
/** Implementation of ++= macro for settings. */
|
||||
def settingAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])(a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[T]] =
|
||||
{
|
||||
val init = SettingMacro.settingMacroImpl[U](c)(vs)
|
||||
val append = appendMacroImpl(c)(init.tree, a.tree)(AppendNInitName)
|
||||
c.Expr[Setting[T]](append)
|
||||
}
|
||||
def settingAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])(
|
||||
a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[T]] = {
|
||||
val init = SettingMacro.settingMacroImpl[U](c)(vs)
|
||||
val append = appendMacroImpl(c)(init.tree, a.tree)(AppendNInitName)
|
||||
c.Expr[Setting[T]](append)
|
||||
}
|
||||
|
||||
/** Implementation of -= macro for tasks. */
|
||||
def taskRemove1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])(r: c.Expr[Remove.Value[T, U]]): c.Expr[Setting[Task[T]]] =
|
||||
{
|
||||
val init = taskMacroImpl[U](c)(v)
|
||||
val remove = removeMacroImpl(c)(init.tree, r.tree)(Remove1InitName)
|
||||
c.Expr[Setting[Task[T]]](remove)
|
||||
}
|
||||
def taskRemove1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])(
|
||||
r: c.Expr[Remove.Value[T, U]]): c.Expr[Setting[Task[T]]] = {
|
||||
val init = taskMacroImpl[U](c)(v)
|
||||
val remove = removeMacroImpl(c)(init.tree, r.tree)(Remove1InitName)
|
||||
c.Expr[Setting[Task[T]]](remove)
|
||||
}
|
||||
|
||||
/** Implementation of -= macro for settings. */
|
||||
def settingRemove1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])(r: c.Expr[Remove.Value[T, U]]): c.Expr[Setting[T]] =
|
||||
{
|
||||
val init = SettingMacro.settingMacroImpl[U](c)(v)
|
||||
val remove = removeMacroImpl(c)(init.tree, r.tree)(Remove1InitName)
|
||||
c.Expr[Setting[T]](remove)
|
||||
}
|
||||
def settingRemove1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(v: c.Expr[U])(
|
||||
r: c.Expr[Remove.Value[T, U]]): c.Expr[Setting[T]] = {
|
||||
val init = SettingMacro.settingMacroImpl[U](c)(v)
|
||||
val remove = removeMacroImpl(c)(init.tree, r.tree)(Remove1InitName)
|
||||
c.Expr[Setting[T]](remove)
|
||||
}
|
||||
|
||||
/** Implementation of --= macro for tasks. */
|
||||
def taskRemoveNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])(r: c.Expr[Remove.Values[T, U]]): c.Expr[Setting[Task[T]]] =
|
||||
{
|
||||
val init = taskMacroImpl[U](c)(vs)
|
||||
val remove = removeMacroImpl(c)(init.tree, r.tree)(RemoveNInitName)
|
||||
c.Expr[Setting[Task[T]]](remove)
|
||||
}
|
||||
def taskRemoveNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])(
|
||||
r: c.Expr[Remove.Values[T, U]]): c.Expr[Setting[Task[T]]] = {
|
||||
val init = taskMacroImpl[U](c)(vs)
|
||||
val remove = removeMacroImpl(c)(init.tree, r.tree)(RemoveNInitName)
|
||||
c.Expr[Setting[Task[T]]](remove)
|
||||
}
|
||||
|
||||
/** Implementation of --= macro for settings. */
|
||||
def settingRemoveNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])(r: c.Expr[Remove.Values[T, U]]): c.Expr[Setting[T]] =
|
||||
{
|
||||
val init = SettingMacro.settingMacroImpl[U](c)(vs)
|
||||
val remove = removeMacroImpl(c)(init.tree, r.tree)(RemoveNInitName)
|
||||
c.Expr[Setting[T]](remove)
|
||||
}
|
||||
def settingRemoveNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)(vs: c.Expr[U])(
|
||||
r: c.Expr[Remove.Values[T, U]]): c.Expr[Setting[T]] = {
|
||||
val init = SettingMacro.settingMacroImpl[U](c)(vs)
|
||||
val remove = removeMacroImpl(c)(init.tree, r.tree)(RemoveNInitName)
|
||||
c.Expr[Setting[T]](remove)
|
||||
}
|
||||
|
||||
private[this] def appendMacroImpl(c: blackbox.Context)(init: c.Tree, append: c.Tree)(newName: String): c.Tree =
|
||||
{
|
||||
import c.universe._
|
||||
private[this] def appendMacroImpl(c: blackbox.Context)(init: c.Tree, append: c.Tree)(
|
||||
newName: String): c.Tree = {
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), _) =>
|
||||
Apply(Apply(TypeApply(Select(preT, TermName(newName).encodedName), targs),
|
||||
init :: sourcePosition(c).tree :: Nil),
|
||||
append :: Nil)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def removeMacroImpl(c: blackbox.Context)(init: c.Tree, remove: c.Tree)(
|
||||
newName: String): c.Tree = {
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), r) =>
|
||||
Apply(Apply(TypeApply(Select(preT, TermName(newName).encodedName), targs),
|
||||
init :: sourcePosition(c).tree :: Nil),
|
||||
r)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def transformMacroImpl(c: blackbox.Context)(init: c.Tree)(
|
||||
newName: String): c.Tree = {
|
||||
import c.universe._
|
||||
val target =
|
||||
c.macroApplication match {
|
||||
case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), _) =>
|
||||
Apply(Apply(TypeApply(Select(preT, TermName(newName).encodedName), targs), init :: sourcePosition(c).tree :: Nil), append :: Nil)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
case Apply(Select(prefix, _), _) => prefix
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
Apply.apply(Select(target, TermName(newName).encodedName),
|
||||
init :: sourcePosition(c).tree :: Nil)
|
||||
}
|
||||
|
||||
private[this] def removeMacroImpl(c: blackbox.Context)(init: c.Tree, remove: c.Tree)(newName: String): c.Tree =
|
||||
{
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), r) =>
|
||||
Apply(Apply(TypeApply(Select(preT, TermName(newName).encodedName), targs), init :: sourcePosition(c).tree :: Nil), r)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
private[this] def sourcePosition(c: blackbox.Context): c.Expr[SourcePosition] = {
|
||||
import c.universe.reify
|
||||
val pos = c.enclosingPosition
|
||||
if (!pos.isInstanceOf[UndefinedPosition] && pos.line >= 0 && pos.source != null) {
|
||||
val f = pos.source.file
|
||||
val name = constant[String](c, settingSource(c, f.path, f.name))
|
||||
val line = constant[Int](c, pos.line)
|
||||
reify { LinePosition(name.splice, line.splice) }
|
||||
} else
|
||||
reify { NoPosition }
|
||||
}
|
||||
|
||||
private[this] def transformMacroImpl(c: blackbox.Context)(init: c.Tree)(newName: String): c.Tree =
|
||||
{
|
||||
import c.universe._
|
||||
val target =
|
||||
c.macroApplication match {
|
||||
case Apply(Select(prefix, _), _) => prefix
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
Apply.apply(Select(target, TermName(newName).encodedName), init :: sourcePosition(c).tree :: Nil)
|
||||
}
|
||||
|
||||
private[this] def sourcePosition(c: blackbox.Context): c.Expr[SourcePosition] =
|
||||
{
|
||||
import c.universe.reify
|
||||
val pos = c.enclosingPosition
|
||||
if (!pos.isInstanceOf[UndefinedPosition] && pos.line >= 0 && pos.source != null) {
|
||||
val f = pos.source.file
|
||||
val name = constant[String](c, settingSource(c, f.path, f.name))
|
||||
val line = constant[Int](c, pos.line)
|
||||
reify { LinePosition(name.splice, line.splice) }
|
||||
} else
|
||||
reify { NoPosition }
|
||||
}
|
||||
|
||||
private[this] def settingSource(c: blackbox.Context, path: String, name: String): String =
|
||||
{
|
||||
@tailrec def inEmptyPackage(s: c.Symbol): Boolean = s != c.universe.NoSymbol && (
|
||||
s.owner == c.mirror.EmptyPackage || s.owner == c.mirror.EmptyPackageClass || inEmptyPackage(s.owner)
|
||||
)
|
||||
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 settingSource(c: blackbox.Context, path: String, name: String): String = {
|
||||
@tailrec def inEmptyPackage(s: c.Symbol): Boolean = s != c.universe.NoSymbol && (
|
||||
s.owner == c.mirror.EmptyPackage || s.owner == c.mirror.EmptyPackageClass || inEmptyPackage(
|
||||
s.owner)
|
||||
)
|
||||
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: c.TypeTag](c: blackbox.Context, t: T): c.Expr[T] = {
|
||||
import c.universe._
|
||||
c.Expr[T](Literal(Constant(t)))
|
||||
}
|
||||
|
||||
def inputTaskMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] =
|
||||
def inputTaskMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] =
|
||||
inputTaskMacro0[T](c)(t)
|
||||
|
||||
def inputTaskDynMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] =
|
||||
def inputTaskDynMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] =
|
||||
inputTaskDynMacro0[T](c)(t)
|
||||
|
||||
private[this] def inputTaskMacro0[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] =
|
||||
private[this] def inputTaskMacro0[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] =
|
||||
iInitializeMacro(c)(t) { et =>
|
||||
val pt = iParserMacro(c)(et) { pt =>
|
||||
iTaskMacro(c)(pt)
|
||||
|
|
@ -310,145 +348,153 @@ object TaskMacro {
|
|||
c.universe.reify { InputTask.make(pt.splice) }
|
||||
}
|
||||
|
||||
private[this] def iInitializeMacro[M[_], T](c: blackbox.Context)(t: c.Expr[T])(f: c.Expr[T] => c.Expr[M[T]])(implicit tt: c.WeakTypeTag[T], mt: c.WeakTypeTag[M[T]]): c.Expr[Initialize[M[T]]] =
|
||||
{
|
||||
val inner: Transform[c.type, M] = new Transform[c.type, M] { def apply(in: c.Tree): c.Tree = f(c.Expr[T](in)).tree }
|
||||
val cond = c.Expr[T](conditionInputTaskTree(c)(t.tree))
|
||||
Instance.contImpl[T, M](c, InitializeInstance, InputInitConvert, MixedBuilder)(Left(cond), inner)
|
||||
private[this] def iInitializeMacro[M[_], T](c: blackbox.Context)(t: c.Expr[T])(
|
||||
f: c.Expr[T] => c.Expr[M[T]])(implicit tt: c.WeakTypeTag[T],
|
||||
mt: c.WeakTypeTag[M[T]]): c.Expr[Initialize[M[T]]] = {
|
||||
val inner: Transform[c.type, M] = new Transform[c.type, M] {
|
||||
def apply(in: c.Tree): c.Tree = f(c.Expr[T](in)).tree
|
||||
}
|
||||
val cond = c.Expr[T](conditionInputTaskTree(c)(t.tree))
|
||||
Instance
|
||||
.contImpl[T, M](c, InitializeInstance, InputInitConvert, MixedBuilder)(Left(cond), inner)
|
||||
}
|
||||
|
||||
private[this] def conditionInputTaskTree(c: blackbox.Context)(t: c.Tree): c.Tree = {
|
||||
import c.universe._
|
||||
import InputWrapper._
|
||||
def wrapInitTask[T: c.WeakTypeTag](tree: Tree) = {
|
||||
val e = c.Expr[Initialize[Task[T]]](tree)
|
||||
wrapTask[T](c)(wrapInit[Task[T]](c)(e, tree.pos), tree.pos).tree
|
||||
}
|
||||
def wrapInitParser[T: c.WeakTypeTag](tree: Tree) = {
|
||||
val e = c.Expr[Initialize[State => Parser[T]]](tree)
|
||||
ParserInput.wrap[T](c)(wrapInit[State => Parser[T]](c)(e, tree.pos), tree.pos).tree
|
||||
}
|
||||
def wrapInitInput[T: c.WeakTypeTag](tree: Tree) = {
|
||||
val e = c.Expr[Initialize[InputTask[T]]](tree)
|
||||
wrapInput[T](wrapInit[InputTask[T]](c)(e, tree.pos).tree)
|
||||
}
|
||||
def wrapInput[T: c.WeakTypeTag](tree: Tree) = {
|
||||
val e = c.Expr[InputTask[T]](tree)
|
||||
val p = ParserInput.wrap[Task[T]](c)(ParserInput.inputParser(c)(e), tree.pos)
|
||||
wrapTask[T](c)(p, tree.pos).tree
|
||||
}
|
||||
|
||||
private[this] def conditionInputTaskTree(c: blackbox.Context)(t: c.Tree): c.Tree =
|
||||
{
|
||||
import c.universe._
|
||||
import InputWrapper._
|
||||
def wrapInitTask[T: c.WeakTypeTag](tree: Tree) =
|
||||
{
|
||||
val e = c.Expr[Initialize[Task[T]]](tree)
|
||||
wrapTask[T](c)(wrapInit[Task[T]](c)(e, tree.pos), tree.pos).tree
|
||||
}
|
||||
def wrapInitParser[T: c.WeakTypeTag](tree: Tree) =
|
||||
{
|
||||
val e = c.Expr[Initialize[State => Parser[T]]](tree)
|
||||
ParserInput.wrap[T](c)(wrapInit[State => Parser[T]](c)(e, tree.pos), tree.pos).tree
|
||||
}
|
||||
def wrapInitInput[T: c.WeakTypeTag](tree: Tree) =
|
||||
{
|
||||
val e = c.Expr[Initialize[InputTask[T]]](tree)
|
||||
wrapInput[T](wrapInit[InputTask[T]](c)(e, tree.pos).tree)
|
||||
}
|
||||
def wrapInput[T: c.WeakTypeTag](tree: Tree) =
|
||||
{
|
||||
val e = c.Expr[InputTask[T]](tree)
|
||||
val p = ParserInput.wrap[Task[T]](c)(ParserInput.inputParser(c)(e), tree.pos)
|
||||
wrapTask[T](c)(p, tree.pos).tree
|
||||
}
|
||||
def expand(nme: String, tpe: Type, tree: Tree): Converted[c.type] = nme match {
|
||||
case WrapInitTaskName => Converted.Success(wrapInitTask(tree)(c.WeakTypeTag(tpe)))
|
||||
case ParserInput.WrapInitName => Converted.Success(wrapInitParser(tree)(c.WeakTypeTag(tpe)))
|
||||
case WrapInitInputName => Converted.Success(wrapInitInput(tree)(c.WeakTypeTag(tpe)))
|
||||
case WrapInputName => Converted.Success(wrapInput(tree)(c.WeakTypeTag(tpe)))
|
||||
case _ => Converted.NotApplicable
|
||||
}
|
||||
val util = ContextUtil[c.type](c)
|
||||
util.transformWrappers(t, (nme, tpe, tree, original) => expand(nme, tpe, tree))
|
||||
}
|
||||
|
||||
def expand(nme: String, tpe: Type, tree: Tree): Converted[c.type] = nme match {
|
||||
case WrapInitTaskName => Converted.Success(wrapInitTask(tree)(c.WeakTypeTag(tpe)))
|
||||
case ParserInput.WrapInitName => Converted.Success(wrapInitParser(tree)(c.WeakTypeTag(tpe)))
|
||||
case WrapInitInputName => Converted.Success(wrapInitInput(tree)(c.WeakTypeTag(tpe)))
|
||||
case WrapInputName => Converted.Success(wrapInput(tree)(c.WeakTypeTag(tpe)))
|
||||
case _ => Converted.NotApplicable
|
||||
private[this] def iParserMacro[M[_], T](c: blackbox.Context)(t: c.Expr[T])(
|
||||
f: c.Expr[T] => c.Expr[M[T]])(implicit tt: c.WeakTypeTag[T],
|
||||
mt: c.WeakTypeTag[M[T]]): c.Expr[State => Parser[M[T]]] = {
|
||||
val inner: Transform[c.type, M] = new Transform[c.type, M] {
|
||||
def apply(in: c.Tree): c.Tree = f(c.Expr[T](in)).tree
|
||||
}
|
||||
Instance.contImpl[T, M](c, ParserInstance, ParserConvert, MixedBuilder)(Left(t), inner)
|
||||
}
|
||||
|
||||
private[this] def iTaskMacro[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[T]): c.Expr[Task[T]] =
|
||||
Instance
|
||||
.contImpl[T, Id](c, TaskInstance, TaskConvert, MixedBuilder)(Left(t), Instance.idTransform)
|
||||
|
||||
private[this] def inputTaskDynMacro0[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] = {
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
import internal.decorators._
|
||||
|
||||
val tag = implicitly[c.WeakTypeTag[T]]
|
||||
val util = ContextUtil[c.type](c)
|
||||
val it = Ident(util.singleton(InputTask))
|
||||
val isParserWrapper = InitParserConvert.asPredicate(c)
|
||||
val isTaskWrapper = FullConvert.asPredicate(c)
|
||||
val isAnyWrapper = (n: String, tpe: Type, tr: Tree) =>
|
||||
isParserWrapper(n, tpe, tr) || isTaskWrapper(n, tpe, tr)
|
||||
val ttree = t.tree
|
||||
val defs = util.collectDefs(ttree, isAnyWrapper)
|
||||
val checkQual = util.checkReferences(defs, isAnyWrapper)
|
||||
|
||||
// the Symbol for the anonymous function passed to the appropriate Instance.map/flatMap/pure method
|
||||
// this Symbol needs to be known up front so that it can be used as the owner of synthetic vals
|
||||
val functionSym = util.functionSymbol(ttree.pos)
|
||||
var result: Option[(Tree, Type, ValDef)] = None
|
||||
|
||||
// original is the Tree being replaced. It is needed for preserving attributes.
|
||||
def subWrapper(tpe: Type, qual: Tree, original: Tree): Tree =
|
||||
if (result.isDefined) {
|
||||
c.error(
|
||||
qual.pos,
|
||||
"Implementation restriction: a dynamic InputTask can only have a single input parser.")
|
||||
EmptyTree
|
||||
} else {
|
||||
qual.foreach(checkQual)
|
||||
val vd = util.freshValDef(tpe, qual.symbol.pos, functionSym) // val $x: <tpe>
|
||||
result = Some((qual, tpe, vd))
|
||||
val tree = util.refVal(original, vd) // $x
|
||||
tree.setPos(qual.pos) // position needs to be set so that wrapKey passes the position onto the wrapper
|
||||
assert(tree.tpe != null, "Null type: " + tree)
|
||||
tree.setType(tpe)
|
||||
tree
|
||||
}
|
||||
val util = ContextUtil[c.type](c)
|
||||
util.transformWrappers(t, (nme, tpe, tree, original) => expand(nme, tpe, tree))
|
||||
// Tree for InputTask.<name>[<tpeA>, <tpeB>](arg1)(arg2)
|
||||
def inputTaskCreate(name: String, tpeA: Type, tpeB: Type, arg1: Tree, arg2: Tree) = {
|
||||
val typedApp = TypeApply(util.select(it, name), TypeTree(tpeA) :: TypeTree(tpeB) :: Nil)
|
||||
val app = ApplyTree(ApplyTree(typedApp, arg1 :: Nil), arg2 :: Nil)
|
||||
c.Expr[Initialize[InputTask[T]]](app)
|
||||
}
|
||||
|
||||
private[this] def iParserMacro[M[_], T](c: blackbox.Context)(t: c.Expr[T])(f: c.Expr[T] => c.Expr[M[T]])(implicit tt: c.WeakTypeTag[T], mt: c.WeakTypeTag[M[T]]): c.Expr[State => Parser[M[T]]] =
|
||||
{
|
||||
val inner: Transform[c.type, M] = new Transform[c.type, M] { def apply(in: c.Tree): c.Tree = f(c.Expr[T](in)).tree }
|
||||
Instance.contImpl[T, M](c, ParserInstance, ParserConvert, MixedBuilder)(Left(t), inner)
|
||||
// Tree for InputTask.createFree[<tpe>](arg1)
|
||||
def inputTaskCreateFree(tpe: Type, arg: Tree) = {
|
||||
val typedApp = TypeApply(util.select(it, InputTaskCreateFreeName), TypeTree(tpe) :: Nil)
|
||||
val app = ApplyTree(typedApp, arg :: Nil)
|
||||
c.Expr[Initialize[InputTask[T]]](app)
|
||||
}
|
||||
def expandTask[I: WeakTypeTag](dyn: Boolean, tx: Tree): c.Expr[Initialize[Task[I]]] =
|
||||
if (dyn)
|
||||
taskDynMacroImpl[I](c)(c.Expr[Initialize[Task[I]]](tx))
|
||||
else
|
||||
taskMacroImpl[I](c)(c.Expr[I](tx))
|
||||
def wrapTag[I: WeakTypeTag]: WeakTypeTag[Initialize[Task[I]]] = weakTypeTag
|
||||
|
||||
private[this] def iTaskMacro[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[Task[T]] =
|
||||
Instance.contImpl[T, Id](c, TaskInstance, TaskConvert, MixedBuilder)(Left(t), Instance.idTransform)
|
||||
|
||||
private[this] def inputTaskDynMacro0[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] =
|
||||
{
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
import internal.decorators._
|
||||
|
||||
val tag = implicitly[c.WeakTypeTag[T]]
|
||||
val util = ContextUtil[c.type](c)
|
||||
val it = Ident(util.singleton(InputTask))
|
||||
val isParserWrapper = InitParserConvert.asPredicate(c)
|
||||
val isTaskWrapper = FullConvert.asPredicate(c)
|
||||
val isAnyWrapper = (n: String, tpe: Type, tr: Tree) => isParserWrapper(n, tpe, tr) || isTaskWrapper(n, tpe, tr)
|
||||
val ttree = t.tree
|
||||
val defs = util.collectDefs(ttree, isAnyWrapper)
|
||||
val checkQual = util.checkReferences(defs, isAnyWrapper)
|
||||
|
||||
// the Symbol for the anonymous function passed to the appropriate Instance.map/flatMap/pure method
|
||||
// this Symbol needs to be known up front so that it can be used as the owner of synthetic vals
|
||||
val functionSym = util.functionSymbol(ttree.pos)
|
||||
var result: Option[(Tree, Type, ValDef)] = None
|
||||
|
||||
// original is the Tree being replaced. It is needed for preserving attributes.
|
||||
def subWrapper(tpe: Type, qual: Tree, original: Tree): Tree =
|
||||
if (result.isDefined) {
|
||||
c.error(qual.pos, "Implementation restriction: a dynamic InputTask can only have a single input parser.")
|
||||
EmptyTree
|
||||
} else {
|
||||
qual.foreach(checkQual)
|
||||
val vd = util.freshValDef(tpe, qual.symbol.pos, functionSym) // val $x: <tpe>
|
||||
result = Some((qual, tpe, vd))
|
||||
val tree = util.refVal(original, vd) // $x
|
||||
tree.setPos(qual.pos) // position needs to be set so that wrapKey passes the position onto the wrapper
|
||||
assert(tree.tpe != null, "Null type: " + tree)
|
||||
tree.setType(tpe)
|
||||
tree
|
||||
}
|
||||
// Tree for InputTask.<name>[<tpeA>, <tpeB>](arg1)(arg2)
|
||||
def inputTaskCreate(name: String, tpeA: Type, tpeB: Type, arg1: Tree, arg2: Tree) =
|
||||
{
|
||||
val typedApp = TypeApply(util.select(it, name), TypeTree(tpeA) :: TypeTree(tpeB) :: Nil)
|
||||
val app = ApplyTree(ApplyTree(typedApp, arg1 :: Nil), arg2 :: Nil)
|
||||
c.Expr[Initialize[InputTask[T]]](app)
|
||||
}
|
||||
// Tree for InputTask.createFree[<tpe>](arg1)
|
||||
def inputTaskCreateFree(tpe: Type, arg: Tree) =
|
||||
{
|
||||
val typedApp = TypeApply(util.select(it, InputTaskCreateFreeName), TypeTree(tpe) :: Nil)
|
||||
val app = ApplyTree(typedApp, arg :: Nil)
|
||||
c.Expr[Initialize[InputTask[T]]](app)
|
||||
}
|
||||
def expandTask[I: WeakTypeTag](dyn: Boolean, tx: Tree): c.Expr[Initialize[Task[I]]] =
|
||||
if (dyn)
|
||||
taskDynMacroImpl[I](c)(c.Expr[Initialize[Task[I]]](tx))
|
||||
else
|
||||
taskMacroImpl[I](c)(c.Expr[I](tx))
|
||||
def wrapTag[I: WeakTypeTag]: WeakTypeTag[Initialize[Task[I]]] = weakTypeTag
|
||||
|
||||
def sub(name: String, tpe: Type, qual: Tree, selection: Tree): Converted[c.type] =
|
||||
{
|
||||
val tag = c.WeakTypeTag[T](tpe)
|
||||
InitParserConvert(c)(name, qual)(tag) transform { tree =>
|
||||
subWrapper(tpe, tree, selection)
|
||||
}
|
||||
}
|
||||
|
||||
val tx = util.transformWrappers(ttree, (n, tpe, tree, replace) => sub(n, tpe, tree, replace))
|
||||
result match {
|
||||
case Some((p, tpe, param)) =>
|
||||
val fCore = util.createFunction(param :: Nil, tx, functionSym)
|
||||
val bodyTpe = wrapTag(tag).tpe
|
||||
val fTpe = util.functionType(tpe :: Nil, bodyTpe)
|
||||
val fTag = c.WeakTypeTag[Any](fTpe) // don't know the actual type yet, so use Any
|
||||
val fInit = expandTask(false, fCore)(fTag).tree
|
||||
inputTaskCreate(InputTaskCreateDynName, tpe, tag.tpe, p, fInit)
|
||||
case None =>
|
||||
val init = expandTask[T](true, tx).tree
|
||||
inputTaskCreateFree(tag.tpe, init)
|
||||
def sub(name: String, tpe: Type, qual: Tree, selection: Tree): Converted[c.type] = {
|
||||
val tag = c.WeakTypeTag[T](tpe)
|
||||
InitParserConvert(c)(name, qual)(tag) transform { tree =>
|
||||
subWrapper(tpe, tree, selection)
|
||||
}
|
||||
}
|
||||
|
||||
val tx = util.transformWrappers(ttree, (n, tpe, tree, replace) => sub(n, tpe, tree, replace))
|
||||
result match {
|
||||
case Some((p, tpe, param)) =>
|
||||
val fCore = util.createFunction(param :: Nil, tx, functionSym)
|
||||
val bodyTpe = wrapTag(tag).tpe
|
||||
val fTpe = util.functionType(tpe :: Nil, bodyTpe)
|
||||
val fTag = c.WeakTypeTag[Any](fTpe) // don't know the actual type yet, so use Any
|
||||
val fInit = expandTask(false, fCore)(fTag).tree
|
||||
inputTaskCreate(InputTaskCreateDynName, tpe, tag.tpe, p, fInit)
|
||||
case None =>
|
||||
val init = expandTask[T](true, tx).tree
|
||||
inputTaskCreateFree(tag.tpe, init)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object PlainTaskMacro {
|
||||
def task[T](t: T): Task[T] = macro taskImpl[T]
|
||||
def taskImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[T]): c.Expr[Task[T]] =
|
||||
Instance.contImpl[T, Id](c, TaskInstance, TaskConvert, MixedBuilder)(Left(t), Instance.idTransform[c.type])
|
||||
Instance.contImpl[T, Id](c, TaskInstance, TaskConvert, MixedBuilder)(
|
||||
Left(t),
|
||||
Instance.idTransform[c.type])
|
||||
|
||||
def taskDyn[T](t: Task[T]): Task[T] = macro taskDynImpl[T]
|
||||
def taskDynImpl[T: c.WeakTypeTag](c: blackbox.Context)(t: c.Expr[Task[T]]): c.Expr[Task[T]] =
|
||||
Instance.contImpl[T, Id](c, TaskInstance, TaskConvert, MixedBuilder)(Right(t), Instance.idTransform[c.type])
|
||||
Instance.contImpl[T, Id](c, TaskInstance, TaskConvert, MixedBuilder)(
|
||||
Right(t),
|
||||
Instance.idTransform[c.type])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ object Assign {
|
|||
bk ++= Seq(z.value)
|
||||
)*/
|
||||
|
||||
val zz = Def.task { mk.value + tk.value + mk.value + tk.value + mk.value + tk.value + mk.value + tk.value + mk.value + tk.value + mk.value + tk.value }
|
||||
val zz = Def.task {
|
||||
mk.value + tk.value + mk.value + tk.value + mk.value + tk.value + mk.value + tk.value + mk.value + tk.value + mk.value + tk.value
|
||||
}
|
||||
|
||||
import DefaultParsers._
|
||||
val p = Def.setting { name.value ~> Space ~> ID }
|
||||
|
|
@ -61,7 +63,7 @@ object Assign {
|
|||
name := "asdf",
|
||||
tk := (math.random * 1000).toInt,
|
||||
isk := dummys.value.parsed // should not compile: cannot use a task to define the parser
|
||||
// ik := { if( tsk.parsed.value == "blue") tk.value else mk.value }
|
||||
// ik := { if( tsk.parsed.value == "blue") tk.value else mk.value }
|
||||
)
|
||||
|
||||
val it1 = Def.inputTask {
|
||||
|
|
|
|||
|
|
@ -7,20 +7,25 @@ import sbt.internal.util.complete._
|
|||
import java.io.File
|
||||
|
||||
abstract class BackgroundJobService extends Closeable {
|
||||
|
||||
/**
|
||||
* Launch a background job which is a function that runs inside another thread;
|
||||
* killing the job will interrupt() the thread. If your thread blocks on a process,
|
||||
* then you should get an InterruptedException while blocking on the process, and
|
||||
* then you could process.destroy() for example.
|
||||
*/
|
||||
def runInBackground(spawningTask: ScopedKey[_], state: State)(start: (Logger, File) => Unit): JobHandle
|
||||
def runInBackground(spawningTask: ScopedKey[_], state: State)(
|
||||
start: (Logger, File) => Unit): JobHandle
|
||||
|
||||
/** Same as shutown. */
|
||||
def close(): Unit
|
||||
|
||||
/** Shuts down all background jobs. */
|
||||
def shutdown(): Unit
|
||||
def jobs: Vector[JobHandle]
|
||||
def stop(job: JobHandle): Unit
|
||||
def waitFor(job: JobHandle): Unit
|
||||
|
||||
/** Copies classpath to temporary directories. */
|
||||
def copyClasspath(products: Classpath, full: Classpath, workingDirectory: File): Classpath
|
||||
}
|
||||
|
|
@ -28,12 +33,15 @@ abstract class BackgroundJobService extends Closeable {
|
|||
object BackgroundJobService {
|
||||
private[sbt] def jobIdParser: (State, Seq[JobHandle]) => Parser[Seq[JobHandle]] = {
|
||||
import DefaultParsers._
|
||||
(state, handles) => {
|
||||
val stringIdParser: Parser[Seq[String]] = Space ~> token(NotSpace examples handles.map(_.id.toString).toSet, description = "<job id>").+
|
||||
stringIdParser.map { strings =>
|
||||
strings.map(Integer.parseInt(_)).flatMap(id => handles.find(_.id == id))
|
||||
(state, handles) =>
|
||||
{
|
||||
val stringIdParser: Parser[Seq[String]] = Space ~> token(
|
||||
NotSpace examples handles.map(_.id.toString).toSet,
|
||||
description = "<job id>").+
|
||||
stringIdParser.map { strings =>
|
||||
strings.map(Integer.parseInt(_)).flatMap(id => handles.find(_.id == id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,22 @@ import sbt.io.{ GlobFilter, Path }
|
|||
import sbt.internal.util.AttributeKey
|
||||
|
||||
object BuildPaths {
|
||||
val globalBaseDirectory = AttributeKey[File]("global-base-directory", "The base directory for global sbt configuration and staging.", DSetting)
|
||||
val globalPluginsDirectory = AttributeKey[File]("global-plugins-directory", "The base directory for global sbt plugins.", DSetting)
|
||||
val globalSettingsDirectory = AttributeKey[File]("global-settings-directory", "The base directory for global sbt settings.", DSetting)
|
||||
val stagingDirectory = AttributeKey[File]("staging-directory", "The directory for staging remote projects.", DSetting)
|
||||
val dependencyBaseDirectory = AttributeKey[File]("dependency-base-directory", "The base directory for caching dependency resolution.", DSetting)
|
||||
val globalBaseDirectory = AttributeKey[File](
|
||||
"global-base-directory",
|
||||
"The base directory for global sbt configuration and staging.",
|
||||
DSetting)
|
||||
val globalPluginsDirectory = AttributeKey[File]("global-plugins-directory",
|
||||
"The base directory for global sbt plugins.",
|
||||
DSetting)
|
||||
val globalSettingsDirectory = AttributeKey[File]("global-settings-directory",
|
||||
"The base directory for global sbt settings.",
|
||||
DSetting)
|
||||
val stagingDirectory =
|
||||
AttributeKey[File]("staging-directory", "The directory for staging remote projects.", DSetting)
|
||||
val dependencyBaseDirectory = AttributeKey[File](
|
||||
"dependency-base-directory",
|
||||
"The base directory for caching dependency resolution.",
|
||||
DSetting)
|
||||
|
||||
import sbt.io.syntax._
|
||||
|
||||
|
|
@ -37,29 +48,36 @@ object BuildPaths {
|
|||
fileSetting(stagingDirectory, StagingProperty, defaultStaging(globalBase))(state)
|
||||
|
||||
def getGlobalPluginsDirectory(state: State, globalBase: File): File =
|
||||
fileSetting(globalPluginsDirectory, GlobalPluginsProperty, defaultGlobalPlugins(globalBase))(state)
|
||||
fileSetting(globalPluginsDirectory, GlobalPluginsProperty, defaultGlobalPlugins(globalBase))(
|
||||
state)
|
||||
|
||||
def getGlobalSettingsDirectory(state: State, globalBase: File): File =
|
||||
fileSetting(globalSettingsDirectory, GlobalSettingsProperty, globalBase)(state)
|
||||
|
||||
def getDependencyDirectory(state: State, globalBase: File): File =
|
||||
fileSetting(dependencyBaseDirectory, DependencyBaseProperty, defaultDependencyBase(globalBase))(state)
|
||||
fileSetting(dependencyBaseDirectory,
|
||||
DependencyBaseProperty,
|
||||
defaultDependencyBase(globalBase))(state)
|
||||
|
||||
private[this] def fileSetting(stateKey: AttributeKey[File], property: String, default: File)(state: State): File =
|
||||
private[this] def fileSetting(stateKey: AttributeKey[File], property: String, default: File)(
|
||||
state: State): File =
|
||||
getFileSetting(stateKey, property, default)(state)
|
||||
|
||||
def getFileSetting(stateKey: AttributeKey[File], property: String, default: => File)(state: State): File =
|
||||
def getFileSetting(stateKey: AttributeKey[File], property: String, default: => File)(
|
||||
state: State): File =
|
||||
state get stateKey orElse getFileProperty(property) getOrElse default
|
||||
|
||||
def getFileProperty(name: String): Option[File] = Option(System.getProperty(name)) flatMap { path =>
|
||||
if (path.isEmpty) None else Some(new File(path))
|
||||
def getFileProperty(name: String): Option[File] = Option(System.getProperty(name)) flatMap {
|
||||
path =>
|
||||
if (path.isEmpty) None else Some(new File(path))
|
||||
}
|
||||
|
||||
def defaultVersionedGlobalBase(sbtVersion: String): File = defaultGlobalBase / sbtVersion
|
||||
def defaultGlobalBase = Path.userHome / ConfigDirectoryName
|
||||
|
||||
private[this] def binarySbtVersion(state: State): String =
|
||||
sbt.internal.librarymanagement.cross.CrossVersionUtil.binarySbtVersion(state.configuration.provider.id.version)
|
||||
sbt.internal.librarymanagement.cross.CrossVersionUtil
|
||||
.binarySbtVersion(state.configuration.provider.id.version)
|
||||
private[this] def defaultStaging(globalBase: File) = globalBase / "staging"
|
||||
private[this] def defaultGlobalPlugins(globalBase: File) = globalBase / PluginsDirectoryName
|
||||
private[this] def defaultDependencyBase(globalBase: File) = globalBase / "dependency"
|
||||
|
|
@ -81,7 +99,8 @@ object BuildPaths {
|
|||
final val GlobalSettingsProperty = "sbt.global.settings"
|
||||
final val DependencyBaseProperty = "sbt.dependency.base"
|
||||
|
||||
def crossPath(base: File, instance: xsbti.compile.ScalaInstance): File = base / ("scala_" + instance.version)
|
||||
def crossPath(base: File, instance: xsbti.compile.ScalaInstance): File =
|
||||
base / ("scala_" + instance.version)
|
||||
|
||||
private[this] def globalDirTransitionWarning(unversioned: File, versioned: File): String =
|
||||
s"""The global sbt directory is now versioned and is located at $versioned.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ private[sbt] trait BuildSyntax {
|
|||
def enablePlugins(ps: AutoPlugin*): DslEntry = DslEntry.DslEnablePlugins(ps)
|
||||
def disablePlugins(ps: AutoPlugin*): DslEntry = DslEntry.DslDisablePlugins(ps)
|
||||
def configs(cs: Configuration*): DslEntry = DslEntry.DslConfigs(cs)
|
||||
def dependsOn(deps: Eval[ClasspathDep[ProjectReference]]*): DslEntry = DslEntry.DslDependsOn(deps)
|
||||
def dependsOn(deps: Eval[ClasspathDep[ProjectReference]]*): DslEntry =
|
||||
DslEntry.DslDependsOn(deps)
|
||||
// avoid conflict with `sbt.Keys.aggregate`
|
||||
def aggregateProjects(refs: Eval[ProjectReference]*): DslEntry = DslEntry.DslAggregate(refs)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,14 @@ import sbt.internal.util.complete.{ DefaultParsers, Parser }
|
|||
import sbt.internal.util.AttributeKey
|
||||
import DefaultParsers._
|
||||
import Def.{ ScopedKey, Setting }
|
||||
import sbt.internal.CommandStrings.{ CrossCommand, CrossRestoreSessionCommand, SwitchCommand, crossHelp, crossRestoreSessionHelp, switchHelp }
|
||||
import sbt.internal.CommandStrings.{
|
||||
CrossCommand,
|
||||
CrossRestoreSessionCommand,
|
||||
SwitchCommand,
|
||||
crossHelp,
|
||||
crossRestoreSessionHelp,
|
||||
switchHelp
|
||||
}
|
||||
import java.io.File
|
||||
|
||||
import sbt.internal.inc.ScalaInstance
|
||||
|
|
@ -24,7 +31,8 @@ object Cross {
|
|||
def force: Boolean
|
||||
}
|
||||
private case class NamedScalaVersion(name: String, force: Boolean) extends ScalaVersion
|
||||
private case class ScalaHomeVersion(home: File, resolveVersion: Option[String], force: Boolean) extends ScalaVersion
|
||||
private case class ScalaHomeVersion(home: File, resolveVersion: Option[String], force: Boolean)
|
||||
extends ScalaVersion
|
||||
|
||||
private def switchParser(state: State): Parser[Switch] = {
|
||||
import DefaultParsers._
|
||||
|
|
@ -36,9 +44,11 @@ object Cross {
|
|||
val force = arg.endsWith("!")
|
||||
val versionArg = if (force) arg.dropRight(1) else arg
|
||||
versionArg.split("=", 2) match {
|
||||
case Array(home) if new File(home).exists() => ScalaHomeVersion(new File(home), None, force)
|
||||
case Array(v) => NamedScalaVersion(v, force)
|
||||
case Array(v, home) => ScalaHomeVersion(new File(home), Some(v).filterNot(_.isEmpty), force)
|
||||
case Array(home) if new File(home).exists() =>
|
||||
ScalaHomeVersion(new File(home), None, force)
|
||||
case Array(v) => NamedScalaVersion(v, force)
|
||||
case Array(v, home) =>
|
||||
ScalaHomeVersion(new File(home), Some(v).filterNot(_.isEmpty), force)
|
||||
}
|
||||
}
|
||||
val spacedVersion = if (spacePresent) version else version & spacedFirst(SwitchCommand)
|
||||
|
|
@ -50,7 +60,9 @@ object Cross {
|
|||
}
|
||||
}
|
||||
|
||||
token(SwitchCommand ~> OptSpace) flatMap { sp => versionAndCommand(sp.nonEmpty) }
|
||||
token(SwitchCommand ~> OptSpace) flatMap { sp =>
|
||||
versionAndCommand(sp.nonEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
private case class CrossArgs(command: String, verbose: Boolean)
|
||||
|
|
@ -62,10 +74,11 @@ object Cross {
|
|||
} & spacedFirst(CrossCommand)
|
||||
}
|
||||
|
||||
private def crossRestoreSessionParser(state: State): Parser[String] = token(CrossRestoreSessionCommand)
|
||||
private def crossRestoreSessionParser(state: State): Parser[String] =
|
||||
token(CrossRestoreSessionCommand)
|
||||
|
||||
private def requireSession[T](p: State => Parser[T]): State => Parser[T] = s =>
|
||||
if (s get sessionSettings isEmpty) failure("No project loaded") else p(s)
|
||||
private def requireSession[T](p: State => Parser[T]): State => Parser[T] =
|
||||
s => if (s get sessionSettings isEmpty) failure("No project loaded") else p(s)
|
||||
|
||||
private def resolveAggregates(extracted: Extracted): Seq[ProjectRef] = {
|
||||
import extracted._
|
||||
|
|
@ -134,7 +147,8 @@ object Cross {
|
|||
s"$SwitchCommand $verbose $version" :: projects.map(project => s"$project/$aggCommand")
|
||||
case (version, projects) =>
|
||||
// First switch scala version, then use the all command to run the command on each project concurrently
|
||||
Seq(s"$SwitchCommand $verbose $version", projects.map(_ + "/" + aggCommand).mkString("all ", " ", ""))
|
||||
Seq(s"$SwitchCommand $verbose $version",
|
||||
projects.map(_ + "/" + aggCommand).mkString("all ", " ", ""))
|
||||
}
|
||||
|
||||
allCommands.toList ::: CrossRestoreSessionCommand :: captureCurrentSession(state, x)
|
||||
|
|
@ -158,7 +172,9 @@ object Cross {
|
|||
state.get(CapturedSession) match {
|
||||
case Some(rawAppend) =>
|
||||
val restoredSession = extracted.session.copy(rawAppend = rawAppend)
|
||||
BuiltinCommands.reapply(restoredSession, extracted.structure, state).remove(CapturedSession)
|
||||
BuiltinCommands
|
||||
.reapply(restoredSession, extracted.structure, state)
|
||||
.remove(CapturedSession)
|
||||
case None => state
|
||||
}
|
||||
}
|
||||
|
|
@ -191,7 +207,8 @@ object Cross {
|
|||
|
||||
val binaryVersion = CrossVersion.binaryScalaVersion(version)
|
||||
|
||||
def logSwitchInfo(included: Seq[(ProjectRef, Seq[String])], excluded: Seq[(ProjectRef, Seq[String])]) = {
|
||||
def logSwitchInfo(included: Seq[(ProjectRef, Seq[String])],
|
||||
excluded: Seq[(ProjectRef, Seq[String])]) = {
|
||||
|
||||
instance.foreach {
|
||||
case (home, instance) =>
|
||||
|
|
@ -206,7 +223,8 @@ object Cross {
|
|||
state.log.info(s"Excluded ${excluded.size} projects, run ++ $version -v for more details.")
|
||||
}
|
||||
|
||||
def detailedLog(msg: => String) = if (switch.verbose) state.log.info(msg) else state.log.debug(msg)
|
||||
def detailedLog(msg: => String) =
|
||||
if (switch.verbose) state.log.info(msg) else state.log.debug(msg)
|
||||
|
||||
def logProject: (ProjectRef, Seq[String]) => Unit = (proj, scalaVersions) => {
|
||||
val current = if (proj == currentRef) "*" else " "
|
||||
|
|
@ -219,14 +237,16 @@ object Cross {
|
|||
}
|
||||
|
||||
val projects: Seq[Reference] = {
|
||||
val projectScalaVersions = structure.allProjectRefs.map(proj => proj -> crossVersions(x, proj))
|
||||
val projectScalaVersions =
|
||||
structure.allProjectRefs.map(proj => proj -> crossVersions(x, proj))
|
||||
if (switch.version.force) {
|
||||
logSwitchInfo(projectScalaVersions, Nil)
|
||||
structure.allProjectRefs ++ structure.units.keys.map(BuildRef.apply)
|
||||
} else {
|
||||
|
||||
val (included, excluded) = projectScalaVersions.partition {
|
||||
case (proj, scalaVersions) => scalaVersions.exists(v => CrossVersion.binaryScalaVersion(v) == binaryVersion)
|
||||
case (proj, scalaVersions) =>
|
||||
scalaVersions.exists(v => CrossVersion.binaryScalaVersion(v) == binaryVersion)
|
||||
}
|
||||
logSwitchInfo(included, excluded)
|
||||
included.map(_._1)
|
||||
|
|
@ -236,23 +256,28 @@ object Cross {
|
|||
setScalaVersionForProjects(version, instance, projects, state, x)
|
||||
}
|
||||
|
||||
private def setScalaVersionForProjects(version: String, instance: Option[(File, ScalaInstance)],
|
||||
projects: Seq[Reference], state: State, extracted: Extracted): State = {
|
||||
private def setScalaVersionForProjects(version: String,
|
||||
instance: Option[(File, ScalaInstance)],
|
||||
projects: Seq[Reference],
|
||||
state: State,
|
||||
extracted: Extracted): State = {
|
||||
import extracted._
|
||||
|
||||
val newSettings = projects.flatMap { project =>
|
||||
val scope = Scope(Select(project), Global, Global, Global)
|
||||
|
||||
instance match {
|
||||
case Some((home, inst)) => Seq(
|
||||
scalaVersion in scope := version,
|
||||
scalaHome in scope := Some(home),
|
||||
scalaInstance in scope := inst
|
||||
)
|
||||
case None => Seq(
|
||||
scalaVersion in scope := version,
|
||||
scalaHome in scope := None
|
||||
)
|
||||
case Some((home, inst)) =>
|
||||
Seq(
|
||||
scalaVersion in scope := version,
|
||||
scalaHome in scope := Some(home),
|
||||
scalaInstance in scope := inst
|
||||
)
|
||||
case None =>
|
||||
Seq(
|
||||
scalaVersion in scope := version,
|
||||
scalaHome in scope := None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -260,7 +285,9 @@ object Cross {
|
|||
|
||||
// Filter out any old scala version settings that were added, this is just for hygiene.
|
||||
val filteredRawAppend = session.rawAppend.filter(_.key match {
|
||||
case ScopedKey(Scope(Select(ref), Global, Global, Global), key) if filterKeys.contains(key) && projects.contains(ref) => false
|
||||
case ScopedKey(Scope(Select(ref), Global, Global, Global), key)
|
||||
if filterKeys.contains(key) && projects.contains(ref) =>
|
||||
false
|
||||
case _ => true
|
||||
})
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -34,6 +34,7 @@ import TaskName._
|
|||
* invoking `cancelAndShutdown()` allows you to cancel the current task execution.
|
||||
*/
|
||||
trait RunningTaskEngine {
|
||||
|
||||
/** Attempts to kill and shutdown the running task engine.*/
|
||||
def cancelAndShutdown(): Unit
|
||||
}
|
||||
|
|
@ -47,6 +48,7 @@ trait RunningTaskEngine {
|
|||
* All methods on this API are expected to be called from the same thread.
|
||||
*/
|
||||
trait TaskCancellationStrategy {
|
||||
|
||||
/** The state used by this task. */
|
||||
type State
|
||||
|
||||
|
|
@ -63,6 +65,7 @@ trait TaskCancellationStrategy {
|
|||
}
|
||||
|
||||
object TaskCancellationStrategy {
|
||||
|
||||
/** An empty handler that does not cancel tasks. */
|
||||
object Null extends TaskCancellationStrategy {
|
||||
type State = Unit
|
||||
|
|
@ -105,35 +108,41 @@ sealed trait EvaluateTaskConfig {
|
|||
}
|
||||
|
||||
object EvaluateTaskConfig {
|
||||
|
||||
/** Raw constructor for EvaluateTaskConfig. */
|
||||
def apply(
|
||||
restrictions: Seq[Tags.Rule],
|
||||
checkCycles: Boolean,
|
||||
progressReporter: ExecuteProgress[Task],
|
||||
cancelStrategy: TaskCancellationStrategy,
|
||||
forceGarbageCollection: Boolean,
|
||||
minForcegcInterval: Duration
|
||||
restrictions: Seq[Tags.Rule],
|
||||
checkCycles: Boolean,
|
||||
progressReporter: ExecuteProgress[Task],
|
||||
cancelStrategy: TaskCancellationStrategy,
|
||||
forceGarbageCollection: Boolean,
|
||||
minForcegcInterval: Duration
|
||||
): EvaluateTaskConfig =
|
||||
DefaultEvaluateTaskConfig(
|
||||
restrictions, checkCycles, progressReporter, cancelStrategy, forceGarbageCollection, minForcegcInterval
|
||||
restrictions,
|
||||
checkCycles,
|
||||
progressReporter,
|
||||
cancelStrategy,
|
||||
forceGarbageCollection,
|
||||
minForcegcInterval
|
||||
)
|
||||
|
||||
private[this] case class DefaultEvaluateTaskConfig(
|
||||
restrictions: Seq[Tags.Rule],
|
||||
checkCycles: Boolean,
|
||||
progressReporter: ExecuteProgress[Task],
|
||||
cancelStrategy: TaskCancellationStrategy,
|
||||
forceGarbageCollection: Boolean,
|
||||
minForcegcInterval: Duration
|
||||
restrictions: Seq[Tags.Rule],
|
||||
checkCycles: Boolean,
|
||||
progressReporter: ExecuteProgress[Task],
|
||||
cancelStrategy: TaskCancellationStrategy,
|
||||
forceGarbageCollection: Boolean,
|
||||
minForcegcInterval: Duration
|
||||
) extends EvaluateTaskConfig
|
||||
}
|
||||
|
||||
final case class PluginData(
|
||||
dependencyClasspath: Seq[Attributed[File]],
|
||||
definitionClasspath: Seq[Attributed[File]],
|
||||
resolvers: Option[Seq[Resolver]],
|
||||
report: Option[UpdateReport],
|
||||
scalacOptions: Seq[String]
|
||||
dependencyClasspath: Seq[Attributed[File]],
|
||||
definitionClasspath: Seq[Attributed[File]],
|
||||
resolvers: Option[Seq[Resolver]],
|
||||
report: Option[UpdateReport],
|
||||
scalacOptions: Seq[String]
|
||||
) {
|
||||
val classpath: Seq[Attributed[File]] = definitionClasspath ++ dependencyClasspath
|
||||
}
|
||||
|
|
@ -159,28 +168,31 @@ object EvaluateTask {
|
|||
|
||||
val SystemProcessors = Runtime.getRuntime.availableProcessors
|
||||
|
||||
def extractedTaskConfig(extracted: Extracted, structure: BuildStructure, state: State): EvaluateTaskConfig =
|
||||
{
|
||||
val rs = restrictions(extracted, structure)
|
||||
val canceller = cancelStrategy(extracted, structure, state)
|
||||
val progress = executeProgress(extracted, structure, state)
|
||||
val fgc = forcegc(extracted, structure)
|
||||
val mfi = minForcegcInterval(extracted, structure)
|
||||
EvaluateTaskConfig(rs, false, progress, canceller, fgc, mfi)
|
||||
}
|
||||
def extractedTaskConfig(extracted: Extracted,
|
||||
structure: BuildStructure,
|
||||
state: State): EvaluateTaskConfig = {
|
||||
val rs = restrictions(extracted, structure)
|
||||
val canceller = cancelStrategy(extracted, structure, state)
|
||||
val progress = executeProgress(extracted, structure, state)
|
||||
val fgc = forcegc(extracted, structure)
|
||||
val mfi = minForcegcInterval(extracted, structure)
|
||||
EvaluateTaskConfig(rs, false, progress, canceller, fgc, mfi)
|
||||
}
|
||||
|
||||
def defaultRestrictions(maxWorkers: Int) = Tags.limitAll(maxWorkers) :: Nil
|
||||
def defaultRestrictions(extracted: Extracted, structure: BuildStructure): Seq[Tags.Rule] =
|
||||
Tags.limitAll(maxWorkers(extracted, structure)) :: Nil
|
||||
|
||||
def restrictions(state: State): Seq[Tags.Rule] =
|
||||
{
|
||||
val extracted = Project.extract(state)
|
||||
restrictions(extracted, extracted.structure)
|
||||
}
|
||||
def restrictions(state: State): Seq[Tags.Rule] = {
|
||||
val extracted = Project.extract(state)
|
||||
restrictions(extracted, extracted.structure)
|
||||
}
|
||||
|
||||
def restrictions(extracted: Extracted, structure: BuildStructure): Seq[Tags.Rule] =
|
||||
getSetting(Keys.concurrentRestrictions, defaultRestrictions(extracted, structure), extracted, structure)
|
||||
getSetting(Keys.concurrentRestrictions,
|
||||
defaultRestrictions(extracted, structure),
|
||||
extracted,
|
||||
structure)
|
||||
|
||||
def maxWorkers(extracted: Extracted, structure: BuildStructure): Int =
|
||||
if (getSetting(Keys.parallelExecution, true, extracted, structure))
|
||||
|
|
@ -191,12 +203,22 @@ object EvaluateTask {
|
|||
def cancelable(extracted: Extracted, structure: BuildStructure): Boolean =
|
||||
getSetting(Keys.cancelable, false, extracted, structure)
|
||||
|
||||
def cancelStrategy(extracted: Extracted, structure: BuildStructure, state: State): TaskCancellationStrategy =
|
||||
getSetting(Keys.taskCancelStrategy, { (_: State) => TaskCancellationStrategy.Null }, extracted, structure)(state)
|
||||
def cancelStrategy(extracted: Extracted,
|
||||
structure: BuildStructure,
|
||||
state: State): TaskCancellationStrategy =
|
||||
getSetting(Keys.taskCancelStrategy, { (_: State) =>
|
||||
TaskCancellationStrategy.Null
|
||||
}, extracted, structure)(state)
|
||||
|
||||
private[sbt] def executeProgress(extracted: Extracted, structure: BuildStructure, state: State): ExecuteProgress[Task] = {
|
||||
private[sbt] def executeProgress(extracted: Extracted,
|
||||
structure: BuildStructure,
|
||||
state: State): ExecuteProgress[Task] = {
|
||||
import Types.const
|
||||
val maker: State => Keys.TaskProgress = getSetting(Keys.executeProgress, const(new Keys.TaskProgress(defaultProgress)), extracted, structure)
|
||||
val maker: State => Keys.TaskProgress = getSetting(
|
||||
Keys.executeProgress,
|
||||
const(new Keys.TaskProgress(defaultProgress)),
|
||||
extracted,
|
||||
structure)
|
||||
maker(state).progress
|
||||
}
|
||||
// TODO - Should this pull from Global or from the project itself?
|
||||
|
|
@ -204,9 +226,15 @@ object EvaluateTask {
|
|||
getSetting(Keys.forcegc in Global, GCUtil.defaultForceGarbageCollection, extracted, structure)
|
||||
// TODO - Should this pull from Global or from the project itself?
|
||||
private[sbt] def minForcegcInterval(extracted: Extracted, structure: BuildStructure): Duration =
|
||||
getSetting(Keys.minForcegcInterval in Global, GCUtil.defaultMinForcegcInterval, extracted, structure)
|
||||
getSetting(Keys.minForcegcInterval in Global,
|
||||
GCUtil.defaultMinForcegcInterval,
|
||||
extracted,
|
||||
structure)
|
||||
|
||||
def getSetting[T](key: SettingKey[T], default: T, extracted: Extracted, structure: BuildStructure): T =
|
||||
def getSetting[T](key: SettingKey[T],
|
||||
default: T,
|
||||
extracted: Extracted,
|
||||
structure: BuildStructure): T =
|
||||
key in extracted.currentRef get structure.data getOrElse default
|
||||
|
||||
def injectSettings: Seq[Setting[_]] = Seq(
|
||||
|
|
@ -215,39 +243,55 @@ object EvaluateTask {
|
|||
(executionRoots in GlobalScope) ::= dummyRoots
|
||||
)
|
||||
|
||||
def evalPluginDef(log: Logger)(pluginDef: BuildStructure, state: State): PluginData =
|
||||
{
|
||||
val root = ProjectRef(pluginDef.root, Load.getRootProject(pluginDef.units)(pluginDef.root))
|
||||
val pluginKey = pluginData
|
||||
val config = extractedTaskConfig(Project.extract(state), pluginDef, state)
|
||||
val evaluated = apply(pluginDef, ScopedKey(pluginKey.scope, pluginKey.key), state, root, config)
|
||||
val (newS, result) = evaluated getOrElse sys.error("Plugin data does not exist for plugin definition at " + pluginDef.root)
|
||||
Project.runUnloadHooks(newS) // discard states
|
||||
processResult(result, log)
|
||||
}
|
||||
def evalPluginDef(log: Logger)(pluginDef: BuildStructure, state: State): PluginData = {
|
||||
val root = ProjectRef(pluginDef.root, Load.getRootProject(pluginDef.units)(pluginDef.root))
|
||||
val pluginKey = pluginData
|
||||
val config = extractedTaskConfig(Project.extract(state), pluginDef, state)
|
||||
val evaluated =
|
||||
apply(pluginDef, ScopedKey(pluginKey.scope, pluginKey.key), state, root, config)
|
||||
val (newS, result) = evaluated getOrElse sys.error(
|
||||
"Plugin data does not exist for plugin definition at " + pluginDef.root)
|
||||
Project.runUnloadHooks(newS) // discard states
|
||||
processResult(result, log)
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates `taskKey` and returns the new State and the result of the task wrapped in Some.
|
||||
* If the task is not defined, None is returned. The provided task key is resolved against the current project `ref`.
|
||||
* Task execution is configured according to settings defined in the loaded project.
|
||||
*/
|
||||
def apply[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef): Option[(State, Result[T])] =
|
||||
apply[T](structure, taskKey, state, ref, extractedTaskConfig(Project.extract(state), structure, state))
|
||||
def apply[T](structure: BuildStructure,
|
||||
taskKey: ScopedKey[Task[T]],
|
||||
state: State,
|
||||
ref: ProjectRef): Option[(State, Result[T])] =
|
||||
apply[T](structure,
|
||||
taskKey,
|
||||
state,
|
||||
ref,
|
||||
extractedTaskConfig(Project.extract(state), structure, state))
|
||||
|
||||
/**
|
||||
* Evaluates `taskKey` and returns the new State and the result of the task wrapped in Some.
|
||||
* If the task is not defined, None is returned. The provided task key is resolved against the current project `ref`.
|
||||
* `config` configures concurrency and canceling of task execution.
|
||||
*/
|
||||
def apply[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, config: EvaluateTaskConfig): Option[(State, Result[T])] = {
|
||||
def apply[T](structure: BuildStructure,
|
||||
taskKey: ScopedKey[Task[T]],
|
||||
state: State,
|
||||
ref: ProjectRef,
|
||||
config: EvaluateTaskConfig): Option[(State, Result[T])] = {
|
||||
withStreams(structure, state) { str =>
|
||||
for ((task, toNode) <- getTask(structure, taskKey, state, str, ref)) yield runTask(task, state, str, structure.index.triggers, config)(toNode)
|
||||
for ((task, toNode) <- getTask(structure, taskKey, state, str, ref))
|
||||
yield runTask(task, state, str, structure.index.triggers, config)(toNode)
|
||||
}
|
||||
}
|
||||
def logIncResult(result: Result[_], state: State, streams: Streams) = result match { case Inc(i) => logIncomplete(i, state, streams); case _ => () }
|
||||
def logIncResult(result: Result[_], state: State, streams: Streams) = result match {
|
||||
case Inc(i) => logIncomplete(i, state, streams); case _ => ()
|
||||
}
|
||||
def logIncomplete(result: Incomplete, state: State, streams: Streams): Unit = {
|
||||
val all = Incomplete linearize result
|
||||
val keyed = for (Incomplete(Some(key: ScopedKey[_]), _, msg, _, ex) <- all) yield (key, msg, ex)
|
||||
val keyed = for (Incomplete(Some(key: ScopedKey[_]), _, msg, _, ex) <- all)
|
||||
yield (key, msg, ex)
|
||||
|
||||
import ExceptionCategory._
|
||||
for ((key, msg, Some(ex)) <- keyed) {
|
||||
|
|
@ -266,82 +310,102 @@ object EvaluateTask {
|
|||
log.error("(" + display.show(key) + ") " + msgString)
|
||||
}
|
||||
}
|
||||
private[this] def contextDisplay(state: State, highlight: Boolean) = Project.showContextKey(state, if (highlight) Some(RED) else None)
|
||||
private[this] def contextDisplay(state: State, highlight: Boolean) =
|
||||
Project.showContextKey(state, if (highlight) Some(RED) else None)
|
||||
def suppressedMessage(key: ScopedKey[_])(implicit display: Show[ScopedKey[_]]): String =
|
||||
"Stack trace suppressed. Run 'last %s' for the full log.".format(display.show(key))
|
||||
|
||||
def getStreams(key: ScopedKey[_], streams: Streams): TaskStreams =
|
||||
streams(ScopedKey(Project.fillTaskAxis(key).scope, Keys.streams.key))
|
||||
def withStreams[T](structure: BuildStructure, state: State)(f: Streams => T): T =
|
||||
{
|
||||
val str = std.Streams.closeable(structure.streams(state))
|
||||
try { f(str) } finally { str.close() }
|
||||
def withStreams[T](structure: BuildStructure, state: State)(f: Streams => T): T = {
|
||||
val str = std.Streams.closeable(structure.streams(state))
|
||||
try { f(str) } finally { str.close() }
|
||||
}
|
||||
|
||||
def getTask[T](structure: BuildStructure,
|
||||
taskKey: ScopedKey[Task[T]],
|
||||
state: State,
|
||||
streams: Streams,
|
||||
ref: ProjectRef): Option[(Task[T], NodeView[Task])] = {
|
||||
val thisScope = Load.projectScope(ref)
|
||||
val resolvedScope = Scope.replaceThis(thisScope)(taskKey.scope)
|
||||
for (t <- structure.data.get(resolvedScope, taskKey.key))
|
||||
yield (t, nodeView(state, streams, taskKey :: Nil))
|
||||
}
|
||||
def nodeView[HL <: HList](state: State,
|
||||
streams: Streams,
|
||||
roots: Seq[ScopedKey[_]],
|
||||
dummies: DummyTaskMap = DummyTaskMap(Nil)): NodeView[Task] =
|
||||
Transform(
|
||||
(dummyRoots, roots) :: (Def.dummyStreamsManager, streams) :: (dummyState, state) :: dummies)
|
||||
|
||||
def runTask[T](
|
||||
root: Task[T],
|
||||
state: State,
|
||||
streams: Streams,
|
||||
triggers: Triggers[Task],
|
||||
config: EvaluateTaskConfig)(implicit taskToNode: NodeView[Task]): (State, Result[T]) = {
|
||||
import ConcurrentRestrictions.{ completionService, tagged, tagsKey }
|
||||
|
||||
val log = state.log
|
||||
log.debug(
|
||||
s"Running task... Cancel: ${config.cancelStrategy}, check cycles: ${config.checkCycles}, forcegc: ${config.forceGarbageCollection}")
|
||||
val tags =
|
||||
tagged[Task[_]](_.info get tagsKey getOrElse Map.empty, Tags.predicate(config.restrictions))
|
||||
val (service, shutdownThreads) =
|
||||
completionService[Task[_], Completed](tags, (s: String) => log.warn(s))
|
||||
|
||||
def shutdown(): Unit = {
|
||||
// First ensure that all threads are stopped for task execution.
|
||||
shutdownThreads()
|
||||
|
||||
// Now we run the gc cleanup to force finalizers to clear out file handles (yay GC!)
|
||||
if (config.forceGarbageCollection) {
|
||||
GCUtil.forceGcWithInterval(config.minForcegcInterval, log)
|
||||
}
|
||||
}
|
||||
|
||||
def getTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, streams: Streams, ref: ProjectRef): Option[(Task[T], NodeView[Task])] =
|
||||
{
|
||||
val thisScope = Load.projectScope(ref)
|
||||
val resolvedScope = Scope.replaceThis(thisScope)(taskKey.scope)
|
||||
for (t <- structure.data.get(resolvedScope, taskKey.key)) yield (t, nodeView(state, streams, taskKey :: Nil))
|
||||
// propagate the defining key for reporting the origin
|
||||
def overwriteNode(i: Incomplete): Boolean = i.node match {
|
||||
case Some(t: Task[_]) => transformNode(t).isEmpty
|
||||
case _ => true
|
||||
}
|
||||
def nodeView[HL <: HList](state: State, streams: Streams, roots: Seq[ScopedKey[_]], dummies: DummyTaskMap = DummyTaskMap(Nil)): NodeView[Task] =
|
||||
Transform((dummyRoots, roots) :: (Def.dummyStreamsManager, streams) :: (dummyState, state) :: dummies)
|
||||
|
||||
def runTask[T](root: Task[T], state: State, streams: Streams, triggers: Triggers[Task], config: EvaluateTaskConfig)(implicit taskToNode: NodeView[Task]): (State, Result[T]) =
|
||||
{
|
||||
import ConcurrentRestrictions.{ completionService, tagged, tagsKey }
|
||||
|
||||
val log = state.log
|
||||
log.debug(s"Running task... Cancel: ${config.cancelStrategy}, check cycles: ${config.checkCycles}, forcegc: ${config.forceGarbageCollection}")
|
||||
val tags = tagged[Task[_]](_.info get tagsKey getOrElse Map.empty, Tags.predicate(config.restrictions))
|
||||
val (service, shutdownThreads) = completionService[Task[_], Completed](tags, (s: String) => log.warn(s))
|
||||
|
||||
def shutdown(): Unit = {
|
||||
// First ensure that all threads are stopped for task execution.
|
||||
shutdownThreads()
|
||||
|
||||
// Now we run the gc cleanup to force finalizers to clear out file handles (yay GC!)
|
||||
if (config.forceGarbageCollection) {
|
||||
GCUtil.forceGcWithInterval(config.minForcegcInterval, log)
|
||||
}
|
||||
}
|
||||
// propagate the defining key for reporting the origin
|
||||
def overwriteNode(i: Incomplete): Boolean = i.node match {
|
||||
case Some(t: Task[_]) => transformNode(t).isEmpty
|
||||
case _ => true
|
||||
}
|
||||
def run() = {
|
||||
val x = new Execute[Task](Execute.config(config.checkCycles, overwriteNode), triggers, config.progressReporter)(taskToNode)
|
||||
val (newState, result) =
|
||||
try {
|
||||
val results = x.runKeep(root)(service)
|
||||
storeValuesForPrevious(results, state, streams)
|
||||
applyResults(results, state, root)
|
||||
} catch { case inc: Incomplete => (state, Inc(inc)) }
|
||||
finally shutdown()
|
||||
val replaced = transformInc(result)
|
||||
logIncResult(replaced, state, streams)
|
||||
(newState, replaced)
|
||||
}
|
||||
object runningEngine extends RunningTaskEngine {
|
||||
def cancelAndShutdown(): Unit = {
|
||||
println("")
|
||||
log.warn("Canceling execution...")
|
||||
shutdown()
|
||||
}
|
||||
}
|
||||
// Register with our cancel handler we're about to start.
|
||||
val strat = config.cancelStrategy
|
||||
val cancelState = strat.onTaskEngineStart(runningEngine)
|
||||
try run()
|
||||
finally strat.onTaskEngineFinish(cancelState)
|
||||
def run() = {
|
||||
val x = new Execute[Task](Execute.config(config.checkCycles, overwriteNode),
|
||||
triggers,
|
||||
config.progressReporter)(taskToNode)
|
||||
val (newState, result) =
|
||||
try {
|
||||
val results = x.runKeep(root)(service)
|
||||
storeValuesForPrevious(results, state, streams)
|
||||
applyResults(results, state, root)
|
||||
} catch { case inc: Incomplete => (state, Inc(inc)) } finally shutdown()
|
||||
val replaced = transformInc(result)
|
||||
logIncResult(replaced, state, streams)
|
||||
(newState, replaced)
|
||||
}
|
||||
object runningEngine extends RunningTaskEngine {
|
||||
def cancelAndShutdown(): Unit = {
|
||||
println("")
|
||||
log.warn("Canceling execution...")
|
||||
shutdown()
|
||||
}
|
||||
}
|
||||
// Register with our cancel handler we're about to start.
|
||||
val strat = config.cancelStrategy
|
||||
val cancelState = strat.onTaskEngineStart(runningEngine)
|
||||
try run()
|
||||
finally strat.onTaskEngineFinish(cancelState)
|
||||
}
|
||||
|
||||
private[this] def storeValuesForPrevious(results: RMap[Task, Result], state: State, streams: Streams): Unit =
|
||||
private[this] def storeValuesForPrevious(results: RMap[Task, Result],
|
||||
state: State,
|
||||
streams: Streams): Unit =
|
||||
for (referenced <- Previous.references in Global get Project.structure(state).data)
|
||||
Previous.complete(referenced, results, streams)
|
||||
|
||||
def applyResults[T](results: RMap[Task, Result], state: State, root: Task[T]): (State, Result[T]) =
|
||||
def applyResults[T](results: RMap[Task, Result],
|
||||
state: State,
|
||||
root: Task[T]): (State, Result[T]) =
|
||||
(stateTransform(results)(state), results(root))
|
||||
def stateTransform(results: RMap[Task, Result]): State => State =
|
||||
Function.chain(
|
||||
|
|
@ -353,7 +417,9 @@ object EvaluateTask {
|
|||
|
||||
def transformInc[T](result: Result[T]): Result[T] =
|
||||
// taskToKey needs to be before liftAnonymous. liftA only lifts non-keyed (anonymous) Incompletes.
|
||||
result.toEither.left.map { i => Incomplete.transformBU(i)(convertCyclicInc andThen taskToKey andThen liftAnonymous) }
|
||||
result.toEither.left.map { i =>
|
||||
Incomplete.transformBU(i)(convertCyclicInc andThen taskToKey andThen liftAnonymous)
|
||||
}
|
||||
def taskToKey: Incomplete => Incomplete = {
|
||||
case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node))
|
||||
case i => i
|
||||
|
|
@ -367,7 +433,8 @@ object EvaluateTask {
|
|||
def convertCyclic(c: AnyCyclic): String =
|
||||
(c.caller, c.target) match {
|
||||
case (caller: Task[_], target: Task[_]) =>
|
||||
c.toString + (if (caller eq target) "(task: " + name(caller) + ")" else "(caller: " + name(caller) + ", target: " + name(target) + ")")
|
||||
c.toString + (if (caller eq target) "(task: " + name(caller) + ")"
|
||||
else "(caller: " + name(caller) + ", target: " + name(target) + ")")
|
||||
case _ => c.toString
|
||||
}
|
||||
|
||||
|
|
@ -381,7 +448,9 @@ object EvaluateTask {
|
|||
}
|
||||
|
||||
def processResult[T](result: Result[T], log: Logger, show: Boolean = false): T =
|
||||
onResult(result, log) { v => if (show) println("Result: " + v); v }
|
||||
onResult(result, log) { v =>
|
||||
if (show) println("Result: " + v); v
|
||||
}
|
||||
def onResult[T, S](result: Result[T], log: Logger)(f: T => S): S =
|
||||
result match {
|
||||
case Value(v) => f(v)
|
||||
|
|
@ -399,5 +468,5 @@ object EvaluateTask {
|
|||
}).value
|
||||
})
|
||||
else
|
||||
Nil
|
||||
Nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ import sbt.internal.util.AttributeKey
|
|||
import sbt.util.Show
|
||||
import std.Transform.DummyTaskMap
|
||||
|
||||
final case class Extracted(structure: BuildStructure, session: SessionSettings, currentRef: ProjectRef)(implicit val showKey: Show[ScopedKey[_]]) {
|
||||
final case class Extracted(structure: BuildStructure,
|
||||
session: SessionSettings,
|
||||
currentRef: ProjectRef)(implicit val showKey: Show[ScopedKey[_]]) {
|
||||
def rootProject = structure.rootProject
|
||||
lazy val currentUnit = structure units currentRef.build
|
||||
lazy val currentProject = currentUnit defined currentRef.project
|
||||
|
|
@ -29,7 +31,8 @@ final case class Extracted(structure: BuildStructure, session: SessionSettings,
|
|||
* Other axes are resolved to be `Global` if they are not specified.
|
||||
*/
|
||||
def getOpt[T](key: SettingKey[T]): Option[T] = structure.data.get(inCurrent(key.scope), key.key)
|
||||
def getOpt[T](key: TaskKey[T]): Option[Task[T]] = structure.data.get(inCurrent(key.scope), key.key)
|
||||
def getOpt[T](key: TaskKey[T]): Option[Task[T]] =
|
||||
structure.data.get(inCurrent(key.scope), key.key)
|
||||
|
||||
private[this] def inCurrent[T](scope: Scope): Scope =
|
||||
if (scope.project == This) scope.copy(project = Select(currentRef)) else scope
|
||||
|
|
@ -41,15 +44,15 @@ final case class Extracted(structure: BuildStructure, session: SessionSettings,
|
|||
*
|
||||
* This method requests execution of only the given task and does not aggregate execution. See `runAggregated` for that.
|
||||
*/
|
||||
def runTask[T](key: TaskKey[T], state: State): (State, T) =
|
||||
{
|
||||
import EvaluateTask._
|
||||
val rkey = resolve(key.scopedKey)
|
||||
val config = extractedTaskConfig(this, structure, state)
|
||||
val value: Option[(State, Result[T])] = apply(structure, key.scopedKey, state, currentRef, config)
|
||||
val (newS, result) = getOrError(rkey.scope, rkey.key, value)
|
||||
(newS, processResult(result, newS.log))
|
||||
}
|
||||
def runTask[T](key: TaskKey[T], state: State): (State, T) = {
|
||||
import EvaluateTask._
|
||||
val rkey = resolve(key.scopedKey)
|
||||
val config = extractedTaskConfig(this, structure, state)
|
||||
val value: Option[(State, Result[T])] =
|
||||
apply(structure, key.scopedKey, state, currentRef, config)
|
||||
val (newS, result) = getOrError(rkey.scope, rkey.key, value)
|
||||
(newS, processResult(result, newS.log))
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the input task specified by `key`, using the `input` as the input to it, and returns the transformed State
|
||||
|
|
@ -64,7 +67,9 @@ final case class Extracted(structure: BuildStructure, session: SessionSettings,
|
|||
import EvaluateTask._
|
||||
|
||||
val scopedKey = Scoped.scopedSetting(
|
||||
Scope.resolveScope(Load.projectScope(currentRef), currentRef.build, structure.rootProject)(key.scope), key.key
|
||||
Scope.resolveScope(Load.projectScope(currentRef), currentRef.build, structure.rootProject)(
|
||||
key.scope),
|
||||
key.key
|
||||
)
|
||||
val rkey = resolve(scopedKey.scopedKey)
|
||||
val inputTask = get(Scoped.scopedSetting(rkey.scope, rkey.key))
|
||||
|
|
@ -75,7 +80,8 @@ final case class Extracted(structure: BuildStructure, session: SessionSettings,
|
|||
val config = extractedTaskConfig(this, structure, state)
|
||||
withStreams(structure, state) { str =>
|
||||
val nv = nodeView(state, str, rkey :: Nil)
|
||||
val (newS, result) = EvaluateTask.runTask(task, state, str, structure.index.triggers, config)(nv)
|
||||
val (newS, result) =
|
||||
EvaluateTask.runTask(task, state, str, structure.index.triggers, config)(nv)
|
||||
(newS, processResult(result, newS.log))
|
||||
}
|
||||
}
|
||||
|
|
@ -86,25 +92,33 @@ final case class Extracted(structure: BuildStructure, session: SessionSettings,
|
|||
* The project axis is what determines where aggregation starts, so ensure this is set to what you want.
|
||||
* Other axes are resolved to `Global` if unspecified.
|
||||
*/
|
||||
def runAggregated[T](key: TaskKey[T], state: State): State =
|
||||
{
|
||||
val rkey = resolve(key.scopedKey)
|
||||
val keys = Aggregation.aggregate(rkey, ScopeMask(), structure.extra)
|
||||
val tasks = Act.keyValues(structure)(keys)
|
||||
Aggregation.runTasks(state, structure, tasks, DummyTaskMap(Nil), show = Aggregation.defaultShow(state, false))(showKey)
|
||||
}
|
||||
def runAggregated[T](key: TaskKey[T], state: State): State = {
|
||||
val rkey = resolve(key.scopedKey)
|
||||
val keys = Aggregation.aggregate(rkey, ScopeMask(), structure.extra)
|
||||
val tasks = Act.keyValues(structure)(keys)
|
||||
Aggregation.runTasks(state,
|
||||
structure,
|
||||
tasks,
|
||||
DummyTaskMap(Nil),
|
||||
show = Aggregation.defaultShow(state, false))(showKey)
|
||||
}
|
||||
|
||||
private[this] def resolve[T](key: ScopedKey[T]): ScopedKey[T] =
|
||||
Project.mapScope(Scope.resolveScope(GlobalScope, currentRef.build, rootProject))(key.scopedKey)
|
||||
private def getOrError[T](scope: Scope, key: AttributeKey[_], value: Option[T])(implicit display: Show[ScopedKey[_]]): T =
|
||||
private def getOrError[T](scope: Scope, key: AttributeKey[_], value: Option[T])(
|
||||
implicit display: Show[ScopedKey[_]]): T =
|
||||
value getOrElse sys.error(display.show(ScopedKey(scope, key)) + " is undefined.")
|
||||
private def getOrError[T](scope: Scope, key: AttributeKey[T])(implicit display: Show[ScopedKey[_]]): T =
|
||||
structure.data.get(scope, key) getOrElse sys.error(display.show(ScopedKey(scope, key)) + " is undefined.")
|
||||
private def getOrError[T](scope: Scope, key: AttributeKey[T])(
|
||||
implicit display: Show[ScopedKey[_]]): T =
|
||||
structure.data.get(scope, key) getOrElse sys.error(
|
||||
display.show(ScopedKey(scope, key)) + " is undefined.")
|
||||
|
||||
def append(settings: Seq[Setting[_]], state: State): State =
|
||||
{
|
||||
val appendSettings = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings)
|
||||
val newStructure = Load.reapply(session.original ++ appendSettings, structure)
|
||||
Project.setProject(session, newStructure, state)
|
||||
}
|
||||
def append(settings: Seq[Setting[_]], state: State): State = {
|
||||
val appendSettings = Load.transformSettings(Load.projectScope(currentRef),
|
||||
currentRef.build,
|
||||
rootProject,
|
||||
settings)
|
||||
val newStructure = Load.reapply(session.original ++ appendSettings, structure)
|
||||
Project.setProject(session, newStructure, state)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,14 @@ import descriptor.ModuleDescriptor, id.ModuleRevisionId
|
|||
import testing.Framework
|
||||
import KeyRanks._
|
||||
|
||||
import sbt.internal.{ BuildStructure, LoadedBuild, PluginDiscovery, BuildDependencies, SessionSettings, LogManager }
|
||||
import sbt.internal.{
|
||||
BuildStructure,
|
||||
LoadedBuild,
|
||||
PluginDiscovery,
|
||||
BuildDependencies,
|
||||
SessionSettings,
|
||||
LogManager
|
||||
}
|
||||
import sbt.io.FileFilter
|
||||
import sbt.internal.io.WatchState
|
||||
import sbt.internal.util.{ AttributeKey, SourcePosition }
|
||||
|
|
@ -77,6 +84,8 @@ import sbt.util.{ Level, Logger }
|
|||
import org.apache.logging.log4j.core.Appender
|
||||
import sbt.BuildSyntax._
|
||||
|
||||
// format: off
|
||||
|
||||
object Keys {
|
||||
val TraceValues = "-1 to disable, 0 for up to the first sbt frame, or a positive number to set the maximum number of frames shown."
|
||||
|
||||
|
|
@ -456,3 +465,5 @@ object Keys {
|
|||
type Streams = std.Streams[ScopedKey[_]]
|
||||
type TaskStreams = std.TaskStreams[ScopedKey[_]]
|
||||
}
|
||||
|
||||
// format: on
|
||||
|
|
|
|||
|
|
@ -4,13 +4,36 @@
|
|||
package sbt
|
||||
|
||||
import sbt.internal.{
|
||||
Act, Aggregation, BuildStructure, BuildUnit, CommandExchange, CommandStrings,
|
||||
EvaluateConfigurations, Inspect, IvyConsole, Load, LoadedBuildUnit, Output,
|
||||
PluginsDebug, ProjectNavigation, Script, SessionSettings, SetResult,
|
||||
SettingCompletions, LogManager, DefaultBackgroundJobService
|
||||
Act,
|
||||
Aggregation,
|
||||
BuildStructure,
|
||||
BuildUnit,
|
||||
CommandExchange,
|
||||
CommandStrings,
|
||||
EvaluateConfigurations,
|
||||
Inspect,
|
||||
IvyConsole,
|
||||
Load,
|
||||
LoadedBuildUnit,
|
||||
Output,
|
||||
PluginsDebug,
|
||||
ProjectNavigation,
|
||||
Script,
|
||||
SessionSettings,
|
||||
SetResult,
|
||||
SettingCompletions,
|
||||
LogManager,
|
||||
DefaultBackgroundJobService
|
||||
}
|
||||
import sbt.internal.util.{
|
||||
AttributeKey, AttributeMap, ConsoleOut, GlobalLogging, LineRange, MainAppender, SimpleReader, Types
|
||||
AttributeKey,
|
||||
AttributeMap,
|
||||
ConsoleOut,
|
||||
GlobalLogging,
|
||||
LineRange,
|
||||
MainAppender,
|
||||
SimpleReader,
|
||||
Types
|
||||
}
|
||||
import sbt.util.{ Level, Logger }
|
||||
|
||||
|
|
@ -36,21 +59,21 @@ import CommandStrings.BootCommand
|
|||
|
||||
/** This class is the entry point for sbt. */
|
||||
final class xMain extends xsbti.AppMain {
|
||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||
{
|
||||
import BasicCommands.early
|
||||
import BasicCommandStrings.runEarly
|
||||
import BuiltinCommands.defaults
|
||||
import sbt.internal.CommandStrings.{ BootCommand, DefaultsCommand, InitCommand }
|
||||
if (!java.lang.Boolean.getBoolean("sbt.skip.version.write")) {
|
||||
setSbtVersion(configuration.baseDirectory(), configuration.provider().id().version())
|
||||
}
|
||||
val state = initialState(configuration,
|
||||
Seq(defaults, early),
|
||||
runEarly(DefaultsCommand) :: runEarly(InitCommand) :: BootCommand :: Nil)
|
||||
notifyUsersAboutShell(state)
|
||||
runManaged(state)
|
||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = {
|
||||
import BasicCommands.early
|
||||
import BasicCommandStrings.runEarly
|
||||
import BuiltinCommands.defaults
|
||||
import sbt.internal.CommandStrings.{ BootCommand, DefaultsCommand, InitCommand }
|
||||
if (!java.lang.Boolean.getBoolean("sbt.skip.version.write")) {
|
||||
setSbtVersion(configuration.baseDirectory(), configuration.provider().id().version())
|
||||
}
|
||||
val state = initialState(
|
||||
configuration,
|
||||
Seq(defaults, early),
|
||||
runEarly(DefaultsCommand) :: runEarly(InitCommand) :: BootCommand :: Nil)
|
||||
notifyUsersAboutShell(state)
|
||||
runManaged(state)
|
||||
}
|
||||
|
||||
private val sbtVersionRegex = """sbt\.version\s*=.*""".r
|
||||
private def isSbtVersionLine(s: String) = sbtVersionRegex.pattern matcher s matches ()
|
||||
|
|
@ -67,7 +90,8 @@ final class xMain extends xsbti.AppMain {
|
|||
val sbtVersionAbsent = buildPropsLines forall (!isSbtVersionLine(_))
|
||||
|
||||
if (sbtVersionAbsent) {
|
||||
val errorMessage = s"WARN: No sbt.version set in project/build.properties, base directory: $baseDir"
|
||||
val errorMessage =
|
||||
s"WARN: No sbt.version set in project/build.properties, base directory: $baseDir"
|
||||
try {
|
||||
if (isSbtProject(baseDir, projectDir)) {
|
||||
val line = s"sbt.version=$sbtVersion"
|
||||
|
|
@ -83,16 +107,20 @@ final class xMain extends xsbti.AppMain {
|
|||
|
||||
private def isInteractive = System.console() != null
|
||||
private def hasCommand(state: State, cmd: String): Boolean =
|
||||
(state.remainingCommands find { x => x.commandLine == cmd }).isDefined
|
||||
(state.remainingCommands find { x =>
|
||||
x.commandLine == cmd
|
||||
}).isDefined
|
||||
|
||||
/**
|
||||
* The "boot" command adds "iflast shell" ("if last shell")
|
||||
* which basically means it falls back to shell if there are no further commands
|
||||
*/
|
||||
private def endsWithBoot(state: State) = state.remainingCommands.lastOption exists (_.commandLine == BootCommand)
|
||||
private def endsWithBoot(state: State) =
|
||||
state.remainingCommands.lastOption exists (_.commandLine == BootCommand)
|
||||
|
||||
private def notifyUsersAboutShell(state: State) =
|
||||
if (isInteractive && !hasCommand(state, Shell) && !hasCommand(state, OldShell) && !endsWithBoot(state)) {
|
||||
if (isInteractive && !hasCommand(state, Shell) && !hasCommand(state, OldShell) && !endsWithBoot(
|
||||
state)) {
|
||||
state.log warn "Executing in batch mode."
|
||||
state.log warn " For better performance, hit [ENTER] to switch to interactive mode, or"
|
||||
state.log warn " consider launching sbt without any commands, or explicitly passing 'shell'"
|
||||
|
|
@ -100,54 +128,69 @@ final class xMain extends xsbti.AppMain {
|
|||
}
|
||||
|
||||
final class ScriptMain extends xsbti.AppMain {
|
||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||
{
|
||||
import BasicCommandStrings.runEarly
|
||||
runManaged(initialState(
|
||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = {
|
||||
import BasicCommandStrings.runEarly
|
||||
runManaged(
|
||||
initialState(
|
||||
configuration,
|
||||
BuiltinCommands.ScriptCommands,
|
||||
runEarly(Level.Error.toString) :: Script.Name :: Nil
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class ConsoleMain extends xsbti.AppMain {
|
||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||
runManaged(initialState(
|
||||
configuration,
|
||||
BuiltinCommands.ConsoleCommands,
|
||||
IvyConsole.Name :: Nil
|
||||
))
|
||||
runManaged(
|
||||
initialState(
|
||||
configuration,
|
||||
BuiltinCommands.ConsoleCommands,
|
||||
IvyConsole.Name :: Nil
|
||||
))
|
||||
}
|
||||
|
||||
object StandardMain {
|
||||
private[sbt] lazy val exchange = new CommandExchange()
|
||||
|
||||
def runManaged(s: State): xsbti.MainResult =
|
||||
{
|
||||
val previous = TrapExit.installManager()
|
||||
def runManaged(s: State): xsbti.MainResult = {
|
||||
val previous = TrapExit.installManager()
|
||||
try {
|
||||
try {
|
||||
try {
|
||||
MainLoop.runLogged(s)
|
||||
} finally DefaultBackgroundJobService.backgroundJobService.shutdown()
|
||||
} finally TrapExit.uninstallManager(previous)
|
||||
}
|
||||
MainLoop.runLogged(s)
|
||||
} finally DefaultBackgroundJobService.backgroundJobService.shutdown()
|
||||
} finally TrapExit.uninstallManager(previous)
|
||||
}
|
||||
|
||||
/** The common interface to standard output, used for all built-in ConsoleLoggers. */
|
||||
val console = ConsoleOut.systemOutOverwrite(ConsoleOut.overwriteContaining("Resolving "))
|
||||
|
||||
def initialGlobalLogging: GlobalLogging = GlobalLogging.initial(MainAppender.globalDefault(console), File.createTempFile("sbt", ".log"), console)
|
||||
def initialGlobalLogging: GlobalLogging =
|
||||
GlobalLogging.initial(MainAppender.globalDefault(console),
|
||||
File.createTempFile("sbt", ".log"),
|
||||
console)
|
||||
|
||||
def initialState(configuration: xsbti.AppConfiguration, initialDefinitions: Seq[Command], preCommands: Seq[String]): State =
|
||||
{
|
||||
import BasicCommandStrings.isEarlyCommand
|
||||
val userCommands = configuration.arguments.map(_.trim)
|
||||
val (earlyCommands, normalCommands) = (preCommands ++ userCommands).partition(isEarlyCommand)
|
||||
val commands = (earlyCommands ++ normalCommands).toList map { x => Exec(x, None) }
|
||||
val initAttrs = BuiltinCommands.initialAttributes
|
||||
val s = State(configuration, initialDefinitions, Set.empty, None, commands, State.newHistory, initAttrs, initialGlobalLogging, None, State.Continue)
|
||||
s.initializeClassLoaderCache
|
||||
def initialState(configuration: xsbti.AppConfiguration,
|
||||
initialDefinitions: Seq[Command],
|
||||
preCommands: Seq[String]): State = {
|
||||
import BasicCommandStrings.isEarlyCommand
|
||||
val userCommands = configuration.arguments.map(_.trim)
|
||||
val (earlyCommands, normalCommands) = (preCommands ++ userCommands).partition(isEarlyCommand)
|
||||
val commands = (earlyCommands ++ normalCommands).toList map { x =>
|
||||
Exec(x, None)
|
||||
}
|
||||
val initAttrs = BuiltinCommands.initialAttributes
|
||||
val s = State(configuration,
|
||||
initialDefinitions,
|
||||
Set.empty,
|
||||
None,
|
||||
commands,
|
||||
State.newHistory,
|
||||
initAttrs,
|
||||
initialGlobalLogging,
|
||||
None,
|
||||
State.Continue)
|
||||
s.initializeClassLoaderCache
|
||||
}
|
||||
}
|
||||
|
||||
import DefaultParsers._
|
||||
|
|
@ -160,34 +203,79 @@ import TemplateCommandUtil.templateCommand
|
|||
object BuiltinCommands {
|
||||
def initialAttributes = AttributeMap.empty
|
||||
|
||||
def ConsoleCommands: Seq[Command] = Seq(ignore, exit, IvyConsole.command, setLogLevel, early, act, nop)
|
||||
def ScriptCommands: Seq[Command] = Seq(ignore, exit, Script.command, setLogLevel, early, act, nop)
|
||||
def ConsoleCommands: Seq[Command] =
|
||||
Seq(ignore, exit, IvyConsole.command, setLogLevel, early, act, nop)
|
||||
def ScriptCommands: Seq[Command] =
|
||||
Seq(ignore, exit, Script.command, setLogLevel, early, act, nop)
|
||||
|
||||
def DefaultCommands: Seq[Command] = Seq(
|
||||
ignore, help, completionsCommand, about, tasks, settingsCommand, loadProject,
|
||||
templateCommand, projects, project, reboot, read, history, set, sessionCommand,
|
||||
inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion,
|
||||
Cross.crossRestoreSession, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure,
|
||||
setLogLevel, plugin, plugins, ifLast, multi, shell, oldshell, BasicCommands.client,
|
||||
continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit,
|
||||
early, initialize, act
|
||||
) ++ compatCommands
|
||||
def DefaultCommands: Seq[Command] =
|
||||
Seq(
|
||||
ignore,
|
||||
help,
|
||||
completionsCommand,
|
||||
about,
|
||||
tasks,
|
||||
settingsCommand,
|
||||
loadProject,
|
||||
templateCommand,
|
||||
projects,
|
||||
project,
|
||||
reboot,
|
||||
read,
|
||||
history,
|
||||
set,
|
||||
sessionCommand,
|
||||
inspect,
|
||||
loadProjectImpl,
|
||||
loadFailed,
|
||||
Cross.crossBuild,
|
||||
Cross.switchVersion,
|
||||
Cross.crossRestoreSession,
|
||||
setOnFailure,
|
||||
clearOnFailure,
|
||||
stashOnFailure,
|
||||
popOnFailure,
|
||||
setLogLevel,
|
||||
plugin,
|
||||
plugins,
|
||||
ifLast,
|
||||
multi,
|
||||
shell,
|
||||
oldshell,
|
||||
BasicCommands.client,
|
||||
continuous,
|
||||
eval,
|
||||
alias,
|
||||
append,
|
||||
last,
|
||||
lastGrep,
|
||||
export,
|
||||
boot,
|
||||
nop,
|
||||
call,
|
||||
exit,
|
||||
early,
|
||||
initialize,
|
||||
act
|
||||
) ++ compatCommands
|
||||
|
||||
def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil
|
||||
|
||||
def boot = Command.make(BootCommand)(bootParser)
|
||||
|
||||
def about = Command.command(AboutCommand, aboutBrief, aboutDetailed) { s => s.log.info(aboutString(s)); s }
|
||||
def about = Command.command(AboutCommand, aboutBrief, aboutDetailed) { s =>
|
||||
s.log.info(aboutString(s)); s
|
||||
}
|
||||
|
||||
def setLogLevel = Command.arb(const(logLevelParser), logLevelHelp)(LogManager.setGlobalLogLevel)
|
||||
private[this] def logLevelParser: Parser[Level.Value] = oneOf(Level.values.toSeq.map(v => v.toString ^^^ v))
|
||||
private[this] def logLevelParser: Parser[Level.Value] =
|
||||
oneOf(Level.values.toSeq.map(v => v.toString ^^^ v))
|
||||
|
||||
// This parser schedules the default boot commands unless overridden by an alias
|
||||
def bootParser(s: State) =
|
||||
{
|
||||
val orElse = () => DefaultBootCommands.toList ::: s
|
||||
delegateToAlias(BootCommand, success(orElse))(s)
|
||||
}
|
||||
def bootParser(s: State) = {
|
||||
val orElse = () => DefaultBootCommands.toList ::: s
|
||||
delegateToAlias(BootCommand, success(orElse))(s)
|
||||
}
|
||||
|
||||
def sbtName(s: State): String = s.configuration.provider.id.name
|
||||
def sbtVersion(s: State): String = s.configuration.provider.id.version
|
||||
|
|
@ -202,35 +290,35 @@ object BuiltinCommands {
|
|||
current + built + aboutPlugins(e)
|
||||
} else "No project is currently loaded"
|
||||
|
||||
def aboutPlugins(e: Extracted): String =
|
||||
{
|
||||
def list(b: BuildUnit) = b.plugins.detected.autoPlugins.map(_.value.label)
|
||||
val allPluginNames = e.structure.units.values.flatMap(u => list(u.unit)).toSeq.distinct
|
||||
if (allPluginNames.isEmpty) "" else allPluginNames.mkString("Available Plugins: ", ", ", "")
|
||||
}
|
||||
def aboutPlugins(e: Extracted): String = {
|
||||
def list(b: BuildUnit) = b.plugins.detected.autoPlugins.map(_.value.label)
|
||||
val allPluginNames = e.structure.units.values.flatMap(u => list(u.unit)).toSeq.distinct
|
||||
if (allPluginNames.isEmpty) "" else allPluginNames.mkString("Available Plugins: ", ", ", "")
|
||||
}
|
||||
|
||||
def aboutScala(s: State, e: Extracted): String =
|
||||
{
|
||||
val scalaVersion = e.getOpt(Keys.scalaVersion)
|
||||
val scalaHome = e.getOpt(Keys.scalaHome).flatMap(idFun)
|
||||
val instance = e.getOpt(Keys.scalaInstance).flatMap(_ => quiet(e.runTask(Keys.scalaInstance, s)._2))
|
||||
(scalaVersion, scalaHome, instance) match {
|
||||
case (sv, Some(home), Some(si)) => "local Scala version " + selectScalaVersion(sv, si) + " at " + home.getAbsolutePath
|
||||
case (_, Some(home), None) => "a local Scala build at " + home.getAbsolutePath
|
||||
case (sv, None, Some(si)) => "Scala " + selectScalaVersion(sv, si)
|
||||
case (Some(sv), None, None) => "Scala " + sv
|
||||
case (None, None, None) => ""
|
||||
}
|
||||
def aboutScala(s: State, e: Extracted): String = {
|
||||
val scalaVersion = e.getOpt(Keys.scalaVersion)
|
||||
val scalaHome = e.getOpt(Keys.scalaHome).flatMap(idFun)
|
||||
val instance =
|
||||
e.getOpt(Keys.scalaInstance).flatMap(_ => quiet(e.runTask(Keys.scalaInstance, s)._2))
|
||||
(scalaVersion, scalaHome, instance) match {
|
||||
case (sv, Some(home), Some(si)) =>
|
||||
"local Scala version " + selectScalaVersion(sv, si) + " at " + home.getAbsolutePath
|
||||
case (_, Some(home), None) => "a local Scala build at " + home.getAbsolutePath
|
||||
case (sv, None, Some(si)) => "Scala " + selectScalaVersion(sv, si)
|
||||
case (Some(sv), None, None) => "Scala " + sv
|
||||
case (None, None, None) => ""
|
||||
}
|
||||
}
|
||||
|
||||
def aboutString(s: State): String =
|
||||
{
|
||||
val (name, ver, scalaVer, about) = (sbtName(s), sbtVersion(s), scalaVersion(s), aboutProject(s))
|
||||
"""This is %s %s
|
||||
def aboutString(s: State): String = {
|
||||
val (name, ver, scalaVer, about) =
|
||||
(sbtName(s), sbtVersion(s), scalaVersion(s), aboutProject(s))
|
||||
"""This is %s %s
|
||||
|%s
|
||||
|%s, %s plugins, and build definitions are using Scala %s
|
||||
|""".stripMargin.format(name, ver, about, name, name, scalaVer)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def selectScalaVersion(sv: Option[String], si: ScalaInstance): String =
|
||||
sv match {
|
||||
|
|
@ -238,15 +326,26 @@ object BuiltinCommands {
|
|||
case _ => si.actualVersion
|
||||
}
|
||||
|
||||
private[this] def quiet[T](t: => T): Option[T] = try { Some(t) } catch { case e: Exception => None }
|
||||
private[this] def quiet[T](t: => T): Option[T] = try { Some(t) } catch {
|
||||
case e: Exception => None
|
||||
}
|
||||
|
||||
def settingsCommand: Command =
|
||||
showSettingLike(SettingsCommand, settingsPreamble, KeyRanks.MainSettingCutoff, key => !isTask(key.manifest))
|
||||
showSettingLike(SettingsCommand,
|
||||
settingsPreamble,
|
||||
KeyRanks.MainSettingCutoff,
|
||||
key => !isTask(key.manifest))
|
||||
|
||||
def tasks: Command =
|
||||
showSettingLike(TasksCommand, tasksPreamble, KeyRanks.MainTaskCutoff, key => isTask(key.manifest))
|
||||
showSettingLike(TasksCommand,
|
||||
tasksPreamble,
|
||||
KeyRanks.MainTaskCutoff,
|
||||
key => isTask(key.manifest))
|
||||
|
||||
def showSettingLike(command: String, preamble: String, cutoff: Int, keep: AttributeKey[_] => Boolean): Command =
|
||||
def showSettingLike(command: String,
|
||||
preamble: String,
|
||||
cutoff: Int,
|
||||
keep: AttributeKey[_] => Boolean): Command =
|
||||
Command(command, settingsBrief(command), settingsDetailed(command))(showSettingParser(keep)) {
|
||||
case (s: State, (verbosity: Int, selected: Option[String])) =>
|
||||
if (selected.isEmpty) System.out.println(preamble)
|
||||
|
|
@ -257,92 +356,107 @@ object BuiltinCommands {
|
|||
if (prominentOnly) System.out.println(moreAvailableMessage(command, selected.isDefined))
|
||||
s
|
||||
}
|
||||
def showSettingParser(keepKeys: AttributeKey[_] => Boolean)(s: State): Parser[(Int, Option[String])] =
|
||||
def showSettingParser(keepKeys: AttributeKey[_] => Boolean)(
|
||||
s: State): Parser[(Int, Option[String])] =
|
||||
verbosityParser ~ selectedParser(s, keepKeys).?
|
||||
def selectedParser(s: State, keepKeys: AttributeKey[_] => Boolean): Parser[String] =
|
||||
singleArgument(allTaskAndSettingKeys(s).filter(keepKeys).map(_.label).toSet)
|
||||
def verbosityParser: Parser[Int] = success(1) | ((Space ~ "-") ~> (
|
||||
'v'.id.+.map(_.size + 1) |
|
||||
("V" ^^^ Int.MaxValue)
|
||||
))
|
||||
def verbosityParser: Parser[Int] =
|
||||
success(1) | ((Space ~ "-") ~> (
|
||||
'v'.id.+.map(_.size + 1) |
|
||||
("V" ^^^ Int.MaxValue)
|
||||
))
|
||||
def taskDetail(keys: Seq[AttributeKey[_]]): Seq[(String, String)] =
|
||||
sortByLabel(withDescription(keys)) flatMap taskStrings
|
||||
|
||||
def allTaskAndSettingKeys(s: State): Seq[AttributeKey[_]] =
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
val index = structure.index
|
||||
index.keyIndex.keys(Some(currentRef)).toSeq.map { key =>
|
||||
try
|
||||
Some(index.keyMap(key))
|
||||
def allTaskAndSettingKeys(s: State): Seq[AttributeKey[_]] = {
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
val index = structure.index
|
||||
index.keyIndex
|
||||
.keys(Some(currentRef))
|
||||
.toSeq
|
||||
.map { key =>
|
||||
try Some(index.keyMap(key))
|
||||
catch {
|
||||
case NonFatal(ex) =>
|
||||
s.log debug ex.getMessage
|
||||
None
|
||||
}
|
||||
}.collect { case Some(s) => s }.distinct
|
||||
}
|
||||
}
|
||||
.collect { case Some(s) => s }
|
||||
.distinct
|
||||
}
|
||||
|
||||
def sortByLabel(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] = keys.sortBy(_.label)
|
||||
def sortByRank(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] = keys.sortBy(_.rank)
|
||||
def withDescription(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] = keys.filter(_.description.isDefined)
|
||||
def isTask(mf: Manifest[_])(implicit taskMF: Manifest[Task[_]], inputMF: Manifest[InputTask[_]]): Boolean =
|
||||
def withDescription(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] =
|
||||
keys.filter(_.description.isDefined)
|
||||
def isTask(mf: Manifest[_])(implicit taskMF: Manifest[Task[_]],
|
||||
inputMF: Manifest[InputTask[_]]): Boolean =
|
||||
mf.runtimeClass == taskMF.runtimeClass || mf.runtimeClass == inputMF.runtimeClass
|
||||
def topNRanked(n: Int) = (keys: Seq[AttributeKey[_]]) => sortByRank(keys).take(n)
|
||||
def highPass(rankCutoff: Int) = (keys: Seq[AttributeKey[_]]) => sortByRank(keys).takeWhile(_.rank <= rankCutoff)
|
||||
def highPass(rankCutoff: Int) =
|
||||
(keys: Seq[AttributeKey[_]]) => sortByRank(keys).takeWhile(_.rank <= rankCutoff)
|
||||
|
||||
def tasksHelp(s: State, filter: Seq[AttributeKey[_]] => Seq[AttributeKey[_]], arg: Option[String]): String =
|
||||
{
|
||||
val commandAndDescription = taskDetail(filter(allTaskAndSettingKeys(s)))
|
||||
arg match {
|
||||
case Some(selected) => detail(selected, commandAndDescription.toMap)
|
||||
case None => aligned(" ", " ", commandAndDescription) mkString ("\n", "\n", "")
|
||||
}
|
||||
def tasksHelp(s: State,
|
||||
filter: Seq[AttributeKey[_]] => Seq[AttributeKey[_]],
|
||||
arg: Option[String]): String = {
|
||||
val commandAndDescription = taskDetail(filter(allTaskAndSettingKeys(s)))
|
||||
arg match {
|
||||
case Some(selected) => detail(selected, commandAndDescription.toMap)
|
||||
case None => aligned(" ", " ", commandAndDescription) mkString ("\n", "\n", "")
|
||||
}
|
||||
}
|
||||
|
||||
def taskStrings(key: AttributeKey[_]): Option[(String, String)] = key.description map { d => (key.label, d) }
|
||||
def taskStrings(key: AttributeKey[_]): Option[(String, String)] = key.description map { d =>
|
||||
(key.label, d)
|
||||
}
|
||||
|
||||
def defaults = Command.command(DefaultsCommand) { s =>
|
||||
s.copy(definedCommands = DefaultCommands)
|
||||
}
|
||||
|
||||
def initialize: Command = Command.command(InitCommand) { s =>
|
||||
/*"load-commands -base ~/.sbt/commands" :: */ readLines(readable(sbtRCs(s))).toList ::: s
|
||||
/*"load-commands -base ~/.sbt/commands" :: */
|
||||
readLines(readable(sbtRCs(s))).toList ::: s
|
||||
}
|
||||
|
||||
def eval: Command = Command.single(EvalCommand, Help.more(EvalCommand, evalDetailed)) { (s, arg) =>
|
||||
if (Project.isProjectLoaded(s)) loadedEval(s, arg) else rawEval(s, arg)
|
||||
s
|
||||
def eval: Command = Command.single(EvalCommand, Help.more(EvalCommand, evalDetailed)) {
|
||||
(s, arg) =>
|
||||
if (Project.isProjectLoaded(s)) loadedEval(s, arg) else rawEval(s, arg)
|
||||
s
|
||||
}
|
||||
|
||||
private[this] def loadedEval(s: State, arg: String): Unit = {
|
||||
val extracted = Project extract s
|
||||
import extracted._
|
||||
val result = session.currentEval().eval(arg, srcName = "<eval>", imports = autoImports(extracted))
|
||||
val result =
|
||||
session.currentEval().eval(arg, srcName = "<eval>", imports = autoImports(extracted))
|
||||
s.log.info(s"ans: ${result.tpe} = ${result.getValue(currentLoader)}")
|
||||
}
|
||||
|
||||
private[this] def rawEval(s: State, arg: String): Unit = {
|
||||
val app = s.configuration.provider
|
||||
val classpath = app.mainClasspath ++ app.scalaProvider.jars
|
||||
val result = Load.mkEval(classpath, s.baseDir, Nil).eval(arg, srcName = "<eval>", imports = new EvalImports(Nil, ""))
|
||||
val result = Load
|
||||
.mkEval(classpath, s.baseDir, Nil)
|
||||
.eval(arg, srcName = "<eval>", imports = new EvalImports(Nil, ""))
|
||||
s.log.info(s"ans: ${result.tpe} = ${result.getValue(app.loader)}")
|
||||
}
|
||||
|
||||
def sessionCommand: Command =
|
||||
Command.make(SessionCommand, sessionBrief, SessionSettings.Help)(SessionSettings.command)
|
||||
|
||||
def reapply(newSession: SessionSettings, structure: BuildStructure, s: State): State =
|
||||
{
|
||||
s.log.info("Reapplying settings...")
|
||||
// For correct behavior, we also need to re-inject a settings logger, as we'll be re-evaluating settings
|
||||
val loggerInject = LogManager.settingsLogger(s)
|
||||
val withLogger = newSession.appendRaw(loggerInject :: Nil)
|
||||
val show = Project.showContextKey(newSession, structure)
|
||||
val newStructure = Load.reapply(withLogger.mergeSettings, structure)(show)
|
||||
Project.setProject(newSession, newStructure, s)
|
||||
}
|
||||
def reapply(newSession: SessionSettings, structure: BuildStructure, s: State): State = {
|
||||
s.log.info("Reapplying settings...")
|
||||
// For correct behavior, we also need to re-inject a settings logger, as we'll be re-evaluating settings
|
||||
val loggerInject = LogManager.settingsLogger(s)
|
||||
val withLogger = newSession.appendRaw(loggerInject :: Nil)
|
||||
val show = Project.showContextKey(newSession, structure)
|
||||
val newStructure = Load.reapply(withLogger.mergeSettings, structure)(show)
|
||||
Project.setProject(newSession, newStructure, s)
|
||||
}
|
||||
|
||||
def set: Command = Command(SetCommand, setBrief, setDetailed)(setParser) {
|
||||
case (s, (all, arg)) =>
|
||||
|
|
@ -369,7 +483,10 @@ object BuiltinCommands {
|
|||
reapply(setResult.session, structure, s)
|
||||
}
|
||||
|
||||
def setThis(s: State, extracted: Extracted, settings: Seq[Def.Setting[_]], arg: String): SetResult =
|
||||
def setThis(s: State,
|
||||
extracted: Extracted,
|
||||
settings: Seq[Def.Setting[_]],
|
||||
arg: String): SetResult =
|
||||
SettingCompletions.setThis(s, extracted, settings, arg)
|
||||
|
||||
def inspect: Command = Command(InspectCommand, inspectBrief, inspectDetailed)(Inspect.parser) {
|
||||
|
|
@ -378,15 +495,16 @@ object BuiltinCommands {
|
|||
s
|
||||
}
|
||||
|
||||
def lastGrep: Command = Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) {
|
||||
case (s, (pattern, Some(sks))) =>
|
||||
val (str, _, display) = extractLast(s)
|
||||
Output.lastGrep(sks, str.streams(s), pattern, printLast(s))(display)
|
||||
keepLastLog(s)
|
||||
case (s, (pattern, None)) =>
|
||||
for (logFile <- lastLogFile(s)) yield Output.lastGrep(logFile, pattern, printLast(s))
|
||||
keepLastLog(s)
|
||||
}
|
||||
def lastGrep: Command =
|
||||
Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) {
|
||||
case (s, (pattern, Some(sks))) =>
|
||||
val (str, _, display) = extractLast(s)
|
||||
Output.lastGrep(sks, str.streams(s), pattern, printLast(s))(display)
|
||||
keepLastLog(s)
|
||||
case (s, (pattern, None)) =>
|
||||
for (logFile <- lastLogFile(s)) yield Output.lastGrep(logFile, pattern, printLast(s))
|
||||
keepLastLog(s)
|
||||
}
|
||||
def extractLast(s: State) = {
|
||||
val ext = Project.extract(s)
|
||||
(ext.structure, Select(ext.currentRef), ext.showKey)
|
||||
|
|
@ -399,32 +517,43 @@ object BuiltinCommands {
|
|||
SettingCompletions.settingParser(structure.data, structure.index.keyMap, currentProject)
|
||||
}
|
||||
|
||||
val spacedAggregatedParser = (s: State) => Act.requireSession(s, token(Space) ~> Act.aggregatedKeyParser(s))
|
||||
val aggregatedKeyValueParser: State => Parser[Option[AnyKeys]] = (s: State) => spacedAggregatedParser(s).map(x => Act.keyValues(s)(x)).?
|
||||
val spacedAggregatedParser = (s: State) =>
|
||||
Act.requireSession(s, token(Space) ~> Act.aggregatedKeyParser(s))
|
||||
val aggregatedKeyValueParser: State => Parser[Option[AnyKeys]] = (s: State) =>
|
||||
spacedAggregatedParser(s).map(x => Act.keyValues(s)(x)).?
|
||||
|
||||
val exportParser: State => Parser[() => State] = (s: State) => Act.requireSession(s, token(Space) ~> exportParser0(s))
|
||||
private[sbt] def exportParser0(s: State): Parser[() => State] =
|
||||
{
|
||||
val extracted = Project extract s
|
||||
import extracted.{ showKey, structure }
|
||||
val keysParser = token(flag("--last" <~ Space)) ~ Act.aggregatedKeyParser(extracted)
|
||||
val show = Aggregation.ShowConfig(settingValues = true, taskValues = false, print = println(_), success = false)
|
||||
for {
|
||||
lastOnly_keys <- keysParser
|
||||
kvs = Act.keyValues(structure)(lastOnly_keys._2)
|
||||
f <- if (lastOnly_keys._1) success(() => s) else Aggregation.evaluatingParser(s, structure, show)(kvs)
|
||||
} yield () => {
|
||||
val exportParser: State => Parser[() => State] = (s: State) =>
|
||||
Act.requireSession(s, token(Space) ~> exportParser0(s))
|
||||
private[sbt] def exportParser0(s: State): Parser[() => State] = {
|
||||
val extracted = Project extract s
|
||||
import extracted.{ showKey, structure }
|
||||
val keysParser = token(flag("--last" <~ Space)) ~ Act.aggregatedKeyParser(extracted)
|
||||
val show = Aggregation.ShowConfig(settingValues = true,
|
||||
taskValues = false,
|
||||
print = println(_),
|
||||
success = false)
|
||||
for {
|
||||
lastOnly_keys <- keysParser
|
||||
kvs = Act.keyValues(structure)(lastOnly_keys._2)
|
||||
f <- if (lastOnly_keys._1) success(() => s)
|
||||
else Aggregation.evaluatingParser(s, structure, show)(kvs)
|
||||
} yield
|
||||
() => {
|
||||
def export0(s: State): State = lastImpl(s, kvs, Some(ExportStream))
|
||||
val newS = try f() catch {
|
||||
val newS = try f()
|
||||
catch {
|
||||
case NonFatal(e) =>
|
||||
try export0(s)
|
||||
finally { throw e }
|
||||
}
|
||||
export0(newS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def lastGrepParser(s: State) = Act.requireSession(s, (token(Space) ~> token(NotSpace, "<pattern>")) ~ aggregatedKeyValueParser(s))
|
||||
def lastGrepParser(s: State) =
|
||||
Act.requireSession(
|
||||
s,
|
||||
(token(Space) ~> token(NotSpace, "<pattern>")) ~ aggregatedKeyValueParser(s))
|
||||
def last = Command(LastCommand, lastBrief, lastDetailed)(aggregatedKeyValueParser) {
|
||||
case (s, Some(sks)) => lastImpl(s, sks, None)
|
||||
case (s, None) =>
|
||||
|
|
@ -433,19 +562,17 @@ object BuiltinCommands {
|
|||
}
|
||||
def export = Command(ExportCommand, exportBrief, exportDetailed)(exportParser)((s, f) => f())
|
||||
|
||||
private[this] def lastImpl(s: State, sks: AnyKeys, sid: Option[String]): State =
|
||||
{
|
||||
val (str, _, display) = extractLast(s)
|
||||
Output.last(sks, str.streams(s), printLast(s), sid)(display)
|
||||
keepLastLog(s)
|
||||
}
|
||||
private[this] def lastImpl(s: State, sks: AnyKeys, sid: Option[String]): State = {
|
||||
val (str, _, display) = extractLast(s)
|
||||
Output.last(sks, str.streams(s), printLast(s), sid)(display)
|
||||
keepLastLog(s)
|
||||
}
|
||||
|
||||
/** Determines the log file that last* commands should operate on. See also isLastOnly. */
|
||||
def lastLogFile(s: State) =
|
||||
{
|
||||
val backing = s.globalLogging.backing
|
||||
if (isLastOnly(s)) backing.last else Some(backing.file)
|
||||
}
|
||||
def lastLogFile(s: State) = {
|
||||
val backing = s.globalLogging.backing
|
||||
if (isLastOnly(s)) backing.last else Some(backing.file)
|
||||
}
|
||||
|
||||
/**
|
||||
* If false, shift the current log file to be the log file that 'last' will operate on.
|
||||
|
|
@ -464,22 +591,26 @@ object BuiltinCommands {
|
|||
|
||||
def printLast(s: State): Seq[String] => Unit = _ foreach println
|
||||
|
||||
def autoImports(extracted: Extracted): EvalImports = new EvalImports(imports(extracted), "<auto-imports>")
|
||||
def imports(extracted: Extracted): Seq[(String, Int)] =
|
||||
{
|
||||
val curi = extracted.currentRef.build
|
||||
extracted.structure.units(curi).imports.map(s => (s, -1))
|
||||
}
|
||||
def autoImports(extracted: Extracted): EvalImports =
|
||||
new EvalImports(imports(extracted), "<auto-imports>")
|
||||
def imports(extracted: Extracted): Seq[(String, Int)] = {
|
||||
val curi = extracted.currentRef.build
|
||||
extracted.structure.units(curi).imports.map(s => (s, -1))
|
||||
}
|
||||
|
||||
def listBuild(uri: URI, build: LoadedBuildUnit, current: Boolean, currentID: String, log: Logger) =
|
||||
{
|
||||
log.info("In " + uri)
|
||||
def prefix(id: String) = if (currentID != id) " " else if (current) " * " else "(*)"
|
||||
for (id <- build.defined.keys.toSeq.sorted) log.info("\t" + prefix(id) + id)
|
||||
}
|
||||
def listBuild(uri: URI,
|
||||
build: LoadedBuildUnit,
|
||||
current: Boolean,
|
||||
currentID: String,
|
||||
log: Logger) = {
|
||||
log.info("In " + uri)
|
||||
def prefix(id: String) = if (currentID != id) " " else if (current) " * " else "(*)"
|
||||
for (id <- build.defined.keys.toSeq.sorted) log.info("\t" + prefix(id) + id)
|
||||
}
|
||||
|
||||
def act: Command = Command.customHelp(Act.actParser, actHelp)
|
||||
def actHelp: State => Help = s => CommandStrings.showHelp ++ CommandStrings.multiTaskHelp ++ keysHelp(s)
|
||||
def actHelp: State => Help =
|
||||
s => CommandStrings.showHelp ++ CommandStrings.multiTaskHelp ++ keysHelp(s)
|
||||
|
||||
def keysHelp(s: State): Help =
|
||||
if (Project.isProjectLoaded(s))
|
||||
|
|
@ -505,7 +636,8 @@ object BuiltinCommands {
|
|||
}
|
||||
|
||||
def projects: Command =
|
||||
Command(ProjectsCommand, (ProjectsCommand, projectsBrief), projectsDetailed)(s => projectsParser(s).?) {
|
||||
Command(ProjectsCommand, (ProjectsCommand, projectsBrief), projectsDetailed)(s =>
|
||||
projectsParser(s).?) {
|
||||
case (s, Some(modifyBuilds)) => transformExtraBuilds(s, modifyBuilds)
|
||||
case (s, None) => showProjects(s); s
|
||||
}
|
||||
|
|
@ -518,47 +650,48 @@ object BuiltinCommands {
|
|||
for ((uri, build) <- structure.units if curi != uri) listBuild(uri, build, false, cid, s.log)
|
||||
}
|
||||
|
||||
def transformExtraBuilds(s: State, f: List[URI] => List[URI]): State =
|
||||
{
|
||||
val original = Project.extraBuilds(s)
|
||||
val extraUpdated = Project.updateExtraBuilds(s, f)
|
||||
try doLoadProject(extraUpdated, LoadAction.Current)
|
||||
catch {
|
||||
case e: Exception =>
|
||||
s.log.error("Project loading failed: reverting to previous state.")
|
||||
Project.setExtraBuilds(s, original)
|
||||
}
|
||||
def transformExtraBuilds(s: State, f: List[URI] => List[URI]): State = {
|
||||
val original = Project.extraBuilds(s)
|
||||
val extraUpdated = Project.updateExtraBuilds(s, f)
|
||||
try doLoadProject(extraUpdated, LoadAction.Current)
|
||||
catch {
|
||||
case e: Exception =>
|
||||
s.log.error("Project loading failed: reverting to previous state.")
|
||||
Project.setExtraBuilds(s, original)
|
||||
}
|
||||
}
|
||||
|
||||
def projectsParser(s: State): Parser[List[URI] => List[URI]] =
|
||||
{
|
||||
val addBase = token(Space ~> "add") ~> token(Space ~> basicUri, "<build URI>").+
|
||||
val removeBase = token(Space ~> "remove") ~> token(Space ~> Uri(Project.extraBuilds(s).toSet)).+
|
||||
addBase.map(toAdd => (xs: List[URI]) => (toAdd.toList ::: xs).distinct) |
|
||||
removeBase.map(toRemove => (xs: List[URI]) => xs.filterNot(toRemove.toSet))
|
||||
}
|
||||
def projectsParser(s: State): Parser[List[URI] => List[URI]] = {
|
||||
val addBase = token(Space ~> "add") ~> token(Space ~> basicUri, "<build URI>").+
|
||||
val removeBase = token(Space ~> "remove") ~> token(Space ~> Uri(Project.extraBuilds(s).toSet)).+
|
||||
addBase.map(toAdd => (xs: List[URI]) => (toAdd.toList ::: xs).distinct) |
|
||||
removeBase.map(toRemove => (xs: List[URI]) => xs.filterNot(toRemove.toSet))
|
||||
}
|
||||
|
||||
def project: Command = Command.make(ProjectCommand, projectBrief, projectDetailed)(ProjectNavigation.command)
|
||||
def project: Command =
|
||||
Command.make(ProjectCommand, projectBrief, projectDetailed)(ProjectNavigation.command)
|
||||
|
||||
def loadFailed: Command = Command(LoadFailed)(loadProjectParser)(doLoadFailed)
|
||||
|
||||
@tailrec
|
||||
private[this] def doLoadFailed(s: State, loadArg: String): State =
|
||||
{
|
||||
val result = (SimpleReader.readLine("Project loading failed: (r)etry, (q)uit, (l)ast, or (i)gnore? ") getOrElse Quit).toLowerCase(Locale.ENGLISH)
|
||||
def matches(s: String) = !result.isEmpty && (s startsWith result)
|
||||
def retry = loadProjectCommand(LoadProject, loadArg) :: s.clearGlobalLog
|
||||
def ignoreMsg = if (Project.isProjectLoaded(s)) "using previously loaded project" else "no project loaded"
|
||||
private[this] def doLoadFailed(s: State, loadArg: String): State = {
|
||||
val result = (SimpleReader.readLine(
|
||||
"Project loading failed: (r)etry, (q)uit, (l)ast, or (i)gnore? ") getOrElse Quit)
|
||||
.toLowerCase(Locale.ENGLISH)
|
||||
def matches(s: String) = !result.isEmpty && (s startsWith result)
|
||||
def retry = loadProjectCommand(LoadProject, loadArg) :: s.clearGlobalLog
|
||||
def ignoreMsg =
|
||||
if (Project.isProjectLoaded(s)) "using previously loaded project" else "no project loaded"
|
||||
|
||||
result match {
|
||||
case "" => retry
|
||||
case _ if matches("retry") => retry
|
||||
case _ if matches(Quit) => s.exit(ok = false)
|
||||
case _ if matches("ignore") => s.log.warn(s"Ignoring load failure: $ignoreMsg."); s
|
||||
case _ if matches("last") => LastCommand :: loadProjectCommand(LoadFailed, loadArg) :: s
|
||||
case _ => println("Invalid response."); doLoadFailed(s, loadArg)
|
||||
}
|
||||
result match {
|
||||
case "" => retry
|
||||
case _ if matches("retry") => retry
|
||||
case _ if matches(Quit) => s.exit(ok = false)
|
||||
case _ if matches("ignore") => s.log.warn(s"Ignoring load failure: $ignoreMsg."); s
|
||||
case _ if matches("last") => LastCommand :: loadProjectCommand(LoadFailed, loadArg) :: s
|
||||
case _ => println("Invalid response."); doLoadFailed(s, loadArg)
|
||||
}
|
||||
}
|
||||
|
||||
def loadProjectCommands(arg: String): List[String] =
|
||||
StashOnFailure ::
|
||||
|
|
@ -570,49 +703,51 @@ object BuiltinCommands {
|
|||
|
||||
def loadProject: Command =
|
||||
Command(LoadProject, LoadProjectBrief, LoadProjectDetailed)(loadProjectParser)((s, arg) =>
|
||||
loadProjectCommands(arg) ::: s
|
||||
)
|
||||
loadProjectCommands(arg) ::: s)
|
||||
|
||||
private[this] def loadProjectParser: State => Parser[String] = _ => matched(Project.loadActionParser)
|
||||
private[this] def loadProjectCommand(command: String, arg: String): String = s"$command $arg".trim
|
||||
private[this] def loadProjectParser: State => Parser[String] =
|
||||
_ => matched(Project.loadActionParser)
|
||||
private[this] def loadProjectCommand(command: String, arg: String): String =
|
||||
s"$command $arg".trim
|
||||
|
||||
def loadProjectImpl: Command = Command(LoadProjectImpl)(_ => Project.loadActionParser)(doLoadProject)
|
||||
def loadProjectImpl: Command =
|
||||
Command(LoadProjectImpl)(_ => Project.loadActionParser)(doLoadProject)
|
||||
|
||||
def doLoadProject(s0: State, action: LoadAction.Value): State =
|
||||
{
|
||||
val (s1, base) = Project.loadAction(SessionVar.clear(s0), action)
|
||||
IO.createDirectory(base)
|
||||
val s = if (s1 has Keys.stateCompilerCache) s1 else registerCompilerCache(s1)
|
||||
def doLoadProject(s0: State, action: LoadAction.Value): State = {
|
||||
val (s1, base) = Project.loadAction(SessionVar.clear(s0), action)
|
||||
IO.createDirectory(base)
|
||||
val s = if (s1 has Keys.stateCompilerCache) s1 else registerCompilerCache(s1)
|
||||
|
||||
val (eval, structure) =
|
||||
try Load.defaultLoad(s, base, s.log, Project.inPluginProject(s), Project.extraBuilds(s))
|
||||
val (eval, structure) =
|
||||
try Load.defaultLoad(s, base, s.log, Project.inPluginProject(s), Project.extraBuilds(s))
|
||||
catch {
|
||||
case ex: compiler.EvalException =>
|
||||
s0.log.debug(ex.getMessage)
|
||||
ex.getStackTrace map (ste => s"\tat $ste") foreach (s0.log.debug(_))
|
||||
ex.setStackTrace(Array.empty)
|
||||
throw ex
|
||||
}
|
||||
|
||||
val session = Load.initialSession(structure, eval, s0)
|
||||
SessionSettings.checkSession(session, s)
|
||||
Project.setProject(session, structure, s)
|
||||
}
|
||||
|
||||
def registerCompilerCache(s: State): State = {
|
||||
val maxCompilers = System.getProperty("sbt.resident.limit")
|
||||
val cache =
|
||||
if (maxCompilers == null)
|
||||
CompilerCache.fresh
|
||||
else {
|
||||
val num = try maxCompilers.toInt
|
||||
catch {
|
||||
case ex: compiler.EvalException =>
|
||||
s0.log.debug(ex.getMessage)
|
||||
ex.getStackTrace map (ste => s"\tat $ste") foreach (s0.log.debug(_))
|
||||
ex.setStackTrace(Array.empty)
|
||||
throw ex
|
||||
case e: NumberFormatException =>
|
||||
throw new RuntimeException("Resident compiler limit must be an integer.", e)
|
||||
}
|
||||
|
||||
val session = Load.initialSession(structure, eval, s0)
|
||||
SessionSettings.checkSession(session, s)
|
||||
Project.setProject(session, structure, s)
|
||||
}
|
||||
|
||||
def registerCompilerCache(s: State): State =
|
||||
{
|
||||
val maxCompilers = System.getProperty("sbt.resident.limit")
|
||||
val cache =
|
||||
if (maxCompilers == null)
|
||||
CompilerCache.fresh
|
||||
else {
|
||||
val num = try maxCompilers.toInt catch {
|
||||
case e: NumberFormatException => throw new RuntimeException("Resident compiler limit must be an integer.", e)
|
||||
}
|
||||
if (num <= 0) CompilerCache.fresh else CompilerCache(num)
|
||||
}
|
||||
s.put(Keys.stateCompilerCache, cache)
|
||||
}
|
||||
if (num <= 0) CompilerCache.fresh else CompilerCache(num)
|
||||
}
|
||||
s.put(Keys.stateCompilerCache, cache)
|
||||
}
|
||||
|
||||
def shell: Command = Command.command(Shell, Help.more(Shell, ShellDetailed)) { s0 =>
|
||||
import sbt.internal.{ ConsolePromptEvent, ConsoleUnpromptEvent }
|
||||
|
|
@ -620,7 +755,10 @@ object BuiltinCommands {
|
|||
val s1 = exchange run s0
|
||||
exchange publishEventMessage ConsolePromptEvent(s0)
|
||||
val exec: Exec = exchange.blockUntilNextExec
|
||||
val newState = s1.copy(onFailure = Some(Exec(Shell, None)), remainingCommands = exec +: Exec(Shell, None) +: s1.remainingCommands).setInteractive(true)
|
||||
val newState = s1
|
||||
.copy(onFailure = Some(Exec(Shell, None)),
|
||||
remainingCommands = exec +: Exec(Shell, None) +: s1.remainingCommands)
|
||||
.setInteractive(true)
|
||||
exchange publishEventMessage ConsoleUnpromptEvent(exec.source)
|
||||
if (exec.commandLine.trim.isEmpty) newState
|
||||
else newState.clearGlobalLog
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import sbt.util.Logger
|
|||
import sbt.protocol._
|
||||
|
||||
object MainLoop {
|
||||
|
||||
/** Entry point to run the remaining commands in State with managed global logging.*/
|
||||
def runLogged(state: State): xsbti.MainResult = {
|
||||
// We've disabled jline shutdown hooks to prevent classloader leaks, and have been careful to always restore
|
||||
|
|
@ -48,14 +49,14 @@ object MainLoop {
|
|||
|
||||
/** Runs the next sequence of commands, cleaning up global logging after any exceptions. */
|
||||
def runAndClearLast(state: State, logBacking: GlobalLogBacking): RunNext =
|
||||
try
|
||||
runWithNewLog(state, logBacking)
|
||||
try runWithNewLog(state, logBacking)
|
||||
catch {
|
||||
case e: xsbti.FullReload =>
|
||||
deleteLastLog(logBacking)
|
||||
throw e // pass along a reboot request
|
||||
case NonFatal(e) =>
|
||||
System.err.println("sbt appears to be exiting abnormally.\n The log file for this session is at " + logBacking.file)
|
||||
System.err.println(
|
||||
"sbt appears to be exiting abnormally.\n The log file for this session is at " + logBacking.file)
|
||||
deleteLastLog(logBacking)
|
||||
throw e
|
||||
}
|
||||
|
|
@ -72,7 +73,8 @@ object MainLoop {
|
|||
val newLogging = state.globalLogging.newAppender(full, out, logBacking)
|
||||
// transferLevels(state, newLogging)
|
||||
val loggedState = state.copy(globalLogging = newLogging)
|
||||
try run(loggedState) finally out.close()
|
||||
try run(loggedState)
|
||||
finally out.close()
|
||||
}
|
||||
|
||||
// /** Transfers logging and trace levels from the old global loggers to the new ones. */
|
||||
|
|
@ -110,7 +112,10 @@ object MainLoop {
|
|||
def processCommand(exec: Exec, state: State): State = {
|
||||
import DefaultParsers._
|
||||
val channelName = exec.source map (_.channelName)
|
||||
StandardMain.exchange publishEventMessage ExecStatusEvent("Processing", channelName, exec.execId, Vector())
|
||||
StandardMain.exchange publishEventMessage ExecStatusEvent("Processing",
|
||||
channelName,
|
||||
exec.execId,
|
||||
Vector())
|
||||
val parser = Command combine state.definedCommands
|
||||
val newState = parse(exec.commandLine, parser(state)) match {
|
||||
case Right(s) => s() // apply command. command side effects happen here
|
||||
|
|
@ -118,7 +123,11 @@ object MainLoop {
|
|||
state.log error errMsg
|
||||
state.fail
|
||||
}
|
||||
StandardMain.exchange publishEventMessage ExecStatusEvent("Done", channelName, exec.execId, newState.remainingCommands.toVector map (_.commandLine))
|
||||
StandardMain.exchange publishEventMessage ExecStatusEvent(
|
||||
"Done",
|
||||
channelName,
|
||||
exec.execId,
|
||||
newState.remainingCommands.toVector map (_.commandLine))
|
||||
newState
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,15 +27,22 @@ object Opts {
|
|||
def sourceUrl(u: String): Seq[String] = Seq("-doc-source-url", u)
|
||||
def title(t: String): Seq[String] = Seq("-doc-title", t)
|
||||
def version(v: String): Seq[String] = Seq("-doc-version", v)
|
||||
def externalAPI(mappings: Iterable[(File, URL)]): Seq[String] = if (mappings.isEmpty) Nil else
|
||||
mappings.map { case (f, u) => s"${f.getAbsolutePath}#${u.toExternalForm}" }.mkString("-doc-external-doc:", ",", "") :: Nil
|
||||
def externalAPI(mappings: Iterable[(File, URL)]): Seq[String] =
|
||||
if (mappings.isEmpty) Nil
|
||||
else
|
||||
mappings
|
||||
.map { case (f, u) => s"${f.getAbsolutePath}#${u.toExternalForm}" }
|
||||
.mkString("-doc-external-doc:", ",", "") :: Nil
|
||||
}
|
||||
object resolver {
|
||||
import sbt.io.syntax._
|
||||
val sonatypeReleases = Resolver.sonatypeRepo("releases")
|
||||
val sonatypeSnapshots = Resolver.sonatypeRepo("snapshots")
|
||||
val sonatypeStaging = MavenRepository("sonatype-staging", "https://oss.sonatype.org/service/local/staging/deploy/maven2")
|
||||
val mavenLocalFile = Resolver.file("Local Repository", userHome / ".m2" / "repository" asFile)(Resolver.defaultPatterns)
|
||||
val sonatypeStaging = MavenRepository(
|
||||
"sonatype-staging",
|
||||
"https://oss.sonatype.org/service/local/staging/deploy/maven2")
|
||||
val mavenLocalFile = Resolver.file("Local Repository", userHome / ".m2" / "repository" asFile)(
|
||||
Resolver.defaultPatterns)
|
||||
val sbtSnapshots = Resolver.bintrayRepo("sbt", "maven-snapshots")
|
||||
val sbtIvySnapshots = Resolver.bintrayIvyRepo("sbt", "ivy-snapshots")
|
||||
}
|
||||
|
|
@ -50,8 +57,10 @@ object DefaultOptions {
|
|||
|
||||
def javac: Seq[String] = compile.encoding("UTF-8")
|
||||
def scalac: Seq[String] = compile.encoding("UTF-8")
|
||||
def javadoc(name: String, version: String): Seq[String] = Seq("-doctitle", "%s %s API".format(name, version))
|
||||
def scaladoc(name: String, version: String): Seq[String] = doc.title(name) ++ doc.version(version)
|
||||
def javadoc(name: String, version: String): Seq[String] =
|
||||
Seq("-doctitle", "%s %s API".format(name, version))
|
||||
def scaladoc(name: String, version: String): Seq[String] =
|
||||
doc.title(name) ++ doc.version(version)
|
||||
|
||||
def resolvers(snapshot: Boolean): Seq[Resolver] = {
|
||||
if (snapshot) Seq(resolver.sbtSnapshots) else Nil
|
||||
|
|
@ -60,12 +69,15 @@ object DefaultOptions {
|
|||
if (plugin && snapshot) Seq(resolver.sbtSnapshots, resolver.sbtIvySnapshots) else Nil
|
||||
}
|
||||
def addResolvers: Setting[_] = Keys.resolvers ++= { resolvers(Keys.isSnapshot.value) }
|
||||
def addPluginResolvers: Setting[_] = Keys.resolvers ++= pluginResolvers(Keys.sbtPlugin.value, Keys.isSnapshot.value)
|
||||
def addPluginResolvers: Setting[_] =
|
||||
Keys.resolvers ++= pluginResolvers(Keys.sbtPlugin.value, Keys.isSnapshot.value)
|
||||
|
||||
def credentials(state: State): Credentials = Credentials(getGlobalSettingsDirectory(state, getGlobalBase(state)) / ".credentials")
|
||||
def credentials(state: State): Credentials =
|
||||
Credentials(getGlobalSettingsDirectory(state, getGlobalBase(state)) / ".credentials")
|
||||
def addCredentials: Setting[_] = Keys.credentials += { credentials(Keys.state.value) }
|
||||
|
||||
def shellPrompt(version: String): State => String =
|
||||
s => "%s:%s:%s> ".format(s.configuration.provider.id.name, extract(s).currentProject.id, version)
|
||||
s =>
|
||||
"%s:%s:%s> ".format(s.configuration.provider.id.name, extract(s).currentProject.id, version)
|
||||
def setupShellPrompt: Setting[_] = Keys.shellPrompt := { shellPrompt(Keys.version.value) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package sbt
|
|||
TODO:
|
||||
- index all available AutoPlugins to get the tasks that will be added
|
||||
- error message when a task doesn't exist that it would be provided by plugin x, enabled by natures y,z, blocked by a, b
|
||||
*/
|
||||
*/
|
||||
|
||||
import sbt.librarymanagement.Configuration
|
||||
|
||||
|
|
@ -60,6 +60,7 @@ import sbt.util.Logger
|
|||
* then the `MyPlugin` settings (and anything that activates only when `MyPlugin` is activated) will not be added.
|
||||
*/
|
||||
abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions {
|
||||
|
||||
/**
|
||||
* Determines whether this AutoPlugin will be activated for this project when the `requires` clause is satisfied.
|
||||
*
|
||||
|
|
@ -122,13 +123,16 @@ abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions {
|
|||
* An error that occurs when auto-plugins aren't configured properly.
|
||||
* It translates the error from the underlying logic system to be targeted at end users.
|
||||
*/
|
||||
final class AutoPluginException private (val message: String, val origin: Option[LogicException]) extends RuntimeException(message) {
|
||||
final class AutoPluginException private (val message: String, val origin: Option[LogicException])
|
||||
extends RuntimeException(message) {
|
||||
|
||||
/** Prepends `p` to the error message derived from `origin`. */
|
||||
def withPrefix(p: String) = new AutoPluginException(p + message, origin)
|
||||
}
|
||||
object AutoPluginException {
|
||||
def apply(msg: String): AutoPluginException = new AutoPluginException(msg, None)
|
||||
def apply(origin: LogicException): AutoPluginException = new AutoPluginException(Plugins.translateMessage(origin), Some(origin))
|
||||
def apply(origin: LogicException): AutoPluginException =
|
||||
new AutoPluginException(Plugins.translateMessage(origin), Some(origin))
|
||||
}
|
||||
|
||||
sealed trait PluginTrigger
|
||||
|
|
@ -141,76 +145,100 @@ sealed trait Plugins {
|
|||
}
|
||||
|
||||
sealed trait PluginsFunctions {
|
||||
|
||||
/** [[Plugins]] instance that doesn't require any [[Plugins]]s. */
|
||||
def empty: Plugins = Plugins.Empty
|
||||
|
||||
/** This plugin is activated when all required plugins are present. */
|
||||
def allRequirements: PluginTrigger = AllRequirements
|
||||
|
||||
/** This plugin is activated only when it is manually activated. */
|
||||
def noTrigger: PluginTrigger = NoTrigger
|
||||
}
|
||||
|
||||
object Plugins extends PluginsFunctions {
|
||||
|
||||
/**
|
||||
* Given the available auto plugins `defined`, returns a function that selects [[AutoPlugin]]s for the provided [[AutoPlugin]]s.
|
||||
* The [[AutoPlugin]]s are topologically sorted so that a required [[AutoPlugin]] comes before its requiring [[AutoPlugin]].
|
||||
*/
|
||||
def deducer(defined0: List[AutoPlugin]): (Plugins, Logger) => Seq[AutoPlugin] =
|
||||
if (defined0.isEmpty) (_, _) => Nil
|
||||
if (defined0.isEmpty)(_, _) => Nil
|
||||
else {
|
||||
// TODO: defined should return all the plugins
|
||||
val allReqs = (defined0 flatMap { asRequirements }).toSet
|
||||
val diff = allReqs diff defined0.toSet
|
||||
val defined = if (diff.nonEmpty) diff.toList ::: defined0
|
||||
else defined0
|
||||
val defined =
|
||||
if (diff.nonEmpty) diff.toList ::: defined0
|
||||
else defined0
|
||||
|
||||
val byAtom = defined map { x => (Atom(x.label), x) }
|
||||
val byAtom = defined map { x =>
|
||||
(Atom(x.label), x)
|
||||
}
|
||||
val byAtomMap = byAtom.toMap
|
||||
if (byAtom.size != byAtomMap.size) duplicateProvidesError(byAtom)
|
||||
// Ignore clauses for plugins that does not require anything else.
|
||||
// Avoids the requirement for pure Nature strings *and* possible
|
||||
// circular dependencies in the logic.
|
||||
val allRequirementsClause = defined.filterNot(_.isRoot).flatMap(d => asRequirementsClauses(d))
|
||||
val allRequirementsClause =
|
||||
defined.filterNot(_.isRoot).flatMap(d => asRequirementsClauses(d))
|
||||
val allEnabledByClause = defined.filterNot(_.isRoot).flatMap(d => asEnabledByClauses(d))
|
||||
|
||||
// Note: Here is where the function begins. We're given a list of plugins now.
|
||||
(requestedPlugins, log) => {
|
||||
timed("Plugins.deducer#function", log) {
|
||||
def explicitlyDisabled(p: AutoPlugin): Boolean = hasExclude(requestedPlugins, p)
|
||||
val alwaysEnabled: List[AutoPlugin] = defined.filter(_.isAlwaysEnabled).filterNot(explicitlyDisabled)
|
||||
val knowledge0: Set[Atom] = ((flatten(requestedPlugins) ++ alwaysEnabled) collect {
|
||||
case x: AutoPlugin => Atom(x.label)
|
||||
}).toSet
|
||||
val clauses = Clauses((allRequirementsClause ::: allEnabledByClause) filterNot { _.head subsetOf knowledge0 })
|
||||
log.debug(s"deducing auto plugins based on known facts ${knowledge0.toString} and clauses ${clauses.toString}")
|
||||
Logic.reduce(clauses, (flattenConvert(requestedPlugins) ++ convertAll(alwaysEnabled)).toSet) match {
|
||||
case Left(problem) => throw AutoPluginException(problem)
|
||||
case Right(results) =>
|
||||
log.debug(s" :: deduced result: ${results}")
|
||||
val selectedAtoms: List[Atom] = results.ordered
|
||||
val selectedPlugins = selectedAtoms map { a =>
|
||||
byAtomMap.getOrElse(a, throw AutoPluginException(s"${a} was not found in atom map."))
|
||||
}
|
||||
val forbidden: Set[AutoPlugin] = (selectedPlugins flatMap { Plugins.asExclusions }).toSet
|
||||
val c = selectedPlugins.toSet & forbidden
|
||||
if (c.nonEmpty) {
|
||||
exclusionConflictError(requestedPlugins, selectedPlugins, c.toSeq sortBy { _.label })
|
||||
}
|
||||
val retval = topologicalSort(selectedPlugins, log)
|
||||
// log.debug(s" :: sorted deduced result: ${retval.toString}")
|
||||
retval
|
||||
(requestedPlugins, log) =>
|
||||
{
|
||||
timed("Plugins.deducer#function", log) {
|
||||
def explicitlyDisabled(p: AutoPlugin): Boolean = hasExclude(requestedPlugins, p)
|
||||
val alwaysEnabled: List[AutoPlugin] =
|
||||
defined.filter(_.isAlwaysEnabled).filterNot(explicitlyDisabled)
|
||||
val knowledge0: Set[Atom] = ((flatten(requestedPlugins) ++ alwaysEnabled) collect {
|
||||
case x: AutoPlugin => Atom(x.label)
|
||||
}).toSet
|
||||
val clauses = Clauses((allRequirementsClause ::: allEnabledByClause) filterNot {
|
||||
_.head subsetOf knowledge0
|
||||
})
|
||||
log.debug(
|
||||
s"deducing auto plugins based on known facts ${knowledge0.toString} and clauses ${clauses.toString}")
|
||||
Logic.reduce(
|
||||
clauses,
|
||||
(flattenConvert(requestedPlugins) ++ convertAll(alwaysEnabled)).toSet) match {
|
||||
case Left(problem) => throw AutoPluginException(problem)
|
||||
case Right(results) =>
|
||||
log.debug(s" :: deduced result: ${results}")
|
||||
val selectedAtoms: List[Atom] = results.ordered
|
||||
val selectedPlugins = selectedAtoms map { a =>
|
||||
byAtomMap.getOrElse(
|
||||
a,
|
||||
throw AutoPluginException(s"${a} was not found in atom map."))
|
||||
}
|
||||
val forbidden: Set[AutoPlugin] =
|
||||
(selectedPlugins flatMap { Plugins.asExclusions }).toSet
|
||||
val c = selectedPlugins.toSet & forbidden
|
||||
if (c.nonEmpty) {
|
||||
exclusionConflictError(requestedPlugins, selectedPlugins, c.toSeq sortBy {
|
||||
_.label
|
||||
})
|
||||
}
|
||||
val retval = topologicalSort(selectedPlugins, log)
|
||||
// log.debug(s" :: sorted deduced result: ${retval.toString}")
|
||||
retval
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private[sbt] def topologicalSort(ns: List[AutoPlugin], log: Logger): List[AutoPlugin] = {
|
||||
// log.debug(s"sorting: ns: ${ns.toString}")
|
||||
@tailrec def doSort(found0: List[AutoPlugin], notFound0: List[AutoPlugin], limit0: Int): List[AutoPlugin] = {
|
||||
@tailrec
|
||||
def doSort(found0: List[AutoPlugin],
|
||||
notFound0: List[AutoPlugin],
|
||||
limit0: Int): List[AutoPlugin] = {
|
||||
// log.debug(s" :: sorting:: found: ${found0.toString} not found ${notFound0.toString}")
|
||||
if (limit0 < 0) throw AutoPluginException(s"Failed to sort ${ns} topologically")
|
||||
else if (notFound0.isEmpty) found0
|
||||
else {
|
||||
val (found1, notFound1) = notFound0 partition { n => asRequirements(n).toSet subsetOf found0.toSet }
|
||||
val (found1, notFound1) = notFound0 partition { n =>
|
||||
asRequirements(n).toSet subsetOf found0.toSet
|
||||
}
|
||||
doSort(found0 ::: found1, notFound1, limit0 - 1)
|
||||
}
|
||||
}
|
||||
|
|
@ -218,38 +246,50 @@ object Plugins extends PluginsFunctions {
|
|||
doSort(roots, nonRoots, ns.size * ns.size + 1)
|
||||
}
|
||||
private[sbt] def translateMessage(e: LogicException) = e match {
|
||||
case ic: InitialContradictions => s"Contradiction in selected plugins. These plugins were both included and excluded: ${literalsString(ic.literals.toSeq)}"
|
||||
case io: InitialOverlap => s"Cannot directly enable plugins. Plugins are enabled when their required plugins are satisfied. The directly selected plugins were: ${literalsString(io.literals.toSeq)}"
|
||||
case cn: CyclicNegation => s"Cycles in plugin requirements cannot involve excludes. The problematic cycle is: ${literalsString(cn.cycle)}"
|
||||
case ic: InitialContradictions =>
|
||||
s"Contradiction in selected plugins. These plugins were both included and excluded: ${literalsString(
|
||||
ic.literals.toSeq)}"
|
||||
case io: InitialOverlap =>
|
||||
s"Cannot directly enable plugins. Plugins are enabled when their required plugins are satisfied. The directly selected plugins were: ${literalsString(
|
||||
io.literals.toSeq)}"
|
||||
case cn: CyclicNegation =>
|
||||
s"Cycles in plugin requirements cannot involve excludes. The problematic cycle is: ${literalsString(cn.cycle)}"
|
||||
}
|
||||
private[this] def literalsString(lits: Seq[Literal]): String =
|
||||
lits map { case Atom(l) => l; case Negated(Atom(l)) => l } mkString (", ")
|
||||
|
||||
private[this] def duplicateProvidesError(byAtom: Seq[(Atom, AutoPlugin)]): Unit = {
|
||||
val dupsByAtom = byAtom.groupBy(_._1).mapValues(_.map(_._2))
|
||||
val dupStrings = for ((atom, dups) <- dupsByAtom if dups.size > 1) yield s"${atom.label} by ${dups.mkString(", ")}"
|
||||
val dupStrings = for ((atom, dups) <- dupsByAtom if dups.size > 1)
|
||||
yield s"${atom.label} by ${dups.mkString(", ")}"
|
||||
val (ns, nl) = if (dupStrings.size > 1) ("s", "\n\t") else ("", " ")
|
||||
val message = s"Plugin$ns provided by multiple AutoPlugins:$nl${dupStrings.mkString(nl)}"
|
||||
throw AutoPluginException(message)
|
||||
}
|
||||
private[this] def exclusionConflictError(requested: Plugins, selected: Seq[AutoPlugin], conflicting: Seq[AutoPlugin]): Unit = {
|
||||
def listConflicts(ns: Seq[AutoPlugin]) = (ns map { c =>
|
||||
val reasons = (if (flatten(requested) contains c) List("requested")
|
||||
else Nil) ++
|
||||
(if (c.requires != empty && c.trigger == allRequirements) List(s"enabled by ${c.requires.toString}")
|
||||
else Nil) ++
|
||||
{
|
||||
val reqs = selected filter { x => asRequirements(x) contains c }
|
||||
private[this] def exclusionConflictError(requested: Plugins,
|
||||
selected: Seq[AutoPlugin],
|
||||
conflicting: Seq[AutoPlugin]): Unit = {
|
||||
def listConflicts(ns: Seq[AutoPlugin]) =
|
||||
(ns map { c =>
|
||||
val reasons = (if (flatten(requested) contains c) List("requested")
|
||||
else Nil) ++
|
||||
(if (c.requires != empty && c.trigger == allRequirements)
|
||||
List(s"enabled by ${c.requires.toString}")
|
||||
else Nil) ++ {
|
||||
val reqs = selected filter { x =>
|
||||
asRequirements(x) contains c
|
||||
}
|
||||
if (reqs.nonEmpty) List(s"""required by ${reqs.mkString(", ")}""")
|
||||
else Nil
|
||||
} ++
|
||||
{
|
||||
val exs = selected filter { x => asExclusions(x) contains c }
|
||||
} ++ {
|
||||
val exs = selected filter { x =>
|
||||
asExclusions(x) contains c
|
||||
}
|
||||
if (exs.nonEmpty) List(s"""excluded by ${exs.mkString(", ")}""")
|
||||
else Nil
|
||||
}
|
||||
s""" - conflict: ${c.label} is ${reasons.mkString("; ")}"""
|
||||
}).mkString("\n")
|
||||
s""" - conflict: ${c.label} is ${reasons.mkString("; ")}"""
|
||||
}).mkString("\n")
|
||||
throw AutoPluginException(s"""Contradiction in enabled plugins:
|
||||
- requested: ${requested.toString}
|
||||
- enabled: ${selected.mkString(", ")}
|
||||
|
|
@ -291,16 +331,21 @@ ${listConflicts(conflicting)}""")
|
|||
// `ap` is the head and the required plugins for `ap` is the body.
|
||||
if (ap.trigger == AllRequirements) Clause(convert(ap.requires), Set(Atom(ap.label))) :: Nil
|
||||
else Nil
|
||||
|
||||
/** Defines requirements clauses for `ap`. */
|
||||
private[sbt] def asRequirementsClauses(ap: AutoPlugin): List[Clause] =
|
||||
// required plugin is the head and `ap` is the body.
|
||||
asRequirements(ap) map { x => Clause(convert(ap), Set(Atom(x.label))) }
|
||||
private[sbt] def asRequirements(ap: AutoPlugin): List[AutoPlugin] = flatten(ap.requires).toList collect {
|
||||
case x: AutoPlugin => x
|
||||
}
|
||||
private[sbt] def asExclusions(ap: AutoPlugin): List[AutoPlugin] = flatten(ap.requires).toList collect {
|
||||
case Exclude(x) => x
|
||||
}
|
||||
asRequirements(ap) map { x =>
|
||||
Clause(convert(ap), Set(Atom(x.label)))
|
||||
}
|
||||
private[sbt] def asRequirements(ap: AutoPlugin): List[AutoPlugin] =
|
||||
flatten(ap.requires).toList collect {
|
||||
case x: AutoPlugin => x
|
||||
}
|
||||
private[sbt] def asExclusions(ap: AutoPlugin): List[AutoPlugin] =
|
||||
flatten(ap.requires).toList collect {
|
||||
case Exclude(x) => x
|
||||
}
|
||||
// TODO - This doesn't handle nested AND boolean logic...
|
||||
private[sbt] def hasExclude(n: Plugins, p: AutoPlugin): Boolean = n match {
|
||||
case `p` => false
|
||||
|
|
@ -308,10 +353,10 @@ ${listConflicts(conflicting)}""")
|
|||
// TODO - This is stupidly advanced. We do a nested check through possible and-ed
|
||||
// lists of plugins exclusions to see if the plugin ever winds up in an excluded=true case.
|
||||
// This would handle things like !!p or !(p && z)
|
||||
case Exclude(n) => hasInclude(n, p)
|
||||
case And(ns) => ns.forall(n => hasExclude(n, p))
|
||||
case b: Basic => false
|
||||
case Empty => false
|
||||
case Exclude(n) => hasInclude(n, p)
|
||||
case And(ns) => ns.forall(n => hasExclude(n, p))
|
||||
case b: Basic => false
|
||||
case Empty => false
|
||||
}
|
||||
private[sbt] def hasInclude(n: Plugins, p: AutoPlugin): Boolean = n match {
|
||||
case `p` => true
|
||||
|
|
@ -352,16 +397,16 @@ ${listConflicts(conflicting)}""")
|
|||
private val autoImport = "autoImport"
|
||||
|
||||
/** Determines whether a plugin has a stable autoImport member by:
|
||||
*
|
||||
* 1. Checking whether there exists a public field.
|
||||
* 2. Checking whether there exists a public object.
|
||||
*
|
||||
* The above checks work for inherited members too.
|
||||
*
|
||||
* @param ap The found plugin.
|
||||
* @param loader The plugin loader.
|
||||
* @return True if plugin has a stable member `autoImport`, otherwise false.
|
||||
*/
|
||||
*
|
||||
* 1. Checking whether there exists a public field.
|
||||
* 2. Checking whether there exists a public object.
|
||||
*
|
||||
* The above checks work for inherited members too.
|
||||
*
|
||||
* @param ap The found plugin.
|
||||
* @param loader The plugin loader.
|
||||
* @return True if plugin has a stable member `autoImport`, otherwise false.
|
||||
*/
|
||||
private[sbt] def hasAutoImportGetter(ap: AutoPlugin, loader: ClassLoader): Boolean = {
|
||||
import java.lang.reflect.Field
|
||||
import scala.util.control.Exception.catching
|
||||
|
|
@ -374,8 +419,9 @@ ${listConflicts(conflicting)}""")
|
|||
|
||||
val pluginClazz = ap.getClass
|
||||
existsAutoImportVal(pluginClazz)
|
||||
.orElse(catching(classOf[ClassNotFoundException]).opt(
|
||||
Class.forName(s"${pluginClazz.getName}$autoImport$$", false, loader)))
|
||||
.orElse(
|
||||
catching(classOf[ClassNotFoundException]).opt(
|
||||
Class.forName(s"${pluginClazz.getName}$autoImport$$", false, loader)))
|
||||
.isDefined
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -26,15 +26,18 @@ object Resolvers {
|
|||
val from = new File(uri)
|
||||
val to = uniqueSubdirectoryFor(uri, in = info.staging)
|
||||
|
||||
if (from.isDirectory) Some { () => if (from.canWrite) from else creates(to) { IO.copyDirectory(from, to) } }
|
||||
else None
|
||||
if (from.isDirectory) Some { () =>
|
||||
if (from.canWrite) from else creates(to) { IO.copyDirectory(from, to) }
|
||||
} else None
|
||||
}
|
||||
|
||||
val remote: Resolver = (info: ResolveInfo) => {
|
||||
val url = info.uri.toURL
|
||||
val to = uniqueSubdirectoryFor(info.uri, in = info.staging)
|
||||
|
||||
Some { () => creates(to) { IO.unzipURL(url, to) } }
|
||||
Some { () =>
|
||||
creates(to) { IO.unzipURL(url, to) }
|
||||
}
|
||||
}
|
||||
|
||||
val subversion: Resolver = (info: ResolveInfo) => {
|
||||
|
|
@ -47,18 +50,16 @@ object Resolvers {
|
|||
|
||||
if (uri.hasFragment) {
|
||||
val revision = uri.getFragment
|
||||
Some {
|
||||
() =>
|
||||
creates(localCopy) {
|
||||
run("svn", "checkout", "-q", "-r", revision, from, to)
|
||||
}
|
||||
Some { () =>
|
||||
creates(localCopy) {
|
||||
run("svn", "checkout", "-q", "-r", revision, from, to)
|
||||
}
|
||||
}
|
||||
} else
|
||||
Some {
|
||||
() =>
|
||||
creates(localCopy) {
|
||||
run("svn", "checkout", "-q", from, to)
|
||||
}
|
||||
Some { () =>
|
||||
creates(localCopy) {
|
||||
run("svn", "checkout", "-q", from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,11 +88,12 @@ object Resolvers {
|
|||
run(Some(localCopy), "git", "checkout", "-q", branch)
|
||||
}
|
||||
}
|
||||
} else Some { () =>
|
||||
creates(localCopy) {
|
||||
run("git", "clone", "--depth", "1", from, localCopy.getAbsolutePath)
|
||||
} else
|
||||
Some { () =>
|
||||
creates(localCopy) {
|
||||
run("git", "clone", "--depth", "1", from, localCopy.getAbsolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DistributedVCS {
|
||||
|
|
@ -108,14 +110,16 @@ object Resolvers {
|
|||
|
||||
if (uri.hasFragment) {
|
||||
val branch = uri.getFragment
|
||||
Some {
|
||||
() =>
|
||||
creates(localCopy) {
|
||||
clone(from, to = localCopy)
|
||||
checkout(branch, in = localCopy)
|
||||
}
|
||||
Some { () =>
|
||||
creates(localCopy) {
|
||||
clone(from, to = localCopy)
|
||||
checkout(branch, in = localCopy)
|
||||
}
|
||||
}
|
||||
} else
|
||||
Some { () =>
|
||||
creates(localCopy) { clone(from, to = localCopy) }
|
||||
}
|
||||
} else Some { () => creates(localCopy) { clone(from, to = localCopy) } }
|
||||
}
|
||||
|
||||
private def normalized(uri: URI) = uri.copy(scheme = scheme)
|
||||
|
|
@ -124,7 +128,8 @@ object Resolvers {
|
|||
private lazy val onWindows = {
|
||||
val os = System.getenv("OSTYPE")
|
||||
val isCygwin = (os != null) && os.toLowerCase(Locale.ENGLISH).contains("cygwin")
|
||||
val isWindows = System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).contains("windows")
|
||||
val isWindows =
|
||||
System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).contains("windows")
|
||||
isWindows && !isCygwin
|
||||
}
|
||||
|
||||
|
|
@ -141,23 +146,24 @@ object Resolvers {
|
|||
sys.error("Nonzero exit code (" + result + "): " + command.mkString(" "))
|
||||
}
|
||||
|
||||
def creates(file: File)(f: => Unit) =
|
||||
{
|
||||
if (!file.exists)
|
||||
try {
|
||||
f
|
||||
} catch {
|
||||
case NonFatal(e) =>
|
||||
IO.delete(file)
|
||||
throw e
|
||||
}
|
||||
file
|
||||
}
|
||||
def creates(file: File)(f: => Unit) = {
|
||||
if (!file.exists)
|
||||
try {
|
||||
f
|
||||
} catch {
|
||||
case NonFatal(e) =>
|
||||
IO.delete(file)
|
||||
throw e
|
||||
}
|
||||
file
|
||||
}
|
||||
|
||||
def uniqueSubdirectoryFor(uri: URI, in: File) = {
|
||||
in.mkdirs()
|
||||
val base = new File(in, Hash.halfHashString(uri.normalize.toASCIIString))
|
||||
val last = shortName(uri) match { case Some(n) => normalizeDirectoryName(n); case None => "root" }
|
||||
val last = shortName(uri) match {
|
||||
case Some(n) => normalizeDirectoryName(n); case None => "root"
|
||||
}
|
||||
new File(base, last)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,14 +8,19 @@ import java.net.URI
|
|||
|
||||
/** Extends `URI` with additional convenience methods. */
|
||||
class RichURI(uri: URI) {
|
||||
|
||||
/**
|
||||
* Provides a case-class-like `copy` method for URI.
|
||||
* Note that this method simply passes the individual components of this URI to the URI constructor
|
||||
* that accepts each component individually. It is thus limited by the implementation restrictions of the relevant methods.
|
||||
*/
|
||||
def copy(scheme: String = uri.getScheme, userInfo: String = uri.getUserInfo,
|
||||
host: String = uri.getHost, port: Int = uri.getPort, path: String = uri.getPath,
|
||||
query: String = uri.getQuery, fragment: String = uri.getFragment) =
|
||||
def copy(scheme: String = uri.getScheme,
|
||||
userInfo: String = uri.getUserInfo,
|
||||
host: String = uri.getHost,
|
||||
port: Int = uri.getPort,
|
||||
path: String = uri.getPath,
|
||||
query: String = uri.getQuery,
|
||||
fragment: String = uri.getFragment) =
|
||||
new URI(scheme, userInfo, host, port, path, query, fragment)
|
||||
|
||||
/** Returns `true` if the fragment of the URI is defined. */
|
||||
|
|
@ -36,19 +41,19 @@ class RichURI(uri: URI) {
|
|||
* If the URI has a fragment, the fragment is transferred to the wrapped URI.
|
||||
* If this URI does not have a marker scheme, it is returned unchanged.
|
||||
*/
|
||||
def withoutMarkerScheme =
|
||||
{
|
||||
if (hasMarkerScheme)
|
||||
if (hasFragment)
|
||||
new URI(uri.getRawSchemeSpecificPart + "#" + uri.getRawFragment)
|
||||
else
|
||||
new URI(uri.getRawSchemeSpecificPart)
|
||||
def withoutMarkerScheme = {
|
||||
if (hasMarkerScheme)
|
||||
if (hasFragment)
|
||||
new URI(uri.getRawSchemeSpecificPart + "#" + uri.getRawFragment)
|
||||
else
|
||||
uri
|
||||
}
|
||||
new URI(uri.getRawSchemeSpecificPart)
|
||||
else
|
||||
uri
|
||||
}
|
||||
}
|
||||
|
||||
object RichURI {
|
||||
|
||||
/** Provides additional convenience methods for `uri`. */
|
||||
implicit def fromURI(uri: URI): RichURI = new RichURI(uri)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,31 +23,34 @@ object ScopeFilter {
|
|||
* If a task filter is not supplied, global is selected.
|
||||
* Generally, always specify the project axis.
|
||||
*/
|
||||
def apply(projects: ProjectFilter = inProjects(ThisProject), configurations: ConfigurationFilter = globalAxis, tasks: TaskFilter = globalAxis): ScopeFilter =
|
||||
def apply(projects: ProjectFilter = inProjects(ThisProject),
|
||||
configurations: ConfigurationFilter = globalAxis,
|
||||
tasks: TaskFilter = globalAxis): ScopeFilter =
|
||||
new ScopeFilter {
|
||||
private[sbt] def apply(data: Data): Scope => Boolean =
|
||||
{
|
||||
val pf = projects(data)
|
||||
val cf = configurations(data)
|
||||
val tf = tasks(data)
|
||||
s => pf(s.project) && cf(s.config) && tf(s.task)
|
||||
}
|
||||
private[sbt] def apply(data: Data): Scope => Boolean = {
|
||||
val pf = projects(data)
|
||||
val cf = configurations(data)
|
||||
val tf = tasks(data)
|
||||
s =>
|
||||
pf(s.project) && cf(s.config) && tf(s.task)
|
||||
}
|
||||
}
|
||||
|
||||
def debug(delegate: ScopeFilter): ScopeFilter =
|
||||
new ScopeFilter {
|
||||
private[sbt] def apply(data: Data): Scope => Boolean =
|
||||
{
|
||||
val d = delegate(data)
|
||||
scope => {
|
||||
private[sbt] def apply(data: Data): Scope => Boolean = {
|
||||
val d = delegate(data)
|
||||
scope =>
|
||||
{
|
||||
val accept = d(scope)
|
||||
println((if (accept) "ACCEPT " else "reject ") + scope)
|
||||
accept
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class SettingKeyAll[T] private[sbt] (i: Initialize[T]) {
|
||||
|
||||
/**
|
||||
* Evaluates the initialization in all scopes selected by the filter. These are dynamic dependencies, so
|
||||
* static inspections will not show them.
|
||||
|
|
@ -57,6 +60,7 @@ object ScopeFilter {
|
|||
}
|
||||
}
|
||||
final class TaskKeyAll[T] private[sbt] (i: Initialize[Task[T]]) {
|
||||
|
||||
/**
|
||||
* Evaluates the task in all scopes selected by the filter. These are dynamic dependencies, so
|
||||
* static inspections will not show them.
|
||||
|
|
@ -69,20 +73,26 @@ object ScopeFilter {
|
|||
|
||||
private[sbt] val Make = new Make {}
|
||||
trait Make {
|
||||
|
||||
/** Selects the Scopes used in `<key>.all(<ScopeFilter>)`.*/
|
||||
type ScopeFilter = Base[Scope]
|
||||
|
||||
/** Selects Scopes with a global task axis. */
|
||||
def inGlobalTask: TaskFilter = globalAxis[AttributeKey[_]]
|
||||
|
||||
/** Selects Scopes with a global project axis. */
|
||||
def inGlobalProject: ProjectFilter = globalAxis[Reference]
|
||||
|
||||
/** Selects Scopes with a global configuration axis. */
|
||||
def inGlobalConfiguration: ConfigurationFilter = globalAxis[ConfigKey]
|
||||
|
||||
/** Selects all scopes that apply to a single project. Global and build-level scopes are excluded. */
|
||||
def inAnyProject: ProjectFilter = selectAxis(const { case p: ProjectRef => true; case _ => false })
|
||||
def inAnyProject: ProjectFilter =
|
||||
selectAxis(const { case p: ProjectRef => true; case _ => false })
|
||||
|
||||
/** Accepts all values for the task axis except Global. */
|
||||
def inAnyTask: TaskFilter = selectAny[AttributeKey[_]]
|
||||
|
||||
/** Accepts all values for the configuration axis except Global. */
|
||||
def inAnyConfiguration: ConfigurationFilter = selectAny[ConfigKey]
|
||||
|
||||
|
|
@ -90,18 +100,31 @@ object ScopeFilter {
|
|||
* Selects Scopes that have a project axis that is aggregated by `ref`, transitively if `transitive` is true.
|
||||
* If `includeRoot` is true, Scopes with `ref` itself as the project axis value are also selected.
|
||||
*/
|
||||
def inAggregates(ref: ProjectReference, transitive: Boolean = true, includeRoot: Boolean = true): ProjectFilter =
|
||||
byDeps(ref, transitive = transitive, includeRoot = includeRoot, aggregate = true, classpath = false)
|
||||
def inAggregates(ref: ProjectReference,
|
||||
transitive: Boolean = true,
|
||||
includeRoot: Boolean = true): ProjectFilter =
|
||||
byDeps(ref,
|
||||
transitive = transitive,
|
||||
includeRoot = includeRoot,
|
||||
aggregate = true,
|
||||
classpath = false)
|
||||
|
||||
/**
|
||||
* Selects Scopes that have a project axis that is a dependency of `ref`, transitively if `transitive` is true.
|
||||
* If `includeRoot` is true, Scopes with `ref` itself as the project axis value are also selected.
|
||||
*/
|
||||
def inDependencies(ref: ProjectReference, transitive: Boolean = true, includeRoot: Boolean = true): ProjectFilter =
|
||||
byDeps(ref, transitive = transitive, includeRoot = includeRoot, aggregate = false, classpath = true)
|
||||
def inDependencies(ref: ProjectReference,
|
||||
transitive: Boolean = true,
|
||||
includeRoot: Boolean = true): ProjectFilter =
|
||||
byDeps(ref,
|
||||
transitive = transitive,
|
||||
includeRoot = includeRoot,
|
||||
aggregate = false,
|
||||
classpath = true)
|
||||
|
||||
/** Selects Scopes that have a project axis with one of the provided values.*/
|
||||
def inProjects(projects: ProjectReference*): ProjectFilter = ScopeFilter.inProjects(projects: _*)
|
||||
def inProjects(projects: ProjectReference*): ProjectFilter =
|
||||
ScopeFilter.inProjects(projects: _*)
|
||||
|
||||
/** Selects Scopes that have a task axis with one of the provided values.*/
|
||||
def inTasks(tasks: Scoped*): TaskFilter = {
|
||||
|
|
@ -123,7 +146,9 @@ object ScopeFilter {
|
|||
* Information provided to Scope filters. These provide project relationships,
|
||||
* project reference resolution, and the list of all static Scopes.
|
||||
*/
|
||||
private final class Data(val units: Map[URI, LoadedBuildUnit], val resolve: ProjectReference => ProjectRef, val allScopes: Set[Scope])
|
||||
private final class Data(val units: Map[URI, LoadedBuildUnit],
|
||||
val resolve: ProjectReference => ProjectRef,
|
||||
val allScopes: Set[Scope])
|
||||
|
||||
/** Constructs a Data instance from the list of static scopes and the project relationships.*/
|
||||
private[this] val getData: Initialize[Data] =
|
||||
|
|
@ -136,20 +161,28 @@ object ScopeFilter {
|
|||
case None => build.root
|
||||
}
|
||||
val rootProject = Load.getRootProject(build.units)
|
||||
val resolve: ProjectReference => ProjectRef = p => (p, thisRef) match {
|
||||
case (ThisProject, Some(pref)) => pref
|
||||
case _ => Scope.resolveProjectRef(current, rootProject, p)
|
||||
val resolve: ProjectReference => ProjectRef = p =>
|
||||
(p, thisRef) match {
|
||||
case (ThisProject, Some(pref)) => pref
|
||||
case _ => Scope.resolveProjectRef(current, rootProject, p)
|
||||
}
|
||||
new Data(build.units, resolve, scopes)
|
||||
}
|
||||
|
||||
private[this] def getDependencies(structure: Map[URI, LoadedBuildUnit], classpath: Boolean, aggregate: Boolean): ProjectRef => Seq[ProjectRef] =
|
||||
ref => Project.getProject(ref, structure).toList flatMap { p =>
|
||||
(if (classpath) p.dependencies.map(_.project) else Nil) ++
|
||||
(if (aggregate) p.aggregate else Nil)
|
||||
private[this] def getDependencies(structure: Map[URI, LoadedBuildUnit],
|
||||
classpath: Boolean,
|
||||
aggregate: Boolean): ProjectRef => Seq[ProjectRef] =
|
||||
ref =>
|
||||
Project.getProject(ref, structure).toList flatMap { p =>
|
||||
(if (classpath) p.dependencies.map(_.project) else Nil) ++
|
||||
(if (aggregate) p.aggregate else Nil)
|
||||
}
|
||||
|
||||
private[this] def byDeps(ref: ProjectReference, transitive: Boolean, includeRoot: Boolean, aggregate: Boolean, classpath: Boolean): ProjectFilter =
|
||||
private[this] def byDeps(ref: ProjectReference,
|
||||
transitive: Boolean,
|
||||
includeRoot: Boolean,
|
||||
aggregate: Boolean,
|
||||
classpath: Boolean): ProjectFilter =
|
||||
inResolvedProjects { data =>
|
||||
val resolvedRef = data.resolve(ref)
|
||||
val direct = getDependencies(data.units, classpath = classpath, aggregate = aggregate)
|
||||
|
|
@ -176,15 +209,17 @@ object ScopeFilter {
|
|||
private[this] def selectAxis[T](f: Data => T => Boolean): AxisFilter[T] = new AxisFilter[T] {
|
||||
private[sbt] def apply(data: Data): ScopeAxis[T] => Boolean = {
|
||||
val g = f(data)
|
||||
s => s match {
|
||||
case Select(t) => g(t)
|
||||
case _ => false
|
||||
}
|
||||
s =>
|
||||
s match {
|
||||
case Select(t) => g(t)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Base functionality for filters on values of type `In` that need access to build data.*/
|
||||
sealed abstract class Base[In] { self =>
|
||||
|
||||
/** Implements this filter. */
|
||||
private[ScopeFilter] def apply(data: Data): In => Boolean
|
||||
|
||||
|
|
@ -196,7 +231,8 @@ object ScopeFilter {
|
|||
private[sbt] def apply(data: Data): In => Boolean = {
|
||||
val a = self(data)
|
||||
val b = other(data)
|
||||
s => a(s) && b(s)
|
||||
s =>
|
||||
a(s) && b(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,14 +241,17 @@ object ScopeFilter {
|
|||
private[sbt] def apply(data: Data): In => Boolean = {
|
||||
val a = self(data)
|
||||
val b = other(data)
|
||||
s => a(s) || b(s)
|
||||
s =>
|
||||
a(s) || b(s)
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructs a filter that selects values that do not match this filter.*/
|
||||
def unary_- : Base[In] = new Base[In] {
|
||||
private[sbt] def apply(data: Data): In => Boolean = {
|
||||
val a = self(data)
|
||||
s => !a(s)
|
||||
s =>
|
||||
!a(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ final case class ScopedKeyData[A](scoped: ScopedKey[A], value: Any) {
|
|||
val scope = scoped.scope
|
||||
def typeName: String = fold(fmtMf("Task[%s]"), fmtMf("InputTask[%s]"), key.manifest.toString)
|
||||
def settingValue: Option[Any] = fold(const(None), const(None), Some(value))
|
||||
def description: String = fold(fmtMf("Task: %s"), fmtMf("Input task: %s"),
|
||||
"Setting: %s = %s" format (key.manifest.toString, value.toString))
|
||||
def description: String =
|
||||
fold(fmtMf("Task: %s"),
|
||||
fmtMf("Input task: %s"),
|
||||
"Setting: %s = %s" format (key.manifest.toString, value.toString))
|
||||
def fold[T](targ: OptManifest[_] => T, itarg: OptManifest[_] => T, s: => T): T =
|
||||
key.manifest.runtimeClass match {
|
||||
case TaskClass => targ(key.manifest.typeArguments.head)
|
||||
|
|
|
|||
|
|
@ -20,54 +20,59 @@ object SessionVar {
|
|||
}
|
||||
def emptyMap = Map(IMap.empty)
|
||||
|
||||
def persistAndSet[T](key: ScopedKey[Task[T]], state: State, value: T)(implicit f: JsonFormat[T]): State =
|
||||
{
|
||||
persist(key, state, value)(f)
|
||||
set(key, state, value)
|
||||
}
|
||||
def persistAndSet[T](key: ScopedKey[Task[T]], state: State, value: T)(
|
||||
implicit f: JsonFormat[T]): State = {
|
||||
persist(key, state, value)(f)
|
||||
set(key, state, value)
|
||||
}
|
||||
|
||||
def persist[T](key: ScopedKey[Task[T]], state: State, value: T)(implicit f: JsonFormat[T]): Unit =
|
||||
Project.structure(state).streams(state).use(key)(s =>
|
||||
s.getOutput(DefaultDataID).write(value))
|
||||
def persist[T](key: ScopedKey[Task[T]], state: State, value: T)(
|
||||
implicit f: JsonFormat[T]): Unit =
|
||||
Project.structure(state).streams(state).use(key)(s => s.getOutput(DefaultDataID).write(value))
|
||||
|
||||
def clear(s: State): State = s.put(sessionVars, SessionVar.emptyMap)
|
||||
|
||||
def get[T](key: ScopedKey[Task[T]], state: State): Option[T] = orEmpty(state get sessionVars) get key
|
||||
def get[T](key: ScopedKey[Task[T]], state: State): Option[T] =
|
||||
orEmpty(state get sessionVars) get key
|
||||
|
||||
def set[T](key: ScopedKey[Task[T]], state: State, value: T): State = state.update(sessionVars)(om => orEmpty(om) put (key, value))
|
||||
def set[T](key: ScopedKey[Task[T]], state: State, value: T): State =
|
||||
state.update(sessionVars)(om => orEmpty(om) put (key, value))
|
||||
|
||||
def orEmpty(opt: Option[Map]) = opt getOrElse emptyMap
|
||||
|
||||
def transform[S](task: Task[S], f: (State, S) => State): Task[S] =
|
||||
{
|
||||
val g = (s: S, map: AttributeMap) => map.put(Keys.transformState, (state: State) => f(state, s))
|
||||
task.copy(info = task.info.postTransform(g))
|
||||
}
|
||||
def transform[S](task: Task[S], f: (State, S) => State): Task[S] = {
|
||||
val g = (s: S, map: AttributeMap) =>
|
||||
map.put(Keys.transformState, (state: State) => f(state, s))
|
||||
task.copy(info = task.info.postTransform(g))
|
||||
}
|
||||
|
||||
def resolveContext[T](key: ScopedKey[Task[T]], context: Scope, state: State): ScopedKey[Task[T]] =
|
||||
{
|
||||
val subScope = Scope.replaceThis(context)(key.scope)
|
||||
val scope = Project.structure(state).data.definingScope(subScope, key.key) getOrElse subScope
|
||||
ScopedKey(scope, key.key)
|
||||
}
|
||||
def resolveContext[T](key: ScopedKey[Task[T]],
|
||||
context: Scope,
|
||||
state: State): ScopedKey[Task[T]] = {
|
||||
val subScope = Scope.replaceThis(context)(key.scope)
|
||||
val scope = Project.structure(state).data.definingScope(subScope, key.key) getOrElse subScope
|
||||
ScopedKey(scope, key.key)
|
||||
}
|
||||
|
||||
def read[T](key: ScopedKey[Task[T]], state: State)(implicit f: JsonFormat[T]): Option[T] =
|
||||
Project.structure(state).streams(state).use(key) { s =>
|
||||
try { Some(s.getInput(key, DefaultDataID).read[T]) }
|
||||
catch { case NonFatal(e) => None }
|
||||
try { Some(s.getInput(key, DefaultDataID).read[T]) } catch { case NonFatal(e) => None }
|
||||
}
|
||||
|
||||
def load[T](key: ScopedKey[Task[T]], state: State)(implicit f: JsonFormat[T]): Option[T] =
|
||||
get(key, state) orElse read(key, state)(f)
|
||||
|
||||
def loadAndSet[T](key: ScopedKey[Task[T]], state: State, setIfUnset: Boolean = true)(implicit f: JsonFormat[T]): (State, Option[T]) =
|
||||
def loadAndSet[T](key: ScopedKey[Task[T]], state: State, setIfUnset: Boolean = true)(
|
||||
implicit f: JsonFormat[T]): (State, Option[T]) =
|
||||
get(key, state) match {
|
||||
case s: Some[T] => (state, s)
|
||||
case None => read(key, state)(f) match {
|
||||
case s @ Some(t) =>
|
||||
val newState = if (setIfUnset && get(key, state).isDefined) state else set(key, state, t)
|
||||
(newState, s)
|
||||
case None => (state, None)
|
||||
}
|
||||
case None =>
|
||||
read(key, state)(f) match {
|
||||
case s @ Some(t) =>
|
||||
val newState =
|
||||
if (setIfUnset && get(key, state).isDefined) state else set(key, state, t)
|
||||
(newState, s)
|
||||
case None => (state, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,12 +87,13 @@ object Tags {
|
|||
def limit(tag: Tag, max: Int): Rule = new Single(tag, max)
|
||||
|
||||
def limitSum(max: Int, tags: Tag*): Rule = new Sum(tags, max)
|
||||
|
||||
/** Ensure that a task with the given tag always executes in isolation.*/
|
||||
def exclusive(exclusiveTag: Tag): Rule = customLimit { (tags: Map[Tag, Int]) =>
|
||||
// if there are no exclusive tasks in this group, this rule adds no restrictions
|
||||
tags.getOrElse(exclusiveTag, 0) == 0 ||
|
||||
// If there is only one task, allow it to execute.
|
||||
tags.getOrElse(Tags.All, 0) == 1
|
||||
// If there is only one task, allow it to execute.
|
||||
tags.getOrElse(Tags.All, 0) == 1
|
||||
}
|
||||
|
||||
/** Ensure that a task with the given tag only executes with tasks also tagged with the given tag.*/
|
||||
|
|
@ -101,10 +102,10 @@ object Tags {
|
|||
val allCount = tags.getOrElse(Tags.All, 0)
|
||||
// If there are no exclusive tasks in this group, this rule adds no restrictions.
|
||||
exclusiveCount == 0 ||
|
||||
// If all tasks have this tag, allow them to execute.
|
||||
exclusiveCount == allCount ||
|
||||
// Always allow a group containing only one task to execute (fallthrough case).
|
||||
allCount == 1
|
||||
// If all tasks have this tag, allow them to execute.
|
||||
exclusiveCount == allCount ||
|
||||
// Always allow a group containing only one task to execute (fallthrough case).
|
||||
allCount == 1
|
||||
}
|
||||
|
||||
/** A task tagged with one of `exclusiveTags` will not execute with another task with any of the other tags in `exclusiveTags`.*/
|
||||
|
|
@ -112,4 +113,4 @@ object Tags {
|
|||
val groups = exclusiveTags.count(tag => tags.getOrElse(tag, 0) > 0)
|
||||
groups <= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,13 @@ private[sbt] object TemplateCommandUtil {
|
|||
}
|
||||
|
||||
private def run(
|
||||
infos: List[TemplateResolverInfo], arguments: List[String], config: AppConfiguration,
|
||||
ivyConf: IvyConfiguration, globalBase: File, ivyScala: Option[IvyScala], log: Logger
|
||||
infos: List[TemplateResolverInfo],
|
||||
arguments: List[String],
|
||||
config: AppConfiguration,
|
||||
ivyConf: IvyConfiguration,
|
||||
globalBase: File,
|
||||
ivyScala: Option[IvyScala],
|
||||
log: Logger
|
||||
): Unit =
|
||||
infos find { info =>
|
||||
val loader = infoLoader(info, config, ivyConf, globalBase, ivyScala, log)
|
||||
|
|
@ -51,57 +56,67 @@ private[sbt] object TemplateCommandUtil {
|
|||
case None => System.err.println("Template not found for: " + arguments.mkString(" "))
|
||||
}
|
||||
|
||||
private def tryTemplate(info: TemplateResolverInfo, arguments: List[String], loader: ClassLoader): Boolean =
|
||||
{
|
||||
val resultObj = call(info.implementationClass, "isDefined", loader)(
|
||||
classOf[Array[String]]
|
||||
)(arguments.toArray)
|
||||
resultObj.asInstanceOf[Boolean]
|
||||
}
|
||||
private def tryTemplate(info: TemplateResolverInfo,
|
||||
arguments: List[String],
|
||||
loader: ClassLoader): Boolean = {
|
||||
val resultObj = call(info.implementationClass, "isDefined", loader)(
|
||||
classOf[Array[String]]
|
||||
)(arguments.toArray)
|
||||
resultObj.asInstanceOf[Boolean]
|
||||
}
|
||||
|
||||
private def runTemplate(info: TemplateResolverInfo, arguments: List[String], loader: ClassLoader): Unit =
|
||||
private def runTemplate(info: TemplateResolverInfo,
|
||||
arguments: List[String],
|
||||
loader: ClassLoader): Unit =
|
||||
call(info.implementationClass, "run", loader)(classOf[Array[String]])(arguments.toArray)
|
||||
|
||||
private def infoLoader(
|
||||
info: TemplateResolverInfo, config: AppConfiguration, ivyConf: IvyConfiguration, globalBase: File,
|
||||
ivyScala: Option[IvyScala], log: Logger
|
||||
info: TemplateResolverInfo,
|
||||
config: AppConfiguration,
|
||||
ivyConf: IvyConfiguration,
|
||||
globalBase: File,
|
||||
ivyScala: Option[IvyScala],
|
||||
log: Logger
|
||||
): ClassLoader = {
|
||||
val cp = classpathForInfo(info, ivyConf, globalBase, ivyScala, log)
|
||||
ClasspathUtilities.toLoader(cp, config.provider.loader)
|
||||
}
|
||||
|
||||
private def call(
|
||||
interfaceClassName: String, methodName: String, loader: ClassLoader
|
||||
)(argTypes: Class[_]*)(args: AnyRef*): AnyRef =
|
||||
{
|
||||
val interfaceClass = getInterfaceClass(interfaceClassName, loader)
|
||||
val interface = interfaceClass.getDeclaredConstructor().newInstance().asInstanceOf[AnyRef]
|
||||
val method = interfaceClass.getMethod(methodName, argTypes: _*)
|
||||
try { method.invoke(interface, args: _*) }
|
||||
catch {
|
||||
case e: InvocationTargetException => throw e.getCause
|
||||
}
|
||||
interfaceClassName: String,
|
||||
methodName: String,
|
||||
loader: ClassLoader
|
||||
)(argTypes: Class[_]*)(args: AnyRef*): AnyRef = {
|
||||
val interfaceClass = getInterfaceClass(interfaceClassName, loader)
|
||||
val interface = interfaceClass.getDeclaredConstructor().newInstance().asInstanceOf[AnyRef]
|
||||
val method = interfaceClass.getMethod(methodName, argTypes: _*)
|
||||
try { method.invoke(interface, args: _*) } catch {
|
||||
case e: InvocationTargetException => throw e.getCause
|
||||
}
|
||||
}
|
||||
|
||||
private def getInterfaceClass(name: String, loader: ClassLoader) = Class.forName(name, true, loader)
|
||||
private def getInterfaceClass(name: String, loader: ClassLoader) =
|
||||
Class.forName(name, true, loader)
|
||||
|
||||
// Cache files under ~/.sbt/0.13/templates/org_name_version
|
||||
private def classpathForInfo(
|
||||
info: TemplateResolverInfo, ivyConf: IvyConfiguration, globalBase: File, ivyScala: Option[IvyScala],
|
||||
info: TemplateResolverInfo,
|
||||
ivyConf: IvyConfiguration,
|
||||
globalBase: File,
|
||||
ivyScala: Option[IvyScala],
|
||||
log: Logger
|
||||
): List[File] =
|
||||
{
|
||||
val lm = new DefaultLibraryManagement(ivyConf, log)
|
||||
val templatesBaseDirectory = new File(globalBase, "templates")
|
||||
val templateId = s"${info.module.organization}_${info.module.name}_${info.module.revision}"
|
||||
val templateDirectory = new File(templatesBaseDirectory, templateId)
|
||||
def jars = (templateDirectory ** -DirectoryFilter).get
|
||||
if (!(info.module.revision endsWith "-SNAPSHOT") && jars.nonEmpty) jars.toList
|
||||
else {
|
||||
IO.createDirectory(templateDirectory)
|
||||
val m = lm.getModule(info.module.withConfigurations(Some("component")), ivyScala)
|
||||
val xs = lm.update(m, templateDirectory)(_ => true).toList.flatten
|
||||
xs
|
||||
}
|
||||
): List[File] = {
|
||||
val lm = new DefaultLibraryManagement(ivyConf, log)
|
||||
val templatesBaseDirectory = new File(globalBase, "templates")
|
||||
val templateId = s"${info.module.organization}_${info.module.name}_${info.module.revision}"
|
||||
val templateDirectory = new File(templatesBaseDirectory, templateId)
|
||||
def jars = (templateDirectory ** -DirectoryFilter).get
|
||||
if (!(info.module.revision endsWith "-SNAPSHOT") && jars.nonEmpty) jars.toList
|
||||
else {
|
||||
IO.createDirectory(templateDirectory)
|
||||
val m = lm.getModule(info.module.withConfigurations(Some("component")), ivyScala)
|
||||
val xs = lm.update(m, templateDirectory)(_ => true).toList.flatten
|
||||
xs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ private[sbt] object APIMappings {
|
|||
def extractFromEntry(entry: Attributed[File], log: Logger): Option[(File, URL)] =
|
||||
entry.get(Keys.entryApiURL) match {
|
||||
case Some(u) => Some((entry.data, u))
|
||||
case None => entry.get(Keys.moduleID.key).flatMap { mid => extractFromID(entry.data, mid, log) }
|
||||
case None =>
|
||||
entry.get(Keys.moduleID.key).flatMap { mid =>
|
||||
extractFromID(entry.data, mid, log)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def extractFromID(entry: File, mid: ModuleID, log: Logger): Option[(File, URL)] =
|
||||
|
|
@ -27,7 +30,8 @@ private[sbt] object APIMappings {
|
|||
} yield (entry, u)
|
||||
|
||||
private[this] def parseURL(s: String, forEntry: File, log: Logger): Option[URL] =
|
||||
try Some(new URL(s)) catch {
|
||||
try Some(new URL(s))
|
||||
catch {
|
||||
case e: MalformedURLException =>
|
||||
log.warn(s"Invalid API base URL '$s' for classpath entry '$forEntry': ${e.toString}")
|
||||
None
|
||||
|
|
|
|||
|
|
@ -21,54 +21,79 @@ object Act {
|
|||
val GlobalString = "*"
|
||||
|
||||
// this does not take aggregation into account
|
||||
def scopedKey(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||
keyMap: Map[String, AttributeKey[_]], data: Settings[Scope]): Parser[ScopedKey[_]] =
|
||||
def scopedKey(index: KeyIndex,
|
||||
current: ProjectRef,
|
||||
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||
keyMap: Map[String, AttributeKey[_]],
|
||||
data: Settings[Scope]): Parser[ScopedKey[_]] =
|
||||
scopedKeySelected(index, current, defaultConfigs, keyMap, data).map(_.key)
|
||||
|
||||
// the index should be an aggregated index for proper tab completion
|
||||
def scopedKeyAggregated(current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], structure: BuildStructure): KeysParser =
|
||||
for (selected <- scopedKeySelected(structure.index.aggregateKeyIndex, current, defaultConfigs, structure.index.keyMap, structure.data)) yield Aggregation.aggregate(selected.key, selected.mask, structure.extra)
|
||||
def scopedKeyAggregated(current: ProjectRef,
|
||||
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||
structure: BuildStructure): KeysParser =
|
||||
for (selected <- scopedKeySelected(structure.index.aggregateKeyIndex,
|
||||
current,
|
||||
defaultConfigs,
|
||||
structure.index.keyMap,
|
||||
structure.data))
|
||||
yield Aggregation.aggregate(selected.key, selected.mask, structure.extra)
|
||||
|
||||
def scopedKeySelected(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||
keyMap: Map[String, AttributeKey[_]], data: Settings[Scope]): Parser[ParsedKey] =
|
||||
def scopedKeySelected(index: KeyIndex,
|
||||
current: ProjectRef,
|
||||
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||
keyMap: Map[String, AttributeKey[_]],
|
||||
data: Settings[Scope]): Parser[ParsedKey] =
|
||||
scopedKeyFull(index, current, defaultConfigs, keyMap) flatMap { choices =>
|
||||
select(choices, data)(showRelativeKey(current, index.buildURIs.size > 1))
|
||||
}
|
||||
|
||||
def scopedKeyFull(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], keyMap: Map[String, AttributeKey[_]]): Parser[Seq[Parser[ParsedKey]]] =
|
||||
{
|
||||
for {
|
||||
rawProject <- optProjectRef(index, current)
|
||||
proj = resolveProject(rawProject, current)
|
||||
confAmb <- config(index configs proj)
|
||||
partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false)
|
||||
} yield taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask)
|
||||
}
|
||||
def scopedKeyFull(index: KeyIndex,
|
||||
current: ProjectRef,
|
||||
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||
keyMap: Map[String, AttributeKey[_]]): Parser[Seq[Parser[ParsedKey]]] = {
|
||||
for {
|
||||
rawProject <- optProjectRef(index, current)
|
||||
proj = resolveProject(rawProject, current)
|
||||
confAmb <- config(index configs proj)
|
||||
partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false)
|
||||
} yield taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask)
|
||||
}
|
||||
|
||||
def taskKeyExtra(
|
||||
index: KeyIndex,
|
||||
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||
keyMap: Map[String, AttributeKey[_]],
|
||||
proj: Option[ResolvedReference],
|
||||
confAmb: ParsedAxis[String],
|
||||
baseMask: ScopeMask
|
||||
index: KeyIndex,
|
||||
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||
keyMap: Map[String, AttributeKey[_]],
|
||||
proj: Option[ResolvedReference],
|
||||
confAmb: ParsedAxis[String],
|
||||
baseMask: ScopeMask
|
||||
): Seq[Parser[ParsedKey]] =
|
||||
for {
|
||||
conf <- configs(confAmb, defaultConfigs, proj, index)
|
||||
} yield for {
|
||||
taskAmb <- taskAxis(conf, index.tasks(proj, conf), keyMap)
|
||||
task = resolveTask(taskAmb)
|
||||
key <- key(index, proj, conf, task, keyMap)
|
||||
extra <- extraAxis(keyMap, IMap.empty)
|
||||
} yield {
|
||||
val mask = baseMask.copy(task = taskAmb.isExplicit, extra = true)
|
||||
new ParsedKey(makeScopedKey(proj, conf, task, extra, key), mask)
|
||||
}
|
||||
} yield
|
||||
for {
|
||||
taskAmb <- taskAxis(conf, index.tasks(proj, conf), keyMap)
|
||||
task = resolveTask(taskAmb)
|
||||
key <- key(index, proj, conf, task, keyMap)
|
||||
extra <- extraAxis(keyMap, IMap.empty)
|
||||
} yield {
|
||||
val mask = baseMask.copy(task = taskAmb.isExplicit, extra = true)
|
||||
new ParsedKey(makeScopedKey(proj, conf, task, extra, key), mask)
|
||||
}
|
||||
|
||||
def makeScopedKey(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]], extra: ScopeAxis[AttributeMap], key: AttributeKey[_]): ScopedKey[_] =
|
||||
ScopedKey(Scope(toAxis(proj, Global), toAxis(conf map ConfigKey.apply, Global), toAxis(task, Global), extra), key)
|
||||
def makeScopedKey(proj: Option[ResolvedReference],
|
||||
conf: Option[String],
|
||||
task: Option[AttributeKey[_]],
|
||||
extra: ScopeAxis[AttributeMap],
|
||||
key: AttributeKey[_]): ScopedKey[_] =
|
||||
ScopedKey(Scope(toAxis(proj, Global),
|
||||
toAxis(conf map ConfigKey.apply, Global),
|
||||
toAxis(task, Global),
|
||||
extra),
|
||||
key)
|
||||
|
||||
def select(allKeys: Seq[Parser[ParsedKey]], data: Settings[Scope])(implicit show: Show[ScopedKey[_]]): Parser[ParsedKey] =
|
||||
def select(allKeys: Seq[Parser[ParsedKey]], data: Settings[Scope])(
|
||||
implicit show: Show[ScopedKey[_]]): Parser[ParsedKey] =
|
||||
seq(allKeys) flatMap { ss =>
|
||||
val default = ss.headOption match {
|
||||
case None => noValidKeys
|
||||
|
|
@ -76,7 +101,8 @@ object Act {
|
|||
}
|
||||
selectFromValid(ss filter isValid(data), default)
|
||||
}
|
||||
def selectFromValid(ss: Seq[ParsedKey], default: Parser[ParsedKey])(implicit show: Show[ScopedKey[_]]): Parser[ParsedKey] =
|
||||
def selectFromValid(ss: Seq[ParsedKey], default: Parser[ParsedKey])(
|
||||
implicit show: Show[ScopedKey[_]]): Parser[ParsedKey] =
|
||||
selectByTask(selectByConfig(ss)) match {
|
||||
case Seq() => default
|
||||
case Seq(single) => success(single)
|
||||
|
|
@ -92,22 +118,20 @@ object Act {
|
|||
case xs => x +: xs
|
||||
}
|
||||
}
|
||||
def selectByTask(ss: Seq[ParsedKey]): Seq[ParsedKey] =
|
||||
{
|
||||
val (selects, globals) = ss.partition(_.key.scope.task.isSelect)
|
||||
if (globals.nonEmpty) globals else selects
|
||||
}
|
||||
def selectByTask(ss: Seq[ParsedKey]): Seq[ParsedKey] = {
|
||||
val (selects, globals) = ss.partition(_.key.scope.task.isSelect)
|
||||
if (globals.nonEmpty) globals else selects
|
||||
}
|
||||
|
||||
def noValidKeys = failure("No such key.")
|
||||
|
||||
def showAmbiguous(keys: Seq[ScopedKey[_]])(implicit show: Show[ScopedKey[_]]): String =
|
||||
keys.take(3).map(x => show.show(x)).mkString("", ", ", if (keys.size > 3) ", ..." else "")
|
||||
|
||||
def isValid(data: Settings[Scope])(parsed: ParsedKey): Boolean =
|
||||
{
|
||||
val key = parsed.key
|
||||
data.definingScope(key.scope, key.key) == Some(key.scope)
|
||||
}
|
||||
def isValid(data: Settings[Scope])(parsed: ParsedKey): Boolean = {
|
||||
val key = parsed.key
|
||||
data.definingScope(key.scope, key.key) == Some(key.scope)
|
||||
}
|
||||
|
||||
def examples(p: Parser[String], exs: Set[String], label: String): Parser[String] =
|
||||
p !!! ("Expected " + label) examples exs
|
||||
|
|
@ -115,47 +139,62 @@ object Act {
|
|||
filterStrings(examples(p, exs, label), exs, label)
|
||||
|
||||
def optionalAxis[T](p: Parser[T], ifNone: ScopeAxis[T]): Parser[ScopeAxis[T]] =
|
||||
p.? map { opt => toAxis(opt, ifNone) }
|
||||
p.? map { opt =>
|
||||
toAxis(opt, ifNone)
|
||||
}
|
||||
def toAxis[T](opt: Option[T], ifNone: ScopeAxis[T]): ScopeAxis[T] =
|
||||
opt match { case Some(t) => Select(t); case None => ifNone }
|
||||
|
||||
def config(confs: Set[String]): Parser[ParsedAxis[String]] =
|
||||
{
|
||||
val sep = ':' !!! "Expected ':' (if selecting a configuration)"
|
||||
token((GlobalString ^^^ ParsedGlobal | value(examples(ID, confs, "configuration"))) <~ sep) ?? Omitted
|
||||
}
|
||||
def config(confs: Set[String]): Parser[ParsedAxis[String]] = {
|
||||
val sep = ':' !!! "Expected ':' (if selecting a configuration)"
|
||||
token((GlobalString ^^^ ParsedGlobal | value(examples(ID, confs, "configuration"))) <~ sep) ?? Omitted
|
||||
}
|
||||
|
||||
def configs(explicit: ParsedAxis[String], defaultConfigs: Option[ResolvedReference] => Seq[String], proj: Option[ResolvedReference], index: KeyIndex): Seq[Option[String]] =
|
||||
def configs(explicit: ParsedAxis[String],
|
||||
defaultConfigs: Option[ResolvedReference] => Seq[String],
|
||||
proj: Option[ResolvedReference],
|
||||
index: KeyIndex): Seq[Option[String]] =
|
||||
explicit match {
|
||||
case Omitted => None +: defaultConfigurations(proj, index, defaultConfigs).flatMap(nonEmptyConfig(index, proj))
|
||||
case Omitted =>
|
||||
None +: defaultConfigurations(proj, index, defaultConfigs).flatMap(
|
||||
nonEmptyConfig(index, proj))
|
||||
case ParsedGlobal => None :: Nil
|
||||
case pv: ParsedValue[x] => Some(pv.value) :: Nil
|
||||
}
|
||||
def defaultConfigurations(proj: Option[ResolvedReference], index: KeyIndex, defaultConfigs: Option[ResolvedReference] => Seq[String]): Seq[String] =
|
||||
def defaultConfigurations(
|
||||
proj: Option[ResolvedReference],
|
||||
index: KeyIndex,
|
||||
defaultConfigs: Option[ResolvedReference] => Seq[String]): Seq[String] =
|
||||
if (index exists proj) defaultConfigs(proj) else Nil
|
||||
def nonEmptyConfig(index: KeyIndex, proj: Option[ResolvedReference]): String => Seq[Option[String]] = config =>
|
||||
if (index.isEmpty(proj, Some(config))) Nil else Some(config) :: Nil
|
||||
def nonEmptyConfig(index: KeyIndex,
|
||||
proj: Option[ResolvedReference]): String => Seq[Option[String]] =
|
||||
config => if (index.isEmpty(proj, Some(config))) Nil else Some(config) :: Nil
|
||||
|
||||
def key(index: KeyIndex, proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]], keyMap: Map[String, AttributeKey[_]]): Parser[AttributeKey[_]] =
|
||||
{
|
||||
def dropHyphenated(keys: Set[String]): Set[String] = keys.filterNot(Util.hasHyphen)
|
||||
def keyParser(keys: Set[String]): Parser[AttributeKey[_]] =
|
||||
token(ID !!! "Expected key" examples dropHyphenated(keys)) flatMap { keyString =>
|
||||
getKey(keyMap, keyString, idFun)
|
||||
}
|
||||
// Fixes sbt/sbt#2460 and sbt/sbt#2851
|
||||
// The parser already accepts build-level keys.
|
||||
// This queries the key index so tab completion will list the build-level keys.
|
||||
val buildKeys: Set[String] =
|
||||
proj match {
|
||||
case Some(ProjectRef(uri, id)) => index.keys(Some(BuildRef(uri)), conf, task)
|
||||
case _ => Set()
|
||||
}
|
||||
val keys: Set[String] = index.keys(proj, conf, task) ++ buildKeys
|
||||
keyParser(keys)
|
||||
}
|
||||
def key(index: KeyIndex,
|
||||
proj: Option[ResolvedReference],
|
||||
conf: Option[String],
|
||||
task: Option[AttributeKey[_]],
|
||||
keyMap: Map[String, AttributeKey[_]]): Parser[AttributeKey[_]] = {
|
||||
def dropHyphenated(keys: Set[String]): Set[String] = keys.filterNot(Util.hasHyphen)
|
||||
def keyParser(keys: Set[String]): Parser[AttributeKey[_]] =
|
||||
token(ID !!! "Expected key" examples dropHyphenated(keys)) flatMap { keyString =>
|
||||
getKey(keyMap, keyString, idFun)
|
||||
}
|
||||
// Fixes sbt/sbt#2460 and sbt/sbt#2851
|
||||
// The parser already accepts build-level keys.
|
||||
// This queries the key index so tab completion will list the build-level keys.
|
||||
val buildKeys: Set[String] =
|
||||
proj match {
|
||||
case Some(ProjectRef(uri, id)) => index.keys(Some(BuildRef(uri)), conf, task)
|
||||
case _ => Set()
|
||||
}
|
||||
val keys: Set[String] = index.keys(proj, conf, task) ++ buildKeys
|
||||
keyParser(keys)
|
||||
}
|
||||
|
||||
def getKey[T](keyMap: Map[String, AttributeKey[_]], keyString: String, f: AttributeKey[_] => T): Parser[T] =
|
||||
def getKey[T](keyMap: Map[String, AttributeKey[_]],
|
||||
keyString: String,
|
||||
f: AttributeKey[_] => T): Parser[T] =
|
||||
keyMap.get(keyString) match {
|
||||
case Some(k) => success(f(k))
|
||||
case None => failure(Command.invalidValue("key", keyMap.keys)(keyString))
|
||||
|
|
@ -163,23 +202,25 @@ object Act {
|
|||
|
||||
val spacedComma = token(OptSpace ~ ',' ~ OptSpace)
|
||||
|
||||
def extraAxis(knownKeys: Map[String, AttributeKey[_]], knownValues: IMap[AttributeKey, Set]): Parser[ScopeAxis[AttributeMap]] =
|
||||
{
|
||||
val extrasP = extrasParser(knownKeys, knownValues)
|
||||
val extras = token('(', hide = _ == 1 && knownValues.isEmpty) ~> extrasP <~ token(')')
|
||||
optionalAxis(extras, Global)
|
||||
}
|
||||
def extraAxis(knownKeys: Map[String, AttributeKey[_]],
|
||||
knownValues: IMap[AttributeKey, Set]): Parser[ScopeAxis[AttributeMap]] = {
|
||||
val extrasP = extrasParser(knownKeys, knownValues)
|
||||
val extras = token('(', hide = _ == 1 && knownValues.isEmpty) ~> extrasP <~ token(')')
|
||||
optionalAxis(extras, Global)
|
||||
}
|
||||
|
||||
def taskAxis(d: Option[String], tasks: Set[AttributeKey[_]], allKnown: Map[String, AttributeKey[_]]): Parser[ParsedAxis[AttributeKey[_]]] =
|
||||
{
|
||||
val taskSeq = tasks.toSeq
|
||||
def taskKeys(f: AttributeKey[_] => String): Seq[(String, AttributeKey[_])] = taskSeq.map(key => (f(key), key))
|
||||
val normKeys = taskKeys(_.label)
|
||||
val valid = allKnown ++ normKeys
|
||||
val suggested = normKeys.map(_._1).toSet
|
||||
val keyP = filterStrings(examples(ID, suggested, "key"), valid.keySet, "key") map valid
|
||||
(token(value(keyP) | GlobalString ^^^ ParsedGlobal) <~ token("::".id)) ?? Omitted
|
||||
}
|
||||
def taskAxis(d: Option[String],
|
||||
tasks: Set[AttributeKey[_]],
|
||||
allKnown: Map[String, AttributeKey[_]]): Parser[ParsedAxis[AttributeKey[_]]] = {
|
||||
val taskSeq = tasks.toSeq
|
||||
def taskKeys(f: AttributeKey[_] => String): Seq[(String, AttributeKey[_])] =
|
||||
taskSeq.map(key => (f(key), key))
|
||||
val normKeys = taskKeys(_.label)
|
||||
val valid = allKnown ++ normKeys
|
||||
val suggested = normKeys.map(_._1).toSet
|
||||
val keyP = filterStrings(examples(ID, suggested, "key"), valid.keySet, "key") map valid
|
||||
(token(value(keyP) | GlobalString ^^^ ParsedGlobal) <~ token("::".id)) ?? Omitted
|
||||
}
|
||||
def resolveTask(task: ParsedAxis[AttributeKey[_]]): Option[AttributeKey[_]] =
|
||||
task match {
|
||||
case ParsedGlobal | Omitted => None
|
||||
|
|
@ -189,24 +230,26 @@ object Act {
|
|||
def filterStrings(base: Parser[String], valid: Set[String], label: String): Parser[String] =
|
||||
base.filter(valid, Command.invalidValue(label, valid))
|
||||
|
||||
def extrasParser(knownKeys: Map[String, AttributeKey[_]], knownValues: IMap[AttributeKey, Set]): Parser[AttributeMap] =
|
||||
{
|
||||
val validKeys = knownKeys.filter { case (_, key) => knownValues get key exists (_.nonEmpty) }
|
||||
if (validKeys.isEmpty)
|
||||
failure("No valid extra keys.")
|
||||
else
|
||||
rep1sep(extraParser(validKeys, knownValues), spacedComma) map AttributeMap.apply
|
||||
}
|
||||
def extrasParser(knownKeys: Map[String, AttributeKey[_]],
|
||||
knownValues: IMap[AttributeKey, Set]): Parser[AttributeMap] = {
|
||||
val validKeys = knownKeys.filter { case (_, key) => knownValues get key exists (_.nonEmpty) }
|
||||
if (validKeys.isEmpty)
|
||||
failure("No valid extra keys.")
|
||||
else
|
||||
rep1sep(extraParser(validKeys, knownValues), spacedComma) map AttributeMap.apply
|
||||
}
|
||||
|
||||
def extraParser(knownKeys: Map[String, AttributeKey[_]], knownValues: IMap[AttributeKey, Set]): Parser[AttributeEntry[_]] =
|
||||
{
|
||||
val keyp = knownIDParser(knownKeys, "Not a valid extra key") <~ token(':' ~ OptSpace)
|
||||
keyp flatMap {
|
||||
case key: AttributeKey[t] =>
|
||||
val valueMap: Map[String, t] = knownValues(key).map(v => (v.toString, v)).toMap
|
||||
knownIDParser(valueMap, "extra value") map { value => AttributeEntry(key, value) }
|
||||
}
|
||||
def extraParser(knownKeys: Map[String, AttributeKey[_]],
|
||||
knownValues: IMap[AttributeKey, Set]): Parser[AttributeEntry[_]] = {
|
||||
val keyp = knownIDParser(knownKeys, "Not a valid extra key") <~ token(':' ~ OptSpace)
|
||||
keyp flatMap {
|
||||
case key: AttributeKey[t] =>
|
||||
val valueMap: Map[String, t] = knownValues(key).map(v => (v.toString, v)).toMap
|
||||
knownIDParser(valueMap, "extra value") map { value =>
|
||||
AttributeEntry(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
def knownIDParser[T](knownKeys: Map[String, T], label: String): Parser[T] =
|
||||
token(examplesStrict(ID, knownKeys.keys.toSet, label)) map knownKeys
|
||||
|
||||
|
|
@ -215,29 +258,33 @@ object Act {
|
|||
token(examplesStrict(pluginLabelParser, knownPlugins.keys.toSet, label)) map knownPlugins
|
||||
}
|
||||
|
||||
def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedAxis[ResolvedReference]] =
|
||||
{
|
||||
val global = token(GlobalString ~ '/') ^^^ ParsedGlobal
|
||||
val trailing = '/' !!! "Expected '/' (if selecting a project)"
|
||||
global | value(resolvedReference(index, currentBuild, trailing))
|
||||
def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedAxis[ResolvedReference]] = {
|
||||
val global = token(GlobalString ~ '/') ^^^ ParsedGlobal
|
||||
val trailing = '/' !!! "Expected '/' (if selecting a project)"
|
||||
global | value(resolvedReference(index, currentBuild, trailing))
|
||||
}
|
||||
def resolvedReference(index: KeyIndex,
|
||||
currentBuild: URI,
|
||||
trailing: Parser[_]): Parser[ResolvedReference] = {
|
||||
def projectID(uri: URI) =
|
||||
token(examplesStrict(ID, index projects uri, "project ID") <~ trailing)
|
||||
def projectRef(uri: URI) = projectID(uri) map { id =>
|
||||
ProjectRef(uri, id)
|
||||
}
|
||||
def resolvedReference(index: KeyIndex, currentBuild: URI, trailing: Parser[_]): Parser[ResolvedReference] =
|
||||
{
|
||||
def projectID(uri: URI) = token(examplesStrict(ID, index projects uri, "project ID") <~ trailing)
|
||||
def projectRef(uri: URI) = projectID(uri) map { id => ProjectRef(uri, id) }
|
||||
|
||||
val uris = index.buildURIs
|
||||
val resolvedURI = Uri(uris).map(uri => Scope.resolveBuild(currentBuild, uri))
|
||||
val buildRef = token('{' ~> resolvedURI <~ '}').?
|
||||
val uris = index.buildURIs
|
||||
val resolvedURI = Uri(uris).map(uri => Scope.resolveBuild(currentBuild, uri))
|
||||
val buildRef = token('{' ~> resolvedURI <~ '}').?
|
||||
|
||||
buildRef flatMap {
|
||||
case None => projectRef(currentBuild)
|
||||
case Some(uri) => projectRef(uri) | token(trailing ^^^ BuildRef(uri))
|
||||
}
|
||||
buildRef flatMap {
|
||||
case None => projectRef(currentBuild)
|
||||
case Some(uri) => projectRef(uri) | token(trailing ^^^ BuildRef(uri))
|
||||
}
|
||||
}
|
||||
def optProjectRef(index: KeyIndex, current: ProjectRef): Parser[ParsedAxis[ResolvedReference]] =
|
||||
projectRef(index, current.build) ?? Omitted
|
||||
def resolveProject(parsed: ParsedAxis[ResolvedReference], current: ProjectRef): Option[ResolvedReference] =
|
||||
def resolveProject(parsed: ParsedAxis[ResolvedReference],
|
||||
current: ProjectRef): Option[ResolvedReference] =
|
||||
parsed match {
|
||||
case Omitted => Some(current)
|
||||
case ParsedGlobal => None
|
||||
|
|
@ -246,30 +293,30 @@ object Act {
|
|||
|
||||
def actParser(s: State): Parser[() => State] = requireSession(s, actParser0(s))
|
||||
|
||||
private[this] def actParser0(state: State): Parser[() => State] =
|
||||
{
|
||||
val extracted = Project extract state
|
||||
import extracted.{ showKey, structure }
|
||||
import Aggregation.evaluatingParser
|
||||
actionParser.flatMap { action =>
|
||||
val akp = aggregatedKeyParser(extracted)
|
||||
def evaluate(kvs: Seq[ScopedKey[_]]): Parser[() => State] = {
|
||||
val preparedPairs = anyKeyValues(structure, kvs)
|
||||
val showConfig = Aggregation.defaultShow(state, showTasks = action == ShowAction)
|
||||
evaluatingParser(state, structure, showConfig)(preparedPairs) map { evaluate => () => {
|
||||
private[this] def actParser0(state: State): Parser[() => State] = {
|
||||
val extracted = Project extract state
|
||||
import extracted.{ showKey, structure }
|
||||
import Aggregation.evaluatingParser
|
||||
actionParser.flatMap { action =>
|
||||
val akp = aggregatedKeyParser(extracted)
|
||||
def evaluate(kvs: Seq[ScopedKey[_]]): Parser[() => State] = {
|
||||
val preparedPairs = anyKeyValues(structure, kvs)
|
||||
val showConfig = Aggregation.defaultShow(state, showTasks = action == ShowAction)
|
||||
evaluatingParser(state, structure, showConfig)(preparedPairs) map { evaluate => () =>
|
||||
{
|
||||
val keyStrings = preparedPairs.map(pp => showKey.show(pp.key)).mkString(", ")
|
||||
state.log.debug("Evaluating tasks: " + keyStrings)
|
||||
evaluate()
|
||||
}
|
||||
}
|
||||
}
|
||||
action match {
|
||||
case SingleAction => akp flatMap evaluate
|
||||
case ShowAction | MultiAction =>
|
||||
rep1sep(akp, token(Space)).flatMap(kvss => evaluate(kvss.flatten))
|
||||
}
|
||||
}
|
||||
action match {
|
||||
case SingleAction => akp flatMap evaluate
|
||||
case ShowAction | MultiAction =>
|
||||
rep1sep(akp, token(Space)).flatMap(kvss => evaluate(kvss.flatten))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private[this] final class ActAction
|
||||
private[this] final val ShowAction, MultiAction, SingleAction = new ActAction
|
||||
|
|
@ -281,29 +328,45 @@ object Act {
|
|||
) ?? SingleAction
|
||||
|
||||
def scopedKeyParser(state: State): Parser[ScopedKey[_]] = scopedKeyParser(Project extract state)
|
||||
def scopedKeyParser(extracted: Extracted): Parser[ScopedKey[_]] = scopedKeyParser(extracted.structure, extracted.currentRef)
|
||||
def scopedKeyParser(extracted: Extracted): Parser[ScopedKey[_]] =
|
||||
scopedKeyParser(extracted.structure, extracted.currentRef)
|
||||
def scopedKeyParser(structure: BuildStructure, currentRef: ProjectRef): Parser[ScopedKey[_]] =
|
||||
scopedKey(structure.index.keyIndex, currentRef, structure.extra.configurationsForAxis, structure.index.keyMap, structure.data)
|
||||
scopedKey(structure.index.keyIndex,
|
||||
currentRef,
|
||||
structure.extra.configurationsForAxis,
|
||||
structure.index.keyMap,
|
||||
structure.data)
|
||||
|
||||
type KeysParser = Parser[Seq[ScopedKey[T]] forSome { type T }]
|
||||
def aggregatedKeyParser(state: State): KeysParser = aggregatedKeyParser(Project extract state)
|
||||
def aggregatedKeyParser(extracted: Extracted): KeysParser = aggregatedKeyParser(extracted.structure, extracted.currentRef)
|
||||
def aggregatedKeyParser(extracted: Extracted): KeysParser =
|
||||
aggregatedKeyParser(extracted.structure, extracted.currentRef)
|
||||
def aggregatedKeyParser(structure: BuildStructure, currentRef: ProjectRef): KeysParser =
|
||||
scopedKeyAggregated(currentRef, structure.extra.configurationsForAxis, structure)
|
||||
|
||||
def keyValues[T](state: State)(keys: Seq[ScopedKey[T]]): Values[T] = keyValues(Project extract state)(keys)
|
||||
def keyValues[T](extracted: Extracted)(keys: Seq[ScopedKey[T]]): Values[T] = keyValues(extracted.structure)(keys)
|
||||
def keyValues[T](state: State)(keys: Seq[ScopedKey[T]]): Values[T] =
|
||||
keyValues(Project extract state)(keys)
|
||||
def keyValues[T](extracted: Extracted)(keys: Seq[ScopedKey[T]]): Values[T] =
|
||||
keyValues(extracted.structure)(keys)
|
||||
def keyValues[T](structure: BuildStructure)(keys: Seq[ScopedKey[T]]): Values[T] =
|
||||
keys.flatMap { key =>
|
||||
getValue(structure.data, key.scope, key.key) map { value => KeyValue(key, value) }
|
||||
getValue(structure.data, key.scope, key.key) map { value =>
|
||||
KeyValue(key, value)
|
||||
}
|
||||
}
|
||||
private[this] def anyKeyValues(structure: BuildStructure, keys: Seq[ScopedKey[_]]): Seq[KeyValue[_]] =
|
||||
private[this] def anyKeyValues(structure: BuildStructure,
|
||||
keys: Seq[ScopedKey[_]]): Seq[KeyValue[_]] =
|
||||
keys.flatMap { key =>
|
||||
getValue(structure.data, key.scope, key.key) map { value => KeyValue(key, value) }
|
||||
getValue(structure.data, key.scope, key.key) map { value =>
|
||||
KeyValue(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def getValue[T](data: Settings[Scope], scope: Scope, key: AttributeKey[T]): Option[T] =
|
||||
if (java.lang.Boolean.getBoolean("sbt.cli.nodelegation")) data.getDirect(scope, key) else data.get(scope, key)
|
||||
private[this] def getValue[T](data: Settings[Scope],
|
||||
scope: Scope,
|
||||
key: AttributeKey[T]): Option[T] =
|
||||
if (java.lang.Boolean.getBoolean("sbt.cli.nodelegation")) data.getDirect(scope, key)
|
||||
else data.get(scope, key)
|
||||
|
||||
def requireSession[T](s: State, p: => Parser[T]): Parser[T] =
|
||||
if (s get sessionSettings isEmpty) failure("No project loaded") else p
|
||||
|
|
@ -314,5 +377,7 @@ object Act {
|
|||
final object ParsedGlobal extends ParsedAxis[Nothing]
|
||||
final object Omitted extends ParsedAxis[Nothing]
|
||||
final class ParsedValue[T](val value: T) extends ParsedAxis[T]
|
||||
def value[T](t: Parser[T]): Parser[ParsedAxis[T]] = t map { v => new ParsedValue(v) }
|
||||
def value[T](t: Parser[T]): Parser[ParsedAxis[T]] = t map { v =>
|
||||
new ParsedValue(v)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ object AddSettings {
|
|||
private[sbt] final object BuildScalaFiles extends AddSettings
|
||||
|
||||
/** Adds all settings from autoplugins. */
|
||||
val autoPlugins: AddSettings = new AutoPlugins(const(true)) // Note: We do not expose fine-grained autoplugins because
|
||||
val autoPlugins
|
||||
: AddSettings = new AutoPlugins(const(true)) // Note: We do not expose fine-grained autoplugins because
|
||||
// it's dangerous to control at that level right now.
|
||||
// Leaving the hook in place in case we need to expose
|
||||
// it, but most likely it will remain locked out
|
||||
|
|
@ -55,19 +56,22 @@ object AddSettings {
|
|||
case _ => seq(a, b)
|
||||
}
|
||||
|
||||
def clearSbtFiles(a: AddSettings): AddSettings = tx(a) {
|
||||
case _: DefaultSbtFiles | _: SbtFiles => None
|
||||
case x => Some(x)
|
||||
} getOrElse seq()
|
||||
def clearSbtFiles(a: AddSettings): AddSettings =
|
||||
tx(a) {
|
||||
case _: DefaultSbtFiles | _: SbtFiles => None
|
||||
case x => Some(x)
|
||||
} getOrElse seq()
|
||||
|
||||
private[sbt] def tx(a: AddSettings)(f: AddSettings => Option[AddSettings]): Option[AddSettings] = a match {
|
||||
case s: Sequence =>
|
||||
s.sequence.flatMap { b => tx(b)(f) } match {
|
||||
case Seq() => None
|
||||
case Seq(x) => Some(x)
|
||||
case ss => Some(new Sequence(ss))
|
||||
}
|
||||
case x => f(x)
|
||||
}
|
||||
private[sbt] def tx(a: AddSettings)(f: AddSettings => Option[AddSettings]): Option[AddSettings] =
|
||||
a match {
|
||||
case s: Sequence =>
|
||||
s.sequence.flatMap { b =>
|
||||
tx(b)(f)
|
||||
} match {
|
||||
case Seq() => None
|
||||
case Seq(x) => Some(x)
|
||||
case ss => Some(new Sequence(ss))
|
||||
}
|
||||
case x => f(x)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,56 +14,79 @@ import std.Transform.DummyTaskMap
|
|||
|
||||
sealed trait Aggregation
|
||||
object Aggregation {
|
||||
final case class ShowConfig(settingValues: Boolean, taskValues: Boolean, print: String => Unit, success: Boolean)
|
||||
final case class Complete[T](start: Long, stop: Long, results: sbt.Result[Seq[KeyValue[T]]], state: State)
|
||||
final case class ShowConfig(settingValues: Boolean,
|
||||
taskValues: Boolean,
|
||||
print: String => Unit,
|
||||
success: Boolean)
|
||||
final case class Complete[T](start: Long,
|
||||
stop: Long,
|
||||
results: sbt.Result[Seq[KeyValue[T]]],
|
||||
state: State)
|
||||
final case class KeyValue[+T](key: ScopedKey[_], value: T)
|
||||
|
||||
def defaultShow(state: State, showTasks: Boolean): ShowConfig = ShowConfig(settingValues = true, taskValues = showTasks, s => state.log.info(s), success = true)
|
||||
def printSettings(xs: Seq[KeyValue[_]], print: String => Unit)(implicit display: Show[ScopedKey[_]]) =
|
||||
def defaultShow(state: State, showTasks: Boolean): ShowConfig =
|
||||
ShowConfig(settingValues = true,
|
||||
taskValues = showTasks,
|
||||
s => state.log.info(s),
|
||||
success = true)
|
||||
def printSettings(xs: Seq[KeyValue[_]], print: String => Unit)(
|
||||
implicit display: Show[ScopedKey[_]]) =
|
||||
xs match {
|
||||
case KeyValue(_, x: Seq[_]) :: Nil => print(x.mkString("* ", "\n* ", ""))
|
||||
case KeyValue(_, x) :: Nil => print(x.toString)
|
||||
case _ => xs foreach { case KeyValue(key, value) => print(display.show(key) + "\n\t" + value.toString) }
|
||||
case _ =>
|
||||
xs foreach {
|
||||
case KeyValue(key, value) => print(display.show(key) + "\n\t" + value.toString)
|
||||
}
|
||||
}
|
||||
type Values[T] = Seq[KeyValue[T]]
|
||||
type AnyKeys = Values[_]
|
||||
def seqParser[T](ps: Values[Parser[T]]): Parser[Seq[KeyValue[T]]] = seq(ps.map { case KeyValue(k, p) => p.map(v => KeyValue(k, v)) })
|
||||
def seqParser[T](ps: Values[Parser[T]]): Parser[Seq[KeyValue[T]]] =
|
||||
seq(ps.map { case KeyValue(k, p) => p.map(v => KeyValue(k, v)) })
|
||||
|
||||
def applyTasks[T](s: State, structure: BuildStructure, ps: Values[Parser[Task[T]]], show: ShowConfig)(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
|
||||
def applyTasks[T](s: State,
|
||||
structure: BuildStructure,
|
||||
ps: Values[Parser[Task[T]]],
|
||||
show: ShowConfig)(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
|
||||
Command.applyEffect(seqParser(ps)) { ts =>
|
||||
runTasks(s, structure, ts, DummyTaskMap(Nil), show)
|
||||
}
|
||||
|
||||
private def showRun[T](complete: Complete[T], show: ShowConfig)(implicit display: Show[ScopedKey[_]]): Unit =
|
||||
{
|
||||
import complete._
|
||||
val log = state.log
|
||||
val extracted = Project.extract(state)
|
||||
val success = results match { case Value(_) => true; case Inc(_) => false }
|
||||
results.toEither.right.foreach { r => if (show.taskValues) printSettings(r, show.print) }
|
||||
if (show.success) printSuccess(start, stop, extracted, success, log)
|
||||
private def showRun[T](complete: Complete[T], show: ShowConfig)(
|
||||
implicit display: Show[ScopedKey[_]]): Unit = {
|
||||
import complete._
|
||||
val log = state.log
|
||||
val extracted = Project.extract(state)
|
||||
val success = results match { case Value(_) => true; case Inc(_) => false }
|
||||
results.toEither.right.foreach { r =>
|
||||
if (show.taskValues) printSettings(r, show.print)
|
||||
}
|
||||
def timedRun[T](s: State, ts: Values[Task[T]], extra: DummyTaskMap): Complete[T] =
|
||||
{
|
||||
import EvaluateTask._
|
||||
import std.TaskExtra._
|
||||
if (show.success) printSuccess(start, stop, extracted, success, log)
|
||||
}
|
||||
def timedRun[T](s: State, ts: Values[Task[T]], extra: DummyTaskMap): Complete[T] = {
|
||||
import EvaluateTask._
|
||||
import std.TaskExtra._
|
||||
|
||||
val extracted = Project extract s
|
||||
import extracted.structure
|
||||
val toRun = ts map { case KeyValue(k, t) => t.map(v => KeyValue(k, v)) } join;
|
||||
val roots = ts map { case KeyValue(k, _) => k }
|
||||
val config = extractedTaskConfig(extracted, structure, s)
|
||||
val extracted = Project extract s
|
||||
import extracted.structure
|
||||
val toRun = ts map { case KeyValue(k, t) => t.map(v => KeyValue(k, v)) } join;
|
||||
val roots = ts map { case KeyValue(k, _) => k }
|
||||
val config = extractedTaskConfig(extracted, structure, s)
|
||||
|
||||
val start = System.currentTimeMillis
|
||||
val (newS, result) = withStreams(structure, s) { str =>
|
||||
val transform = nodeView(s, str, roots, extra)
|
||||
runTask(toRun, s, str, structure.index.triggers, config)(transform)
|
||||
}
|
||||
val stop = System.currentTimeMillis
|
||||
Complete(start, stop, result, newS)
|
||||
val start = System.currentTimeMillis
|
||||
val (newS, result) = withStreams(structure, s) { str =>
|
||||
val transform = nodeView(s, str, roots, extra)
|
||||
runTask(toRun, s, str, structure.index.triggers, config)(transform)
|
||||
}
|
||||
val stop = System.currentTimeMillis
|
||||
Complete(start, stop, result, newS)
|
||||
}
|
||||
|
||||
def runTasks[HL <: HList, T](s: State, structure: BuildStructure, ts: Values[Task[T]], extra: DummyTaskMap, show: ShowConfig)(implicit display: Show[ScopedKey[_]]): State = {
|
||||
def runTasks[HL <: HList, T](s: State,
|
||||
structure: BuildStructure,
|
||||
ts: Values[Task[T]],
|
||||
extra: DummyTaskMap,
|
||||
show: ShowConfig)(implicit display: Show[ScopedKey[_]]): State = {
|
||||
val complete = timedRun[T](s, ts, extra)
|
||||
showRun(complete, show)
|
||||
complete.results match {
|
||||
|
|
@ -72,9 +95,14 @@ object Aggregation {
|
|||
}
|
||||
}
|
||||
|
||||
def printSuccess(start: Long, stop: Long, extracted: Extracted, success: Boolean, log: Logger): Unit = {
|
||||
def printSuccess(start: Long,
|
||||
stop: Long,
|
||||
extracted: Extracted,
|
||||
success: Boolean,
|
||||
log: Logger): Unit = {
|
||||
import extracted._
|
||||
def get(key: SettingKey[Boolean]): Boolean = key in currentRef get structure.data getOrElse true
|
||||
def get(key: SettingKey[Boolean]): Boolean =
|
||||
key in currentRef get structure.data getOrElse true
|
||||
if (get(showSuccess)) {
|
||||
if (get(showTiming)) {
|
||||
val msg = timingString(start, stop, "", structure.data, currentRef, log)
|
||||
|
|
@ -83,100 +111,118 @@ object Aggregation {
|
|||
log.success("")
|
||||
}
|
||||
}
|
||||
private def timingString(startTime: Long, endTime: Long, s: String, data: Settings[Scope], currentRef: ProjectRef, log: Logger): String =
|
||||
{
|
||||
val format = timingFormat in currentRef get data getOrElse defaultFormat
|
||||
timing(format, startTime, endTime, "", log)
|
||||
}
|
||||
def timing(format: java.text.DateFormat, startTime: Long, endTime: Long, s: String, log: Logger): String =
|
||||
{
|
||||
val ss = if (s.isEmpty) "" else s + " "
|
||||
val nowString = format.format(new java.util.Date(endTime))
|
||||
"Total " + ss + "time: " + (endTime - startTime + 500) / 1000 + " s, completed " + nowString
|
||||
}
|
||||
def defaultFormat =
|
||||
{
|
||||
import java.text.DateFormat
|
||||
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM)
|
||||
}
|
||||
private def timingString(startTime: Long,
|
||||
endTime: Long,
|
||||
s: String,
|
||||
data: Settings[Scope],
|
||||
currentRef: ProjectRef,
|
||||
log: Logger): String = {
|
||||
val format = timingFormat in currentRef get data getOrElse defaultFormat
|
||||
timing(format, startTime, endTime, "", log)
|
||||
}
|
||||
def timing(format: java.text.DateFormat,
|
||||
startTime: Long,
|
||||
endTime: Long,
|
||||
s: String,
|
||||
log: Logger): String = {
|
||||
val ss = if (s.isEmpty) "" else s + " "
|
||||
val nowString = format.format(new java.util.Date(endTime))
|
||||
"Total " + ss + "time: " + (endTime - startTime + 500) / 1000 + " s, completed " + nowString
|
||||
}
|
||||
def defaultFormat = {
|
||||
import java.text.DateFormat
|
||||
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM)
|
||||
}
|
||||
|
||||
def applyDynamicTasks[I](s: State, structure: BuildStructure, inputs: Values[InputTask[I]], show: ShowConfig)(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
|
||||
{
|
||||
val parsers = for (KeyValue(k, it) <- inputs) yield it.parser(s).map(v => KeyValue(k, v))
|
||||
Command.applyEffect(seq(parsers)) { roots =>
|
||||
runTasks(s, structure, roots, DummyTaskMap(Nil), show)
|
||||
def applyDynamicTasks[I](
|
||||
s: State,
|
||||
structure: BuildStructure,
|
||||
inputs: Values[InputTask[I]],
|
||||
show: ShowConfig)(implicit display: Show[ScopedKey[_]]): Parser[() => State] = {
|
||||
val parsers = for (KeyValue(k, it) <- inputs) yield it.parser(s).map(v => KeyValue(k, v))
|
||||
Command.applyEffect(seq(parsers)) { roots =>
|
||||
runTasks(s, structure, roots, DummyTaskMap(Nil), show)
|
||||
}
|
||||
}
|
||||
|
||||
def evaluatingParser(s: State, structure: BuildStructure, show: ShowConfig)(
|
||||
keys: Seq[KeyValue[_]])(implicit display: Show[ScopedKey[_]]): Parser[() => State] = {
|
||||
// to make the call sites clearer
|
||||
def separate[L](in: Seq[KeyValue[_]])(
|
||||
f: KeyValue[_] => Either[KeyValue[L], KeyValue[_]]): (Seq[KeyValue[L]], Seq[KeyValue[_]]) =
|
||||
Util.separate(in)(f)
|
||||
|
||||
val kvs = keys.toList
|
||||
if (kvs.isEmpty)
|
||||
failure("No such setting/task")
|
||||
else {
|
||||
val (inputTasks, other) = separate[InputTask[_]](kvs) {
|
||||
case KeyValue(k, v: InputTask[_]) => Left(KeyValue(k, v))
|
||||
case kv => Right(kv)
|
||||
}
|
||||
}
|
||||
|
||||
def evaluatingParser(s: State, structure: BuildStructure, show: ShowConfig)(keys: Seq[KeyValue[_]])(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
|
||||
{
|
||||
// to make the call sites clearer
|
||||
def separate[L](in: Seq[KeyValue[_]])(f: KeyValue[_] => Either[KeyValue[L], KeyValue[_]]): (Seq[KeyValue[L]], Seq[KeyValue[_]]) =
|
||||
Util.separate(in)(f)
|
||||
|
||||
val kvs = keys.toList
|
||||
if (kvs.isEmpty)
|
||||
failure("No such setting/task")
|
||||
else {
|
||||
val (inputTasks, other) = separate[InputTask[_]](kvs) {
|
||||
case KeyValue(k, v: InputTask[_]) => Left(KeyValue(k, v))
|
||||
case kv => Right(kv)
|
||||
}
|
||||
val (tasks, settings) = separate[Task[_]](other) {
|
||||
case KeyValue(k, v: Task[_]) => Left(KeyValue(k, v))
|
||||
case kv => Right(kv)
|
||||
}
|
||||
// currently, disallow input tasks to be mixed with normal tasks.
|
||||
// This occurs in `all` or `show`, which support multiple tasks.
|
||||
// Previously, multiple tasks could be run in one execution, but they were all for the same key, just in different scopes.
|
||||
// When `all` was added, it allowed different keys and thus opened the possibility for mixing settings,
|
||||
// tasks, and input tasks in the same call. The code below allows settings and tasks to be mixed, but not input tasks.
|
||||
// One problem with input tasks in `all` is that many input tasks consume all input and would need syntactic delimiters.
|
||||
// Once that is addressed, the tasks constructed by the input tasks would need to be combined with the explicit tasks.
|
||||
if (inputTasks.nonEmpty) {
|
||||
if (other.nonEmpty) {
|
||||
val inputStrings = inputTasks.map(_.key).mkString("Input task(s):\n\t", "\n\t", "\n")
|
||||
val otherStrings = other.map(_.key).mkString("Task(s)/setting(s):\n\t", "\n\t", "\n")
|
||||
failure(s"Cannot mix input tasks with plain tasks/settings. $inputStrings $otherStrings")
|
||||
} else
|
||||
applyDynamicTasks(s, structure, maps(inputTasks)(castToAny), show)
|
||||
} else {
|
||||
val base = if (tasks.isEmpty) success(() => s) else
|
||||
val (tasks, settings) = separate[Task[_]](other) {
|
||||
case KeyValue(k, v: Task[_]) => Left(KeyValue(k, v))
|
||||
case kv => Right(kv)
|
||||
}
|
||||
// currently, disallow input tasks to be mixed with normal tasks.
|
||||
// This occurs in `all` or `show`, which support multiple tasks.
|
||||
// Previously, multiple tasks could be run in one execution, but they were all for the same key, just in different scopes.
|
||||
// When `all` was added, it allowed different keys and thus opened the possibility for mixing settings,
|
||||
// tasks, and input tasks in the same call. The code below allows settings and tasks to be mixed, but not input tasks.
|
||||
// One problem with input tasks in `all` is that many input tasks consume all input and would need syntactic delimiters.
|
||||
// Once that is addressed, the tasks constructed by the input tasks would need to be combined with the explicit tasks.
|
||||
if (inputTasks.nonEmpty) {
|
||||
if (other.nonEmpty) {
|
||||
val inputStrings = inputTasks.map(_.key).mkString("Input task(s):\n\t", "\n\t", "\n")
|
||||
val otherStrings = other.map(_.key).mkString("Task(s)/setting(s):\n\t", "\n\t", "\n")
|
||||
failure(
|
||||
s"Cannot mix input tasks with plain tasks/settings. $inputStrings $otherStrings")
|
||||
} else
|
||||
applyDynamicTasks(s, structure, maps(inputTasks)(castToAny), show)
|
||||
} else {
|
||||
val base =
|
||||
if (tasks.isEmpty) success(() => s)
|
||||
else
|
||||
applyTasks(s, structure, maps(tasks)(x => success(castToAny(x))), show)
|
||||
base.map { res => () =>
|
||||
val newState = res()
|
||||
if (show.settingValues && settings.nonEmpty) printSettings(settings, show.print)
|
||||
newState
|
||||
}
|
||||
base.map { res => () =>
|
||||
val newState = res()
|
||||
if (show.settingValues && settings.nonEmpty) printSettings(settings, show.print)
|
||||
newState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// this is a hack to avoid duplicating method implementations
|
||||
private[this] def castToAny[T[_]](t: T[_]): T[Any] = t.asInstanceOf[T[Any]]
|
||||
|
||||
private[this] def maps[T, S](vs: Values[T])(f: T => S): Values[S] =
|
||||
vs map { case KeyValue(k, v) => KeyValue(k, f(v)) }
|
||||
|
||||
def projectAggregates[Proj](proj: Option[Reference], extra: BuildUtil[Proj], reverse: Boolean): Seq[ProjectRef] =
|
||||
{
|
||||
val resRef = proj.map(p => extra.projectRefFor(extra.resolveRef(p)))
|
||||
resRef.toList.flatMap(ref =>
|
||||
if (reverse) extra.aggregates.reverse(ref) else extra.aggregates.forward(ref))
|
||||
}
|
||||
def projectAggregates[Proj](proj: Option[Reference],
|
||||
extra: BuildUtil[Proj],
|
||||
reverse: Boolean): Seq[ProjectRef] = {
|
||||
val resRef = proj.map(p => extra.projectRefFor(extra.resolveRef(p)))
|
||||
resRef.toList.flatMap(ref =>
|
||||
if (reverse) extra.aggregates.reverse(ref) else extra.aggregates.forward(ref))
|
||||
}
|
||||
|
||||
def aggregate[T, Proj](key: ScopedKey[T], rawMask: ScopeMask, extra: BuildUtil[Proj], reverse: Boolean = false): Seq[ScopedKey[T]] =
|
||||
{
|
||||
val mask = rawMask.copy(project = true)
|
||||
Dag.topologicalSort(key) { k =>
|
||||
if (reverse)
|
||||
reverseAggregatedKeys(k, extra, mask)
|
||||
else if (aggregationEnabled(k, extra.data))
|
||||
aggregatedKeys(k, extra, mask)
|
||||
else
|
||||
Nil
|
||||
}
|
||||
def aggregate[T, Proj](key: ScopedKey[T],
|
||||
rawMask: ScopeMask,
|
||||
extra: BuildUtil[Proj],
|
||||
reverse: Boolean = false): Seq[ScopedKey[T]] = {
|
||||
val mask = rawMask.copy(project = true)
|
||||
Dag.topologicalSort(key) { k =>
|
||||
if (reverse)
|
||||
reverseAggregatedKeys(k, extra, mask)
|
||||
else if (aggregationEnabled(k, extra.data))
|
||||
aggregatedKeys(k, extra, mask)
|
||||
else
|
||||
Nil
|
||||
}
|
||||
def reverseAggregatedKeys[T](key: ScopedKey[T], extra: BuildUtil[_], mask: ScopeMask): Seq[ScopedKey[T]] =
|
||||
}
|
||||
def reverseAggregatedKeys[T](key: ScopedKey[T],
|
||||
extra: BuildUtil[_],
|
||||
mask: ScopeMask): Seq[ScopedKey[T]] =
|
||||
projectAggregates(key.scope.project.toOption, extra, reverse = true) flatMap { ref =>
|
||||
val toResolve = key.scope.copy(project = Select(ref))
|
||||
val resolved = Resolve(extra, Global, key.key, mask)(toResolve)
|
||||
|
|
@ -184,7 +230,9 @@ object Aggregation {
|
|||
if (aggregationEnabled(skey, extra.data)) skey :: Nil else Nil
|
||||
}
|
||||
|
||||
def aggregatedKeys[T](key: ScopedKey[T], extra: BuildUtil[_], mask: ScopeMask): Seq[ScopedKey[T]] =
|
||||
def aggregatedKeys[T](key: ScopedKey[T],
|
||||
extra: BuildUtil[_],
|
||||
mask: ScopeMask): Seq[ScopedKey[T]] =
|
||||
projectAggregates(key.scope.project.toOption, extra, reverse = false) map { ref =>
|
||||
val toResolve = key.scope.copy(project = Select(ref))
|
||||
val resolved = Resolve(extra, Global, key.key, mask)(toResolve)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ trait BuildDef {
|
|||
// TODO: Should we grab the build core settings here or in a plugin?
|
||||
def settings: Seq[Setting[_]] = Defaults.buildCore
|
||||
def buildLoaders: Seq[BuildLoader.Components] = Nil
|
||||
|
||||
/**
|
||||
* Explicitly defines the root project.
|
||||
* If None, the root project is the first project in the build's root directory or just the first project if none are in the root directory.
|
||||
|
|
@ -28,18 +29,25 @@ trait BuildDef {
|
|||
|
||||
private[sbt] object BuildDef {
|
||||
val defaultEmpty: BuildDef = new BuildDef { override def projects = Nil }
|
||||
val default: BuildDef = new BuildDef { override def projectDefinitions(base: File) = defaultProject(defaultID(base), base) :: Nil }
|
||||
val default: BuildDef = new BuildDef {
|
||||
override def projectDefinitions(base: File) = defaultProject(defaultID(base), base) :: Nil
|
||||
}
|
||||
def defaultAggregated(id: String, aggregate: Seq[ProjectRef]): BuildDef = new BuildDef {
|
||||
override def projectDefinitions(base: File) = defaultAggregatedProject(id, base, aggregate) :: Nil
|
||||
override def projectDefinitions(base: File) =
|
||||
defaultAggregatedProject(id, base, aggregate) :: Nil
|
||||
}
|
||||
|
||||
def defaultID(base: File, prefix: String = "default"): String = prefix + "-" + Hash.trimHashString(base.getAbsolutePath, 6)
|
||||
def defaultID(base: File, prefix: String = "default"): String =
|
||||
prefix + "-" + Hash.trimHashString(base.getAbsolutePath, 6)
|
||||
|
||||
def defaultProject(id: String, base: File): Project = Project(id, base).settings(defaultProjectSettings)
|
||||
def defaultProject(id: String, base: File): Project =
|
||||
Project(id, base).settings(defaultProjectSettings)
|
||||
def defaultAggregatedProject(id: String, base: File, agg: Seq[ProjectRef]): Project =
|
||||
defaultProject(id, base).aggregate(agg: _*)
|
||||
|
||||
private[sbt] def generatedRootWithoutIvyPlugin(id: String, base: File, agg: Seq[ProjectRef]): Project =
|
||||
private[sbt] def generatedRootWithoutIvyPlugin(id: String,
|
||||
base: File,
|
||||
agg: Seq[ProjectRef]): Project =
|
||||
Project.mkGeneratedRoot(id, base, Eval.later(agg)).settings(defaultProjectSettings)
|
||||
private[sbt] def defaultProjectSettings: Seq[Setting[_]] = Seq(
|
||||
// TODO - Can we move this somewhere else? ordering of settings is causing this to get borked.
|
||||
|
|
@ -53,5 +61,7 @@ private[sbt] object BuildDef {
|
|||
},
|
||||
autoGeneratedProject := true
|
||||
)
|
||||
def analyzed(in: Seq[Attributed[_]]): Seq[xsbti.compile.CompileAnalysis] = in.flatMap { _.metadata.get(Keys.analysis) }
|
||||
def analyzed(in: Seq[Attributed[_]]): Seq[xsbti.compile.CompileAnalysis] = in.flatMap {
|
||||
_.metadata.get(Keys.analysis)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@ import sbt.internal.util.Types.idFun
|
|||
import sbt.internal.util.Dag
|
||||
import BuildDependencies._
|
||||
|
||||
final class BuildDependencies private (val classpath: DependencyMap[ClasspathDep[ProjectRef]], val aggregate: DependencyMap[ProjectRef]) {
|
||||
final class BuildDependencies private (val classpath: DependencyMap[ClasspathDep[ProjectRef]],
|
||||
val aggregate: DependencyMap[ProjectRef]) {
|
||||
def classpathRefs(ref: ProjectRef): Seq[ProjectRef] = classpath(ref) map getID
|
||||
def classpathTransitiveRefs(ref: ProjectRef): Seq[ProjectRef] = classpathTransitive(ref)
|
||||
|
||||
lazy val classpathTransitive: DependencyMap[ProjectRef] = transitive(classpath, getID)
|
||||
lazy val aggregateTransitive: DependencyMap[ProjectRef] = transitive(aggregate, idFun[ProjectRef])
|
||||
lazy val aggregateTransitive: DependencyMap[ProjectRef] =
|
||||
transitive(aggregate, idFun[ProjectRef])
|
||||
|
||||
def addClasspath(ref: ProjectRef, deps: ClasspathDep[ProjectRef]*): BuildDependencies =
|
||||
new BuildDependencies(classpath.updated(ref, deps ++ classpath.getOrElse(ref, Nil)), aggregate)
|
||||
|
|
@ -18,7 +20,8 @@ final class BuildDependencies private (val classpath: DependencyMap[ClasspathDep
|
|||
new BuildDependencies(classpath, aggregate.updated(ref, deps ++ aggregate.getOrElse(ref, Nil)))
|
||||
}
|
||||
object BuildDependencies {
|
||||
def apply(classpath: DependencyMap[ClasspathDep[ProjectRef]], aggregate: DependencyMap[ProjectRef]): BuildDependencies =
|
||||
def apply(classpath: DependencyMap[ClasspathDep[ProjectRef]],
|
||||
aggregate: DependencyMap[ProjectRef]): BuildDependencies =
|
||||
new BuildDependencies(classpath, aggregate)
|
||||
|
||||
type DependencyMap[D] = Map[ProjectRef, Seq[D]]
|
||||
|
|
|
|||
|
|
@ -12,25 +12,42 @@ import sbt.internal.util.Types.{ const, idFun }
|
|||
import sbt.util.Logger
|
||||
import sbt.librarymanagement.ModuleID
|
||||
|
||||
final class MultiHandler[S, T](builtIn: S => Option[T], root: Option[S => Option[T]], nonRoots: List[(URI, S => Option[T])], getURI: S => URI, log: S => Logger) {
|
||||
final class MultiHandler[S, T](builtIn: S => Option[T],
|
||||
root: Option[S => Option[T]],
|
||||
nonRoots: List[(URI, S => Option[T])],
|
||||
getURI: S => URI,
|
||||
log: S => Logger) {
|
||||
def applyFun: S => Option[T] = apply
|
||||
def apply(info: S): Option[T] =
|
||||
(baseLoader(info), applyNonRoots(info)) match {
|
||||
case (None, Nil) => None
|
||||
case (None, xs @ (_, nr) :: ignored) =>
|
||||
if (ignored.nonEmpty) warn("Using first of multiple matching non-root build resolvers for " + getURI(info), log(info), xs)
|
||||
if (ignored.nonEmpty)
|
||||
warn("Using first of multiple matching non-root build resolvers for " + getURI(info),
|
||||
log(info),
|
||||
xs)
|
||||
Some(nr)
|
||||
case (Some(b), xs) =>
|
||||
if (xs.nonEmpty) warn("Ignoring shadowed non-root build resolver(s) for " + getURI(info), log(info), xs)
|
||||
if (xs.nonEmpty)
|
||||
warn("Ignoring shadowed non-root build resolver(s) for " + getURI(info), log(info), xs)
|
||||
Some(b)
|
||||
}
|
||||
|
||||
def baseLoader: S => Option[T] = root match { case Some(rl) => rl | builtIn; case None => builtIn }
|
||||
def baseLoader: S => Option[T] = root match {
|
||||
case Some(rl) => rl | builtIn; case None => builtIn
|
||||
}
|
||||
|
||||
def addNonRoot(uri: URI, loader: S => Option[T]) = new MultiHandler(builtIn, root, (uri, loader) :: nonRoots, getURI, log)
|
||||
def setRoot(resolver: S => Option[T]) = new MultiHandler(builtIn, Some(resolver), nonRoots, getURI, log)
|
||||
def addNonRoot(uri: URI, loader: S => Option[T]) =
|
||||
new MultiHandler(builtIn, root, (uri, loader) :: nonRoots, getURI, log)
|
||||
def setRoot(resolver: S => Option[T]) =
|
||||
new MultiHandler(builtIn, Some(resolver), nonRoots, getURI, log)
|
||||
def applyNonRoots(info: S): List[(URI, T)] =
|
||||
nonRoots flatMap { case (definingURI, loader) => loader(info) map { unit => (definingURI, unit) } }
|
||||
nonRoots flatMap {
|
||||
case (definingURI, loader) =>
|
||||
loader(info) map { unit =>
|
||||
(definingURI, unit)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def warn(baseMessage: String, log: Logger, matching: Seq[(URI, T)]): Unit = {
|
||||
log.warn(baseMessage)
|
||||
|
|
@ -40,6 +57,7 @@ final class MultiHandler[S, T](builtIn: S => Option[T], root: Option[S => Option
|
|||
}
|
||||
|
||||
object BuildLoader {
|
||||
|
||||
/**
|
||||
* in: Build URI and staging directory
|
||||
* out: None if unhandled or Some containing the retrieve function, which returns the directory retrieved to (can be the same as the staging directory)
|
||||
|
|
@ -50,16 +68,28 @@ object BuildLoader {
|
|||
type Loader = LoadInfo => Option[() => BuildUnit]
|
||||
type TransformAll = PartBuild => PartBuild
|
||||
|
||||
final class Components(val resolver: Resolver, val builder: Builder, val transformer: Transformer, val full: Loader, val transformAll: TransformAll) {
|
||||
final class Components(val resolver: Resolver,
|
||||
val builder: Builder,
|
||||
val transformer: Transformer,
|
||||
val full: Loader,
|
||||
val transformAll: TransformAll) {
|
||||
def |(cs: Components): Components =
|
||||
new Components(resolver | cs.resolver, builder | cs.builder, seq(transformer, cs.transformer), full | cs.full, transformAll andThen cs.transformAll)
|
||||
new Components(resolver | cs.resolver,
|
||||
builder | cs.builder,
|
||||
seq(transformer, cs.transformer),
|
||||
full | cs.full,
|
||||
transformAll andThen cs.transformAll)
|
||||
}
|
||||
def transform(t: Transformer): Components = components(transformer = t)
|
||||
def resolve(r: Resolver): Components = components(resolver = r)
|
||||
def build(b: Builder): Components = components(builder = b)
|
||||
def full(f: Loader): Components = components(full = f)
|
||||
def transformAll(t: TransformAll) = components(transformAll = t)
|
||||
def components(resolver: Resolver = const(None), builder: Builder = const(None), transformer: Transformer = _.unit, full: Loader = const(None), transformAll: TransformAll = idFun) =
|
||||
def components(resolver: Resolver = const(None),
|
||||
builder: Builder = const(None),
|
||||
transformer: Transformer = _.unit,
|
||||
full: Loader = const(None),
|
||||
transformAll: TransformAll = idFun) =
|
||||
new Components(resolver, builder, transformer, full, transformAll)
|
||||
|
||||
def seq(a: Transformer, b: Transformer): Transformer = info => b(info.setUnit(a(info)))
|
||||
|
|
@ -69,19 +99,48 @@ object BuildLoader {
|
|||
def config: LoadBuildConfiguration
|
||||
def state: State
|
||||
}
|
||||
final class ResolveInfo(val uri: URI, val staging: File, val config: LoadBuildConfiguration, val state: State) extends Info
|
||||
final class BuildInfo(val uri: URI, val base: File, val config: LoadBuildConfiguration, val state: State) extends Info
|
||||
final class TransformInfo(val uri: URI, val base: File, val unit: BuildUnit, val config: LoadBuildConfiguration, val state: State) extends Info {
|
||||
def setUnit(newUnit: BuildUnit): TransformInfo = new TransformInfo(uri, base, newUnit, config, state)
|
||||
final class ResolveInfo(val uri: URI,
|
||||
val staging: File,
|
||||
val config: LoadBuildConfiguration,
|
||||
val state: State)
|
||||
extends Info
|
||||
final class BuildInfo(val uri: URI,
|
||||
val base: File,
|
||||
val config: LoadBuildConfiguration,
|
||||
val state: State)
|
||||
extends Info
|
||||
final class TransformInfo(val uri: URI,
|
||||
val base: File,
|
||||
val unit: BuildUnit,
|
||||
val config: LoadBuildConfiguration,
|
||||
val state: State)
|
||||
extends Info {
|
||||
def setUnit(newUnit: BuildUnit): TransformInfo =
|
||||
new TransformInfo(uri, base, newUnit, config, state)
|
||||
}
|
||||
|
||||
final class LoadInfo(val uri: URI, val staging: File, val config: LoadBuildConfiguration, val state: State, val components: Components) extends Info
|
||||
final class LoadInfo(val uri: URI,
|
||||
val staging: File,
|
||||
val config: LoadBuildConfiguration,
|
||||
val state: State,
|
||||
val components: Components)
|
||||
extends Info
|
||||
|
||||
def apply(base: Components, fail: URI => Nothing, s: State, config: LoadBuildConfiguration): BuildLoader =
|
||||
{
|
||||
def makeMulti[S <: Info, T](base: S => Option[T]) = new MultiHandler[S, T](base, None, Nil, _.uri, _.config.log)
|
||||
new BuildLoader(fail, s, config, makeMulti(base.resolver), makeMulti(base.builder), base.transformer, makeMulti(base.full), base.transformAll)
|
||||
}
|
||||
def apply(base: Components,
|
||||
fail: URI => Nothing,
|
||||
s: State,
|
||||
config: LoadBuildConfiguration): BuildLoader = {
|
||||
def makeMulti[S <: Info, T](base: S => Option[T]) =
|
||||
new MultiHandler[S, T](base, None, Nil, _.uri, _.config.log)
|
||||
new BuildLoader(fail,
|
||||
s,
|
||||
config,
|
||||
makeMulti(base.resolver),
|
||||
makeMulti(base.builder),
|
||||
base.transformer,
|
||||
makeMulti(base.full),
|
||||
base.transformAll)
|
||||
}
|
||||
|
||||
def componentLoader: Loader = (info: LoadInfo) => {
|
||||
import info.{ config, staging, state, uri }
|
||||
|
|
@ -90,24 +149,25 @@ object BuildLoader {
|
|||
resolve <- cs.resolver(new ResolveInfo(uri, staging, config, state))
|
||||
base = resolve()
|
||||
build <- cs.builder(new BuildInfo(uri, base, config, state))
|
||||
} yield () => {
|
||||
val unit = build()
|
||||
cs.transformer(new TransformInfo(uri, base, unit, config, state))
|
||||
}
|
||||
} yield
|
||||
() => {
|
||||
val unit = build()
|
||||
cs.transformer(new TransformInfo(uri, base, unit, config, state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Defines the responsible for loading builds.
|
||||
*
|
||||
* @param fail A reporter for failures.
|
||||
* @param state The state.
|
||||
* @param config The current configuration for any build.
|
||||
* @param resolvers The structure responsible of mapping base directories.
|
||||
* @param builders The structure responsible of mapping to build units.
|
||||
* @param transformer An instance to modify the created build unit.
|
||||
* @param full The structure responsible of mapping to loaded build units.
|
||||
* @param transformAll A function specifying which builds units should be transformed.
|
||||
*/
|
||||
*
|
||||
* @param fail A reporter for failures.
|
||||
* @param state The state.
|
||||
* @param config The current configuration for any build.
|
||||
* @param resolvers The structure responsible of mapping base directories.
|
||||
* @param builders The structure responsible of mapping to build units.
|
||||
* @param transformer An instance to modify the created build unit.
|
||||
* @param full The structure responsible of mapping to loaded build units.
|
||||
* @param transformAll A function specifying which builds units should be transformed.
|
||||
*/
|
||||
final class BuildLoader(
|
||||
val fail: URI => Nothing,
|
||||
val state: State,
|
||||
|
|
@ -119,37 +179,43 @@ final class BuildLoader(
|
|||
val transformAll: TransformAll
|
||||
) {
|
||||
def addNonRoot(uri: URI, loaders: Components): BuildLoader =
|
||||
new BuildLoader(fail, state, config,
|
||||
new BuildLoader(
|
||||
fail,
|
||||
state,
|
||||
config,
|
||||
resolvers.addNonRoot(uri, loaders.resolver),
|
||||
builders.addNonRoot(uri, loaders.builder),
|
||||
seq(transformer, loaders.transformer),
|
||||
full.addNonRoot(uri, loaders.full),
|
||||
transformAll andThen loaders.transformAll)
|
||||
transformAll andThen loaders.transformAll
|
||||
)
|
||||
def setRoot(loaders: Components): BuildLoader =
|
||||
new BuildLoader(fail, state, config,
|
||||
new BuildLoader(
|
||||
fail,
|
||||
state,
|
||||
config,
|
||||
resolvers.setRoot(loaders.resolver),
|
||||
builders.setRoot(loaders.builder),
|
||||
seq(loaders.transformer, transformer),
|
||||
full.setRoot(loaders.full),
|
||||
loaders.transformAll andThen transformAll)
|
||||
loaders.transformAll andThen transformAll
|
||||
)
|
||||
def resetPluginDepth: BuildLoader = copyWithNewPM(config.pluginManagement.resetDepth)
|
||||
|
||||
def updatePluginManagement(overrides: Set[ModuleID]): BuildLoader =
|
||||
{
|
||||
val mgmt = config.pluginManagement
|
||||
copyWithNewPM(mgmt.copy(overrides = mgmt.overrides ++ overrides))
|
||||
}
|
||||
private[this] def copyWithNewPM(newpm: PluginManagement): BuildLoader =
|
||||
{
|
||||
val newConfig = config.copy(pluginManagement = newpm)
|
||||
new BuildLoader(fail, state, newConfig, resolvers, builders, transformer, full, transformAll)
|
||||
}
|
||||
def updatePluginManagement(overrides: Set[ModuleID]): BuildLoader = {
|
||||
val mgmt = config.pluginManagement
|
||||
copyWithNewPM(mgmt.copy(overrides = mgmt.overrides ++ overrides))
|
||||
}
|
||||
private[this] def copyWithNewPM(newpm: PluginManagement): BuildLoader = {
|
||||
val newConfig = config.copy(pluginManagement = newpm)
|
||||
new BuildLoader(fail, state, newConfig, resolvers, builders, transformer, full, transformAll)
|
||||
}
|
||||
|
||||
def components = new Components(resolvers.applyFun, builders.applyFun, transformer, full.applyFun, transformAll)
|
||||
def apply(uri: URI): BuildUnit =
|
||||
{
|
||||
val info = new LoadInfo(uri, config.stagingDirectory, config, state, components)
|
||||
val load = full(info) getOrElse fail(uri)
|
||||
load()
|
||||
}
|
||||
def components =
|
||||
new Components(resolvers.applyFun, builders.applyFun, transformer, full.applyFun, transformAll)
|
||||
def apply(uri: URI): BuildUnit = {
|
||||
val info = new LoadInfo(uri, config.stagingDirectory, config, state, components)
|
||||
val load = full(info) getOrElse fail(uri)
|
||||
load()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,22 +15,35 @@ import sbt.internal.util.{ Attributed, AttributeEntry, AttributeKey, AttributeMa
|
|||
import sbt.internal.util.Attributed.data
|
||||
import sbt.util.Logger
|
||||
|
||||
final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex, val streams: State => Streams, val delegates: Scope => Seq[Scope], val scopeLocal: ScopeLocal) {
|
||||
final class BuildStructure(val units: Map[URI, LoadedBuildUnit],
|
||||
val root: URI,
|
||||
val settings: Seq[Setting[_]],
|
||||
val data: Settings[Scope],
|
||||
val index: StructureIndex,
|
||||
val streams: State => Streams,
|
||||
val delegates: Scope => Seq[Scope],
|
||||
val scopeLocal: ScopeLocal) {
|
||||
val rootProject: URI => String = Load getRootProject units
|
||||
def allProjects: Seq[ResolvedProject] = units.values.flatMap(_.defined.values).toSeq
|
||||
def allProjects(build: URI): Seq[ResolvedProject] = units.get(build).toList.flatMap(_.defined.values)
|
||||
def allProjectRefs: Seq[ProjectRef] = units.toSeq flatMap { case (build, unit) => refs(build, unit.defined.values.toSeq) }
|
||||
def allProjects(build: URI): Seq[ResolvedProject] =
|
||||
units.get(build).toList.flatMap(_.defined.values)
|
||||
def allProjectRefs: Seq[ProjectRef] = units.toSeq flatMap {
|
||||
case (build, unit) => refs(build, unit.defined.values.toSeq)
|
||||
}
|
||||
def allProjectRefs(build: URI): Seq[ProjectRef] = refs(build, allProjects(build))
|
||||
val extra: BuildUtil[ResolvedProject] = BuildUtil(root, units, index.keyIndex, data)
|
||||
private[this] def refs(build: URI, projects: Seq[ResolvedProject]): Seq[ProjectRef] = projects.map { p => ProjectRef(build, p.id) }
|
||||
private[this] def refs(build: URI, projects: Seq[ResolvedProject]): Seq[ProjectRef] =
|
||||
projects.map { p =>
|
||||
ProjectRef(build, p.id)
|
||||
}
|
||||
}
|
||||
// information that is not original, but can be reconstructed from the rest of BuildStructure
|
||||
final class StructureIndex(
|
||||
val keyMap: Map[String, AttributeKey[_]],
|
||||
val taskToKey: Map[Task[_], ScopedKey[Task[_]]],
|
||||
val triggers: Triggers[Task],
|
||||
val keyIndex: KeyIndex,
|
||||
val aggregateKeyIndex: KeyIndex
|
||||
val keyMap: Map[String, AttributeKey[_]],
|
||||
val taskToKey: Map[Task[_], ScopedKey[Task[_]]],
|
||||
val triggers: Triggers[Task],
|
||||
val keyIndex: KeyIndex,
|
||||
val aggregateKeyIndex: KeyIndex
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -41,13 +54,20 @@ final class StructureIndex(
|
|||
* @param rootProjects The list of project IDs for the projects considered roots of this build.
|
||||
* The first root project is used as the default in several situations where a project is not otherwise selected.
|
||||
*/
|
||||
final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, ResolvedProject], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase {
|
||||
final class LoadedBuildUnit(val unit: BuildUnit,
|
||||
val defined: Map[String, ResolvedProject],
|
||||
val rootProjects: Seq[String],
|
||||
val buildSettings: Seq[Setting[_]])
|
||||
extends BuildUnitBase {
|
||||
|
||||
/**
|
||||
* The project to use as the default when one is not otherwise selected.
|
||||
* [[LocalRootProject]] resolves to this from within the same build.
|
||||
*/
|
||||
val root = rootProjects match {
|
||||
case Nil => throw new java.lang.AssertionError("assertion failed: No root projects defined for build unit " + unit)
|
||||
case Nil =>
|
||||
throw new java.lang.AssertionError(
|
||||
"assertion failed: No root projects defined for build unit " + unit)
|
||||
case Seq(root, _*) => root
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +78,8 @@ final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, Resolv
|
|||
* The classpath to use when compiling against this build unit's publicly visible code.
|
||||
* It includes build definition and plugin classes and classes for .sbt file statements and expressions.
|
||||
*/
|
||||
def classpath: Seq[File] = unit.definitions.target ++ unit.plugins.classpath ++ unit.definitions.dslDefinitions.classpath
|
||||
def classpath: Seq[File] =
|
||||
unit.definitions.target ++ unit.plugins.classpath ++ unit.definitions.dslDefinitions.classpath
|
||||
|
||||
/**
|
||||
* The class loader to use for this build unit's publicly visible code.
|
||||
|
|
@ -96,17 +117,18 @@ final class LoadedDefinitions(
|
|||
val dslDefinitions: DefinedSbtValues
|
||||
) {
|
||||
def this(
|
||||
base: File,
|
||||
target: Seq[File],
|
||||
loader: ClassLoader,
|
||||
builds: Seq[BuildDef],
|
||||
projects: Seq[Project],
|
||||
buildNames: Seq[String]
|
||||
base: File,
|
||||
target: Seq[File],
|
||||
loader: ClassLoader,
|
||||
builds: Seq[BuildDef],
|
||||
projects: Seq[Project],
|
||||
buildNames: Seq[String]
|
||||
) = this(base, target, loader, builds, projects, buildNames, DefinedSbtValues.empty)
|
||||
}
|
||||
|
||||
/** Auto-detected top-level modules (as in `object X`) of type `T` paired with their source names. */
|
||||
final class DetectedModules[T](val modules: Seq[(String, T)]) {
|
||||
|
||||
/**
|
||||
* The source names of the modules. This is "X" in `object X`, as opposed to the implementation class name "X$".
|
||||
* The names are returned in a stable order such that `names zip values` pairs a name with the actual module.
|
||||
|
|
@ -129,7 +151,9 @@ case class DetectedAutoPlugin(name: String, value: AutoPlugin, hasAutoImport: Bo
|
|||
*
|
||||
* @param builds The [[Build]]s detected in the build definition. This does not include the default [[Build]] that sbt creates if none is defined.
|
||||
*/
|
||||
final class DetectedPlugins(val autoPlugins: Seq[DetectedAutoPlugin], val builds: DetectedModules[BuildDef]) {
|
||||
final class DetectedPlugins(val autoPlugins: Seq[DetectedAutoPlugin],
|
||||
val builds: DetectedModules[BuildDef]) {
|
||||
|
||||
/**
|
||||
* Sequence of import expressions for the build definition.
|
||||
* This includes the names of the [[BuildDef]], and [[DetectedAutoPlugin]] modules,
|
||||
|
|
@ -141,23 +165,24 @@ final class DetectedPlugins(val autoPlugins: Seq[DetectedAutoPlugin], val builds
|
|||
BuildUtil.importNamesRoot(autoPlugins.map(_.name).filter(nonTopLevelPlugin))
|
||||
|
||||
private[this] lazy val (autoPluginAutoImports, topLevelAutoPluginAutoImports) =
|
||||
autoPlugins.flatMap {
|
||||
case DetectedAutoPlugin(name, ap, hasAutoImport) =>
|
||||
if (hasAutoImport) Some(name)
|
||||
else None
|
||||
}.partition(nonTopLevelPlugin)
|
||||
autoPlugins
|
||||
.flatMap {
|
||||
case DetectedAutoPlugin(name, ap, hasAutoImport) =>
|
||||
if (hasAutoImport) Some(name)
|
||||
else None
|
||||
}
|
||||
.partition(nonTopLevelPlugin)
|
||||
|
||||
/** Selects the right [[AutoPlugin]]s from a [[Project]]. */
|
||||
def deducePluginsFromProject(p: Project, log: Logger): Seq[AutoPlugin] =
|
||||
{
|
||||
val ps0 = p.plugins
|
||||
val allDetected = autoPlugins.toList map { _.value }
|
||||
val detected = p match {
|
||||
case _: GeneratedRootProject => allDetected filterNot { _ == sbt.plugins.IvyPlugin }
|
||||
case _ => allDetected
|
||||
}
|
||||
Plugins.deducer(detected)(ps0, log)
|
||||
def deducePluginsFromProject(p: Project, log: Logger): Seq[AutoPlugin] = {
|
||||
val ps0 = p.plugins
|
||||
val allDetected = autoPlugins.toList map { _.value }
|
||||
val detected = p match {
|
||||
case _: GeneratedRootProject => allDetected filterNot { _ == sbt.plugins.IvyPlugin }
|
||||
case _ => allDetected
|
||||
}
|
||||
Plugins.deducer(detected)(ps0, log)
|
||||
}
|
||||
|
||||
private[this] def autoImports(pluginNames: Seq[String]) = pluginNames.map(_ + ".autoImport")
|
||||
|
||||
|
|
@ -172,7 +197,10 @@ final class DetectedPlugins(val autoPlugins: Seq[DetectedAutoPlugin], val builds
|
|||
* @param loader The class loader for the build definition project, notably excluding classes used for .sbt files.
|
||||
* @param detected Auto-detected modules in the build definition.
|
||||
*/
|
||||
final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader: ClassLoader, val detected: DetectedPlugins) {
|
||||
final class LoadedPlugins(val base: File,
|
||||
val pluginData: PluginData,
|
||||
val loader: ClassLoader,
|
||||
val detected: DetectedPlugins) {
|
||||
def fullClasspath: Seq[Attributed[File]] = pluginData.classpath
|
||||
def classpath = data(fullClasspath)
|
||||
}
|
||||
|
|
@ -183,21 +211,32 @@ final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader
|
|||
* @param localBase The working location of the build on the filesystem.
|
||||
* For local URIs, this is the same as `uri`, but for remote URIs, this is the local copy or workspace allocated for the build.
|
||||
*/
|
||||
final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins) {
|
||||
override def toString = if (uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase + ")")
|
||||
final class BuildUnit(val uri: URI,
|
||||
val localBase: File,
|
||||
val definitions: LoadedDefinitions,
|
||||
val plugins: LoadedPlugins) {
|
||||
override def toString =
|
||||
if (uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase + ")")
|
||||
}
|
||||
|
||||
final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit]) {
|
||||
BuildUtil.checkCycles(units)
|
||||
def allProjectRefs: Seq[(ProjectRef, ResolvedProject)] = for ((uri, unit) <- units.toSeq; (id, proj) <- unit.defined) yield ProjectRef(uri, id) -> proj
|
||||
def extra(data: Settings[Scope])(keyIndex: KeyIndex): BuildUtil[ResolvedProject] = BuildUtil(root, units, keyIndex, data)
|
||||
def allProjectRefs: Seq[(ProjectRef, ResolvedProject)] =
|
||||
for ((uri, unit) <- units.toSeq; (id, proj) <- unit.defined) yield ProjectRef(uri, id) -> proj
|
||||
def extra(data: Settings[Scope])(keyIndex: KeyIndex): BuildUtil[ResolvedProject] =
|
||||
BuildUtil(root, units, keyIndex, data)
|
||||
|
||||
private[sbt] def autos = GroupedAutoPlugins(units)
|
||||
}
|
||||
final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit])
|
||||
sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] }
|
||||
final class PartBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase {
|
||||
def resolve(f: Project => ResolvedProject): LoadedBuildUnit = new LoadedBuildUnit(unit, defined mapValues f toMap, rootProjects, buildSettings)
|
||||
final class PartBuildUnit(val unit: BuildUnit,
|
||||
val defined: Map[String, Project],
|
||||
val rootProjects: Seq[String],
|
||||
val buildSettings: Seq[Setting[_]])
|
||||
extends BuildUnitBase {
|
||||
def resolve(f: Project => ResolvedProject): LoadedBuildUnit =
|
||||
new LoadedBuildUnit(unit, defined mapValues f toMap, rootProjects, buildSettings)
|
||||
def resolveRefs(f: ProjectReference => ProjectRef): LoadedBuildUnit = resolve(_ resolve f)
|
||||
}
|
||||
|
||||
|
|
@ -208,43 +247,58 @@ object BuildStreams {
|
|||
final val BuildUnitPath = "$build"
|
||||
final val StreamsDirectory = "streams"
|
||||
|
||||
def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope]): State => Streams = s => {
|
||||
implicit val isoString: sjsonnew.IsoString[scala.json.ast.unsafe.JValue] = sjsonnew.IsoString.iso(sjsonnew.support.scalajson.unsafe.CompactPrinter.apply, sjsonnew.support.scalajson.unsafe.Parser.parseUnsafe)
|
||||
def mkStreams(units: Map[URI, LoadedBuildUnit],
|
||||
root: URI,
|
||||
data: Settings[Scope]): State => Streams = s => {
|
||||
implicit val isoString: sjsonnew.IsoString[scala.json.ast.unsafe.JValue] =
|
||||
sjsonnew.IsoString.iso(sjsonnew.support.scalajson.unsafe.CompactPrinter.apply,
|
||||
sjsonnew.support.scalajson.unsafe.Parser.parseUnsafe)
|
||||
(s get Keys.stateStreams) getOrElse {
|
||||
std.Streams(path(units, root, data), displayFull, LogManager.construct(data, s), sjsonnew.support.scalajson.unsafe.Converter)
|
||||
std.Streams(path(units, root, data),
|
||||
displayFull,
|
||||
LogManager.construct(data, s),
|
||||
sjsonnew.support.scalajson.unsafe.Converter)
|
||||
}
|
||||
}
|
||||
|
||||
def path(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope])(scoped: ScopedKey[_]): File =
|
||||
def path(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope])(
|
||||
scoped: ScopedKey[_]): File =
|
||||
resolvePath(projectPath(units, root, scoped, data), nonProjectPath(scoped))
|
||||
|
||||
def resolvePath(base: File, components: Seq[String]): File =
|
||||
(base /: components)((b, p) => new File(b, p))
|
||||
|
||||
def pathComponent[T](axis: ScopeAxis[T], scoped: ScopedKey[_], label: String)(show: T => String): String =
|
||||
def pathComponent[T](axis: ScopeAxis[T], scoped: ScopedKey[_], label: String)(
|
||||
show: T => String): String =
|
||||
axis match {
|
||||
case Global => GlobalPath
|
||||
case This => sys.error("Unresolved This reference for " + label + " in " + displayFull(scoped))
|
||||
case Global => GlobalPath
|
||||
case This =>
|
||||
sys.error("Unresolved This reference for " + label + " in " + displayFull(scoped))
|
||||
case Select(t) => show(t)
|
||||
}
|
||||
def nonProjectPath[T](scoped: ScopedKey[T]): Seq[String] =
|
||||
{
|
||||
val scope = scoped.scope
|
||||
pathComponent(scope.config, scoped, "config")(_.name) ::
|
||||
pathComponent(scope.task, scoped, "task")(_.label) ::
|
||||
pathComponent(scope.extra, scoped, "extra")(showAMap) ::
|
||||
scoped.key.label ::
|
||||
Nil
|
||||
}
|
||||
def nonProjectPath[T](scoped: ScopedKey[T]): Seq[String] = {
|
||||
val scope = scoped.scope
|
||||
pathComponent(scope.config, scoped, "config")(_.name) ::
|
||||
pathComponent(scope.task, scoped, "task")(_.label) ::
|
||||
pathComponent(scope.extra, scoped, "extra")(showAMap) ::
|
||||
scoped.key.label ::
|
||||
Nil
|
||||
}
|
||||
def showAMap(a: AttributeMap): String =
|
||||
a.entries.toSeq.sortBy(_.key.label).map { case AttributeEntry(key, value) => key.label + "=" + value.toString } mkString (" ")
|
||||
def projectPath(units: Map[URI, LoadedBuildUnit], root: URI, scoped: ScopedKey[_], data: Settings[Scope]): File =
|
||||
a.entries.toSeq.sortBy(_.key.label).map {
|
||||
case AttributeEntry(key, value) => key.label + "=" + value.toString
|
||||
} mkString (" ")
|
||||
def projectPath(units: Map[URI, LoadedBuildUnit],
|
||||
root: URI,
|
||||
scoped: ScopedKey[_],
|
||||
data: Settings[Scope]): File =
|
||||
scoped.scope.project match {
|
||||
case Global => refTarget(GlobalScope, units(root).localBase, data) / GlobalPath
|
||||
case Select(br @ BuildRef(uri)) => refTarget(br, units(uri).localBase, data) / BuildUnitPath
|
||||
case Select(pr @ ProjectRef(uri, id)) => refTarget(pr, units(uri).defined(id).base, data)
|
||||
case Select(pr) => sys.error("Unresolved project reference (" + pr + ") in " + displayFull(scoped))
|
||||
case This => sys.error("Unresolved project reference (This) in " + displayFull(scoped))
|
||||
case Select(pr) =>
|
||||
sys.error("Unresolved project reference (" + pr + ") in " + displayFull(scoped))
|
||||
case This => sys.error("Unresolved project reference (This) in " + displayFull(scoped))
|
||||
}
|
||||
|
||||
def refTarget(ref: ResolvedReference, fallbackBase: File, data: Settings[Scope]): File =
|
||||
|
|
|
|||
|
|
@ -41,30 +41,33 @@ final class BuildUtil[Proj](
|
|||
refOpt => configurations(projectForAxis(refOpt)).map(_.name)
|
||||
}
|
||||
object BuildUtil {
|
||||
def apply(root: URI, units: Map[URI, LoadedBuildUnit], keyIndex: KeyIndex, data: Settings[Scope]): BuildUtil[ResolvedProject] =
|
||||
{
|
||||
val getp = (build: URI, project: String) => Load.getProject(units, build, project)
|
||||
val configs = (_: ResolvedProject).configurations.map(c => ConfigKey(c.name))
|
||||
val aggregates = aggregationRelation(units)
|
||||
new BuildUtil(keyIndex, data, root, Load getRootProject units, getp, configs, aggregates)
|
||||
}
|
||||
def apply(root: URI,
|
||||
units: Map[URI, LoadedBuildUnit],
|
||||
keyIndex: KeyIndex,
|
||||
data: Settings[Scope]): BuildUtil[ResolvedProject] = {
|
||||
val getp = (build: URI, project: String) => Load.getProject(units, build, project)
|
||||
val configs = (_: ResolvedProject).configurations.map(c => ConfigKey(c.name))
|
||||
val aggregates = aggregationRelation(units)
|
||||
new BuildUtil(keyIndex, data, root, Load getRootProject units, getp, configs, aggregates)
|
||||
}
|
||||
|
||||
def dependencies(units: Map[URI, LoadedBuildUnit]): BuildDependencies =
|
||||
{
|
||||
import scala.collection.mutable
|
||||
val agg = new mutable.HashMap[ProjectRef, Seq[ProjectRef]]
|
||||
val cp = new mutable.HashMap[ProjectRef, Seq[ClasspathDep[ProjectRef]]]
|
||||
for (lbu <- units.values; rp <- lbu.defined.values) {
|
||||
val ref = ProjectRef(lbu.unit.uri, rp.id)
|
||||
cp(ref) = rp.dependencies
|
||||
agg(ref) = rp.aggregate
|
||||
}
|
||||
BuildDependencies(cp.toMap, agg.toMap)
|
||||
def dependencies(units: Map[URI, LoadedBuildUnit]): BuildDependencies = {
|
||||
import scala.collection.mutable
|
||||
val agg = new mutable.HashMap[ProjectRef, Seq[ProjectRef]]
|
||||
val cp = new mutable.HashMap[ProjectRef, Seq[ClasspathDep[ProjectRef]]]
|
||||
for (lbu <- units.values; rp <- lbu.defined.values) {
|
||||
val ref = ProjectRef(lbu.unit.uri, rp.id)
|
||||
cp(ref) = rp.dependencies
|
||||
agg(ref) = rp.aggregate
|
||||
}
|
||||
BuildDependencies(cp.toMap, agg.toMap)
|
||||
}
|
||||
|
||||
def checkCycles(units: Map[URI, LoadedBuildUnit]): Unit = {
|
||||
def getRef(pref: ProjectRef) = units(pref.build).defined(pref.project)
|
||||
def deps(proj: ResolvedProject)(base: ResolvedProject => Seq[ProjectRef]): Seq[ResolvedProject] = Dag.topologicalSort(proj)(p => base(p) map getRef)
|
||||
def deps(proj: ResolvedProject)(
|
||||
base: ResolvedProject => Seq[ProjectRef]): Seq[ResolvedProject] =
|
||||
Dag.topologicalSort(proj)(p => base(p) map getRef)
|
||||
// check for cycles
|
||||
for ((_, lbu) <- units; proj <- lbu.defined.values) {
|
||||
deps(proj)(_.dependencies.map(_.project))
|
||||
|
|
@ -72,15 +75,18 @@ object BuildUtil {
|
|||
}
|
||||
}
|
||||
|
||||
def baseImports: Seq[String] = "import _root_.scala.xml.{TopScope=>$scope}" :: "import _root_.sbt._" :: "import _root_.sbt.Keys._" :: Nil
|
||||
def baseImports: Seq[String] =
|
||||
"import _root_.scala.xml.{TopScope=>$scope}" :: "import _root_.sbt._" :: "import _root_.sbt.Keys._" :: Nil
|
||||
|
||||
def getImports(unit: BuildUnit): Seq[String] = unit.plugins.detected.imports ++ unit.definitions.dslDefinitions.imports
|
||||
def getImports(unit: BuildUnit): Seq[String] =
|
||||
unit.plugins.detected.imports ++ unit.definitions.dslDefinitions.imports
|
||||
|
||||
/** `import sbt._, Keys._`, and wildcard import `._` for all names. */
|
||||
def getImports(names: Seq[String]): Seq[String] = baseImports ++ importAllRoot(names)
|
||||
|
||||
/** Import just the names. */
|
||||
def importNames(names: Seq[String]): Seq[String] = if (names.isEmpty) Nil else names.mkString("import ", ", ", "") :: Nil
|
||||
def importNames(names: Seq[String]): Seq[String] =
|
||||
if (names.isEmpty) Nil else names.mkString("import ", ", ", "") :: Nil
|
||||
|
||||
/** Prepend `_root_` and import just the names. */
|
||||
def importNamesRoot(names: Seq[String]): Seq[String] = importNames(names map rootedName)
|
||||
|
|
@ -90,15 +96,14 @@ object BuildUtil {
|
|||
def importAllRoot(values: Seq[String]): Seq[String] = importAll(values map rootedName)
|
||||
def rootedName(s: String): String = if (s contains '.') "_root_." + s else s
|
||||
|
||||
def aggregationRelation(units: Map[URI, LoadedBuildUnit]): Relation[ProjectRef, ProjectRef] =
|
||||
{
|
||||
val depPairs =
|
||||
for {
|
||||
(uri, unit) <- units.toIterable // don't lose this toIterable, doing so breaks actions/cross-multiproject & actions/update-state-fail
|
||||
project <- unit.defined.values
|
||||
ref = ProjectRef(uri, project.id)
|
||||
agg <- project.aggregate
|
||||
} yield (ref, agg)
|
||||
Relation.empty ++ depPairs
|
||||
}
|
||||
def aggregationRelation(units: Map[URI, LoadedBuildUnit]): Relation[ProjectRef, ProjectRef] = {
|
||||
val depPairs =
|
||||
for {
|
||||
(uri, unit) <- units.toIterable // don't lose this toIterable, doing so breaks actions/cross-multiproject & actions/update-state-fail
|
||||
project <- unit.defined.values
|
||||
ref = ProjectRef(uri, project.id)
|
||||
agg <- project.aggregate
|
||||
} yield (ref, agg)
|
||||
Relation.empty ++ depPairs
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,155 +36,148 @@ private[sbt] final class CommandExchange {
|
|||
}
|
||||
|
||||
// periodically move all messages from all the channels
|
||||
@tailrec def blockUntilNextExec: Exec =
|
||||
{
|
||||
@tailrec def slurpMessages(): Unit =
|
||||
(((None: Option[Exec]) /: channels) { _ orElse _.poll }) match {
|
||||
case Some(x) =>
|
||||
commandQueue.add(x)
|
||||
slurpMessages
|
||||
case _ => ()
|
||||
}
|
||||
slurpMessages()
|
||||
Option(commandQueue.poll) match {
|
||||
case Some(x) => x
|
||||
case _ =>
|
||||
Thread.sleep(50)
|
||||
blockUntilNextExec
|
||||
@tailrec def blockUntilNextExec: Exec = {
|
||||
@tailrec def slurpMessages(): Unit =
|
||||
(((None: Option[Exec]) /: channels) { _ orElse _.poll }) match {
|
||||
case Some(x) =>
|
||||
commandQueue.add(x)
|
||||
slurpMessages
|
||||
case _ => ()
|
||||
}
|
||||
slurpMessages()
|
||||
Option(commandQueue.poll) match {
|
||||
case Some(x) => x
|
||||
case _ =>
|
||||
Thread.sleep(50)
|
||||
blockUntilNextExec
|
||||
}
|
||||
}
|
||||
|
||||
def run(s: State): State =
|
||||
{
|
||||
consoleChannel match {
|
||||
case Some(x) => // do nothing
|
||||
case _ =>
|
||||
val x = new ConsoleChannel("console0")
|
||||
consoleChannel = Some(x)
|
||||
subscribe(x)
|
||||
}
|
||||
runServer(s)
|
||||
def run(s: State): State = {
|
||||
consoleChannel match {
|
||||
case Some(x) => // do nothing
|
||||
case _ =>
|
||||
val x = new ConsoleChannel("console0")
|
||||
consoleChannel = Some(x)
|
||||
subscribe(x)
|
||||
}
|
||||
runServer(s)
|
||||
}
|
||||
|
||||
private def newChannelName: String = s"channel-${nextChannelId.incrementAndGet()}"
|
||||
|
||||
private def runServer(s: State): State =
|
||||
{
|
||||
val port = (s get serverPort) match {
|
||||
case Some(x) => x
|
||||
case None => 5001
|
||||
}
|
||||
def onIncomingSocket(socket: Socket): Unit =
|
||||
{
|
||||
s.log.info(s"new client connected from: ${socket.getPort}")
|
||||
val channel = new NetworkChannel(newChannelName, socket, Project structure s)
|
||||
subscribe(channel)
|
||||
channel.publishEventMessage(ChannelAcceptedEvent(channel.name))
|
||||
private def runServer(s: State): State = {
|
||||
val port = (s get serverPort) match {
|
||||
case Some(x) => x
|
||||
case None => 5001
|
||||
}
|
||||
def onIncomingSocket(socket: Socket): Unit = {
|
||||
s.log.info(s"new client connected from: ${socket.getPort}")
|
||||
val channel = new NetworkChannel(newChannelName, socket, Project structure s)
|
||||
subscribe(channel)
|
||||
channel.publishEventMessage(ChannelAcceptedEvent(channel.name))
|
||||
}
|
||||
server match {
|
||||
case Some(x) => // do nothing
|
||||
case _ =>
|
||||
val x = Server.start("127.0.0.1", port, onIncomingSocket, s.log)
|
||||
Await.ready(x.ready, Duration("10s"))
|
||||
x.ready.value match {
|
||||
case Some(Success(_)) =>
|
||||
case Some(Failure(e)) =>
|
||||
s.log.error(e.toString)
|
||||
case None => // this won't happen because we awaited
|
||||
}
|
||||
server match {
|
||||
case Some(x) => // do nothing
|
||||
case _ =>
|
||||
val x = Server.start("127.0.0.1", port, onIncomingSocket, s.log)
|
||||
Await.ready(x.ready, Duration("10s"))
|
||||
x.ready.value match {
|
||||
case Some(Success(_)) =>
|
||||
case Some(Failure(e)) =>
|
||||
s.log.error(e.toString)
|
||||
case None => // this won't happen because we awaited
|
||||
}
|
||||
server = Some(x)
|
||||
}
|
||||
s
|
||||
server = Some(x)
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
def shutdown(): Unit =
|
||||
{
|
||||
channels foreach { c =>
|
||||
c.shutdown()
|
||||
}
|
||||
// interrupt and kill the thread
|
||||
server.foreach(_.shutdown())
|
||||
server = None
|
||||
def shutdown(): Unit = {
|
||||
channels foreach { c =>
|
||||
c.shutdown()
|
||||
}
|
||||
// interrupt and kill the thread
|
||||
server.foreach(_.shutdown())
|
||||
server = None
|
||||
}
|
||||
|
||||
def publishEvent[A: JsonFormat](event: A): Unit =
|
||||
{
|
||||
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
|
||||
val bytes = Serialization.serializeEvent(event)
|
||||
event match {
|
||||
case entry: StringEvent =>
|
||||
channels.foreach {
|
||||
case c: ConsoleChannel =>
|
||||
if (entry.channelName.isEmpty || entry.channelName == Some(c.name)) {
|
||||
c.publishEvent(event)
|
||||
}
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
if (entry.channelName == Some(c.name)) {
|
||||
c.publishBytes(bytes)
|
||||
}
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
channels.foreach {
|
||||
case c: ConsoleChannel =>
|
||||
def publishEvent[A: JsonFormat](event: A): Unit = {
|
||||
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
|
||||
val bytes = Serialization.serializeEvent(event)
|
||||
event match {
|
||||
case entry: StringEvent =>
|
||||
channels.foreach {
|
||||
case c: ConsoleChannel =>
|
||||
if (entry.channelName.isEmpty || entry.channelName == Some(c.name)) {
|
||||
c.publishEvent(event)
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
}
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
if (entry.channelName == Some(c.name)) {
|
||||
c.publishBytes(bytes)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
}
|
||||
toDel.toList match {
|
||||
case Nil => // do nothing
|
||||
case xs =>
|
||||
lock.synchronized {
|
||||
channelBuffer --= xs
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
channels.foreach {
|
||||
case c: ConsoleChannel =>
|
||||
c.publishEvent(event)
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
c.publishBytes(bytes)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
}
|
||||
toDel.toList match {
|
||||
case Nil => // do nothing
|
||||
case xs =>
|
||||
lock.synchronized {
|
||||
channelBuffer --= xs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fanout publishEvent
|
||||
def publishEventMessage(event: EventMessage): Unit =
|
||||
{
|
||||
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
|
||||
event match {
|
||||
// Special treatment for ConsolePromptEvent since it's hand coded without codec.
|
||||
case e: ConsolePromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEventMessage(e)
|
||||
}
|
||||
case e: ConsoleUnpromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEventMessage(e)
|
||||
}
|
||||
case _ =>
|
||||
// TODO do not do this on the calling thread
|
||||
val bytes = Serialization.serializeEventMessage(event)
|
||||
channels.foreach {
|
||||
case c: ConsoleChannel =>
|
||||
c.publishEventMessage(event)
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
c.publishBytes(bytes)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
}
|
||||
toDel.toList match {
|
||||
case Nil => // do nothing
|
||||
case xs =>
|
||||
lock.synchronized {
|
||||
channelBuffer --= xs
|
||||
}
|
||||
}
|
||||
def publishEventMessage(event: EventMessage): Unit = {
|
||||
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
|
||||
event match {
|
||||
// Special treatment for ConsolePromptEvent since it's hand coded without codec.
|
||||
case e: ConsolePromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEventMessage(e)
|
||||
}
|
||||
case e: ConsoleUnpromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEventMessage(e)
|
||||
}
|
||||
case _ =>
|
||||
// TODO do not do this on the calling thread
|
||||
val bytes = Serialization.serializeEventMessage(event)
|
||||
channels.foreach {
|
||||
case c: ConsoleChannel =>
|
||||
c.publishEventMessage(event)
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
c.publishBytes(bytes)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
}
|
||||
toDel.toList match {
|
||||
case Nil => // do nothing
|
||||
case xs =>
|
||||
lock.synchronized {
|
||||
channelBuffer --= xs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package internal
|
|||
import sbt.io.Path
|
||||
|
||||
object CommandStrings {
|
||||
|
||||
/** The prefix used to identify a request to execute the remaining input on source changes.*/
|
||||
val AboutCommand = "about"
|
||||
val TasksCommand = "tasks"
|
||||
|
|
@ -18,7 +19,8 @@ object CommandStrings {
|
|||
val BootCommand = "boot"
|
||||
|
||||
val EvalCommand = "eval"
|
||||
val evalBrief = (s"$EvalCommand <expression>", "Evaluates a Scala expression and prints the result and type.")
|
||||
val evalBrief =
|
||||
(s"$EvalCommand <expression>", "Evaluates a Scala expression and prints the result and type.")
|
||||
val evalDetailed =
|
||||
s"""$EvalCommand <expression>
|
||||
|
||||
|
|
@ -55,7 +57,8 @@ $ShowCommand <task>
|
|||
val ExportCommand = "export"
|
||||
val ExportStream = "export"
|
||||
|
||||
val lastGrepBrief = (LastGrepCommand, "Shows lines from the last output for 'key' that match 'pattern'.")
|
||||
val lastGrepBrief =
|
||||
(LastGrepCommand, "Shows lines from the last output for 'key' that match 'pattern'.")
|
||||
val lastGrepDetailed =
|
||||
s"""$LastGrepCommand <pattern>
|
||||
Displays lines from the logging of previous commands that match `pattern`.
|
||||
|
|
@ -66,7 +69,8 @@ $LastGrepCommand <pattern> [key]
|
|||
<pattern> is a regular expression interpreted by java.util.Pattern. Matching text is highlighted (when highlighting is supported and enabled).
|
||||
See also '$LastCommand'."""
|
||||
|
||||
val lastBrief = (LastCommand, "Displays output from a previous command or the output from a specific task.")
|
||||
val lastBrief =
|
||||
(LastCommand, "Displays output from a previous command or the output from a specific task.")
|
||||
val lastDetailed =
|
||||
s"""$LastCommand
|
||||
Prints the logging for the previous command, typically at a more verbose level.
|
||||
|
|
@ -76,7 +80,8 @@ $LastCommand <key>
|
|||
|
||||
See also '$LastGrepCommand'."""
|
||||
|
||||
val exportBrief = (s"$ExportCommand <tasks>+", "Executes tasks and displays the equivalent command lines.")
|
||||
val exportBrief =
|
||||
(s"$ExportCommand <tasks>+", "Executes tasks and displays the equivalent command lines.")
|
||||
val exportDetailed =
|
||||
s"""$ExportCommand [--last] <task>+
|
||||
Runs the specified tasks and prints the equivalent command lines or other exportable information for those runs.
|
||||
|
|
@ -92,7 +97,9 @@ $LastCommand <key>
|
|||
"""
|
||||
|
||||
val InspectCommand = "inspect"
|
||||
val inspectBrief = (s"$InspectCommand [uses|tree|definitions] <key>", "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.")
|
||||
val inspectBrief =
|
||||
(s"$InspectCommand [uses|tree|definitions] <key>",
|
||||
"Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.")
|
||||
val inspectDetailed = s"""
|
||||
|$InspectCommand <key>
|
||||
|
|
||||
|
|
@ -131,7 +138,8 @@ $LastCommand <key>
|
|||
""".stripMargin.trim
|
||||
|
||||
val SetCommand = "set"
|
||||
val setBrief = (s"$SetCommand [every] <setting>", "Evaluates a Setting and applies it to the current project.")
|
||||
val setBrief = (s"$SetCommand [every] <setting>",
|
||||
"Evaluates a Setting and applies it to the current project.")
|
||||
val setDetailed =
|
||||
s"""$SetCommand [every] <setting-expression>
|
||||
|
||||
|
|
@ -156,7 +164,8 @@ $LastCommand <key>
|
|||
|
||||
def settingsPreamble = commonPreamble("settings")
|
||||
|
||||
def tasksPreamble = commonPreamble("tasks") + """
|
||||
def tasksPreamble =
|
||||
commonPreamble("tasks") + """
|
||||
Tasks produce values. Use the 'show' command to run the task and print the resulting value."""
|
||||
|
||||
def commonPreamble(label: String) = s"""
|
||||
|
|
@ -200,7 +209,8 @@ $label
|
|||
def aboutBrief = "Displays basic information about sbt and the build."
|
||||
def aboutDetailed = aboutBrief
|
||||
|
||||
def projectBrief = (ProjectCommand, "Displays the current project or changes to the provided `project`.")
|
||||
def projectBrief =
|
||||
(ProjectCommand, "Displays the current project or changes to the provided `project`.")
|
||||
def projectDetailed =
|
||||
s"""$ProjectCommand
|
||||
|
||||
|
|
@ -233,7 +243,8 @@ $ProjectCommand ..
|
|||
Use n+1 dots to change to the nth parent.
|
||||
For example, 'project ....' is equivalent to three consecutive 'project ..' commands."""
|
||||
|
||||
def projectsBrief = "Lists the names of available projects or temporarily adds/removes extra builds to the session."
|
||||
def projectsBrief =
|
||||
"Lists the names of available projects or temporarily adds/removes extra builds to the session."
|
||||
def projectsDetailed =
|
||||
s"""$ProjectsCommand
|
||||
List the names of available builds and the projects defined in those builds.
|
||||
|
|
@ -263,7 +274,9 @@ $ProjectsCommand remove <URI>+
|
|||
|
||||
def LoadProjectImpl = "loadp"
|
||||
def LoadProject = "reload"
|
||||
def LoadProjectBrief = (LoadProject, "(Re)loads the current project or changes to plugins project or returns from it.")
|
||||
def LoadProjectBrief =
|
||||
(LoadProject,
|
||||
"(Re)loads the current project or changes to plugins project or returns from it.")
|
||||
def LoadProjectDetailed =
|
||||
s"""$LoadProject
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import sbt.util.Logger
|
|||
import sbt.internal.inc.{ ClasspathOptionsUtil, ScalaInstance }
|
||||
|
||||
object ConsoleProject {
|
||||
def apply(state: State, extra: String, cleanupCommands: String = "", options: Seq[String] = Nil)(implicit log: Logger): Unit = {
|
||||
def apply(state: State, extra: String, cleanupCommands: String = "", options: Seq[String] = Nil)(
|
||||
implicit log: Logger): Unit = {
|
||||
val extracted = Project extract state
|
||||
val cpImports = new Imports(extracted, state)
|
||||
val bindings = ("currentState" -> state) :: ("extracted" -> extracted) :: ("cpHelpers" -> cpImports) :: Nil
|
||||
|
|
@ -19,19 +20,28 @@ object ConsoleProject {
|
|||
ScalaInstance(scalaProvider.version, scalaProvider.launcher)
|
||||
}
|
||||
val sourcesModule = extracted.get(Keys.scalaCompilerBridgeSource)
|
||||
val compiler = Compiler.scalaCompiler(scalaInstance, ClasspathOptionsUtil.repl, None, ivyConf, sourcesModule)(state.configuration, log)
|
||||
val compiler = Compiler.scalaCompiler(scalaInstance,
|
||||
ClasspathOptionsUtil.repl,
|
||||
None,
|
||||
ivyConf,
|
||||
sourcesModule)(state.configuration, log)
|
||||
val imports = BuildUtil.getImports(unit.unit) ++ BuildUtil.importAll(bindings.map(_._1))
|
||||
val importString = imports.mkString("", ";\n", ";\n\n")
|
||||
val initCommands = importString + extra
|
||||
// TODO - Hook up dsl classpath correctly...
|
||||
(new Console(compiler))(
|
||||
unit.classpath, options, initCommands, cleanupCommands
|
||||
unit.classpath,
|
||||
options,
|
||||
initCommands,
|
||||
cleanupCommands
|
||||
)(Some(unit.loader), bindings)
|
||||
}
|
||||
|
||||
/** Conveniences for consoleProject that shouldn't normally be used for builds. */
|
||||
final class Imports private[sbt] (extracted: Extracted, state: State) {
|
||||
import extracted._
|
||||
implicit def taskKeyEvaluate[T](t: TaskKey[T]): Evaluate[T] = new Evaluate(runTask(t, state)._2)
|
||||
implicit def taskKeyEvaluate[T](t: TaskKey[T]): Evaluate[T] =
|
||||
new Evaluate(runTask(t, state)._2)
|
||||
implicit def settingKeyEvaluate[T](s: SettingKey[T]): Evaluate[T] = new Evaluate(get(s))
|
||||
}
|
||||
final class Evaluate[T] private[sbt] (val eval: T)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ private[sbt] abstract class BackgroundJob {
|
|||
}
|
||||
|
||||
private[sbt] abstract class AbstractJobHandle extends JobHandle {
|
||||
override def toString = s"JobHandle(${id}, ${humanReadableName}, ${Def.showFullKey.show(spawningTask)})"
|
||||
override def toString =
|
||||
s"JobHandle(${id}, ${humanReadableName}, ${Def.showFullKey.show(spawningTask)})"
|
||||
}
|
||||
|
||||
private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobService {
|
||||
|
|
@ -59,8 +60,11 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
|||
override def jobs: Vector[ThreadJobHandle] = jobSet.toVector
|
||||
|
||||
final class ThreadJobHandle(
|
||||
override val id: Long, override val spawningTask: ScopedKey[_],
|
||||
val logger: ManagedLogger, val workingDirectory: File, val job: BackgroundJob
|
||||
override val id: Long,
|
||||
override val spawningTask: ScopedKey[_],
|
||||
val logger: ManagedLogger,
|
||||
val workingDirectory: File,
|
||||
val job: BackgroundJob
|
||||
) extends AbstractJobHandle {
|
||||
def humanReadableName: String = job.humanReadableName
|
||||
// EC for onStop handler below
|
||||
|
|
@ -89,7 +93,9 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
|||
|
||||
protected def makeContext(id: Long, spawningTask: ScopedKey[_], state: State): ManagedLogger
|
||||
|
||||
def doRunInBackground(spawningTask: ScopedKey[_], state: State, start: (Logger, File) => BackgroundJob): JobHandle = {
|
||||
def doRunInBackground(spawningTask: ScopedKey[_],
|
||||
state: State,
|
||||
start: (Logger, File) => BackgroundJob): JobHandle = {
|
||||
val id = nextId.getAndIncrement()
|
||||
val logger = makeContext(id, spawningTask, state)
|
||||
val workingDir = serviceTempDir / s"job-$id"
|
||||
|
|
@ -105,7 +111,8 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
|||
job
|
||||
}
|
||||
|
||||
override def runInBackground(spawningTask: ScopedKey[_], state: State)(start: (Logger, File) => Unit): JobHandle = {
|
||||
override def runInBackground(spawningTask: ScopedKey[_], state: State)(
|
||||
start: (Logger, File) => Unit): JobHandle = {
|
||||
pool.run(this, spawningTask, state)(start)
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +133,9 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
|||
private def withHandle(job: JobHandle)(f: ThreadJobHandle => Unit): Unit = job match {
|
||||
case handle: ThreadJobHandle @unchecked => f(handle)
|
||||
case dead: DeadHandle @unchecked => () // nothing to stop or wait for
|
||||
case other => sys.error(s"BackgroundJobHandle does not originate with the current BackgroundJobService: $other")
|
||||
case other =>
|
||||
sys.error(
|
||||
s"BackgroundJobHandle does not originate with the current BackgroundJobService: $other")
|
||||
}
|
||||
|
||||
override def stop(job: JobHandle): Unit =
|
||||
|
|
@ -144,21 +153,21 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
|||
* Since working directory is wiped out when the background job ends, the product JAR is deleted too.
|
||||
* Meanwhile, the rest of the dependencies are cached for the duration of this service.
|
||||
*/
|
||||
override def copyClasspath(products: Classpath, full: Classpath, workingDirectory: File): Classpath =
|
||||
{
|
||||
def syncTo(dir: File)(source0: Attributed[File]): Attributed[File] =
|
||||
{
|
||||
val source = source0.data
|
||||
val hash8 = Hash.toHex(Hash(source)).take(8)
|
||||
val dest = dir / hash8 / source.getName
|
||||
if (!dest.exists) { IO.copyFile(source, dest) }
|
||||
Attributed.blank(dest)
|
||||
}
|
||||
val xs = (products.toVector map { syncTo(workingDirectory / "target") }) ++
|
||||
((full diff products) map { syncTo(serviceTempDir / "target") })
|
||||
Thread.sleep(100)
|
||||
xs
|
||||
override def copyClasspath(products: Classpath,
|
||||
full: Classpath,
|
||||
workingDirectory: File): Classpath = {
|
||||
def syncTo(dir: File)(source0: Attributed[File]): Attributed[File] = {
|
||||
val source = source0.data
|
||||
val hash8 = Hash.toHex(Hash(source)).take(8)
|
||||
val dest = dir / hash8 / source.getName
|
||||
if (!dest.exists) { IO.copyFile(source, dest) }
|
||||
Attributed.blank(dest)
|
||||
}
|
||||
val xs = (products.toVector map { syncTo(workingDirectory / "target") }) ++
|
||||
((full diff products) map { syncTo(serviceTempDir / "target") })
|
||||
Thread.sleep(100)
|
||||
xs
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] object BackgroundThreadPool {
|
||||
|
|
@ -176,7 +185,8 @@ private[sbt] class BackgroundThreadPool extends java.io.Closeable {
|
|||
|
||||
private val threadFactory = new java.util.concurrent.ThreadFactory() {
|
||||
override def newThread(runnable: Runnable): Thread = {
|
||||
val thread = new Thread(threadGroup, runnable, s"sbt-bg-threads-${nextThreadId.getAndIncrement}")
|
||||
val thread =
|
||||
new Thread(threadGroup, runnable, s"sbt-bg-threads-${nextThreadId.getAndIncrement}")
|
||||
// Do NOT setDaemon because then the code in TaskExit.scala in sbt will insta-kill
|
||||
// the backgrounded process, at least for the case of the run task.
|
||||
thread
|
||||
|
|
@ -186,13 +196,16 @@ private[sbt] class BackgroundThreadPool extends java.io.Closeable {
|
|||
private val executor = new java.util.concurrent.ThreadPoolExecutor(
|
||||
0, /* corePoolSize */
|
||||
32, /* maxPoolSize, max # of bg tasks */
|
||||
2, java.util.concurrent.TimeUnit.SECONDS, /* keep alive unused threads this long (if corePoolSize < maxPoolSize) */
|
||||
2,
|
||||
java.util.concurrent.TimeUnit.SECONDS,
|
||||
/* keep alive unused threads this long (if corePoolSize < maxPoolSize) */
|
||||
new java.util.concurrent.SynchronousQueue[Runnable](),
|
||||
threadFactory
|
||||
)
|
||||
|
||||
private class BackgroundRunnable(val taskName: String, body: () => Unit)
|
||||
extends BackgroundJob with Runnable {
|
||||
extends BackgroundJob
|
||||
with Runnable {
|
||||
import BackgroundThreadPool._
|
||||
private val finishedLatch = new java.util.concurrent.CountDownLatch(1)
|
||||
// synchronize to read/write this, no sync to just read
|
||||
|
|
@ -200,23 +213,24 @@ private[sbt] class BackgroundThreadPool extends java.io.Closeable {
|
|||
private var status: Status = Waiting
|
||||
|
||||
// double-finally for extra paranoia that we will finishedLatch.countDown
|
||||
override def run() = try {
|
||||
val go = synchronized {
|
||||
status match {
|
||||
case Waiting =>
|
||||
status = Running(Thread.currentThread())
|
||||
true
|
||||
case Stopped(_) =>
|
||||
false
|
||||
case Running(_) =>
|
||||
throw new RuntimeException("Impossible status of bg thread")
|
||||
override def run() =
|
||||
try {
|
||||
val go = synchronized {
|
||||
status match {
|
||||
case Waiting =>
|
||||
status = Running(Thread.currentThread())
|
||||
true
|
||||
case Stopped(_) =>
|
||||
false
|
||||
case Running(_) =>
|
||||
throw new RuntimeException("Impossible status of bg thread")
|
||||
}
|
||||
}
|
||||
}
|
||||
try { if (go) body() }
|
||||
finally cleanup()
|
||||
} finally finishedLatch.countDown()
|
||||
try { if (go) body() } finally cleanup()
|
||||
} finally finishedLatch.countDown()
|
||||
|
||||
private class StopListener(val callback: () => Unit, val executionContext: ExecutionContext) extends Closeable {
|
||||
private class StopListener(val callback: () => Unit, val executionContext: ExecutionContext)
|
||||
extends Closeable {
|
||||
override def close(): Unit = removeListener(this)
|
||||
override def hashCode: Int = System.identityHashCode(this)
|
||||
override def equals(other: Any): Boolean = other match {
|
||||
|
|
@ -277,7 +291,8 @@ private[sbt] class BackgroundThreadPool extends java.io.Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
def run(manager: AbstractBackgroundJobService, spawningTask: ScopedKey[_], state: State)(work: (Logger, File) => Unit): JobHandle = {
|
||||
def run(manager: AbstractBackgroundJobService, spawningTask: ScopedKey[_], state: State)(
|
||||
work: (Logger, File) => Unit): JobHandle = {
|
||||
def start(logger: Logger, workingDir: File): BackgroundJob = {
|
||||
val runnable = new BackgroundRunnable(spawningTask.key.label, { () =>
|
||||
work(logger, workingDir)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import Def._
|
|||
|
||||
/** This represents a `Setting` expression configured by the sbt DSL. */
|
||||
sealed trait DslEntry {
|
||||
|
||||
/** Called by the parser. Sets the position where this entry was defined in the build.sbt file. */
|
||||
def withPos(pos: RangePosition): DslEntry
|
||||
}
|
||||
|
|
@ -48,24 +49,31 @@ object DslEntry {
|
|||
/** this represents an actually Setting[_] or Seq[Setting[_]] configured by the sbt DSL. */
|
||||
case class DslSetting(settings: SettingsDefinition) extends ProjectSettings {
|
||||
def toSettings = settings.settings
|
||||
final def withPos(pos: RangePosition): DslEntry = DslSetting(settings.settings.map(_.withPos(pos)))
|
||||
final def withPos(pos: RangePosition): DslEntry =
|
||||
DslSetting(settings.settings.map(_.withPos(pos)))
|
||||
}
|
||||
|
||||
/** this represents an `enablePlugins()` in the sbt DSL */
|
||||
case class DslEnablePlugins(plugins: Seq[AutoPlugin]) extends ProjectManipulation {
|
||||
override val toFunction: Project => Project = _.enablePlugins(plugins: _*)
|
||||
}
|
||||
|
||||
/** this represents an `disablePlugins()` in the sbt DSL */
|
||||
case class DslDisablePlugins(plugins: Seq[AutoPlugin]) extends ProjectManipulation {
|
||||
override val toFunction: Project => Project = _.disablePlugins(plugins: _*)
|
||||
}
|
||||
|
||||
/** Represents registering an internal dependency for the current project */
|
||||
case class DslDependsOn(cs: Seq[Eval[ClasspathDep[ProjectReference]]]) extends ProjectManipulation {
|
||||
case class DslDependsOn(cs: Seq[Eval[ClasspathDep[ProjectReference]]])
|
||||
extends ProjectManipulation {
|
||||
override val toFunction: Project => Project = _.dependsOn(cs: _*)
|
||||
}
|
||||
|
||||
/** Represents registering a set of configurations with the current project. */
|
||||
case class DslConfigs(cs: Seq[Configuration]) extends ProjectManipulation {
|
||||
override val toFunction: Project => Project = _.configs(cs: _*)
|
||||
}
|
||||
|
||||
/** this represents an `aggregate()` in the sbt DSL */
|
||||
case class DslAggregate(refs: Seq[Eval[ProjectReference]]) extends ProjectManipulation {
|
||||
override val toFunction: Project => Project = _.aggregate(refs: _*)
|
||||
|
|
|
|||
|
|
@ -13,16 +13,15 @@ private[sbt] object GCUtil {
|
|||
val defaultMinForcegcInterval: Duration = 60.seconds
|
||||
val lastGcCheck: AtomicLong = new AtomicLong(0L)
|
||||
|
||||
def forceGcWithInterval(minForcegcInterval: Duration, log: Logger): Unit =
|
||||
{
|
||||
val now = System.currentTimeMillis
|
||||
val last = lastGcCheck.get
|
||||
// This throttles System.gc calls to interval
|
||||
if (now - last > minForcegcInterval.toMillis) {
|
||||
lastGcCheck.lazySet(now)
|
||||
forceGc(log)
|
||||
}
|
||||
def forceGcWithInterval(minForcegcInterval: Duration, log: Logger): Unit = {
|
||||
val now = System.currentTimeMillis
|
||||
val last = lastGcCheck.get
|
||||
// This throttles System.gc calls to interval
|
||||
if (now - last > minForcegcInterval.toMillis) {
|
||||
lastGcCheck.lazySet(now)
|
||||
forceGc(log)
|
||||
}
|
||||
}
|
||||
|
||||
def forceGc(log: Logger): Unit =
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
package sbt
|
||||
package internal
|
||||
|
||||
import sbt.librarymanagement.{ Configuration, Configurations, ModuleID, Resolver, SbtArtifacts, UpdateReport }
|
||||
import sbt.librarymanagement.{
|
||||
Configuration,
|
||||
Configurations,
|
||||
ModuleID,
|
||||
Resolver,
|
||||
SbtArtifacts,
|
||||
UpdateReport
|
||||
}
|
||||
import sbt.internal.util.Attributed
|
||||
import Def.{ ScopedKey, Setting }
|
||||
import Keys._
|
||||
|
|
@ -27,61 +34,78 @@ object GlobalPlugin {
|
|||
injectInternalClasspath(Runtime, gp.internalClasspath),
|
||||
injectInternalClasspath(Compile, gp.internalClasspath)
|
||||
)
|
||||
private[this] def injectInternalClasspath(config: Configuration, cp: Seq[Attributed[File]]): Setting[_] =
|
||||
internalDependencyClasspath in config ~= { prev => (prev ++ cp).distinct }
|
||||
private[this] def injectInternalClasspath(config: Configuration,
|
||||
cp: Seq[Attributed[File]]): Setting[_] =
|
||||
internalDependencyClasspath in config ~= { prev =>
|
||||
(prev ++ cp).distinct
|
||||
}
|
||||
|
||||
def build(base: File, s: State, config: LoadBuildConfiguration): (BuildStructure, State) =
|
||||
{
|
||||
val newInject = config.injectSettings.copy(global = config.injectSettings.global ++ globalPluginSettings)
|
||||
val globalConfig = config.copy(injectSettings = newInject, pluginManagement = config.pluginManagement.forGlobalPlugin)
|
||||
val (eval, structure) = Load(base, s, globalConfig)
|
||||
val session = Load.initialSession(structure, eval)
|
||||
(structure, Project.setProject(session, structure, s))
|
||||
}
|
||||
def load(base: File, s: State, config: LoadBuildConfiguration): GlobalPlugin =
|
||||
{
|
||||
val (structure, state) = build(base, s, config)
|
||||
val (newS, data) = extract(state, structure)
|
||||
Project.runUnloadHooks(newS) // discard state
|
||||
GlobalPlugin(data, structure, inject(data), base)
|
||||
}
|
||||
def extract(state: State, structure: BuildStructure): (State, GlobalPluginData) =
|
||||
{
|
||||
import structure.{ data, root, rootProject }
|
||||
val p: Scope = Scope.GlobalScope in ProjectRef(root, rootProject(root))
|
||||
def build(base: File, s: State, config: LoadBuildConfiguration): (BuildStructure, State) = {
|
||||
val newInject =
|
||||
config.injectSettings.copy(global = config.injectSettings.global ++ globalPluginSettings)
|
||||
val globalConfig = config.copy(injectSettings = newInject,
|
||||
pluginManagement = config.pluginManagement.forGlobalPlugin)
|
||||
val (eval, structure) = Load(base, s, globalConfig)
|
||||
val session = Load.initialSession(structure, eval)
|
||||
(structure, Project.setProject(session, structure, s))
|
||||
}
|
||||
def load(base: File, s: State, config: LoadBuildConfiguration): GlobalPlugin = {
|
||||
val (structure, state) = build(base, s, config)
|
||||
val (newS, data) = extract(state, structure)
|
||||
Project.runUnloadHooks(newS) // discard state
|
||||
GlobalPlugin(data, structure, inject(data), base)
|
||||
}
|
||||
def extract(state: State, structure: BuildStructure): (State, GlobalPluginData) = {
|
||||
import structure.{ data, root, rootProject }
|
||||
val p: Scope = Scope.GlobalScope in ProjectRef(root, rootProject(root))
|
||||
|
||||
val taskInit = Def.task {
|
||||
val intcp = (internalDependencyClasspath in Runtime).value
|
||||
val prods = (exportedProducts in Runtime).value
|
||||
val depMap = projectDescriptors.value + ivyModule.value.dependencyMapping(state.log)
|
||||
// If we reference it directly (if it's an executionRoot) then it forces an update, which is not what we want.
|
||||
val updateReport = Def.taskDyn { Def.task { update.value } }.value
|
||||
val taskInit = Def.task {
|
||||
val intcp = (internalDependencyClasspath in Runtime).value
|
||||
val prods = (exportedProducts in Runtime).value
|
||||
val depMap = projectDescriptors.value + ivyModule.value.dependencyMapping(state.log)
|
||||
// If we reference it directly (if it's an executionRoot) then it forces an update, which is not what we want.
|
||||
val updateReport = Def.taskDyn { Def.task { update.value } }.value
|
||||
|
||||
GlobalPluginData(projectID.value, projectDependencies.value, depMap, resolvers.value, (fullClasspath in Runtime).value,
|
||||
(prods ++ intcp).distinct)(updateReport)
|
||||
}
|
||||
val resolvedTaskInit = taskInit mapReferenced Project.mapScope(Scope replaceThis p)
|
||||
val task = resolvedTaskInit evaluate data
|
||||
val roots = resolvedTaskInit.dependencies
|
||||
evaluate(state, structure, task, roots)
|
||||
GlobalPluginData(projectID.value,
|
||||
projectDependencies.value,
|
||||
depMap,
|
||||
resolvers.value,
|
||||
(fullClasspath in Runtime).value,
|
||||
(prods ++ intcp).distinct)(updateReport)
|
||||
}
|
||||
def evaluate[T](state: State, structure: BuildStructure, t: Task[T], roots: Seq[ScopedKey[_]]): (State, T) =
|
||||
{
|
||||
import EvaluateTask._
|
||||
withStreams(structure, state) { str =>
|
||||
val nv = nodeView(state, str, roots)
|
||||
val config = EvaluateTask.extractedTaskConfig(Project.extract(state), structure, state)
|
||||
val (newS, result) = runTask(t, state, str, structure.index.triggers, config)(nv)
|
||||
(newS, processResult(result, newS.log))
|
||||
}
|
||||
val resolvedTaskInit = taskInit mapReferenced Project.mapScope(Scope replaceThis p)
|
||||
val task = resolvedTaskInit evaluate data
|
||||
val roots = resolvedTaskInit.dependencies
|
||||
evaluate(state, structure, task, roots)
|
||||
}
|
||||
def evaluate[T](state: State,
|
||||
structure: BuildStructure,
|
||||
t: Task[T],
|
||||
roots: Seq[ScopedKey[_]]): (State, T) = {
|
||||
import EvaluateTask._
|
||||
withStreams(structure, state) { str =>
|
||||
val nv = nodeView(state, str, roots)
|
||||
val config = EvaluateTask.extractedTaskConfig(Project.extract(state), structure, state)
|
||||
val (newS, result) = runTask(t, state, str, structure.index.triggers, config)(nv)
|
||||
(newS, processResult(result, newS.log))
|
||||
}
|
||||
val globalPluginSettings = Project.inScope(Scope.GlobalScope in LocalRootProject)(Seq(
|
||||
organization := SbtArtifacts.Organization,
|
||||
onLoadMessage := Keys.baseDirectory("Loading global plugins from " + _).value,
|
||||
name := "global-plugin",
|
||||
sbtPlugin := true,
|
||||
version := "0.0"
|
||||
))
|
||||
}
|
||||
val globalPluginSettings = Project.inScope(Scope.GlobalScope in LocalRootProject)(
|
||||
Seq(
|
||||
organization := SbtArtifacts.Organization,
|
||||
onLoadMessage := Keys.baseDirectory("Loading global plugins from " + _).value,
|
||||
name := "global-plugin",
|
||||
sbtPlugin := true,
|
||||
version := "0.0"
|
||||
))
|
||||
}
|
||||
final case class GlobalPluginData(projectID: ModuleID, dependencies: Seq[ModuleID], descriptors: Map[ModuleRevisionId, ModuleDescriptor], resolvers: Seq[Resolver], fullClasspath: Classpath, internalClasspath: Classpath)(val updateReport: UpdateReport)
|
||||
final case class GlobalPlugin(data: GlobalPluginData, structure: BuildStructure, inject: Seq[Setting[_]], base: File)
|
||||
final case class GlobalPluginData(projectID: ModuleID,
|
||||
dependencies: Seq[ModuleID],
|
||||
descriptors: Map[ModuleRevisionId, ModuleDescriptor],
|
||||
resolvers: Seq[Resolver],
|
||||
fullClasspath: Classpath,
|
||||
internalClasspath: Classpath)(val updateReport: UpdateReport)
|
||||
final case class GlobalPlugin(data: GlobalPluginData,
|
||||
structure: BuildStructure,
|
||||
inject: Seq[Setting[_]],
|
||||
base: File)
|
||||
|
|
|
|||
|
|
@ -4,16 +4,18 @@ package internal
|
|||
import Def.Setting
|
||||
import java.net.URI
|
||||
|
||||
private[sbt] final class GroupedAutoPlugins(val all: Seq[AutoPlugin], val byBuild: Map[URI, Seq[AutoPlugin]]) {
|
||||
private[sbt] final class GroupedAutoPlugins(val all: Seq[AutoPlugin],
|
||||
val byBuild: Map[URI, Seq[AutoPlugin]]) {
|
||||
def globalSettings: Seq[Setting[_]] = all.flatMap(_.globalSettings)
|
||||
def buildSettings(uri: URI): Seq[Setting[_]] = byBuild.getOrElse(uri, Nil).flatMap(_.buildSettings)
|
||||
def buildSettings(uri: URI): Seq[Setting[_]] =
|
||||
byBuild.getOrElse(uri, Nil).flatMap(_.buildSettings)
|
||||
}
|
||||
|
||||
private[sbt] object GroupedAutoPlugins {
|
||||
private[sbt] def apply(units: Map[URI, LoadedBuildUnit]): GroupedAutoPlugins =
|
||||
{
|
||||
val byBuild: Map[URI, Seq[AutoPlugin]] = units.mapValues(unit => unit.defined.values.flatMap(_.autoPlugins).toSeq.distinct).toMap
|
||||
val all: Seq[AutoPlugin] = byBuild.values.toSeq.flatten.distinct
|
||||
new GroupedAutoPlugins(all, byBuild)
|
||||
}
|
||||
private[sbt] def apply(units: Map[URI, LoadedBuildUnit]): GroupedAutoPlugins = {
|
||||
val byBuild: Map[URI, Seq[AutoPlugin]] =
|
||||
units.mapValues(unit => unit.defined.values.flatMap(_.autoPlugins).toSeq.distinct).toMap
|
||||
val all: Seq[AutoPlugin] = byBuild.values.toSeq.flatten.distinct
|
||||
new GroupedAutoPlugins(all, byBuild)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,13 @@ object Inspect {
|
|||
val Uses: Mode = UsesMode
|
||||
val Definitions: Mode = DefinitionsMode
|
||||
|
||||
def parser: State => Parser[(Inspect.Mode, ScopedKey[_])] = (s: State) => spacedModeParser(s) flatMap {
|
||||
case opt @ (UsesMode | DefinitionsMode) => allKeyParser(s).map(key => (opt, Def.ScopedKey(Global, key)))
|
||||
case opt @ (DependencyTreeMode | Details(_)) => spacedKeyParser(s).map(key => (opt, key))
|
||||
}
|
||||
def parser: State => Parser[(Inspect.Mode, ScopedKey[_])] =
|
||||
(s: State) =>
|
||||
spacedModeParser(s) flatMap {
|
||||
case opt @ (UsesMode | DefinitionsMode) =>
|
||||
allKeyParser(s).map(key => (opt, Def.ScopedKey(Global, key)))
|
||||
case opt @ (DependencyTreeMode | Details(_)) => spacedKeyParser(s).map(key => (opt, key))
|
||||
}
|
||||
val spacedModeParser: (State => Parser[Mode]) = (s: State) => {
|
||||
val actual = "actual" ^^^ Details(true)
|
||||
val tree = "tree" ^^^ DependencyTree
|
||||
|
|
@ -31,28 +34,29 @@ object Inspect {
|
|||
token(Space ~> (tree | actual | uses | definitions)) ?? Details(false)
|
||||
}
|
||||
|
||||
def allKeyParser(s: State): Parser[AttributeKey[_]] =
|
||||
{
|
||||
val keyMap = Project.structure(s).index.keyMap
|
||||
token(Space ~> (ID !!! "Expected key" examples keyMap.keySet)) flatMap { key => Act.getKey(keyMap, key, idFun) }
|
||||
def allKeyParser(s: State): Parser[AttributeKey[_]] = {
|
||||
val keyMap = Project.structure(s).index.keyMap
|
||||
token(Space ~> (ID !!! "Expected key" examples keyMap.keySet)) flatMap { key =>
|
||||
Act.getKey(keyMap, key, idFun)
|
||||
}
|
||||
val spacedKeyParser: State => Parser[ScopedKey[_]] = (s: State) => Act.requireSession(s, token(Space) ~> Act.scopedKeyParser(s))
|
||||
}
|
||||
val spacedKeyParser: State => Parser[ScopedKey[_]] = (s: State) =>
|
||||
Act.requireSession(s, token(Space) ~> Act.scopedKeyParser(s))
|
||||
|
||||
def output(s: State, option: Mode, sk: Def.ScopedKey[_]): String =
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
option match {
|
||||
case Details(actual) =>
|
||||
Project.details(structure, actual, sk.scope, sk.key)
|
||||
case DependencyTreeMode =>
|
||||
val basedir = new File(Project.session(s).current.build)
|
||||
Project.settingGraph(structure, basedir, sk).dependsAscii(get(sbt.Keys.asciiGraphWidth))
|
||||
case UsesMode =>
|
||||
Project.showUses(Project.usedBy(structure, true, sk.key))
|
||||
case DefinitionsMode =>
|
||||
Project.showDefinitions(sk.key, Project.definitions(structure, true, sk.key))
|
||||
}
|
||||
def output(s: State, option: Mode, sk: Def.ScopedKey[_]): String = {
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
option match {
|
||||
case Details(actual) =>
|
||||
Project.details(structure, actual, sk.scope, sk.key)
|
||||
case DependencyTreeMode =>
|
||||
val basedir = new File(Project.session(s).current.build)
|
||||
Project.settingGraph(structure, basedir, sk).dependsAscii(get(sbt.Keys.asciiGraphWidth))
|
||||
case UsesMode =>
|
||||
Project.showUses(Project.usedBy(structure, true, sk.key))
|
||||
case DefinitionsMode =>
|
||||
Project.showDefinitions(sk.key, Project.definitions(structure, true, sk.key))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,14 @@ package internal
|
|||
import sbt.internal.util.Attributed
|
||||
import sbt.util.{ Level, Logger }
|
||||
|
||||
import sbt.librarymanagement.{ Configurations, CrossVersion, Disabled, MavenRepository, ModuleID, Resolver }
|
||||
import sbt.librarymanagement.{
|
||||
Configurations,
|
||||
CrossVersion,
|
||||
Disabled,
|
||||
MavenRepository,
|
||||
ModuleID,
|
||||
Resolver
|
||||
}
|
||||
|
||||
import java.io.File
|
||||
import Configurations.Compile
|
||||
|
|
@ -20,7 +27,9 @@ object IvyConsole {
|
|||
final val Name = "ivy-console"
|
||||
lazy val command =
|
||||
Command.command(Name) { state =>
|
||||
val Dependencies(managed, repos, unmanaged) = parseDependencies(state.remainingCommands map { _.commandLine }, state.log)
|
||||
val Dependencies(managed, repos, unmanaged) = parseDependencies(state.remainingCommands map {
|
||||
_.commandLine
|
||||
}, state.log)
|
||||
val base = new File(CommandUtil.bootDirectory(state), Name)
|
||||
IO.createDirectory(base)
|
||||
|
||||
|
|
@ -36,15 +45,21 @@ object IvyConsole {
|
|||
logLevel in Global := Level.Warn,
|
||||
showSuccess in Global := false
|
||||
)
|
||||
val append = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, depSettings)
|
||||
val append = Load.transformSettings(Load.projectScope(currentRef),
|
||||
currentRef.build,
|
||||
rootProject,
|
||||
depSettings)
|
||||
|
||||
val newStructure = Load.reapply(session.original ++ append, structure)
|
||||
val newState = state.copy(remainingCommands = Exec("console-quick", None) :: Nil)
|
||||
Project.setProject(session, newStructure, newState)
|
||||
}
|
||||
|
||||
final case class Dependencies(managed: Seq[ModuleID], resolvers: Seq[Resolver], unmanaged: Seq[File])
|
||||
def parseDependencies(args: Seq[String], log: Logger): Dependencies = (Dependencies(Nil, Nil, Nil) /: args)(parseArgument(log))
|
||||
final case class Dependencies(managed: Seq[ModuleID],
|
||||
resolvers: Seq[Resolver],
|
||||
unmanaged: Seq[File])
|
||||
def parseDependencies(args: Seq[String], log: Logger): Dependencies =
|
||||
(Dependencies(Nil, Nil, Nil) /: args)(parseArgument(log))
|
||||
def parseArgument(log: Logger)(acc: Dependencies, arg: String): Dependencies =
|
||||
arg match {
|
||||
case _ if arg contains " at " => acc.copy(resolvers = parseResolver(arg) +: acc.resolvers)
|
||||
|
|
@ -52,11 +67,10 @@ object IvyConsole {
|
|||
case _ => acc.copy(managed = parseManaged(arg, log) ++ acc.managed)
|
||||
}
|
||||
|
||||
private[this] def parseResolver(arg: String): MavenRepository =
|
||||
{
|
||||
val Array(name, url) = arg.split(" at ")
|
||||
MavenRepository(name.trim, url.trim)
|
||||
}
|
||||
private[this] def parseResolver(arg: String): MavenRepository = {
|
||||
val Array(name, url) = arg.split(" at ")
|
||||
MavenRepository(name.trim, url.trim)
|
||||
}
|
||||
|
||||
val DepPattern = """([^%]+)%(%?)([^%]+)%([^%]+)""".r
|
||||
def parseManaged(arg: String, log: Logger): Seq[ModuleID] =
|
||||
|
|
|
|||
|
|
@ -14,16 +14,19 @@ object KeyIndex {
|
|||
def empty: ExtendableKeyIndex = new KeyIndex0(emptyBuildIndex)
|
||||
def apply(known: Iterable[ScopedKey[_]], projects: Map[URI, Set[String]]): ExtendableKeyIndex =
|
||||
(base(projects) /: known) { _ add _ }
|
||||
def aggregate(known: Iterable[ScopedKey[_]], extra: BuildUtil[_], projects: Map[URI, Set[String]]): ExtendableKeyIndex =
|
||||
(base(projects) /: known) { (index, key) => index.addAggregated(key, extra) }
|
||||
private[this] def base(projects: Map[URI, Set[String]]): ExtendableKeyIndex =
|
||||
{
|
||||
val data = for ((uri, ids) <- projects) yield {
|
||||
val data = ids.map(id => Option(id) -> new ConfigIndex(Map.empty))
|
||||
Option(uri) -> new ProjectIndex(data.toMap)
|
||||
}
|
||||
new KeyIndex0(new BuildIndex(data.toMap))
|
||||
def aggregate(known: Iterable[ScopedKey[_]],
|
||||
extra: BuildUtil[_],
|
||||
projects: Map[URI, Set[String]]): ExtendableKeyIndex =
|
||||
(base(projects) /: known) { (index, key) =>
|
||||
index.addAggregated(key, extra)
|
||||
}
|
||||
private[this] def base(projects: Map[URI, Set[String]]): ExtendableKeyIndex = {
|
||||
val data = for ((uri, ids) <- projects) yield {
|
||||
val data = ids.map(id => Option(id) -> new ConfigIndex(Map.empty))
|
||||
Option(uri) -> new ProjectIndex(data.toMap)
|
||||
}
|
||||
new KeyIndex0(new BuildIndex(data.toMap))
|
||||
}
|
||||
|
||||
def combine(indices: Seq[KeyIndex]): KeyIndex = new KeyIndex {
|
||||
def buildURIs = concat(_.buildURIs)
|
||||
|
|
@ -31,10 +34,13 @@ object KeyIndex {
|
|||
def exists(project: Option[ResolvedReference]): Boolean = indices.exists(_ exists project)
|
||||
def configs(proj: Option[ResolvedReference]) = concat(_.configs(proj))
|
||||
def tasks(proj: Option[ResolvedReference], conf: Option[String]) = concat(_.tasks(proj, conf))
|
||||
def tasks(proj: Option[ResolvedReference], conf: Option[String], key: String) = concat(_.tasks(proj, conf, key))
|
||||
def tasks(proj: Option[ResolvedReference], conf: Option[String], key: String) =
|
||||
concat(_.tasks(proj, conf, key))
|
||||
def keys(proj: Option[ResolvedReference]) = concat(_.keys(proj))
|
||||
def keys(proj: Option[ResolvedReference], conf: Option[String]) = concat(_.keys(proj, conf))
|
||||
def keys(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]]) = concat(_.keys(proj, conf, task))
|
||||
def keys(proj: Option[ResolvedReference],
|
||||
conf: Option[String],
|
||||
task: Option[AttributeKey[_]]) = concat(_.keys(proj, conf, task))
|
||||
def concat[T](f: KeyIndex => Set[T]): Set[T] =
|
||||
(Set.empty[T] /: indices)((s, k) => s ++ f(k))
|
||||
}
|
||||
|
|
@ -49,18 +55,25 @@ import KeyIndex._
|
|||
|
||||
trait KeyIndex {
|
||||
// TODO, optimize
|
||||
def isEmpty(proj: Option[ResolvedReference], conf: Option[String]): Boolean = keys(proj, conf).isEmpty
|
||||
def isEmpty(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]]): Boolean = keys(proj, conf, task).isEmpty
|
||||
def isEmpty(proj: Option[ResolvedReference], conf: Option[String]): Boolean =
|
||||
keys(proj, conf).isEmpty
|
||||
def isEmpty(proj: Option[ResolvedReference],
|
||||
conf: Option[String],
|
||||
task: Option[AttributeKey[_]]): Boolean = keys(proj, conf, task).isEmpty
|
||||
|
||||
def buildURIs: Set[URI]
|
||||
def projects(uri: URI): Set[String]
|
||||
def exists(project: Option[ResolvedReference]): Boolean
|
||||
def configs(proj: Option[ResolvedReference]): Set[String]
|
||||
def tasks(proj: Option[ResolvedReference], conf: Option[String]): Set[AttributeKey[_]]
|
||||
def tasks(proj: Option[ResolvedReference], conf: Option[String], key: String): Set[AttributeKey[_]]
|
||||
def tasks(proj: Option[ResolvedReference],
|
||||
conf: Option[String],
|
||||
key: String): Set[AttributeKey[_]]
|
||||
def keys(proj: Option[ResolvedReference]): Set[String]
|
||||
def keys(proj: Option[ResolvedReference], conf: Option[String]): Set[String]
|
||||
def keys(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]]): Set[String]
|
||||
def keys(proj: Option[ResolvedReference],
|
||||
conf: Option[String],
|
||||
task: Option[AttributeKey[_]]): Set[String]
|
||||
}
|
||||
trait ExtendableKeyIndex extends KeyIndex {
|
||||
def add(scoped: ScopedKey[_]): ExtendableKeyIndex
|
||||
|
|
@ -68,26 +81,36 @@ trait ExtendableKeyIndex extends KeyIndex {
|
|||
}
|
||||
// task axis <-> key
|
||||
private[sbt] final class AKeyIndex(val data: Relation[Option[AttributeKey[_]], String]) {
|
||||
def add(task: Option[AttributeKey[_]], key: AttributeKey[_]): AKeyIndex = new AKeyIndex(data + (task, key.label))
|
||||
def add(task: Option[AttributeKey[_]], key: AttributeKey[_]): AKeyIndex =
|
||||
new AKeyIndex(data + (task, key.label))
|
||||
def keys(task: Option[AttributeKey[_]]): Set[String] = data.forward(task)
|
||||
def allKeys: Set[String] = data._2s.toSet
|
||||
def tasks: Set[AttributeKey[_]] = data._1s.flatten.toSet
|
||||
def tasks(key: String): Set[AttributeKey[_]] = data.reverse(key).flatten.toSet
|
||||
}
|
||||
private[sbt] final class ConfigIndex(val data: Map[Option[String], AKeyIndex]) {
|
||||
def add(config: Option[String], task: Option[AttributeKey[_]], key: AttributeKey[_]): ConfigIndex =
|
||||
def add(config: Option[String],
|
||||
task: Option[AttributeKey[_]],
|
||||
key: AttributeKey[_]): ConfigIndex =
|
||||
new ConfigIndex(data updated (config, keyIndex(config).add(task, key)))
|
||||
def keyIndex(conf: Option[String]): AKeyIndex = getOr(data, conf, emptyAKeyIndex)
|
||||
def configs: Set[String] = keySet(data)
|
||||
}
|
||||
private[sbt] final class ProjectIndex(val data: Map[Option[String], ConfigIndex]) {
|
||||
def add(id: Option[String], config: Option[String], task: Option[AttributeKey[_]], key: AttributeKey[_]): ProjectIndex =
|
||||
def add(id: Option[String],
|
||||
config: Option[String],
|
||||
task: Option[AttributeKey[_]],
|
||||
key: AttributeKey[_]): ProjectIndex =
|
||||
new ProjectIndex(data updated (id, confIndex(id).add(config, task, key)))
|
||||
def confIndex(id: Option[String]): ConfigIndex = getOr(data, id, emptyConfigIndex)
|
||||
def projects: Set[String] = keySet(data)
|
||||
}
|
||||
private[sbt] final class BuildIndex(val data: Map[Option[URI], ProjectIndex]) {
|
||||
def add(build: Option[URI], project: Option[String], config: Option[String], task: Option[AttributeKey[_]], key: AttributeKey[_]): BuildIndex =
|
||||
def add(build: Option[URI],
|
||||
project: Option[String],
|
||||
config: Option[String],
|
||||
task: Option[AttributeKey[_]],
|
||||
key: AttributeKey[_]): BuildIndex =
|
||||
new BuildIndex(data updated (build, projectIndex(build).add(project, config, task, key)))
|
||||
def projectIndex(build: Option[URI]): ProjectIndex = getOr(data, build, emptyProjectIndex)
|
||||
def builds: Set[URI] = keySet(data)
|
||||
|
|
@ -95,32 +118,40 @@ private[sbt] final class BuildIndex(val data: Map[Option[URI], ProjectIndex]) {
|
|||
private[sbt] final class KeyIndex0(val data: BuildIndex) extends ExtendableKeyIndex {
|
||||
def buildURIs: Set[URI] = data.builds
|
||||
def projects(uri: URI): Set[String] = data.projectIndex(Some(uri)).projects
|
||||
def exists(proj: Option[ResolvedReference]): Boolean =
|
||||
{
|
||||
val (build, project) = parts(proj)
|
||||
data.data.get(build).flatMap(_.data.get(project)).isDefined
|
||||
}
|
||||
def exists(proj: Option[ResolvedReference]): Boolean = {
|
||||
val (build, project) = parts(proj)
|
||||
data.data.get(build).flatMap(_.data.get(project)).isDefined
|
||||
}
|
||||
def configs(project: Option[ResolvedReference]): Set[String] = confIndex(project).configs
|
||||
def tasks(proj: Option[ResolvedReference], conf: Option[String]): Set[AttributeKey[_]] = keyIndex(proj, conf).tasks
|
||||
def tasks(proj: Option[ResolvedReference], conf: Option[String], key: String): Set[AttributeKey[_]] = keyIndex(proj, conf).tasks(key)
|
||||
def keys(proj: Option[ResolvedReference]): Set[String] = (Set.empty[String] /: optConfigs(proj)) { (s, c) => s ++ keys(proj, c) }
|
||||
def keys(proj: Option[ResolvedReference], conf: Option[String]): Set[String] = keyIndex(proj, conf).allKeys
|
||||
def keys(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]]): Set[String] = keyIndex(proj, conf).keys(task)
|
||||
def tasks(proj: Option[ResolvedReference], conf: Option[String]): Set[AttributeKey[_]] =
|
||||
keyIndex(proj, conf).tasks
|
||||
def tasks(proj: Option[ResolvedReference],
|
||||
conf: Option[String],
|
||||
key: String): Set[AttributeKey[_]] = keyIndex(proj, conf).tasks(key)
|
||||
def keys(proj: Option[ResolvedReference]): Set[String] =
|
||||
(Set.empty[String] /: optConfigs(proj)) { (s, c) =>
|
||||
s ++ keys(proj, c)
|
||||
}
|
||||
def keys(proj: Option[ResolvedReference], conf: Option[String]): Set[String] =
|
||||
keyIndex(proj, conf).allKeys
|
||||
def keys(proj: Option[ResolvedReference],
|
||||
conf: Option[String],
|
||||
task: Option[AttributeKey[_]]): Set[String] = keyIndex(proj, conf).keys(task)
|
||||
|
||||
def keyIndex(proj: Option[ResolvedReference], conf: Option[String]): AKeyIndex =
|
||||
confIndex(proj).keyIndex(conf)
|
||||
def confIndex(proj: Option[ResolvedReference]): ConfigIndex =
|
||||
{
|
||||
val (build, project) = parts(proj)
|
||||
data.projectIndex(build).confIndex(project)
|
||||
}
|
||||
def confIndex(proj: Option[ResolvedReference]): ConfigIndex = {
|
||||
val (build, project) = parts(proj)
|
||||
data.projectIndex(build).confIndex(project)
|
||||
}
|
||||
def parts(proj: Option[Reference]): (Option[URI], Option[String]) =
|
||||
proj match {
|
||||
case Some(ProjectRef(uri, id)) => (Some(uri), Some(id))
|
||||
case Some(BuildRef(uri)) => (Some(uri), None)
|
||||
case _ => (None, None)
|
||||
}
|
||||
private[this] def optConfigs(project: Option[ResolvedReference]): Seq[Option[String]] = None +: (configs(project).toSeq map some.fn)
|
||||
private[this] def optConfigs(project: Option[ResolvedReference]): Seq[Option[String]] =
|
||||
None +: (configs(project).toSeq map some.fn)
|
||||
|
||||
def addAggregated(scoped: ScopedKey[_], extra: BuildUtil[_]): ExtendableKeyIndex =
|
||||
if (validID(scoped.key.label)) {
|
||||
|
|
@ -131,11 +162,14 @@ private[sbt] final class KeyIndex0(val data: BuildIndex) extends ExtendableKeyIn
|
|||
|
||||
def add(scoped: ScopedKey[_]): ExtendableKeyIndex =
|
||||
if (validID(scoped.key.label)) add0(scoped) else this
|
||||
private[this] def add0(scoped: ScopedKey[_]): ExtendableKeyIndex =
|
||||
{
|
||||
val (build, project) = parts(scoped.scope.project.toOption)
|
||||
add1(build, project, scoped.scope.config, scoped.scope.task, scoped.key)
|
||||
}
|
||||
private[this] def add1(uri: Option[URI], id: Option[String], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], key: AttributeKey[_]): ExtendableKeyIndex =
|
||||
private[this] def add0(scoped: ScopedKey[_]): ExtendableKeyIndex = {
|
||||
val (build, project) = parts(scoped.scope.project.toOption)
|
||||
add1(build, project, scoped.scope.config, scoped.scope.task, scoped.key)
|
||||
}
|
||||
private[this] def add1(uri: Option[URI],
|
||||
id: Option[String],
|
||||
config: ScopeAxis[ConfigKey],
|
||||
task: ScopeAxis[AttributeKey[_]],
|
||||
key: AttributeKey[_]): ExtendableKeyIndex =
|
||||
new KeyIndex0(data.add(uri, id, config.toOption.map(_.name), task.toOption, key))
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -29,7 +29,8 @@ private[sbt] final class LoadedSbtFile(
|
|||
generatedFiles ++ o.generatedFiles
|
||||
)
|
||||
|
||||
def clearProjects = new LoadedSbtFile(settings, Nil, importedDefs, manipulations, definitions, generatedFiles)
|
||||
def clearProjects =
|
||||
new LoadedSbtFile(settings, Nil, importedDefs, manipulations, definitions, generatedFiles)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -42,7 +43,9 @@ private[sbt] final class DefinedSbtValues(val sbtFiles: Seq[compiler.EvalDefinit
|
|||
sbtFiles flatMap (_ values parent)
|
||||
|
||||
def classloader(parent: ClassLoader): ClassLoader =
|
||||
sbtFiles.foldLeft(parent) { (cl, e) => e.loader(cl) }
|
||||
sbtFiles.foldLeft(parent) { (cl, e) =>
|
||||
e.loader(cl)
|
||||
}
|
||||
|
||||
def imports: Seq[String] = {
|
||||
// TODO - Sanity check duplicates and such, so users get a nice warning rather
|
||||
|
|
@ -69,16 +72,18 @@ private[sbt] final class DefinedSbtValues(val sbtFiles: Seq[compiler.EvalDefinit
|
|||
new DefinedSbtValues(sbtFiles ++ other.sbtFiles)
|
||||
}
|
||||
private[sbt] object DefinedSbtValues {
|
||||
|
||||
/** Construct a DefinedSbtValues object directly from the underlying representation. */
|
||||
def apply(eval: compiler.EvalDefinitions): DefinedSbtValues =
|
||||
new DefinedSbtValues(Seq(eval))
|
||||
|
||||
/** Construct an empty value object. */
|
||||
def empty = new DefinedSbtValues(Nil)
|
||||
|
||||
}
|
||||
|
||||
private[sbt] object LoadedSbtFile {
|
||||
|
||||
/** Represents an empty .sbt file: no Projects, imports, or settings.*/
|
||||
def empty = new LoadedSbtFile(Nil, Nil, Nil, Nil, DefinedSbtValues.empty, Nil)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,23 @@ import Def.ScopedKey
|
|||
import Scope.GlobalScope
|
||||
import Keys.{ logLevel, logManager, persistLogLevel, persistTraceLevel, sLog, traceLevel }
|
||||
import scala.Console.{ BLUE, RESET }
|
||||
import sbt.internal.util.{ AttributeKey, ConsoleOut, Settings, SuppressedTraceContext, MainAppender }
|
||||
import sbt.internal.util.{
|
||||
AttributeKey,
|
||||
ConsoleOut,
|
||||
Settings,
|
||||
SuppressedTraceContext,
|
||||
MainAppender
|
||||
}
|
||||
import MainAppender._
|
||||
import sbt.util.{ Level, Logger, LogExchange }
|
||||
import sbt.internal.util.ManagedLogger
|
||||
import org.apache.logging.log4j.core.Appender
|
||||
|
||||
sealed abstract class LogManager {
|
||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], writer: PrintWriter): ManagedLogger
|
||||
def apply(data: Settings[Scope],
|
||||
state: State,
|
||||
task: ScopedKey[_],
|
||||
writer: PrintWriter): ManagedLogger
|
||||
def backgroundLog(data: Settings[Scope], state: State, task: ScopedKey[_]): ManagedLogger
|
||||
}
|
||||
|
||||
|
|
@ -25,31 +34,40 @@ object LogManager {
|
|||
private val generateId: AtomicInteger = new AtomicInteger
|
||||
|
||||
// This is called by mkStreams
|
||||
def construct(data: Settings[Scope], state: State): (ScopedKey[_], PrintWriter) => ManagedLogger = (task: ScopedKey[_], to: PrintWriter) =>
|
||||
{
|
||||
val manager: LogManager = (logManager in task.scope).get(data) getOrElse { defaultManager(state.globalLogging.console) }
|
||||
def construct(data: Settings[Scope],
|
||||
state: State): (ScopedKey[_], PrintWriter) => ManagedLogger =
|
||||
(task: ScopedKey[_], to: PrintWriter) => {
|
||||
val manager: LogManager = (logManager in task.scope).get(data) getOrElse {
|
||||
defaultManager(state.globalLogging.console)
|
||||
}
|
||||
manager(data, state, task, to)
|
||||
}
|
||||
|
||||
def constructBackgroundLog(data: Settings[Scope], state: State): (ScopedKey[_]) => ManagedLogger = (task: ScopedKey[_]) =>
|
||||
{
|
||||
val manager: LogManager = (logManager in task.scope).get(data) getOrElse { defaultManager(state.globalLogging.console) }
|
||||
def constructBackgroundLog(data: Settings[Scope],
|
||||
state: State): (ScopedKey[_]) => ManagedLogger =
|
||||
(task: ScopedKey[_]) => {
|
||||
val manager: LogManager = (logManager in task.scope).get(data) getOrElse {
|
||||
defaultManager(state.globalLogging.console)
|
||||
}
|
||||
manager.backgroundLog(data, state, task)
|
||||
}
|
||||
|
||||
def defaultManager(console: ConsoleOut): LogManager = withLoggers((sk, s) => defaultScreen(console))
|
||||
def defaultManager(console: ConsoleOut): LogManager =
|
||||
withLoggers((sk, s) => defaultScreen(console))
|
||||
|
||||
// This is called by Defaults.
|
||||
def defaults(extra: ScopedKey[_] => Seq[Appender], console: ConsoleOut): LogManager =
|
||||
withLoggers((task, state) => defaultScreen(console, suppressedMessage(task, state)), extra = extra)
|
||||
withLoggers((task, state) => defaultScreen(console, suppressedMessage(task, state)),
|
||||
extra = extra)
|
||||
|
||||
def withScreenLogger(mk: (ScopedKey[_], State) => Appender): LogManager = withLoggers(screen = mk)
|
||||
def withScreenLogger(mk: (ScopedKey[_], State) => Appender): LogManager =
|
||||
withLoggers(screen = mk)
|
||||
|
||||
def withLoggers(
|
||||
screen: (ScopedKey[_], State) => Appender = (sk, s) => defaultScreen(s.globalLogging.console),
|
||||
backed: PrintWriter => Appender = defaultBacked,
|
||||
relay: Unit => Appender = defaultRelay,
|
||||
extra: ScopedKey[_] => Seq[Appender] = _ => Nil
|
||||
screen: (ScopedKey[_], State) => Appender = (sk, s) => defaultScreen(s.globalLogging.console),
|
||||
backed: PrintWriter => Appender = defaultBacked,
|
||||
relay: Unit => Appender = defaultRelay,
|
||||
extra: ScopedKey[_] => Seq[Appender] = _ => Nil
|
||||
): LogManager = new DefaultLogManager(screen, backed, relay, extra)
|
||||
|
||||
private class DefaultLogManager(
|
||||
|
|
@ -58,34 +76,61 @@ object LogManager {
|
|||
relay: Unit => Appender,
|
||||
extra: ScopedKey[_] => Seq[Appender]
|
||||
) extends LogManager {
|
||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], to: PrintWriter): ManagedLogger =
|
||||
defaultLogger(data, state, task, screen(task, state), backed(to), relay(()), extra(task).toList)
|
||||
def apply(data: Settings[Scope],
|
||||
state: State,
|
||||
task: ScopedKey[_],
|
||||
to: PrintWriter): ManagedLogger =
|
||||
defaultLogger(data,
|
||||
state,
|
||||
task,
|
||||
screen(task, state),
|
||||
backed(to),
|
||||
relay(()),
|
||||
extra(task).toList)
|
||||
|
||||
def backgroundLog(data: Settings[Scope], state: State, task: ScopedKey[_]): ManagedLogger =
|
||||
LogManager.backgroundLog(data, state, task, screen(task, state), relay(()), extra(task).toList)
|
||||
LogManager.backgroundLog(data,
|
||||
state,
|
||||
task,
|
||||
screen(task, state),
|
||||
relay(()),
|
||||
extra(task).toList)
|
||||
}
|
||||
|
||||
// This is the main function that is used to generate the logger for tasks.
|
||||
def defaultLogger(data: Settings[Scope], state: State, task: ScopedKey[_],
|
||||
console: Appender, backed: Appender, relay: Appender, extra: List[Appender]): ManagedLogger =
|
||||
{
|
||||
val execOpt = state.currentCommand
|
||||
val loggerName: String = s"${task.key.label}-${generateId.incrementAndGet}"
|
||||
val channelName: Option[String] = execOpt flatMap { e => e.source map { _.channelName } }
|
||||
val execId: Option[String] = execOpt flatMap { _.execId }
|
||||
val log = LogExchange.logger(loggerName, channelName, execId)
|
||||
val scope = task.scope
|
||||
// to change from global being the default to overriding, switch the order of state.get and data.get
|
||||
def getOr[T](key: AttributeKey[T], default: T): T = data.get(scope, key) orElse state.get(key) getOrElse default
|
||||
val screenLevel = getOr(logLevel.key, Level.Info)
|
||||
val backingLevel = getOr(persistLogLevel.key, Level.Debug)
|
||||
val screenTrace = getOr(traceLevel.key, defaultTraceLevel(state))
|
||||
val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue)
|
||||
val extraBacked = state.globalLogging.backed :: relay :: Nil
|
||||
val consoleOpt = consoleLocally(state, console)
|
||||
multiLogger(log, MainAppender.MainAppenderConfig(consoleOpt, backed,
|
||||
extraBacked ::: extra, screenLevel, backingLevel, screenTrace, backingTrace))
|
||||
def defaultLogger(data: Settings[Scope],
|
||||
state: State,
|
||||
task: ScopedKey[_],
|
||||
console: Appender,
|
||||
backed: Appender,
|
||||
relay: Appender,
|
||||
extra: List[Appender]): ManagedLogger = {
|
||||
val execOpt = state.currentCommand
|
||||
val loggerName: String = s"${task.key.label}-${generateId.incrementAndGet}"
|
||||
val channelName: Option[String] = execOpt flatMap { e =>
|
||||
e.source map { _.channelName }
|
||||
}
|
||||
val execId: Option[String] = execOpt flatMap { _.execId }
|
||||
val log = LogExchange.logger(loggerName, channelName, execId)
|
||||
val scope = task.scope
|
||||
// to change from global being the default to overriding, switch the order of state.get and data.get
|
||||
def getOr[T](key: AttributeKey[T], default: T): T =
|
||||
data.get(scope, key) orElse state.get(key) getOrElse default
|
||||
val screenLevel = getOr(logLevel.key, Level.Info)
|
||||
val backingLevel = getOr(persistLogLevel.key, Level.Debug)
|
||||
val screenTrace = getOr(traceLevel.key, defaultTraceLevel(state))
|
||||
val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue)
|
||||
val extraBacked = state.globalLogging.backed :: relay :: Nil
|
||||
val consoleOpt = consoleLocally(state, console)
|
||||
multiLogger(log,
|
||||
MainAppender.MainAppenderConfig(consoleOpt,
|
||||
backed,
|
||||
extraBacked ::: extra,
|
||||
screenLevel,
|
||||
backingLevel,
|
||||
screenTrace,
|
||||
backingTrace))
|
||||
}
|
||||
// Return None if the exec is not from console origin.
|
||||
def consoleLocally(state: State, console: Appender): Option[Appender] =
|
||||
state.currentCommand match {
|
||||
|
|
@ -100,31 +145,39 @@ object LogManager {
|
|||
}
|
||||
def defaultTraceLevel(state: State): Int =
|
||||
if (state.interactive) -1 else Int.MaxValue
|
||||
def suppressedMessage(key: ScopedKey[_], state: State): SuppressedTraceContext => Option[String] =
|
||||
{
|
||||
lazy val display = Project.showContextKey(state)
|
||||
def commandBase = "last " + display.show(unwrapStreamsKey(key))
|
||||
def command(useColor: Boolean) = if (useColor) BLUE + commandBase + RESET else "'" + commandBase + "'"
|
||||
context => Some("Stack trace suppressed: run %s for the full output.".format(command(context.useColor)))
|
||||
}
|
||||
def suppressedMessage(key: ScopedKey[_],
|
||||
state: State): SuppressedTraceContext => Option[String] = {
|
||||
lazy val display = Project.showContextKey(state)
|
||||
def commandBase = "last " + display.show(unwrapStreamsKey(key))
|
||||
def command(useColor: Boolean) =
|
||||
if (useColor) BLUE + commandBase + RESET else "'" + commandBase + "'"
|
||||
context =>
|
||||
Some("Stack trace suppressed: run %s for the full output.".format(command(context.useColor)))
|
||||
}
|
||||
def unwrapStreamsKey(key: ScopedKey[_]): ScopedKey[_] = key.scope.task match {
|
||||
case Select(task) => ScopedKey(key.scope.copy(task = Global), task)
|
||||
case _ => key // should never get here
|
||||
}
|
||||
|
||||
def backgroundLog(data: Settings[Scope], state: State, task: ScopedKey[_],
|
||||
console: Appender, /* TODO: backed: Appender,*/ relay: Appender, extra: List[Appender]): ManagedLogger =
|
||||
{
|
||||
val execOpt = state.currentCommand
|
||||
val loggerName: String = s"bg-${task.key.label}-${generateId.incrementAndGet}"
|
||||
val channelName: Option[String] = execOpt flatMap { e => e.source map { _.channelName } }
|
||||
// val execId: Option[String] = execOpt flatMap { _.execId }
|
||||
val log = LogExchange.logger(loggerName, channelName, None)
|
||||
LogExchange.unbindLoggerAppenders(loggerName)
|
||||
val consoleOpt = consoleLocally(state, console)
|
||||
LogExchange.bindLoggerAppenders(loggerName, (consoleOpt.toList map { _ -> Level.Debug }) ::: (relay -> Level.Debug) :: Nil)
|
||||
log
|
||||
def backgroundLog(data: Settings[Scope],
|
||||
state: State,
|
||||
task: ScopedKey[_],
|
||||
console: Appender, /* TODO: backed: Appender,*/ relay: Appender,
|
||||
extra: List[Appender]): ManagedLogger = {
|
||||
val execOpt = state.currentCommand
|
||||
val loggerName: String = s"bg-${task.key.label}-${generateId.incrementAndGet}"
|
||||
val channelName: Option[String] = execOpt flatMap { e =>
|
||||
e.source map { _.channelName }
|
||||
}
|
||||
// val execId: Option[String] = execOpt flatMap { _.execId }
|
||||
val log = LogExchange.logger(loggerName, channelName, None)
|
||||
LogExchange.unbindLoggerAppenders(loggerName)
|
||||
val consoleOpt = consoleLocally(state, console)
|
||||
LogExchange.bindLoggerAppenders(
|
||||
loggerName,
|
||||
(consoleOpt.toList map { _ -> Level.Debug }) ::: (relay -> Level.Debug) :: Nil)
|
||||
log
|
||||
}
|
||||
|
||||
// TODO: Fix this
|
||||
// if global logging levels are not explicitly set, set them from project settings
|
||||
|
|
@ -167,7 +220,8 @@ object LogManager {
|
|||
private[this] def globalWrapper(s: State): Logger = {
|
||||
new Logger {
|
||||
private[this] val ref = new java.lang.ref.WeakReference(s.globalLogging.full)
|
||||
private[this] def slog: Logger = Option(ref.get) getOrElse sys.error("Settings logger used after project was loaded.")
|
||||
private[this] def slog: Logger =
|
||||
Option(ref.get) getOrElse sys.error("Settings logger used after project was loaded.")
|
||||
|
||||
override val ansiCodesSupported = slog.ansiCodesSupported
|
||||
override def trace(t: => Throwable) = slog.trace(t)
|
||||
|
|
|
|||
|
|
@ -20,58 +20,73 @@ import sbt.io.IO
|
|||
object Output {
|
||||
final val DefaultTail = "> "
|
||||
|
||||
def last(keys: Values[_], streams: Streams, printLines: Seq[String] => Unit, sid: Option[String])(implicit display: Show[ScopedKey[_]]): Unit =
|
||||
def last(keys: Values[_],
|
||||
streams: Streams,
|
||||
printLines: Seq[String] => Unit,
|
||||
sid: Option[String])(implicit display: Show[ScopedKey[_]]): Unit =
|
||||
printLines(flatLines(lastLines(keys, streams, sid))(idFun))
|
||||
|
||||
def last(file: File, printLines: Seq[String] => Unit, tailDelim: String = DefaultTail): Unit =
|
||||
printLines(tailLines(file, tailDelim))
|
||||
|
||||
def lastGrep(keys: Values[_], streams: Streams, patternString: String, printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit =
|
||||
{
|
||||
val pattern = Pattern compile patternString
|
||||
val lines = flatLines(lastLines(keys, streams))(_ flatMap showMatches(pattern))
|
||||
printLines(lines)
|
||||
}
|
||||
def lastGrep(file: File, patternString: String, printLines: Seq[String] => Unit, tailDelim: String = DefaultTail): Unit =
|
||||
def lastGrep(keys: Values[_],
|
||||
streams: Streams,
|
||||
patternString: String,
|
||||
printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit = {
|
||||
val pattern = Pattern compile patternString
|
||||
val lines = flatLines(lastLines(keys, streams))(_ flatMap showMatches(pattern))
|
||||
printLines(lines)
|
||||
}
|
||||
def lastGrep(file: File,
|
||||
patternString: String,
|
||||
printLines: Seq[String] => Unit,
|
||||
tailDelim: String = DefaultTail): Unit =
|
||||
printLines(grep(tailLines(file, tailDelim), patternString))
|
||||
def grep(lines: Seq[String], patternString: String): Seq[String] =
|
||||
lines flatMap showMatches(Pattern compile patternString)
|
||||
|
||||
def flatLines(outputs: Values[Seq[String]])(f: Seq[String] => Seq[String])(implicit display: Show[ScopedKey[_]]): Seq[String] =
|
||||
{
|
||||
val single = outputs.size == 1
|
||||
outputs flatMap {
|
||||
case KeyValue(key, lines) =>
|
||||
val flines = f(lines)
|
||||
if (!single) bold(display.show(key)) +: flines else flines
|
||||
}
|
||||
def flatLines(outputs: Values[Seq[String]])(f: Seq[String] => Seq[String])(
|
||||
implicit display: Show[ScopedKey[_]]): Seq[String] = {
|
||||
val single = outputs.size == 1
|
||||
outputs flatMap {
|
||||
case KeyValue(key, lines) =>
|
||||
val flines = f(lines)
|
||||
if (!single) bold(display.show(key)) +: flines else flines
|
||||
}
|
||||
}
|
||||
|
||||
def lastLines(keys: Values[_], streams: Streams, sid: Option[String] = None): Values[Seq[String]] =
|
||||
{
|
||||
val outputs = keys map { (kv: KeyValue[_]) => KeyValue(kv.key, lastLines(kv.key, streams, sid)) }
|
||||
outputs.filterNot(_.value.isEmpty)
|
||||
def lastLines(keys: Values[_],
|
||||
streams: Streams,
|
||||
sid: Option[String] = None): Values[Seq[String]] = {
|
||||
val outputs = keys map { (kv: KeyValue[_]) =>
|
||||
KeyValue(kv.key, lastLines(kv.key, streams, sid))
|
||||
}
|
||||
outputs.filterNot(_.value.isEmpty)
|
||||
}
|
||||
|
||||
def lastLines(key: ScopedKey[_], mgr: Streams, sid: Option[String]): Seq[String] =
|
||||
mgr.use(key) { s =>
|
||||
// Workaround for #1155 - Keys.streams are always scoped by the task they're included in
|
||||
// but are keyed by the Keys.streams key. I think this isn't actually a workaround, but
|
||||
// is how things are expected to work now.
|
||||
// You can see where streams are injected using their own key scope in
|
||||
// You can see where streams are injected using their own key scope in
|
||||
// EvaluateTask.injectStreams.
|
||||
val streamScopedKey: ScopedKey[_] = ScopedKey(Project.fillTaskAxis(key).scope, Keys.streams.key)
|
||||
val streamScopedKey: ScopedKey[_] =
|
||||
ScopedKey(Project.fillTaskAxis(key).scope, Keys.streams.key)
|
||||
val tmp = s.readText(streamScopedKey, sid)
|
||||
IO.readLines(tmp)
|
||||
}
|
||||
|
||||
def tailLines(file: File, tailDelim: String): Seq[String] = headLines(IO.readLines(file).reverse, tailDelim).reverse
|
||||
def tailLines(file: File, tailDelim: String): Seq[String] =
|
||||
headLines(IO.readLines(file).reverse, tailDelim).reverse
|
||||
|
||||
@tailrec def headLines(lines: Seq[String], tailDelim: String): Seq[String] =
|
||||
if (lines.isEmpty)
|
||||
lines
|
||||
else {
|
||||
val (first, tail) = lines.span { line => !(line startsWith tailDelim) }
|
||||
val (first, tail) = lines.span { line =>
|
||||
!(line startsWith tailDelim)
|
||||
}
|
||||
if (first.isEmpty) headLines(tail drop 1, tailDelim) else first
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import sbt.internal.inc.ModuleUtilities
|
|||
import sbt.io.IO
|
||||
|
||||
object PluginDiscovery {
|
||||
|
||||
/**
|
||||
* Relative paths of resources that list top-level modules that are available.
|
||||
* Normally, the classes for those modules will be in the same classpath entry as the resource.
|
||||
|
|
@ -21,6 +22,7 @@ object PluginDiscovery {
|
|||
final val AutoPlugins = "sbt/sbt.autoplugins"
|
||||
final val Builds = "sbt/sbt.builds"
|
||||
}
|
||||
|
||||
/** Names of top-level modules that subclass sbt plugin-related classes: [[AutoPlugin]], and [[BuildDef]]. */
|
||||
final class DiscoveredNames(val autoPlugins: Seq[String], val builds: Seq[String]) {
|
||||
override def toString: String = s"""DiscoveredNames($autoPlugins, $builds)"""
|
||||
|
|
@ -29,111 +31,117 @@ object PluginDiscovery {
|
|||
def emptyDiscoveredNames: DiscoveredNames = new DiscoveredNames(Nil, Nil)
|
||||
|
||||
/** Discovers and loads the sbt-plugin-related top-level modules from the classpath and source analysis in `data` and using the provided class `loader`. */
|
||||
def discoverAll(data: PluginData, loader: ClassLoader): DetectedPlugins =
|
||||
{
|
||||
def discover[T](resource: String)(implicit classTag: reflect.ClassTag[T]) =
|
||||
binarySourceModules[T](data, loader, resource)
|
||||
import Paths._
|
||||
// TODO - Fix this once we can autodetect AutoPlugins defined by sbt itself.
|
||||
val defaultAutoPlugins = Seq(
|
||||
"sbt.plugins.IvyPlugin" -> sbt.plugins.IvyPlugin,
|
||||
"sbt.plugins.JvmPlugin" -> sbt.plugins.JvmPlugin,
|
||||
"sbt.plugins.CorePlugin" -> sbt.plugins.CorePlugin,
|
||||
"sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin,
|
||||
"sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin
|
||||
)
|
||||
val detectedAutoPlugins = discover[AutoPlugin](AutoPlugins)
|
||||
val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPlugins.modules) map {
|
||||
case (name, value) =>
|
||||
DetectedAutoPlugin(name, value, sbt.Plugins.hasAutoImportGetter(value, loader))
|
||||
}
|
||||
new DetectedPlugins(allAutoPlugins, discover[BuildDef](Builds))
|
||||
def discoverAll(data: PluginData, loader: ClassLoader): DetectedPlugins = {
|
||||
def discover[T](resource: String)(implicit classTag: reflect.ClassTag[T]) =
|
||||
binarySourceModules[T](data, loader, resource)
|
||||
import Paths._
|
||||
// TODO - Fix this once we can autodetect AutoPlugins defined by sbt itself.
|
||||
val defaultAutoPlugins = Seq(
|
||||
"sbt.plugins.IvyPlugin" -> sbt.plugins.IvyPlugin,
|
||||
"sbt.plugins.JvmPlugin" -> sbt.plugins.JvmPlugin,
|
||||
"sbt.plugins.CorePlugin" -> sbt.plugins.CorePlugin,
|
||||
"sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin,
|
||||
"sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin
|
||||
)
|
||||
val detectedAutoPlugins = discover[AutoPlugin](AutoPlugins)
|
||||
val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPlugins.modules) map {
|
||||
case (name, value) =>
|
||||
DetectedAutoPlugin(name, value, sbt.Plugins.hasAutoImportGetter(value, loader))
|
||||
}
|
||||
new DetectedPlugins(allAutoPlugins, discover[BuildDef](Builds))
|
||||
}
|
||||
|
||||
/** Discovers the sbt-plugin-related top-level modules from the provided source `analysis`. */
|
||||
def discoverSourceAll(analysis: CompileAnalysis): DiscoveredNames =
|
||||
{
|
||||
def discover[T](implicit classTag: reflect.ClassTag[T]): Seq[String] =
|
||||
sourceModuleNames(analysis, classTag.runtimeClass.getName)
|
||||
new DiscoveredNames(discover[AutoPlugin], discover[BuildDef])
|
||||
}
|
||||
def discoverSourceAll(analysis: CompileAnalysis): DiscoveredNames = {
|
||||
def discover[T](implicit classTag: reflect.ClassTag[T]): Seq[String] =
|
||||
sourceModuleNames(analysis, classTag.runtimeClass.getName)
|
||||
new DiscoveredNames(discover[AutoPlugin], discover[BuildDef])
|
||||
}
|
||||
|
||||
// TODO: for 0.14.0, consider consolidating into a single file, which would make the classpath search 4x faster
|
||||
/** Writes discovered module `names` to zero or more files in `dir` as per [[writeDescriptor]] and returns the list of files written. */
|
||||
def writeDescriptors(names: DiscoveredNames, dir: File): Seq[File] =
|
||||
{
|
||||
import Paths._
|
||||
val files =
|
||||
writeDescriptor(names.autoPlugins, dir, AutoPlugins) ::
|
||||
writeDescriptor(names.builds, dir, Builds) ::
|
||||
Nil
|
||||
files.flatMap(_.toList)
|
||||
}
|
||||
def writeDescriptors(names: DiscoveredNames, dir: File): Seq[File] = {
|
||||
import Paths._
|
||||
val files =
|
||||
writeDescriptor(names.autoPlugins, dir, AutoPlugins) ::
|
||||
writeDescriptor(names.builds, dir, Builds) ::
|
||||
Nil
|
||||
files.flatMap(_.toList)
|
||||
}
|
||||
|
||||
/** Stores the module `names` in `dir / path`, one per line, unless `names` is empty and then the file is deleted and `None` returned. */
|
||||
def writeDescriptor(names: Seq[String], dir: File, path: String): Option[File] =
|
||||
{
|
||||
val descriptor: File = new File(dir, path)
|
||||
if (names.isEmpty) {
|
||||
IO.delete(descriptor)
|
||||
None
|
||||
} else {
|
||||
IO.writeLines(descriptor, names.distinct.sorted)
|
||||
Some(descriptor)
|
||||
}
|
||||
def writeDescriptor(names: Seq[String], dir: File, path: String): Option[File] = {
|
||||
val descriptor: File = new File(dir, path)
|
||||
if (names.isEmpty) {
|
||||
IO.delete(descriptor)
|
||||
None
|
||||
} else {
|
||||
IO.writeLines(descriptor, names.distinct.sorted)
|
||||
Some(descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Discovers the names of top-level modules listed in resources named `resourceName` as per [[binaryModuleNames]] or
|
||||
* available as analyzed source and extending from any of `subclasses` as per [[sourceModuleNames]].
|
||||
*/
|
||||
def binarySourceModuleNames(classpath: Seq[Attributed[File]], loader: ClassLoader, resourceName: String, subclasses: String*): Seq[String] =
|
||||
def binarySourceModuleNames(classpath: Seq[Attributed[File]],
|
||||
loader: ClassLoader,
|
||||
resourceName: String,
|
||||
subclasses: String*): Seq[String] =
|
||||
(
|
||||
binaryModuleNames(data(classpath), loader, resourceName) ++
|
||||
(analyzed(classpath) flatMap (a => sourceModuleNames(a, subclasses: _*)))
|
||||
(analyzed(classpath) flatMap (a => sourceModuleNames(a, subclasses: _*)))
|
||||
).distinct
|
||||
|
||||
/** Discovers top-level modules in `analysis` that inherit from any of `subclasses`. */
|
||||
def sourceModuleNames(analysis: CompileAnalysis, subclasses: String*): Seq[String] =
|
||||
{
|
||||
val subclassSet = subclasses.toSet
|
||||
val defs = Tests.allDefs(analysis)
|
||||
val ds = Discovery(subclassSet, Set.empty)(defs)
|
||||
ds.flatMap {
|
||||
case (definition, Discovered(subs, _, _, true)) =>
|
||||
if ((subs & subclassSet).isEmpty) Nil else definition.name :: Nil
|
||||
case _ => Nil
|
||||
}
|
||||
def sourceModuleNames(analysis: CompileAnalysis, subclasses: String*): Seq[String] = {
|
||||
val subclassSet = subclasses.toSet
|
||||
val defs = Tests.allDefs(analysis)
|
||||
val ds = Discovery(subclassSet, Set.empty)(defs)
|
||||
ds.flatMap {
|
||||
case (definition, Discovered(subs, _, _, true)) =>
|
||||
if ((subs & subclassSet).isEmpty) Nil else definition.name :: Nil
|
||||
case _ => Nil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the list of modules identified in all resource files `resourceName` from `loader` that are on `classpath`.
|
||||
* `classpath` and `loader` are both required to ensure that `loader`
|
||||
* doesn't bring in any resources outside of the intended `classpath`, such as from parent loaders.
|
||||
*/
|
||||
def binaryModuleNames(classpath: Seq[File], loader: ClassLoader, resourceName: String): Seq[String] =
|
||||
{
|
||||
import collection.JavaConverters._
|
||||
loader.getResources(resourceName).asScala.toSeq.filter(onClasspath(classpath)) flatMap { u =>
|
||||
IO.readLinesURL(u).map(_.trim).filter(!_.isEmpty)
|
||||
}
|
||||
def binaryModuleNames(classpath: Seq[File],
|
||||
loader: ClassLoader,
|
||||
resourceName: String): Seq[String] = {
|
||||
import collection.JavaConverters._
|
||||
loader.getResources(resourceName).asScala.toSeq.filter(onClasspath(classpath)) flatMap { u =>
|
||||
IO.readLinesURL(u).map(_.trim).filter(!_.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns `true` if `url` is an entry in `classpath`.*/
|
||||
def onClasspath(classpath: Seq[File])(url: URL): Boolean =
|
||||
IO.urlAsFile(url) exists (classpath.contains _)
|
||||
|
||||
private[sbt] def binarySourceModules[T](data: PluginData, loader: ClassLoader, resourceName: String)(implicit classTag: reflect.ClassTag[T]): DetectedModules[T] =
|
||||
{
|
||||
val classpath = data.classpath
|
||||
val namesAndValues = if (classpath.isEmpty) Nil else {
|
||||
val names = binarySourceModuleNames(classpath, loader, resourceName, classTag.runtimeClass.getName)
|
||||
private[sbt] def binarySourceModules[T](
|
||||
data: PluginData,
|
||||
loader: ClassLoader,
|
||||
resourceName: String)(implicit classTag: reflect.ClassTag[T]): DetectedModules[T] = {
|
||||
val classpath = data.classpath
|
||||
val namesAndValues =
|
||||
if (classpath.isEmpty) Nil
|
||||
else {
|
||||
val names =
|
||||
binarySourceModuleNames(classpath, loader, resourceName, classTag.runtimeClass.getName)
|
||||
loadModules[T](data, names, loader)
|
||||
}
|
||||
new DetectedModules(namesAndValues)
|
||||
}
|
||||
new DetectedModules(namesAndValues)
|
||||
}
|
||||
|
||||
private[this] def loadModules[T: reflect.ClassTag](data: PluginData, names: Seq[String], loader: ClassLoader): Seq[(String, T)] =
|
||||
private[this] def loadModules[T: reflect.ClassTag](data: PluginData,
|
||||
names: Seq[String],
|
||||
loader: ClassLoader): Seq[(String, T)] =
|
||||
try ModuleUtilities.getCheckedObjects[T](names, loader)
|
||||
catch {
|
||||
case e: ExceptionInInitializerError =>
|
||||
|
|
@ -142,13 +150,18 @@ object PluginDiscovery {
|
|||
case e: LinkageError => incompatiblePlugins(data, e)
|
||||
}
|
||||
|
||||
private[this] def incompatiblePlugins(data: PluginData, t: LinkageError): Nothing =
|
||||
{
|
||||
val evicted = data.report.toList.flatMap(_.configurations.flatMap(_.evicted))
|
||||
val evictedModules = evicted map { id => (id.organization, id.name) } distinct;
|
||||
val evictedStrings = evictedModules map { case (o, n) => o + ":" + n }
|
||||
val msgBase = "Binary incompatibility in plugins detected."
|
||||
val msgExtra = if (evictedStrings.isEmpty) "" else "\nNote that conflicts were resolved for some dependencies:\n\t" + evictedStrings.mkString("\n\t")
|
||||
throw new IncompatiblePluginsException(msgBase + msgExtra, t)
|
||||
}
|
||||
private[this] def incompatiblePlugins(data: PluginData, t: LinkageError): Nothing = {
|
||||
val evicted = data.report.toList.flatMap(_.configurations.flatMap(_.evicted))
|
||||
val evictedModules = evicted map { id =>
|
||||
(id.organization, id.name)
|
||||
} distinct;
|
||||
val evictedStrings = evictedModules map { case (o, n) => o + ":" + n }
|
||||
val msgBase = "Binary incompatibility in plugins detected."
|
||||
val msgExtra =
|
||||
if (evictedStrings.isEmpty) ""
|
||||
else
|
||||
"\nNote that conflicts were resolved for some dependencies:\n\t" + evictedStrings.mkString(
|
||||
"\n\t")
|
||||
throw new IncompatiblePluginsException(msgBase + msgExtra, t)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,17 @@ import sbt.librarymanagement.ModuleID
|
|||
|
||||
import java.net.{ URI, URL, URLClassLoader }
|
||||
|
||||
final case class PluginManagement(overrides: Set[ModuleID], applyOverrides: Set[ModuleID], loader: PluginClassLoader, initialLoader: ClassLoader, context: Context) {
|
||||
final case class PluginManagement(overrides: Set[ModuleID],
|
||||
applyOverrides: Set[ModuleID],
|
||||
loader: PluginClassLoader,
|
||||
initialLoader: ClassLoader,
|
||||
context: Context) {
|
||||
def shift: PluginManagement =
|
||||
PluginManagement(Set.empty, overrides, new PluginClassLoader(initialLoader), initialLoader, context)
|
||||
PluginManagement(Set.empty,
|
||||
overrides,
|
||||
new PluginClassLoader(initialLoader),
|
||||
initialLoader,
|
||||
context)
|
||||
|
||||
def addOverrides(os: Set[ModuleID]): PluginManagement =
|
||||
copy(overrides = overrides ++ os)
|
||||
|
|
@ -22,16 +30,23 @@ final case class PluginManagement(overrides: Set[ModuleID], applyOverrides: Set[
|
|||
Keys.dependencyOverrides ++= overrides
|
||||
)
|
||||
|
||||
def resetDepth: PluginManagement = copy(context = Context(globalPluginProject = false, pluginProjectDepth = 0))
|
||||
def forGlobalPlugin: PluginManagement = copy(context = Context(globalPluginProject = true, pluginProjectDepth = 0))
|
||||
def forPlugin: PluginManagement = copy(context = context.copy(pluginProjectDepth = context.pluginProjectDepth + 1))
|
||||
def resetDepth: PluginManagement =
|
||||
copy(context = Context(globalPluginProject = false, pluginProjectDepth = 0))
|
||||
def forGlobalPlugin: PluginManagement =
|
||||
copy(context = Context(globalPluginProject = true, pluginProjectDepth = 0))
|
||||
def forPlugin: PluginManagement =
|
||||
copy(context = context.copy(pluginProjectDepth = context.pluginProjectDepth + 1))
|
||||
}
|
||||
object PluginManagement {
|
||||
final case class Context private[sbt] (globalPluginProject: Boolean, pluginProjectDepth: Int)
|
||||
val emptyContext: Context = Context(false, 0)
|
||||
|
||||
def apply(initialLoader: ClassLoader): PluginManagement =
|
||||
PluginManagement(Set.empty, Set.empty, new PluginClassLoader(initialLoader), initialLoader, emptyContext)
|
||||
PluginManagement(Set.empty,
|
||||
Set.empty,
|
||||
new PluginClassLoader(initialLoader),
|
||||
initialLoader,
|
||||
emptyContext)
|
||||
|
||||
def extractOverrides(classpath: Classpath): Set[ModuleID] =
|
||||
classpath flatMap { _.metadata get Keys.moduleID.key map keepOverrideInfo } toSet;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ private[sbt] class PluginsDebug(
|
|||
val nameToKey: Map[String, AttributeKey[_]],
|
||||
val provided: Relation[AutoPlugin, AttributeKey[_]]
|
||||
) {
|
||||
|
||||
/**
|
||||
* The set of [[AutoPlugin]]s that might define a key named `keyName`.
|
||||
* Because plugins can define keys in different scopes, this should only be used as a guideline.
|
||||
|
|
@ -28,69 +29,82 @@ private[sbt] class PluginsDebug(
|
|||
providers(keyName).toList.map(plugin => pluginEnable(context, plugin))
|
||||
|
||||
/** Provides text to suggest how `notFoundKey` can be defined in [[Context]]. */
|
||||
def debug(notFoundKey: String, context: Context): String =
|
||||
{
|
||||
val (activated, deactivated) = Util.separate(toEnable(notFoundKey, context)) {
|
||||
case pa: PluginActivated => Left(pa)
|
||||
case pd: EnableDeactivated => Right(pd)
|
||||
}
|
||||
val activePrefix = if (activated.isEmpty) "" else
|
||||
def debug(notFoundKey: String, context: Context): String = {
|
||||
val (activated, deactivated) = Util.separate(toEnable(notFoundKey, context)) {
|
||||
case pa: PluginActivated => Left(pa)
|
||||
case pd: EnableDeactivated => Right(pd)
|
||||
}
|
||||
val activePrefix =
|
||||
if (activated.isEmpty) ""
|
||||
else
|
||||
s"Some already activated plugins define $notFoundKey: ${activated.mkString(", ")}\n"
|
||||
activePrefix + debugDeactivated(notFoundKey, deactivated)
|
||||
}
|
||||
activePrefix + debugDeactivated(notFoundKey, deactivated)
|
||||
}
|
||||
|
||||
private[this] def debugDeactivated(notFoundKey: String, deactivated: Seq[EnableDeactivated]): String =
|
||||
{
|
||||
val (impossible, possible) = Util.separate(deactivated) {
|
||||
case pi: PluginImpossible => Left(pi)
|
||||
case pr: PluginRequirements => Right(pr)
|
||||
}
|
||||
if (possible.nonEmpty) {
|
||||
val explained = possible.map(explainPluginEnable)
|
||||
val possibleString =
|
||||
if (explained.size > 1) explained.zipWithIndex.map { case (s, i) => s"$i. $s" }
|
||||
.mkString(s"Multiple plugins are available that can provide $notFoundKey:\n", "\n", "")
|
||||
else s"$notFoundKey is provided by an available (but not activated) plugin:\n${explained.mkString}"
|
||||
def impossiblePlugins = impossible.map(_.plugin.label).mkString(", ")
|
||||
val imPostfix = if (impossible
|
||||
.isEmpty) "" else s"\n\nThere are other available plugins that provide $notFoundKey, but they are " +
|
||||
s"impossible to add: $impossiblePlugins"
|
||||
possibleString + imPostfix
|
||||
} else if (impossible.isEmpty)
|
||||
s"No available plugin provides key $notFoundKey."
|
||||
else {
|
||||
val explanations = impossible.map(explainPluginEnable)
|
||||
val preamble = s"Plugins are available that could provide $notFoundKey"
|
||||
explanations.mkString(s"$preamble, but they are impossible to add:\n\t", "\n\t", "")
|
||||
}
|
||||
private[this] def debugDeactivated(notFoundKey: String,
|
||||
deactivated: Seq[EnableDeactivated]): String = {
|
||||
val (impossible, possible) = Util.separate(deactivated) {
|
||||
case pi: PluginImpossible => Left(pi)
|
||||
case pr: PluginRequirements => Right(pr)
|
||||
}
|
||||
if (possible.nonEmpty) {
|
||||
val explained = possible.map(explainPluginEnable)
|
||||
val possibleString =
|
||||
if (explained.size > 1)
|
||||
explained.zipWithIndex
|
||||
.map { case (s, i) => s"$i. $s" }
|
||||
.mkString(s"Multiple plugins are available that can provide $notFoundKey:\n", "\n", "")
|
||||
else
|
||||
s"$notFoundKey is provided by an available (but not activated) plugin:\n${explained.mkString}"
|
||||
def impossiblePlugins = impossible.map(_.plugin.label).mkString(", ")
|
||||
val imPostfix =
|
||||
if (impossible.isEmpty) ""
|
||||
else
|
||||
s"\n\nThere are other available plugins that provide $notFoundKey, but they are " +
|
||||
s"impossible to add: $impossiblePlugins"
|
||||
possibleString + imPostfix
|
||||
} else if (impossible.isEmpty)
|
||||
s"No available plugin provides key $notFoundKey."
|
||||
else {
|
||||
val explanations = impossible.map(explainPluginEnable)
|
||||
val preamble = s"Plugins are available that could provide $notFoundKey"
|
||||
explanations.mkString(s"$preamble, but they are impossible to add:\n\t", "\n\t", "")
|
||||
}
|
||||
}
|
||||
|
||||
/** Text that suggests how to activate [[AutoPlugin]] in [[Context]] if possible and if it is not already activated. */
|
||||
def help(plugin: AutoPlugin, context: Context): String =
|
||||
if (context.enabled.contains(plugin)) activatedHelp(plugin) else deactivatedHelp(plugin, context)
|
||||
if (context.enabled.contains(plugin)) activatedHelp(plugin)
|
||||
else deactivatedHelp(plugin, context)
|
||||
|
||||
private def activatedHelp(plugin: AutoPlugin): String =
|
||||
{
|
||||
val prefix = s"${plugin.label} is activated."
|
||||
val keys = provided.forward(plugin)
|
||||
val keysString = if (keys.isEmpty) "" else s"\nIt may affect these keys: ${multi(keys.toList.map(_.label))}"
|
||||
val configs = plugin.projectConfigurations
|
||||
val confsString = if (configs.isEmpty) "" else s"\nIt defines these configurations: ${multi(configs.map(_.name))}"
|
||||
prefix + keysString + confsString
|
||||
}
|
||||
private def activatedHelp(plugin: AutoPlugin): String = {
|
||||
val prefix = s"${plugin.label} is activated."
|
||||
val keys = provided.forward(plugin)
|
||||
val keysString =
|
||||
if (keys.isEmpty) "" else s"\nIt may affect these keys: ${multi(keys.toList.map(_.label))}"
|
||||
val configs = plugin.projectConfigurations
|
||||
val confsString =
|
||||
if (configs.isEmpty) ""
|
||||
else s"\nIt defines these configurations: ${multi(configs.map(_.name))}"
|
||||
prefix + keysString + confsString
|
||||
}
|
||||
|
||||
private def deactivatedHelp(plugin: AutoPlugin, context: Context): String =
|
||||
{
|
||||
val prefix = s"${plugin.label} is NOT activated."
|
||||
val keys = provided.forward(plugin)
|
||||
val keysString = if (keys.isEmpty) "" else s"\nActivating it may affect these keys: ${multi(keys.toList.map(_.label))}"
|
||||
val configs = plugin.projectConfigurations
|
||||
val confsString = if (configs.isEmpty) "" else s"\nActivating it will define these configurations: ${multi(configs.map(_.name))}"
|
||||
val toActivate = explainPluginEnable(pluginEnable(context, plugin))
|
||||
s"$prefix$keysString$confsString\n$toActivate"
|
||||
}
|
||||
private def deactivatedHelp(plugin: AutoPlugin, context: Context): String = {
|
||||
val prefix = s"${plugin.label} is NOT activated."
|
||||
val keys = provided.forward(plugin)
|
||||
val keysString =
|
||||
if (keys.isEmpty) ""
|
||||
else s"\nActivating it may affect these keys: ${multi(keys.toList.map(_.label))}"
|
||||
val configs = plugin.projectConfigurations
|
||||
val confsString =
|
||||
if (configs.isEmpty) ""
|
||||
else s"\nActivating it will define these configurations: ${multi(configs.map(_.name))}"
|
||||
val toActivate = explainPluginEnable(pluginEnable(context, plugin))
|
||||
s"$prefix$keysString$confsString\n$toActivate"
|
||||
}
|
||||
|
||||
private[this] def multi(strs: Seq[String]): String = strs.mkString(if (strs.size > 4) "\n\t" else ", ")
|
||||
private[this] def multi(strs: Seq[String]): String =
|
||||
strs.mkString(if (strs.size > 4) "\n\t" else ", ")
|
||||
}
|
||||
|
||||
private[sbt] object PluginsDebug {
|
||||
|
|
@ -98,71 +112,84 @@ private[sbt] object PluginsDebug {
|
|||
if (Project.isProjectLoaded(s)) {
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
def helpBuild(uri: URI, build: LoadedBuildUnit): String =
|
||||
{
|
||||
val pluginStrings = for (plugin <- availableAutoPlugins(build)) yield {
|
||||
val activatedIn = build.defined.values.toList.filter(_.autoPlugins.contains(plugin)).map(_.id)
|
||||
val actString = if (activatedIn.nonEmpty) activatedIn.mkString(": enabled in ", ", ", "") else "" // TODO: deal with large builds
|
||||
s"\n\t${plugin.label}$actString"
|
||||
}
|
||||
s"In $uri${pluginStrings.mkString}"
|
||||
def helpBuild(uri: URI, build: LoadedBuildUnit): String = {
|
||||
val pluginStrings = for (plugin <- availableAutoPlugins(build)) yield {
|
||||
val activatedIn =
|
||||
build.defined.values.toList.filter(_.autoPlugins.contains(plugin)).map(_.id)
|
||||
val actString =
|
||||
if (activatedIn.nonEmpty) activatedIn.mkString(": enabled in ", ", ", "")
|
||||
else "" // TODO: deal with large builds
|
||||
s"\n\t${plugin.label}$actString"
|
||||
}
|
||||
s"In $uri${pluginStrings.mkString}"
|
||||
}
|
||||
val buildStrings = for ((uri, build) <- structure.units) yield helpBuild(uri, build)
|
||||
buildStrings.mkString("\n")
|
||||
} else "No project is currently loaded."
|
||||
|
||||
def autoPluginMap(s: State): Map[String, AutoPlugin] =
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
structure.units.values.toList.flatMap(availableAutoPlugins).map(plugin => (plugin.label, plugin)).toMap
|
||||
}
|
||||
def autoPluginMap(s: State): Map[String, AutoPlugin] = {
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
structure.units.values.toList
|
||||
.flatMap(availableAutoPlugins)
|
||||
.map(plugin => (plugin.label, plugin))
|
||||
.toMap
|
||||
}
|
||||
|
||||
private[this] def availableAutoPlugins(build: LoadedBuildUnit): Seq[AutoPlugin] =
|
||||
build.unit.plugins.detected.autoPlugins map { _.value }
|
||||
|
||||
def help(plugin: AutoPlugin, s: State): String =
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
def definesPlugin(p: ResolvedProject): Boolean = p.autoPlugins.contains(plugin)
|
||||
def projectForRef(ref: ProjectRef): ResolvedProject = get(Keys.thisProject in ref)
|
||||
val perBuild: Map[URI, Set[AutoPlugin]] = structure.units.mapValues(unit => availableAutoPlugins(unit).toSet)
|
||||
val pluginsThisBuild = perBuild.getOrElse(currentRef.build, Set.empty).toList
|
||||
lazy val context = Context(currentProject.plugins, currentProject.autoPlugins, Plugins.deducer(pluginsThisBuild), pluginsThisBuild, s.log)
|
||||
lazy val debug = PluginsDebug(context.available)
|
||||
if (!pluginsThisBuild.contains(plugin)) {
|
||||
val availableInBuilds: List[URI] = perBuild.toList.filter(_._2(plugin)).map(_._1)
|
||||
val s1 = s"Plugin ${plugin.label} is only available in builds:"
|
||||
val s2 = availableInBuilds.mkString("\n\t")
|
||||
val s3 = s"Switch to a project in one of those builds using `project` and rerun this command for more information."
|
||||
s"$s1\n\t$s2\n$s3"
|
||||
} else if (definesPlugin(currentProject))
|
||||
debug.activatedHelp(plugin)
|
||||
else {
|
||||
val thisAggregated = BuildUtil.dependencies(structure.units).aggregateTransitive.getOrElse(currentRef, Nil)
|
||||
val definedInAggregated = thisAggregated.filter(ref => definesPlugin(projectForRef(ref)))
|
||||
if (definedInAggregated.nonEmpty) {
|
||||
val projectNames = definedInAggregated.map(_.project) // TODO: usually in this build, but could technically require the build to be qualified
|
||||
val s2 = projectNames.mkString("\n\t")
|
||||
s"Plugin ${plugin.label} is not activated on this project, but this project aggregates projects where it is activated:\n\t$s2"
|
||||
} else {
|
||||
val base = debug.deactivatedHelp(plugin, context)
|
||||
val aggNote = if (thisAggregated.nonEmpty) "Note: This project aggregates other projects and this" else "Note: This"
|
||||
val common = " information is for this project only."
|
||||
val helpOther = "To see how to activate this plugin for another project, change to the project using `project <name>` and rerun this command."
|
||||
s"$base\n$aggNote$common\n$helpOther"
|
||||
}
|
||||
def help(plugin: AutoPlugin, s: State): String = {
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
def definesPlugin(p: ResolvedProject): Boolean = p.autoPlugins.contains(plugin)
|
||||
def projectForRef(ref: ProjectRef): ResolvedProject = get(Keys.thisProject in ref)
|
||||
val perBuild: Map[URI, Set[AutoPlugin]] =
|
||||
structure.units.mapValues(unit => availableAutoPlugins(unit).toSet)
|
||||
val pluginsThisBuild = perBuild.getOrElse(currentRef.build, Set.empty).toList
|
||||
lazy val context = Context(currentProject.plugins,
|
||||
currentProject.autoPlugins,
|
||||
Plugins.deducer(pluginsThisBuild),
|
||||
pluginsThisBuild,
|
||||
s.log)
|
||||
lazy val debug = PluginsDebug(context.available)
|
||||
if (!pluginsThisBuild.contains(plugin)) {
|
||||
val availableInBuilds: List[URI] = perBuild.toList.filter(_._2(plugin)).map(_._1)
|
||||
val s1 = s"Plugin ${plugin.label} is only available in builds:"
|
||||
val s2 = availableInBuilds.mkString("\n\t")
|
||||
val s3 =
|
||||
s"Switch to a project in one of those builds using `project` and rerun this command for more information."
|
||||
s"$s1\n\t$s2\n$s3"
|
||||
} else if (definesPlugin(currentProject))
|
||||
debug.activatedHelp(plugin)
|
||||
else {
|
||||
val thisAggregated =
|
||||
BuildUtil.dependencies(structure.units).aggregateTransitive.getOrElse(currentRef, Nil)
|
||||
val definedInAggregated = thisAggregated.filter(ref => definesPlugin(projectForRef(ref)))
|
||||
if (definedInAggregated.nonEmpty) {
|
||||
val projectNames = definedInAggregated.map(_.project) // TODO: usually in this build, but could technically require the build to be qualified
|
||||
val s2 = projectNames.mkString("\n\t")
|
||||
s"Plugin ${plugin.label} is not activated on this project, but this project aggregates projects where it is activated:\n\t$s2"
|
||||
} else {
|
||||
val base = debug.deactivatedHelp(plugin, context)
|
||||
val aggNote =
|
||||
if (thisAggregated.nonEmpty) "Note: This project aggregates other projects and this"
|
||||
else "Note: This"
|
||||
val common = " information is for this project only."
|
||||
val helpOther =
|
||||
"To see how to activate this plugin for another project, change to the project using `project <name>` and rerun this command."
|
||||
s"$base\n$aggNote$common\n$helpOther"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Pre-computes information for debugging plugins. */
|
||||
def apply(available: List[AutoPlugin]): PluginsDebug =
|
||||
{
|
||||
val keyR = definedKeys(available)
|
||||
val nameToKey: Map[String, AttributeKey[_]] = keyR._2s.toList.map(key => (key.label, key)).toMap
|
||||
new PluginsDebug(available, nameToKey, keyR)
|
||||
}
|
||||
def apply(available: List[AutoPlugin]): PluginsDebug = {
|
||||
val keyR = definedKeys(available)
|
||||
val nameToKey: Map[String, AttributeKey[_]] =
|
||||
keyR._2s.toList.map(key => (key.label, key)).toMap
|
||||
new PluginsDebug(available, nameToKey, keyR)
|
||||
}
|
||||
|
||||
/**
|
||||
* The context for debugging a plugin (de)activation.
|
||||
|
|
@ -188,7 +215,9 @@ private[sbt] object PluginsDebug {
|
|||
sealed abstract class EnableDeactivated extends PluginEnable
|
||||
|
||||
/** Describes a [[plugin]] that cannot be activated in a [[context]] due to [[contradictions]] in requirements. */
|
||||
final case class PluginImpossible(plugin: AutoPlugin, context: Context, contradictions: Set[AutoPlugin])
|
||||
final case class PluginImpossible(plugin: AutoPlugin,
|
||||
context: Context,
|
||||
contradictions: Set[AutoPlugin])
|
||||
extends EnableDeactivated
|
||||
|
||||
/**
|
||||
|
|
@ -220,7 +249,9 @@ private[sbt] object PluginsDebug {
|
|||
* affecting the other plugin. If empty, a direct exclusion is required.
|
||||
* @param newlySelected If false, this plugin was selected in the original context.
|
||||
*/
|
||||
final case class DeactivatePlugin(plugin: AutoPlugin, removeOneOf: Set[AutoPlugin], newlySelected: Boolean)
|
||||
final case class DeactivatePlugin(plugin: AutoPlugin,
|
||||
removeOneOf: Set[AutoPlugin],
|
||||
newlySelected: Boolean)
|
||||
|
||||
/** Determines how to enable [[AutoPlugin]] in [[Context]]. */
|
||||
def pluginEnable(context: Context, plugin: AutoPlugin): PluginEnable =
|
||||
|
|
@ -252,7 +283,7 @@ private[sbt] object PluginsDebug {
|
|||
C :- B
|
||||
initial: <empty>
|
||||
propose: B, exclude C
|
||||
*/
|
||||
*/
|
||||
|
||||
// `plugin` will only be activated when all of these plugins are activated
|
||||
// Deactivating any one of these would deactivate `plugin`.
|
||||
|
|
@ -293,7 +324,8 @@ private[sbt] object PluginsDebug {
|
|||
// If both A and B must be deactivated, but A transitively depends on B, deactivating B will deactivate A.
|
||||
// If A must be deactivated, but one if its (transitively) required plugins isn't present, it won't be activated.
|
||||
// So, in either of these cases, A doesn't need to be considered further and won't be included in this set.
|
||||
val minDeactivate = minAbsentPlugins.filter(p => Plugins.satisfied(p.requires, incrementalModel))
|
||||
val minDeactivate =
|
||||
minAbsentPlugins.filter(p => Plugins.satisfied(p.requires, incrementalModel))
|
||||
|
||||
val deactivate = for (d <- minDeactivate.toList) yield {
|
||||
// removing any one of these plugins will deactivate `d`. TODO: This is not an especially efficient implementation.
|
||||
|
|
@ -305,55 +337,72 @@ private[sbt] object PluginsDebug {
|
|||
DeactivatePlugin(d, removeToDeactivate, newlySelected)
|
||||
}
|
||||
|
||||
PluginRequirements(plugin, context, blockingExcludes, addToExistingPlugins, extraPlugins, willRemove, deactivate)
|
||||
PluginRequirements(plugin,
|
||||
context,
|
||||
blockingExcludes,
|
||||
addToExistingPlugins,
|
||||
extraPlugins,
|
||||
willRemove,
|
||||
deactivate)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def includeAll[T <: Basic](basic: Set[T]): Plugins = And(basic.toList)
|
||||
private[this] def excludeAll(plugins: Set[AutoPlugin]): Plugins = And(plugins map (p => Exclude(p)) toList)
|
||||
private[this] def excludeAll(plugins: Set[AutoPlugin]): Plugins =
|
||||
And(plugins map (p => Exclude(p)) toList)
|
||||
|
||||
private[this] def excludes(bs: Seq[Basic]): Set[AutoPlugin] = bs.collect { case Exclude(b) => b }.toSet
|
||||
private[this] def plugins(bs: Seq[Basic]): Set[AutoPlugin] = bs.collect { case n: AutoPlugin => n }.toSet
|
||||
private[this] def excludes(bs: Seq[Basic]): Set[AutoPlugin] =
|
||||
bs.collect { case Exclude(b) => b }.toSet
|
||||
private[this] def plugins(bs: Seq[Basic]): Set[AutoPlugin] =
|
||||
bs.collect { case n: AutoPlugin => n }.toSet
|
||||
|
||||
// If there is a model that includes `plugin`, it includes at least what is returned by this method.
|
||||
// This is the list of plugins that must be included as well as list of plugins that must not be present.
|
||||
// It might not be valid, such as if there are contradictions or if there are cycles that are unsatisfiable.
|
||||
// The actual model might be larger, since other plugins might be enabled by the selected plugins.
|
||||
private[this] def minimalModel(plugin: AutoPlugin): Seq[Basic] = Dag.topologicalSortUnchecked(plugin: Basic) {
|
||||
case _: Exclude => Nil
|
||||
case ap: AutoPlugin => Plugins.flatten(ap.requires) :+ plugin
|
||||
}
|
||||
private[this] def minimalModel(plugin: AutoPlugin): Seq[Basic] =
|
||||
Dag.topologicalSortUnchecked(plugin: Basic) {
|
||||
case _: Exclude => Nil
|
||||
case ap: AutoPlugin => Plugins.flatten(ap.requires) :+ plugin
|
||||
}
|
||||
|
||||
/** String representation of [[PluginEnable]], intended for end users. */
|
||||
def explainPluginEnable(ps: PluginEnable): String =
|
||||
ps match {
|
||||
case PluginRequirements(plugin, context, blockingExcludes, enablingPlugins, extraEnabledPlugins, toBeRemoved, deactivate) =>
|
||||
case PluginRequirements(plugin,
|
||||
context,
|
||||
blockingExcludes,
|
||||
enablingPlugins,
|
||||
extraEnabledPlugins,
|
||||
toBeRemoved,
|
||||
deactivate) =>
|
||||
def indent(str: String) = if (str.isEmpty) "" else s"\t$str"
|
||||
def note(str: String) = if (str.isEmpty) "" else s"Note: $str"
|
||||
val parts =
|
||||
indent(excludedError(false /* TODO */ , blockingExcludes.toList)) ::
|
||||
indent(excludedError(false /* TODO */, blockingExcludes.toList)) ::
|
||||
indent(required(enablingPlugins.toList)) ::
|
||||
indent(needToDeactivate(deactivate)) ::
|
||||
note(willAdd(plugin, extraEnabledPlugins.toList)) ::
|
||||
note(willRemove(plugin, toBeRemoved.toList)) ::
|
||||
Nil
|
||||
parts.filterNot(_.isEmpty).mkString("\n")
|
||||
case PluginImpossible(plugin, context, contradictions) => pluginImpossible(plugin, contradictions)
|
||||
case PluginActivated(plugin, context) => s"Plugin ${plugin.label} already activated."
|
||||
case PluginImpossible(plugin, context, contradictions) =>
|
||||
pluginImpossible(plugin, contradictions)
|
||||
case PluginActivated(plugin, context) => s"Plugin ${plugin.label} already activated."
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a [[Relation]] between plugins and the keys they potentially define.
|
||||
* Because plugins can define keys in different scopes and keys can be overridden, this is not definitive.
|
||||
*/
|
||||
def definedKeys(available: List[AutoPlugin]): Relation[AutoPlugin, AttributeKey[_]] =
|
||||
{
|
||||
def extractDefinedKeys(ss: Seq[Setting[_]]): Seq[AttributeKey[_]] =
|
||||
ss.map(_.key.key)
|
||||
def allSettings(p: AutoPlugin): Seq[Setting[_]] = p.projectSettings ++ p.buildSettings ++ p.globalSettings
|
||||
val empty = Relation.empty[AutoPlugin, AttributeKey[_]]
|
||||
(empty /: available)((r, p) => r + (p, extractDefinedKeys(allSettings(p))))
|
||||
}
|
||||
def definedKeys(available: List[AutoPlugin]): Relation[AutoPlugin, AttributeKey[_]] = {
|
||||
def extractDefinedKeys(ss: Seq[Setting[_]]): Seq[AttributeKey[_]] =
|
||||
ss.map(_.key.key)
|
||||
def allSettings(p: AutoPlugin): Seq[Setting[_]] =
|
||||
p.projectSettings ++ p.buildSettings ++ p.globalSettings
|
||||
val empty = Relation.empty[AutoPlugin, AttributeKey[_]]
|
||||
(empty /: available)((r, p) => r + (p, extractDefinedKeys(allSettings(p))))
|
||||
}
|
||||
|
||||
private[this] def excludedError(transitive: Boolean, dependencies: List[AutoPlugin]): String =
|
||||
str(dependencies)(excludedPluginError(transitive), excludedPluginsError(transitive))
|
||||
|
|
@ -362,7 +411,8 @@ private[sbt] object PluginsDebug {
|
|||
s"Required ${transitiveString(transitive)}dependency ${dependency.label} was excluded."
|
||||
|
||||
private[this] def excludedPluginsError(transitive: Boolean)(dependencies: List[AutoPlugin]) =
|
||||
s"Required ${transitiveString(transitive)}dependencies were excluded:\n\t${labels(dependencies).mkString("\n\t")}"
|
||||
s"Required ${transitiveString(transitive)}dependencies were excluded:\n\t${labels(dependencies)
|
||||
.mkString("\n\t")}"
|
||||
|
||||
private[this] def transitiveString(transitive: Boolean) =
|
||||
if (transitive) "(transitive) " else ""
|
||||
|
|
@ -413,28 +463,29 @@ private[sbt] object PluginsDebug {
|
|||
private[this] def deactivate1(deactivate: DeactivatePlugin): String =
|
||||
s"Need to deactivate ${deactivateString(deactivate)}"
|
||||
|
||||
private[this] def deactivateString(d: DeactivatePlugin): String =
|
||||
{
|
||||
val removePluginsString: String =
|
||||
d.removeOneOf.toList match {
|
||||
case Nil => ""
|
||||
case x :: Nil => s" or no longer include $x"
|
||||
case xs => s" or remove one of ${xs.mkString(", ")}"
|
||||
}
|
||||
s"${d.plugin.label}: directly exclude it${removePluginsString}"
|
||||
}
|
||||
private[this] def deactivateString(d: DeactivatePlugin): String = {
|
||||
val removePluginsString: String =
|
||||
d.removeOneOf.toList match {
|
||||
case Nil => ""
|
||||
case x :: Nil => s" or no longer include $x"
|
||||
case xs => s" or remove one of ${xs.mkString(", ")}"
|
||||
}
|
||||
s"${d.plugin.label}: directly exclude it${removePluginsString}"
|
||||
}
|
||||
|
||||
private[this] def pluginImpossible(plugin: AutoPlugin, contradictions: Set[AutoPlugin]): String =
|
||||
str(contradictions.toList)(pluginImpossible1(plugin), pluginImpossibleN(plugin))
|
||||
|
||||
private[this] def pluginImpossible1(plugin: AutoPlugin)(contradiction: AutoPlugin): String = {
|
||||
val s1 = s"There is no way to enable plugin ${plugin.label}."
|
||||
val s2 = s"It (or its dependencies) requires plugin ${contradiction.label} to both be present and absent."
|
||||
val s2 =
|
||||
s"It (or its dependencies) requires plugin ${contradiction.label} to both be present and absent."
|
||||
val s3 = s"Please report the problem to the plugin's author."
|
||||
s"$s1 $s2 $s3"
|
||||
}
|
||||
|
||||
private[this] def pluginImpossibleN(plugin: AutoPlugin)(contradictions: List[AutoPlugin]): String = {
|
||||
private[this] def pluginImpossibleN(plugin: AutoPlugin)(
|
||||
contradictions: List[AutoPlugin]): String = {
|
||||
val s1 = s"There is no way to enable plugin ${plugin.label}."
|
||||
val s2 = s"It (or its dependencies) requires these plugins to be both present and absent:"
|
||||
val s3 = s"Please report the problem to the plugin's author."
|
||||
|
|
|
|||
|
|
@ -12,18 +12,18 @@ import Project.updateCurrent
|
|||
|
||||
object ProjectNavigation {
|
||||
def command(s: State): Parser[() => State] =
|
||||
if (s get sessionSettings isEmpty) failure("No project loaded") else (new ProjectNavigation(s)).command
|
||||
if (s get sessionSettings isEmpty) failure("No project loaded")
|
||||
else (new ProjectNavigation(s)).command
|
||||
}
|
||||
|
||||
final class ProjectNavigation(s: State) {
|
||||
val extracted: Extracted = Project extract s
|
||||
import extracted.{ currentRef, structure, session }
|
||||
|
||||
def setProject(nuri: URI, nid: String): State =
|
||||
{
|
||||
val neval = if (currentRef.build == nuri) session.currentEval else mkEval(nuri)
|
||||
updateCurrent(s.put(sessionSettings, session.setCurrent(nuri, nid, neval)))
|
||||
}
|
||||
def setProject(nuri: URI, nid: String): State = {
|
||||
val neval = if (currentRef.build == nuri) session.currentEval else mkEval(nuri)
|
||||
updateCurrent(s.put(sessionSettings, session.setCurrent(nuri, nid, neval)))
|
||||
}
|
||||
|
||||
def mkEval(nuri: URI): () => Eval = Load.lazyEval(structure.units(nuri).unit)
|
||||
def getRoot(uri: URI): String = Load.getRootProject(structure.units)(uri)
|
||||
|
|
@ -44,7 +44,8 @@ final class ProjectNavigation(s: State) {
|
|||
if (structure.units(uri).defined.contains(to))
|
||||
setProject(uri, to)
|
||||
else
|
||||
fail(s"Invalid project name '$to' in build $uri (type 'projects' to list available projects).")
|
||||
fail(
|
||||
s"Invalid project name '$to' in build $uri (type 'projects' to list available projects).")
|
||||
|
||||
def changeBuild(newBuild: URI): State =
|
||||
if (structure.units contains newBuild)
|
||||
|
|
@ -56,12 +57,11 @@ final class ProjectNavigation(s: State) {
|
|||
|
||||
import Parser._, complete.Parsers._
|
||||
|
||||
val parser: Parser[Option[ResolvedReference]] =
|
||||
{
|
||||
val reference = Act.resolvedReference(structure.index.keyIndex, currentRef.build, success(()))
|
||||
val root = token('/' ^^^ rootRef)
|
||||
success(None) | some(token(Space) ~> (root | reference))
|
||||
}
|
||||
val parser: Parser[Option[ResolvedReference]] = {
|
||||
val reference = Act.resolvedReference(structure.index.keyIndex, currentRef.build, success(()))
|
||||
val root = token('/' ^^^ rootRef)
|
||||
success(None) | some(token(Space) ~> (root | reference))
|
||||
}
|
||||
|
||||
def rootRef = ProjectRef(currentRef.build, getRoot(currentRef.build))
|
||||
|
||||
|
|
|
|||
|
|
@ -12,43 +12,45 @@ import sbt.protocol.LogEvent
|
|||
import sbt.internal.util.codec._
|
||||
import scala.json.ast.unsafe._
|
||||
|
||||
class RelayAppender(name: String) extends AbstractAppender(name, null, PatternLayout.createDefaultLayout(), true) {
|
||||
class RelayAppender(name: String)
|
||||
extends AbstractAppender(name, null, PatternLayout.createDefaultLayout(), true) {
|
||||
lazy val exchange = StandardMain.exchange
|
||||
lazy val jsonFormat = new sjsonnew.BasicJsonProtocol
|
||||
with JValueFormats {}
|
||||
lazy val jsonFormat = new sjsonnew.BasicJsonProtocol with JValueFormats {}
|
||||
|
||||
def append(event: XLogEvent): Unit =
|
||||
{
|
||||
val level = ConsoleAppender.toLevel(event.getLevel)
|
||||
val message = event.getMessage
|
||||
message match {
|
||||
case o: ObjectMessage => appendEvent(level, o.getParameter)
|
||||
case p: ParameterizedMessage => appendLog(level, p.getFormattedMessage)
|
||||
case r: RingBufferLogEvent => appendLog(level, r.getFormattedMessage)
|
||||
case _ => appendLog(level, message.toString)
|
||||
}
|
||||
def append(event: XLogEvent): Unit = {
|
||||
val level = ConsoleAppender.toLevel(event.getLevel)
|
||||
val message = event.getMessage
|
||||
message match {
|
||||
case o: ObjectMessage => appendEvent(level, o.getParameter)
|
||||
case p: ParameterizedMessage => appendLog(level, p.getFormattedMessage)
|
||||
case r: RingBufferLogEvent => appendLog(level, r.getFormattedMessage)
|
||||
case _ => appendLog(level, message.toString)
|
||||
}
|
||||
}
|
||||
def appendLog(level: Level.Value, message: => String): Unit = {
|
||||
exchange.publishEventMessage(LogEvent(level.toString, message))
|
||||
}
|
||||
def appendEvent(level: Level.Value, event: AnyRef): Unit =
|
||||
event match {
|
||||
case x: StringEvent =>
|
||||
{
|
||||
import JsonProtocol._
|
||||
exchange.publishEvent(x: AbstractEntry)
|
||||
}
|
||||
case x: ObjectEvent[_] =>
|
||||
{
|
||||
import jsonFormat._
|
||||
val json = JObject(JField("type", JString(x.contentType)), (
|
||||
Vector(JField("message", x.json),
|
||||
JField("level", JString(x.level.toString))) ++
|
||||
(x.channelName.toVector map { channelName => JField("channelName", JString(channelName)) }) ++
|
||||
(x.execId.toVector map { execId => JField("execId", JString(execId)) })): _*)
|
||||
exchange.publishEvent(json: JValue)
|
||||
}
|
||||
case _ =>
|
||||
case x: StringEvent => {
|
||||
import JsonProtocol._
|
||||
exchange.publishEvent(x: AbstractEntry)
|
||||
}
|
||||
case x: ObjectEvent[_] => {
|
||||
import jsonFormat._
|
||||
val json = JObject(
|
||||
JField("type", JString(x.contentType)),
|
||||
(Vector(JField("message", x.json), JField("level", JString(x.level.toString))) ++
|
||||
(x.channelName.toVector map { channelName =>
|
||||
JField("channelName", JString(channelName))
|
||||
}) ++
|
||||
(x.execId.toVector map { execId =>
|
||||
JField("execId", JString(execId))
|
||||
})): _*
|
||||
)
|
||||
exchange.publishEvent(json: JValue)
|
||||
}
|
||||
case _ =>
|
||||
println(s"appendEvent: ${event.getClass}")
|
||||
()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,21 @@ package internal
|
|||
import sbt.internal.util.AttributeKey
|
||||
|
||||
object Resolve {
|
||||
def apply(index: BuildUtil[_], current: ScopeAxis[Reference], key: AttributeKey[_], mask: ScopeMask): Scope => Scope =
|
||||
{
|
||||
val rs =
|
||||
resolveProject(current, mask) _ ::
|
||||
resolveExtra(mask) _ ::
|
||||
resolveTask(mask) _ ::
|
||||
resolveConfig(index, key, mask) _ ::
|
||||
Nil
|
||||
scope => (scope /: rs) { (s, f) => f(s) }
|
||||
}
|
||||
def apply(index: BuildUtil[_],
|
||||
current: ScopeAxis[Reference],
|
||||
key: AttributeKey[_],
|
||||
mask: ScopeMask): Scope => Scope = {
|
||||
val rs =
|
||||
resolveProject(current, mask) _ ::
|
||||
resolveExtra(mask) _ ::
|
||||
resolveTask(mask) _ ::
|
||||
resolveConfig(index, key, mask) _ ::
|
||||
Nil
|
||||
scope =>
|
||||
(scope /: rs) { (s, f) =>
|
||||
f(s)
|
||||
}
|
||||
}
|
||||
def resolveTask(mask: ScopeMask)(scope: Scope): Scope =
|
||||
if (mask.task) scope else scope.copy(task = Global)
|
||||
|
||||
|
|
@ -23,7 +28,8 @@ object Resolve {
|
|||
def resolveExtra(mask: ScopeMask)(scope: Scope): Scope =
|
||||
if (mask.extra) scope else scope.copy(extra = Global)
|
||||
|
||||
def resolveConfig[P](index: BuildUtil[P], key: AttributeKey[_], mask: ScopeMask)(scope: Scope): Scope =
|
||||
def resolveConfig[P](index: BuildUtil[P], key: AttributeKey[_], mask: ScopeMask)(
|
||||
scope: Scope): Scope =
|
||||
if (mask.config)
|
||||
scope
|
||||
else {
|
||||
|
|
@ -36,9 +42,11 @@ object Resolve {
|
|||
}
|
||||
val task = scope.task.toOption
|
||||
val keyIndex = index.keyIndex
|
||||
val definesKey = (c: ScopeAxis[ConfigKey]) => keyIndex.keys(resolvedRef, c.toOption.map(_.name), task) contains key.label
|
||||
val definesKey = (c: ScopeAxis[ConfigKey]) =>
|
||||
keyIndex.keys(resolvedRef, c.toOption.map(_.name), task) contains key.label
|
||||
val projectConfigs = index.configurations(proj).map(ck => Select(ck))
|
||||
val config: ScopeAxis[ConfigKey] = (Global +: projectConfigs) find definesKey getOrElse Global
|
||||
val config
|
||||
: ScopeAxis[ConfigKey] = (Global +: projectConfigs) find definesKey getOrElse Global
|
||||
scope.copy(config = config)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,18 +9,17 @@ import java.net.URI
|
|||
import sbt.internal.BuildLoader.ResolveInfo
|
||||
|
||||
object RetrieveUnit {
|
||||
def apply(info: ResolveInfo): Option[() => File] =
|
||||
{
|
||||
info.uri match {
|
||||
case Scheme("svn") | Scheme("svn+ssh") => Resolvers.subversion(info)
|
||||
case Scheme("hg") => Resolvers.mercurial(info)
|
||||
case Scheme("git") => Resolvers.git(info)
|
||||
case Path(path) if path.endsWith(".git") => Resolvers.git(info)
|
||||
case Scheme("http") | Scheme("https") | Scheme("ftp") => Resolvers.remote(info)
|
||||
case Scheme("file") => Resolvers.local(info)
|
||||
case _ => None
|
||||
}
|
||||
def apply(info: ResolveInfo): Option[() => File] = {
|
||||
info.uri match {
|
||||
case Scheme("svn") | Scheme("svn+ssh") => Resolvers.subversion(info)
|
||||
case Scheme("hg") => Resolvers.mercurial(info)
|
||||
case Scheme("git") => Resolvers.git(info)
|
||||
case Path(path) if path.endsWith(".git") => Resolvers.git(info)
|
||||
case Scheme("http") | Scheme("https") | Scheme("ftp") => Resolvers.remote(info)
|
||||
case Scheme("file") => Resolvers.local(info)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
object Scheme {
|
||||
def unapply(uri: URI) = Option(uri.getScheme)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ object Script {
|
|||
final val Name = "script"
|
||||
lazy val command =
|
||||
Command.command(Name) { state =>
|
||||
val scriptArg = state.remainingCommands.headOption map { _.commandLine } getOrElse sys.error("No script file specified")
|
||||
val scriptArg = state.remainingCommands.headOption map { _.commandLine } getOrElse sys.error(
|
||||
"No script file specified")
|
||||
val scriptFile = new File(scriptArg).getAbsoluteFile
|
||||
val hash = Hash.halve(Hash.toHex(Hash(scriptFile.getAbsolutePath)))
|
||||
val base = new File(CommandUtil.bootDirectory(state), hash)
|
||||
|
|
@ -45,8 +46,14 @@ object Script {
|
|||
}
|
||||
val scriptAsSource = sources in Compile := script :: Nil
|
||||
val asScript = scalacOptions ++= Seq("-Xscript", script.getName.stripSuffix(".scala"))
|
||||
val scriptSettings = Seq(asScript, scriptAsSource, logLevel in Global := Level.Warn, showSuccess in Global := false)
|
||||
val append = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, scriptSettings ++ embeddedSettings)
|
||||
val scriptSettings = Seq(asScript,
|
||||
scriptAsSource,
|
||||
logLevel in Global := Level.Warn,
|
||||
showSuccess in Global := false)
|
||||
val append = Load.transformSettings(Load.projectScope(currentRef),
|
||||
currentRef.build,
|
||||
rootProject,
|
||||
scriptSettings ++ embeddedSettings)
|
||||
|
||||
val newStructure = Load.reapply(session.original ++ append, structure)
|
||||
val arguments = state.remainingCommands.drop(1).map(e => s""""${e.commandLine}"""")
|
||||
|
|
@ -55,25 +62,27 @@ object Script {
|
|||
}
|
||||
|
||||
final case class Block(offset: Int, lines: Seq[String])
|
||||
def blocks(file: File): Seq[Block] =
|
||||
{
|
||||
val lines = IO.readLines(file).toIndexedSeq
|
||||
def blocks(b: Block, acc: List[Block]): List[Block] =
|
||||
if (b.lines.isEmpty)
|
||||
acc.reverse
|
||||
else {
|
||||
val (dropped, blockToEnd) = b.lines.span { line => !line.startsWith(BlockStart) }
|
||||
val (block, remaining) = blockToEnd.span { line => !line.startsWith(BlockEnd) }
|
||||
val offset = b.offset + dropped.length
|
||||
blocks(Block(offset + block.length, remaining), Block(offset, block.drop(1)) :: acc)
|
||||
def blocks(file: File): Seq[Block] = {
|
||||
val lines = IO.readLines(file).toIndexedSeq
|
||||
def blocks(b: Block, acc: List[Block]): List[Block] =
|
||||
if (b.lines.isEmpty)
|
||||
acc.reverse
|
||||
else {
|
||||
val (dropped, blockToEnd) = b.lines.span { line =>
|
||||
!line.startsWith(BlockStart)
|
||||
}
|
||||
blocks(Block(0, lines), Nil)
|
||||
}
|
||||
val (block, remaining) = blockToEnd.span { line =>
|
||||
!line.startsWith(BlockEnd)
|
||||
}
|
||||
val offset = b.offset + dropped.length
|
||||
blocks(Block(offset + block.length, remaining), Block(offset, block.drop(1)) :: acc)
|
||||
}
|
||||
blocks(Block(0, lines), Nil)
|
||||
}
|
||||
val BlockStart = "/***"
|
||||
val BlockEnd = "*/"
|
||||
def fail(s: State, msg: String): State =
|
||||
{
|
||||
System.err.println(msg)
|
||||
s.fail
|
||||
}
|
||||
def fail(s: State, msg: String): State = {
|
||||
System.err.println(msg)
|
||||
s.fail
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ final case class SessionSettings(
|
|||
) {
|
||||
|
||||
assert(currentProject contains currentBuild,
|
||||
s"Current build ($currentBuild) not associated with a current project.")
|
||||
s"Current build ($currentBuild) not associated with a current project.")
|
||||
|
||||
/**
|
||||
* Modifiy the current state.
|
||||
|
|
@ -48,7 +48,9 @@ final case class SessionSettings(
|
|||
* @return A new SessionSettings object
|
||||
*/
|
||||
def setCurrent(build: URI, project: String, eval: () => Eval): SessionSettings =
|
||||
copy(currentBuild = build, currentProject = currentProject.updated(build, project), currentEval = eval)
|
||||
copy(currentBuild = build,
|
||||
currentProject = currentProject.updated(build, project),
|
||||
currentEval = eval)
|
||||
|
||||
/**
|
||||
* @return The current ProjectRef with which we scope settings.
|
||||
|
|
@ -60,7 +62,8 @@ final case class SessionSettings(
|
|||
* @param s A sequence of SessionSetting objects, which contain a Setting[_] and a string.
|
||||
* @return A new SessionSettings which contains this new sequence.
|
||||
*/
|
||||
def appendSettings(s: Seq[SessionSetting]): SessionSettings = copy(append = modify(append, _ ++ s))
|
||||
def appendSettings(s: Seq[SessionSetting]): SessionSettings =
|
||||
copy(append = modify(append, _ ++ s))
|
||||
|
||||
/**
|
||||
* Appends a set of raw Setting[_] objects to the current session.
|
||||
|
|
@ -79,7 +82,8 @@ final case class SessionSettings(
|
|||
*/
|
||||
def clearExtraSettings: SessionSettings = copy(append = Map.empty, rawAppend = Nil)
|
||||
|
||||
private[this] def merge(map: SessionMap): Seq[Setting[_]] = map.values.toSeq.flatten[SessionSetting].map(_._1)
|
||||
private[this] def merge(map: SessionMap): Seq[Setting[_]] =
|
||||
map.values.toSeq.flatten[SessionSetting].map(_._1)
|
||||
|
||||
private[this] def modify(map: SessionMap, onSeq: Endo[Seq[SessionSetting]]): SessionMap = {
|
||||
val cur = current
|
||||
|
|
@ -88,6 +92,7 @@ final case class SessionSettings(
|
|||
}
|
||||
|
||||
object SessionSettings {
|
||||
|
||||
/** A session setting is simply a tuple of a Setting[_] and the strings which define it. */
|
||||
type SessionSetting = (Setting[_], Seq[String])
|
||||
|
||||
|
|
@ -137,7 +142,8 @@ object SessionSettings {
|
|||
def checkSession(newSession: SessionSettings, oldState: State): Unit = {
|
||||
val oldSettings = (oldState get Keys.sessionSettings).toList.flatMap(_.append).flatMap(_._2)
|
||||
if (newSession.append.isEmpty && oldSettings.nonEmpty)
|
||||
oldState.log.warn("Discarding " + pluralize(oldSettings.size, " session setting") + ". Use 'session save' to persist session settings.")
|
||||
oldState.log.warn(
|
||||
"Discarding " + pluralize(oldSettings.size, " session setting") + ". Use 'session save' to persist session settings.")
|
||||
}
|
||||
|
||||
def removeRanges[T](in: Seq[T], ranges: Seq[(Int, Int)]): Seq[T] = {
|
||||
|
|
@ -154,7 +160,8 @@ object SessionSettings {
|
|||
def removeSettings(s: State, ranges: Seq[(Int, Int)]): State =
|
||||
withSettings(s) { session =>
|
||||
val current = session.current
|
||||
val newAppend = session.append.updated(current, removeRanges(session.append.getOrElse(current, Nil), ranges))
|
||||
val newAppend = session.append
|
||||
.updated(current, removeRanges(session.append.getOrElse(current, Nil), ranges))
|
||||
reapply(session.copy(append = newAppend), s)
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +184,8 @@ object SessionSettings {
|
|||
withSettings(s) { session =>
|
||||
val newSettings =
|
||||
for ((ref, settings) <- session.append if settings.nonEmpty && include(ref)) yield {
|
||||
val (news, olds) = writeSettings(ref, settings.toList, session.original, Project.structure(s))
|
||||
val (news, olds) =
|
||||
writeSettings(ref, settings.toList, session.original, Project.structure(s))
|
||||
(ref -> news, olds)
|
||||
}
|
||||
val (newAppend, newOriginal) = newSettings.unzip
|
||||
|
|
@ -185,26 +193,36 @@ object SessionSettings {
|
|||
reapply(newSession.copy(original = newSession.mergeSettings, append = Map.empty), s)
|
||||
}
|
||||
|
||||
def writeSettings(pref: ProjectRef, settings: List[SessionSetting], original: Seq[Setting[_]], structure: BuildStructure): (Seq[SessionSetting], Seq[Setting[_]]) = {
|
||||
val project = Project.getProject(pref, structure).getOrElse(sys.error("Invalid project reference " + pref))
|
||||
val writeTo: File = BuildPaths.configurationSources(project.base).headOption.getOrElse(new File(project.base, "build.sbt"))
|
||||
def writeSettings(pref: ProjectRef,
|
||||
settings: List[SessionSetting],
|
||||
original: Seq[Setting[_]],
|
||||
structure: BuildStructure): (Seq[SessionSetting], Seq[Setting[_]]) = {
|
||||
val project =
|
||||
Project.getProject(pref, structure).getOrElse(sys.error("Invalid project reference " + pref))
|
||||
val writeTo: File = BuildPaths
|
||||
.configurationSources(project.base)
|
||||
.headOption
|
||||
.getOrElse(new File(project.base, "build.sbt"))
|
||||
writeTo.createNewFile()
|
||||
|
||||
val path = writeTo.getAbsolutePath
|
||||
val (inFile, other, _) = ((List[Setting[_]](), List[Setting[_]](), Set.empty[ScopedKey[_]]) /: original.reverse) {
|
||||
case ((in, oth, keys), s) =>
|
||||
s.pos match {
|
||||
case RangePosition(`path`, _) if !keys.contains(s.key) => (s :: in, oth, keys + s.key)
|
||||
case _ => (in, s :: oth, keys)
|
||||
}
|
||||
}
|
||||
val (inFile, other, _) =
|
||||
((List[Setting[_]](), List[Setting[_]](), Set.empty[ScopedKey[_]]) /: original.reverse) {
|
||||
case ((in, oth, keys), s) =>
|
||||
s.pos match {
|
||||
case RangePosition(`path`, _) if !keys.contains(s.key) => (s :: in, oth, keys + s.key)
|
||||
case _ => (in, s :: oth, keys)
|
||||
}
|
||||
}
|
||||
|
||||
val (_, oldShifted, replace) = ((0, List[Setting[_]](), Seq[SessionSetting]()) /: inFile) {
|
||||
case ((offs, olds, repl), s) =>
|
||||
val RangePosition(_, r @ LineRange(start, end)) = s.pos
|
||||
settings find (_._1.key == s.key) match {
|
||||
case Some(ss @ (ns, newLines)) if !ns.init.dependencies.contains(ns.key) =>
|
||||
val shifted = ns withPos RangePosition(path, LineRange(start - offs, start - offs + newLines.size))
|
||||
val shifted = ns withPos RangePosition(path,
|
||||
LineRange(start - offs,
|
||||
start - offs + newLines.size))
|
||||
(offs + end - start - newLines.size, shifted :: olds, ss +: repl)
|
||||
case _ =>
|
||||
val shifted = s withPos RangePosition(path, r shift -offs)
|
||||
|
|
@ -225,7 +243,8 @@ object SessionSettings {
|
|||
(newWithPos.reverse, other ++ oldShifted)
|
||||
}
|
||||
|
||||
def needsTrailingBlank(lines: Seq[String]) = lines.nonEmpty && !lines.takeRight(1).exists(_.trim.isEmpty)
|
||||
def needsTrailingBlank(lines: Seq[String]) =
|
||||
lines.nonEmpty && !lines.takeRight(1).exists(_.trim.isEmpty)
|
||||
|
||||
/** Prints all the user-defined SessionSettings (not raw) to System.out. */
|
||||
def printAllSettings(s: State): State =
|
||||
|
|
@ -249,7 +268,8 @@ object SessionSettings {
|
|||
for (((_, stringRep), index) <- settings.zipWithIndex)
|
||||
println(" " + (index + 1) + ". " + stringRep.mkString("\n"))
|
||||
|
||||
def Help = """session <command>
|
||||
def Help =
|
||||
"""session <command>
|
||||
|
||||
Manipulates session settings, which are temporary settings that do not persist past the current sbt execution (that is, the current session).
|
||||
Valid commands are:
|
||||
|
|
@ -299,15 +319,19 @@ save, save-all
|
|||
/** Parser for the session command. */
|
||||
lazy val parser =
|
||||
token(Space) ~>
|
||||
(token("list-all" ^^^ new Print(true)) | token("list" ^^^ new Print(false)) | token("clear" ^^^ new Clear(false)) |
|
||||
token("save-all" ^^^ new Save(true)) | token("save" ^^^ new Save(false)) | token("clear-all" ^^^ new Clear(true)) |
|
||||
(token("list-all" ^^^ new Print(true)) | token("list" ^^^ new Print(false)) | token(
|
||||
"clear" ^^^ new Clear(false)) |
|
||||
token("save-all" ^^^ new Save(true)) | token("save" ^^^ new Save(false)) | token(
|
||||
"clear-all" ^^^ new Clear(true)) |
|
||||
remove)
|
||||
|
||||
lazy val remove = token("remove") ~> token(Space) ~> natSelect.map(ranges => new Remove(ranges))
|
||||
|
||||
def natSelect = rep1sep(token(range, "<range>"), ',')
|
||||
|
||||
def range: Parser[(Int, Int)] = (NatBasic ~ ('-' ~> NatBasic).?).map { case lo ~ hi => (lo, hi getOrElse lo) }
|
||||
def range: Parser[(Int, Int)] = (NatBasic ~ ('-' ~> NatBasic).?).map {
|
||||
case lo ~ hi => (lo, hi getOrElse lo)
|
||||
}
|
||||
|
||||
/** The raw implementation of the session command. */
|
||||
def command(s: State): Parser[() => State] = Command.applyEffect(parser) {
|
||||
|
|
|
|||
|
|
@ -17,88 +17,104 @@ import DefaultParsers._
|
|||
* The verbose summary will typically use more vertical space and show full details,
|
||||
* while the quiet summary will be a couple of lines and truncate information.
|
||||
*/
|
||||
private[sbt] class SetResult(val session: SessionSettings, val verboseSummary: String, val quietSummary: String)
|
||||
private[sbt] class SetResult(val session: SessionSettings,
|
||||
val verboseSummary: String,
|
||||
val quietSummary: String)
|
||||
|
||||
/** Defines methods for implementing the `set` command.*/
|
||||
private[sbt] object SettingCompletions {
|
||||
|
||||
/**
|
||||
* Implementation of the `set every` command. Each setting in the provided `settings` sequence will be applied in all scopes,
|
||||
* overriding all previous definitions of the underlying AttributeKey.
|
||||
* The settings injected by this method cannot be later persisted by the `session save` command.
|
||||
*/
|
||||
def setAll(extracted: Extracted, settings: Seq[Setting[_]]): SetResult =
|
||||
{
|
||||
import extracted._
|
||||
val r = relation(extracted.structure, true)
|
||||
val allDefs = Def.flattenLocals(Def.compiled(extracted.structure.settings, true)(structure.delegates, structure.scopeLocal, implicitly[Show[ScopedKey[_]]])).keys
|
||||
val projectScope = Load.projectScope(currentRef)
|
||||
def resolve(s: Setting[_]): Seq[Setting[_]] = Load.transformSettings(projectScope, currentRef.build, rootProject, s :: Nil)
|
||||
def rescope[T](setting: Setting[T]): Seq[Setting[_]] =
|
||||
{
|
||||
val akey = setting.key.key
|
||||
val global = ScopedKey(Global, akey)
|
||||
val globalSetting = resolve(Def.setting(global, setting.init, setting.pos))
|
||||
globalSetting ++ allDefs.flatMap { d =>
|
||||
if (d.key == akey)
|
||||
Seq(SettingKey(akey) in d.scope := { global.value })
|
||||
else
|
||||
Nil
|
||||
}
|
||||
}
|
||||
val redefined = settings.flatMap(x => rescope(x))
|
||||
val session = extracted.session.appendRaw(redefined)
|
||||
setResult(session, r, redefined)
|
||||
}
|
||||
|
||||
/** Implementation of the `set` command that will reload the current project with `settings` appended to the current settings. */
|
||||
def setThis(s: State, extracted: Extracted, settings: Seq[Def.Setting[_]], arg: String): SetResult =
|
||||
{
|
||||
import extracted._
|
||||
val append = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings)
|
||||
val newSession = session.appendSettings(append map (a => (a, arg.split('\n').toList)))
|
||||
val r = relation(newSession.mergeSettings, true)(structure.delegates, structure.scopeLocal, implicitly)
|
||||
setResult(newSession, r, append)
|
||||
}
|
||||
|
||||
private[this] def setResult(session: SessionSettings, r: Relation[ScopedKey[_], ScopedKey[_]], redefined: Seq[Setting[_]])(implicit show: Show[ScopedKey[_]]): SetResult =
|
||||
{
|
||||
val redefinedKeys = redefined.map(_.key).toSet
|
||||
val affectedKeys = redefinedKeys.flatMap(r.reverse)
|
||||
def summary(verbose: Boolean): String = setSummary(redefinedKeys, affectedKeys, verbose)
|
||||
new SetResult(session, summary(true), summary(false))
|
||||
}
|
||||
|
||||
private[this] def setSummary(redefined: Set[ScopedKey[_]], affected: Set[ScopedKey[_]], verbose: Boolean)(implicit display: Show[ScopedKey[_]]): String =
|
||||
{
|
||||
val QuietLimit = 3
|
||||
def strings(in: Set[ScopedKey[_]]): Seq[String] = in.toSeq.map(sk => display.show(sk)).sorted
|
||||
def lines(in: Seq[String]): (String, Boolean) =
|
||||
if (in.isEmpty)
|
||||
("no settings or tasks.", false)
|
||||
else if (verbose)
|
||||
(in.mkString("\n\t", "\n\t", "\n"), false)
|
||||
def setAll(extracted: Extracted, settings: Seq[Setting[_]]): SetResult = {
|
||||
import extracted._
|
||||
val r = relation(extracted.structure, true)
|
||||
val allDefs = Def
|
||||
.flattenLocals(
|
||||
Def.compiled(extracted.structure.settings, true)(structure.delegates,
|
||||
structure.scopeLocal,
|
||||
implicitly[Show[ScopedKey[_]]]))
|
||||
.keys
|
||||
val projectScope = Load.projectScope(currentRef)
|
||||
def resolve(s: Setting[_]): Seq[Setting[_]] =
|
||||
Load.transformSettings(projectScope, currentRef.build, rootProject, s :: Nil)
|
||||
def rescope[T](setting: Setting[T]): Seq[Setting[_]] = {
|
||||
val akey = setting.key.key
|
||||
val global = ScopedKey(Global, akey)
|
||||
val globalSetting = resolve(Def.setting(global, setting.init, setting.pos))
|
||||
globalSetting ++ allDefs.flatMap { d =>
|
||||
if (d.key == akey)
|
||||
Seq(SettingKey(akey) in d.scope := { global.value })
|
||||
else
|
||||
quietList(in)
|
||||
def quietList(in: Seq[String]): (String, Boolean) =
|
||||
{
|
||||
val (first, last) = in.splitAt(QuietLimit)
|
||||
if (last.isEmpty)
|
||||
(first.mkString(", "), false)
|
||||
else {
|
||||
val s = first.take(QuietLimit - 1).mkString("", ", ", " and " + last.size + " others.")
|
||||
(s, true)
|
||||
}
|
||||
}
|
||||
if (redefined.isEmpty)
|
||||
"No settings or tasks were redefined."
|
||||
else {
|
||||
val (redef, trimR) = lines(strings(redefined))
|
||||
val (used, trimU) = lines(strings(affected))
|
||||
val details = if (trimR || trimU) "\n\tRun `last` for details." else ""
|
||||
val valuesString = if (redefined.size == 1) "value" else "values"
|
||||
"Defining %s\nThe new %s will be used by %s%s".format(redef, valuesString, used, details)
|
||||
Nil
|
||||
}
|
||||
}
|
||||
val redefined = settings.flatMap(x => rescope(x))
|
||||
val session = extracted.session.appendRaw(redefined)
|
||||
setResult(session, r, redefined)
|
||||
}
|
||||
|
||||
/** Implementation of the `set` command that will reload the current project with `settings` appended to the current settings. */
|
||||
def setThis(s: State,
|
||||
extracted: Extracted,
|
||||
settings: Seq[Def.Setting[_]],
|
||||
arg: String): SetResult = {
|
||||
import extracted._
|
||||
val append = Load.transformSettings(Load.projectScope(currentRef),
|
||||
currentRef.build,
|
||||
rootProject,
|
||||
settings)
|
||||
val newSession = session.appendSettings(append map (a => (a, arg.split('\n').toList)))
|
||||
val r = relation(newSession.mergeSettings, true)(structure.delegates,
|
||||
structure.scopeLocal,
|
||||
implicitly)
|
||||
setResult(newSession, r, append)
|
||||
}
|
||||
|
||||
private[this] def setResult(
|
||||
session: SessionSettings,
|
||||
r: Relation[ScopedKey[_], ScopedKey[_]],
|
||||
redefined: Seq[Setting[_]])(implicit show: Show[ScopedKey[_]]): SetResult = {
|
||||
val redefinedKeys = redefined.map(_.key).toSet
|
||||
val affectedKeys = redefinedKeys.flatMap(r.reverse)
|
||||
def summary(verbose: Boolean): String = setSummary(redefinedKeys, affectedKeys, verbose)
|
||||
new SetResult(session, summary(true), summary(false))
|
||||
}
|
||||
|
||||
private[this] def setSummary(redefined: Set[ScopedKey[_]],
|
||||
affected: Set[ScopedKey[_]],
|
||||
verbose: Boolean)(implicit display: Show[ScopedKey[_]]): String = {
|
||||
val QuietLimit = 3
|
||||
def strings(in: Set[ScopedKey[_]]): Seq[String] = in.toSeq.map(sk => display.show(sk)).sorted
|
||||
def lines(in: Seq[String]): (String, Boolean) =
|
||||
if (in.isEmpty)
|
||||
("no settings or tasks.", false)
|
||||
else if (verbose)
|
||||
(in.mkString("\n\t", "\n\t", "\n"), false)
|
||||
else
|
||||
quietList(in)
|
||||
def quietList(in: Seq[String]): (String, Boolean) = {
|
||||
val (first, last) = in.splitAt(QuietLimit)
|
||||
if (last.isEmpty)
|
||||
(first.mkString(", "), false)
|
||||
else {
|
||||
val s = first.take(QuietLimit - 1).mkString("", ", ", " and " + last.size + " others.")
|
||||
(s, true)
|
||||
}
|
||||
}
|
||||
if (redefined.isEmpty)
|
||||
"No settings or tasks were redefined."
|
||||
else {
|
||||
val (redef, trimR) = lines(strings(redefined))
|
||||
val (used, trimU) = lines(strings(affected))
|
||||
val details = if (trimR || trimU) "\n\tRun `last` for details." else ""
|
||||
val valuesString = if (redefined.size == 1) "value" else "values"
|
||||
"Defining %s\nThe new %s will be used by %s%s".format(redef, valuesString, used, details)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser that provides tab completion for the main argument to the `set` command.
|
||||
|
|
@ -108,29 +124,36 @@ private[sbt] object SettingCompletions {
|
|||
* when there are fewer choices or tab is pressed multiple times.
|
||||
* The last part of the completion will generate a template for the value or function literal that will initialize the setting or task.
|
||||
*/
|
||||
def settingParser(settings: Settings[Scope], rawKeyMap: Map[String, AttributeKey[_]], context: ResolvedProject): Parser[String] =
|
||||
{
|
||||
val keyMap: Map[String, AttributeKey[_]] = rawKeyMap.map { case (k, v) => (keyScalaID(k), v) }.toMap
|
||||
def inputScopedKey(pred: AttributeKey[_] => Boolean): Parser[ScopedKey[_]] =
|
||||
scopedKeyParser(keyMap.filter { case (_, k) => pred(k) }, settings, context)
|
||||
val full = for {
|
||||
defineKey <- scopedKeyParser(keyMap, settings, context)
|
||||
a <- assign(defineKey)
|
||||
_ <- valueParser(defineKey, a, inputScopedKey(keyFilter(defineKey.key)))
|
||||
} yield () // parser is currently only for completion and the parsed data structures are not used
|
||||
def settingParser(settings: Settings[Scope],
|
||||
rawKeyMap: Map[String, AttributeKey[_]],
|
||||
context: ResolvedProject): Parser[String] = {
|
||||
val keyMap
|
||||
: Map[String, AttributeKey[_]] = rawKeyMap.map { case (k, v) => (keyScalaID(k), v) }.toMap
|
||||
def inputScopedKey(pred: AttributeKey[_] => Boolean): Parser[ScopedKey[_]] =
|
||||
scopedKeyParser(keyMap.filter { case (_, k) => pred(k) }, settings, context)
|
||||
val full = for {
|
||||
defineKey <- scopedKeyParser(keyMap, settings, context)
|
||||
a <- assign(defineKey)
|
||||
_ <- valueParser(defineKey, a, inputScopedKey(keyFilter(defineKey.key)))
|
||||
} yield
|
||||
() // parser is currently only for completion and the parsed data structures are not used
|
||||
|
||||
matched(full) | any.+.string
|
||||
}
|
||||
matched(full) | any.+.string
|
||||
}
|
||||
|
||||
/** Parser for a Scope+AttributeKey (ScopedKey). */
|
||||
def scopedKeyParser(keyMap: Map[String, AttributeKey[_]], settings: Settings[Scope], context: ResolvedProject): Parser[ScopedKey[_]] =
|
||||
{
|
||||
val cutoff = KeyRanks.MainCutoff
|
||||
val keyCompletions = fixedCompletions { (seen, level) => completeKey(seen, keyMap, level, cutoff, 10).toSet }
|
||||
val keyID: Parser[AttributeKey[_]] = scalaID(keyMap, "key")
|
||||
val keyParser = token(keyID, keyCompletions)
|
||||
for (key <- keyParser; scope <- scopeParser(key, settings, context)) yield ScopedKey(scope, key)
|
||||
def scopedKeyParser(keyMap: Map[String, AttributeKey[_]],
|
||||
settings: Settings[Scope],
|
||||
context: ResolvedProject): Parser[ScopedKey[_]] = {
|
||||
val cutoff = KeyRanks.MainCutoff
|
||||
val keyCompletions = fixedCompletions { (seen, level) =>
|
||||
completeKey(seen, keyMap, level, cutoff, 10).toSet
|
||||
}
|
||||
val keyID: Parser[AttributeKey[_]] = scalaID(keyMap, "key")
|
||||
val keyParser = token(keyID, keyCompletions)
|
||||
for (key <- keyParser; scope <- scopeParser(key, settings, context))
|
||||
yield ScopedKey(scope, key)
|
||||
}
|
||||
|
||||
/** Parser for the `in` method name that slightly augments the naive completion to give a hint of the purpose of `in`.*/
|
||||
val inParser = tokenDisplay(Space ~> InMethod, "%s <scope>".format(InMethod))
|
||||
|
|
@ -139,18 +162,19 @@ private[sbt] object SettingCompletions {
|
|||
* Parser for the initialization expression for the assignment method `assign` on the key `sk`.
|
||||
* `scopedKeyP` is used to parse and complete the input keys for an initialization that depends on other keys.
|
||||
*/
|
||||
def valueParser(sk: ScopedKey[_], assign: Assign.Value, scopedKeyP: Parser[ScopedKey[_]]): Parser[Seq[ScopedKey[_]]] =
|
||||
{
|
||||
val fullTypeString = keyTypeString(sk.key)
|
||||
val typeString = if (assignNoAppend(assign)) fullTypeString else "..."
|
||||
if (assign == Assign.Update) {
|
||||
val function = "{(prev: " + typeString + ") => /*" + typeString + "*/ }"
|
||||
token(OptSpace ~ function) ^^^ Nil
|
||||
} else {
|
||||
val value = "/* value of type " + typeString + " */"
|
||||
token(Space ~ value) ^^^ Nil
|
||||
}
|
||||
def valueParser(sk: ScopedKey[_],
|
||||
assign: Assign.Value,
|
||||
scopedKeyP: Parser[ScopedKey[_]]): Parser[Seq[ScopedKey[_]]] = {
|
||||
val fullTypeString = keyTypeString(sk.key)
|
||||
val typeString = if (assignNoAppend(assign)) fullTypeString else "..."
|
||||
if (assign == Assign.Update) {
|
||||
val function = "{(prev: " + typeString + ") => /*" + typeString + "*/ }"
|
||||
token(OptSpace ~ function) ^^^ Nil
|
||||
} else {
|
||||
val value = "/* value of type " + typeString + " */"
|
||||
token(Space ~ value) ^^^ Nil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a setting definition `definingKey <<= (..., in, ...) { ... }`,
|
||||
|
|
@ -165,106 +189,136 @@ private[sbt] object SettingCompletions {
|
|||
* The completions are restricted to be more useful. Currently, this parser will suggest
|
||||
* only known axis values for configurations and tasks and only in that order.
|
||||
*/
|
||||
def scopeParser(key: AttributeKey[_], settings: Settings[Scope], context: ResolvedProject): Parser[Scope] = {
|
||||
def scopeParser(key: AttributeKey[_],
|
||||
settings: Settings[Scope],
|
||||
context: ResolvedProject): Parser[Scope] = {
|
||||
val data = settings.data
|
||||
val allScopes = data.keys.toSeq
|
||||
val definedScopes = data.toSeq flatMap { case (scope, attrs) => if (attrs contains key) scope :: Nil else Nil }
|
||||
val definedScopes = data.toSeq flatMap {
|
||||
case (scope, attrs) => if (attrs contains key) scope :: Nil else Nil
|
||||
}
|
||||
scope(key, allScopes, definedScopes, context)
|
||||
}
|
||||
|
||||
private[this] def scope(key: AttributeKey[_], allScopes: Seq[Scope], definedScopes: Seq[Scope], context: ResolvedProject): Parser[Scope] =
|
||||
{
|
||||
def axisParser[T](axis: Scope => ScopeAxis[T], name: T => String, description: T => Option[String], label: String): Parser[ScopeAxis[T]] =
|
||||
{
|
||||
def getChoice(s: Scope): Seq[(String, T)] = axis(s) match {
|
||||
case Select(t) => (name(t), t) :: Nil
|
||||
case _ => Nil
|
||||
}
|
||||
def getChoices(scopes: Seq[Scope]): Map[String, T] = scopes.flatMap(getChoice).toMap
|
||||
val definedChoices: Set[String] = definedScopes.flatMap(s => axis(s).toOption.map(name)).toSet
|
||||
val fullChoices: Map[String, T] = getChoices(allScopes.toSeq)
|
||||
val completions = fixedCompletions { (seen, level) => completeScope(seen, level, definedChoices, fullChoices)(description).toSet }
|
||||
Act.optionalAxis(inParser ~> token(Space) ~> token(scalaID(fullChoices, label), completions), This)
|
||||
}
|
||||
val configurations: Map[String, Configuration] = context.configurations.map(c => (configScalaID(c.name), c)).toMap
|
||||
val configParser = axisParser[ConfigKey](_.config, c => configScalaID(c.name), ck => configurations.get(ck.name).map(_.description), "configuration")
|
||||
val taskParser = axisParser[AttributeKey[_]](_.task, k => keyScalaID(k.label), _.description, "task")
|
||||
val nonGlobal = (configParser ~ taskParser) map { case (c, t) => Scope(This, c, t, Global) }
|
||||
val global = inParser ~> token((Space ~ GlobalID) ^^^ GlobalScope)
|
||||
global | nonGlobal
|
||||
private[this] def scope(key: AttributeKey[_],
|
||||
allScopes: Seq[Scope],
|
||||
definedScopes: Seq[Scope],
|
||||
context: ResolvedProject): Parser[Scope] = {
|
||||
def axisParser[T](axis: Scope => ScopeAxis[T],
|
||||
name: T => String,
|
||||
description: T => Option[String],
|
||||
label: String): Parser[ScopeAxis[T]] = {
|
||||
def getChoice(s: Scope): Seq[(String, T)] = axis(s) match {
|
||||
case Select(t) => (name(t), t) :: Nil
|
||||
case _ => Nil
|
||||
}
|
||||
def getChoices(scopes: Seq[Scope]): Map[String, T] = scopes.flatMap(getChoice).toMap
|
||||
val definedChoices: Set[String] =
|
||||
definedScopes.flatMap(s => axis(s).toOption.map(name)).toSet
|
||||
val fullChoices: Map[String, T] = getChoices(allScopes.toSeq)
|
||||
val completions = fixedCompletions { (seen, level) =>
|
||||
completeScope(seen, level, definedChoices, fullChoices)(description).toSet
|
||||
}
|
||||
Act.optionalAxis(inParser ~> token(Space) ~> token(scalaID(fullChoices, label), completions),
|
||||
This)
|
||||
}
|
||||
val configurations: Map[String, Configuration] =
|
||||
context.configurations.map(c => (configScalaID(c.name), c)).toMap
|
||||
val configParser = axisParser[ConfigKey](_.config,
|
||||
c => configScalaID(c.name),
|
||||
ck => configurations.get(ck.name).map(_.description),
|
||||
"configuration")
|
||||
val taskParser =
|
||||
axisParser[AttributeKey[_]](_.task, k => keyScalaID(k.label), _.description, "task")
|
||||
val nonGlobal = (configParser ~ taskParser) map { case (c, t) => Scope(This, c, t, Global) }
|
||||
val global = inParser ~> token((Space ~ GlobalID) ^^^ GlobalScope)
|
||||
global | nonGlobal
|
||||
}
|
||||
|
||||
/** Parser for the assignment method (such as `:=`) for defining `key`. */
|
||||
def assign(key: ScopedKey[_]): Parser[Assign.Value] =
|
||||
{
|
||||
val completions = fixedCompletions { (seen, level) => completeAssign(seen, level, key).toSet }
|
||||
val identifier = Act.filterStrings(Op, Assign.values.map(_.toString), "assignment method") map Assign.withName
|
||||
token(Space) ~> token(optionallyQuoted(identifier), completions)
|
||||
def assign(key: ScopedKey[_]): Parser[Assign.Value] = {
|
||||
val completions = fixedCompletions { (seen, level) =>
|
||||
completeAssign(seen, level, key).toSet
|
||||
}
|
||||
val identifier = Act.filterStrings(Op, Assign.values.map(_.toString), "assignment method") map Assign.withName
|
||||
token(Space) ~> token(optionallyQuoted(identifier), completions)
|
||||
}
|
||||
|
||||
private[this] def fixedCompletions(f: (String, Int) => Set[Completion]): TokenCompletions =
|
||||
TokenCompletions.fixed((s, l) => Completions(f(s, l)))
|
||||
|
||||
private[this] def scalaID[T](keyMap: Map[String, T], label: String): Parser[T] =
|
||||
{
|
||||
val identifier = Act.filterStrings(ScalaID, keyMap.keySet, label) map keyMap
|
||||
optionallyQuoted(identifier)
|
||||
}
|
||||
private[this] def scalaID[T](keyMap: Map[String, T], label: String): Parser[T] = {
|
||||
val identifier = Act.filterStrings(ScalaID, keyMap.keySet, label) map keyMap
|
||||
optionallyQuoted(identifier)
|
||||
}
|
||||
|
||||
/** Produce a new parser that allows the input accepted by `p` to be quoted in backticks. */
|
||||
def optionallyQuoted[T](p: Parser[T]): Parser[T] =
|
||||
(Backtick.? ~ p) flatMap { case (quote, id) => if (quote.isDefined) Backtick.? ^^^ id else success(id) }
|
||||
(Backtick.? ~ p) flatMap {
|
||||
case (quote, id) => if (quote.isDefined) Backtick.? ^^^ id else success(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Completions for an assignment method for `key` given the tab completion `level` and existing partial string `seen`.
|
||||
* This will filter possible assignment methods based on the underlying type of `key`, so that only `<<=` is shown for input tasks, for example.
|
||||
*/
|
||||
def completeAssign(seen: String, level: Int, key: ScopedKey[_]): Seq[Completion] =
|
||||
{
|
||||
val allowed: Iterable[Assign.Value] =
|
||||
if (appendable(key.key)) Assign.values
|
||||
else assignNoAppend
|
||||
val applicable = allowed.toSeq.flatMap { a =>
|
||||
val s = a.toString
|
||||
if (s startsWith seen) (s, a) :: Nil else Nil
|
||||
def completeAssign(seen: String, level: Int, key: ScopedKey[_]): Seq[Completion] = {
|
||||
val allowed: Iterable[Assign.Value] =
|
||||
if (appendable(key.key)) Assign.values
|
||||
else assignNoAppend
|
||||
val applicable = allowed.toSeq.flatMap { a =>
|
||||
val s = a.toString
|
||||
if (s startsWith seen) (s, a) :: Nil else Nil
|
||||
}
|
||||
completeDescribed(seen, true, applicable)(assignDescription)
|
||||
}
|
||||
|
||||
def completeKey(seen: String,
|
||||
keys: Map[String, AttributeKey[_]],
|
||||
level: Int,
|
||||
prominentCutoff: Int,
|
||||
detailLimit: Int): Seq[Completion] =
|
||||
completeSelectDescribed(seen, level, keys, detailLimit)(_.description) {
|
||||
case (k, v) => v.rank <= prominentCutoff
|
||||
}
|
||||
|
||||
def completeScope[T](
|
||||
seen: String,
|
||||
level: Int,
|
||||
definedChoices: Set[String],
|
||||
allChoices: Map[String, T])(description: T => Option[String]): Seq[Completion] =
|
||||
completeSelectDescribed(seen, level, allChoices, 10)(description) {
|
||||
case (k, v) => definedChoices(k)
|
||||
}
|
||||
|
||||
def completeSelectDescribed[T](seen: String, level: Int, all: Map[String, T], detailLimit: Int)(
|
||||
description: T => Option[String])(prominent: (String, T) => Boolean): Seq[Completion] = {
|
||||
val applicable = all.toSeq.filter { case (k, v) => k startsWith seen }
|
||||
val prominentOnly = applicable filter { case (k, v) => prominent(k, v) }
|
||||
|
||||
val showAll = (level >= 3) || (level == 2 && prominentOnly.size <= detailLimit) || prominentOnly.isEmpty
|
||||
val showKeys = if (showAll) applicable else prominentOnly
|
||||
val showDescriptions = (level >= 2) || (showKeys.size <= detailLimit)
|
||||
completeDescribed(seen, showDescriptions, showKeys)(s => description(s).toList.mkString)
|
||||
}
|
||||
def completeDescribed[T](seen: String, showDescriptions: Boolean, in: Seq[(String, T)])(
|
||||
description: T => String): Seq[Completion] = {
|
||||
def appendString(id: String): String = id.stripPrefix(seen) + " "
|
||||
if (in.isEmpty)
|
||||
Nil
|
||||
else if (showDescriptions) {
|
||||
val withDescriptions = in map { case (id, key) => (id, description(key)) }
|
||||
val padded = CommandUtil.aligned("", " ", withDescriptions)
|
||||
(padded, in).zipped.map {
|
||||
case (line, (id, key)) =>
|
||||
Completion.tokenDisplay(append = appendString(id), display = line + "\n")
|
||||
}
|
||||
completeDescribed(seen, true, applicable)(assignDescription)
|
||||
}
|
||||
|
||||
def completeKey(seen: String, keys: Map[String, AttributeKey[_]], level: Int, prominentCutoff: Int, detailLimit: Int): Seq[Completion] =
|
||||
completeSelectDescribed(seen, level, keys, detailLimit)(_.description) { case (k, v) => v.rank <= prominentCutoff }
|
||||
|
||||
def completeScope[T](seen: String, level: Int, definedChoices: Set[String], allChoices: Map[String, T])(description: T => Option[String]): Seq[Completion] =
|
||||
completeSelectDescribed(seen, level, allChoices, 10)(description) { case (k, v) => definedChoices(k) }
|
||||
|
||||
def completeSelectDescribed[T](seen: String, level: Int, all: Map[String, T], detailLimit: Int)(description: T => Option[String])(prominent: (String, T) => Boolean): Seq[Completion] =
|
||||
{
|
||||
val applicable = all.toSeq.filter { case (k, v) => k startsWith seen }
|
||||
val prominentOnly = applicable filter { case (k, v) => prominent(k, v) }
|
||||
|
||||
val showAll = (level >= 3) || (level == 2 && prominentOnly.size <= detailLimit) || prominentOnly.isEmpty
|
||||
val showKeys = if (showAll) applicable else prominentOnly
|
||||
val showDescriptions = (level >= 2) || (showKeys.size <= detailLimit)
|
||||
completeDescribed(seen, showDescriptions, showKeys)(s => description(s).toList.mkString)
|
||||
}
|
||||
def completeDescribed[T](seen: String, showDescriptions: Boolean, in: Seq[(String, T)])(description: T => String): Seq[Completion] =
|
||||
{
|
||||
def appendString(id: String): String = id.stripPrefix(seen) + " "
|
||||
if (in.isEmpty)
|
||||
Nil
|
||||
else if (showDescriptions) {
|
||||
val withDescriptions = in map { case (id, key) => (id, description(key)) }
|
||||
val padded = CommandUtil.aligned("", " ", withDescriptions)
|
||||
(padded, in).zipped.map {
|
||||
case (line, (id, key)) =>
|
||||
Completion.tokenDisplay(append = appendString(id), display = line + "\n")
|
||||
}
|
||||
} else
|
||||
in map {
|
||||
case (id, key) =>
|
||||
Completion.tokenDisplay(display = id, append = appendString(id))
|
||||
}
|
||||
}
|
||||
} else
|
||||
in map {
|
||||
case (id, key) =>
|
||||
Completion.tokenDisplay(display = id, append = appendString(id))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the hyphenated key label `k` into camel-case and quotes it with backticks if it is a Scala keyword.
|
||||
|
|
@ -279,17 +333,19 @@ private[sbt] object SettingCompletions {
|
|||
def configScalaID(c: String): String = Util.quoteIfKeyword(c.capitalize)
|
||||
|
||||
/** Applies a function on the underlying manifest for T for `key` depending if it is for a `Setting[T]`, `Task[T]`, or `InputTask[T]`.*/
|
||||
def keyType[S](key: AttributeKey[_])(onSetting: Manifest[_] => S, onTask: Manifest[_] => S, onInput: Manifest[_] => S)(implicit tm: Manifest[Task[_]], im: Manifest[InputTask[_]]): S =
|
||||
{
|
||||
def argTpe = key.manifest.typeArguments.head
|
||||
val TaskClass = tm.runtimeClass
|
||||
val InputTaskClass = im.runtimeClass
|
||||
key.manifest.runtimeClass match {
|
||||
case TaskClass => onTask(argTpe)
|
||||
case InputTaskClass => onInput(argTpe)
|
||||
case _ => onSetting(key.manifest)
|
||||
}
|
||||
def keyType[S](key: AttributeKey[_])(
|
||||
onSetting: Manifest[_] => S,
|
||||
onTask: Manifest[_] => S,
|
||||
onInput: Manifest[_] => S)(implicit tm: Manifest[Task[_]], im: Manifest[InputTask[_]]): S = {
|
||||
def argTpe = key.manifest.typeArguments.head
|
||||
val TaskClass = tm.runtimeClass
|
||||
val InputTaskClass = im.runtimeClass
|
||||
key.manifest.runtimeClass match {
|
||||
case TaskClass => onTask(argTpe)
|
||||
case InputTaskClass => onInput(argTpe)
|
||||
case _ => onSetting(key.manifest)
|
||||
}
|
||||
}
|
||||
|
||||
/** For a Task[T], InputTask[T], or Setting[T], this returns the manifest for T. */
|
||||
def keyUnderlyingType(key: AttributeKey[_]): Manifest[_] = keyType(key)(idFun, idFun, idFun)
|
||||
|
|
@ -298,27 +354,28 @@ private[sbt] object SettingCompletions {
|
|||
* Returns a string representation of the underlying type T for a `key` representing a `Setting[T]`, `Task[T]`, or `InputTask[T]`.
|
||||
* This string representation is currently a cleaned up toString of the underlying Manifest.
|
||||
*/
|
||||
def keyTypeString[T](key: AttributeKey[_]): String =
|
||||
{
|
||||
val mfToString = (mf: Manifest[_]) => complete.TypeString.cleanup(mf.toString)
|
||||
keyType(key)(mfToString, mfToString, mfToString)
|
||||
}
|
||||
def keyTypeString[T](key: AttributeKey[_]): String = {
|
||||
val mfToString = (mf: Manifest[_]) => complete.TypeString.cleanup(mf.toString)
|
||||
keyType(key)(mfToString, mfToString, mfToString)
|
||||
}
|
||||
|
||||
/** True if the `key` represents an input task, false if it represents a task or setting. */
|
||||
def isInputTask(key: AttributeKey[_]): Boolean = keyType(key)(const(false), const(false), const(true))
|
||||
def isInputTask(key: AttributeKey[_]): Boolean =
|
||||
keyType(key)(const(false), const(false), const(true))
|
||||
|
||||
/** True if the `key` represents a setting, false if it represents a task or an input task.*/
|
||||
def isSetting(key: AttributeKey[_]): Boolean = keyType(key)(const(true), const(false), const(false))
|
||||
def isSetting(key: AttributeKey[_]): Boolean =
|
||||
keyType(key)(const(true), const(false), const(false))
|
||||
|
||||
/** True if the `key` represents a setting or task, false if it is for an input task. */
|
||||
def isTaskOrSetting(key: AttributeKey[_]): Boolean = keyType(key)(const(true), const(true), const(false))
|
||||
def isTaskOrSetting(key: AttributeKey[_]): Boolean =
|
||||
keyType(key)(const(true), const(true), const(false))
|
||||
|
||||
/** True if the `key` represents a setting or task that may be appended using an assignment method such as `+=`. */
|
||||
def appendable(key: AttributeKey[_]): Boolean =
|
||||
{
|
||||
val underlying = keyUnderlyingType(key).runtimeClass
|
||||
appendableClasses.exists(_ isAssignableFrom underlying)
|
||||
}
|
||||
def appendable(key: AttributeKey[_]): Boolean = {
|
||||
val underlying = keyUnderlyingType(key).runtimeClass
|
||||
appendableClasses.exists(_ isAssignableFrom underlying)
|
||||
}
|
||||
|
||||
/** The simple name of the global scope axis, which can be used to reference it in the default setting context. */
|
||||
final val GlobalID = Global.getClass.getSimpleName.stripSuffix("$")
|
||||
|
|
@ -337,6 +394,7 @@ private[sbt] object SettingCompletions {
|
|||
val Update = Value("~=")
|
||||
}
|
||||
import Assign._
|
||||
|
||||
/** Returns the description associated with the provided assignment method. */
|
||||
def assignDescription(a: Assign.Value): String = a match {
|
||||
case AppendValue => "append value"
|
||||
|
|
@ -344,6 +402,7 @@ private[sbt] object SettingCompletions {
|
|||
case Define => "define value, overwriting any existing value"
|
||||
case Update => "transform existing value"
|
||||
}
|
||||
|
||||
/** The assignment methods except for the ones that append. */
|
||||
val assignNoAppend: Set[Assign.Value] = Set(Define, Update)
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue