mirror of https://github.com/sbt/sbt.git
commit
605e6047f1
|
|
@ -8,19 +8,20 @@ import sbt.internal.inc.AnalyzingCompiler
|
|||
import sbt.util.Logger
|
||||
|
||||
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): Option[String] =
|
||||
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): Option[String] =
|
||||
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): Option[String] =
|
||||
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): Option[String] =
|
||||
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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package sbt
|
||||
|
||||
import java.io.Closeable
|
||||
import sbt.util.Logger
|
||||
import Def.{ ScopedKey, Classpath }
|
||||
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
|
||||
/** 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
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class JobHandle {
|
||||
def id: Long
|
||||
def humanReadableName: String
|
||||
def spawningTask: ScopedKey[_]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package sbt
|
||||
|
||||
import sbt.internal.DslEntry
|
||||
import sbt.librarymanagement.Configuration
|
||||
import sbt.util.Eval
|
||||
|
||||
private[sbt] trait BuildSyntax {
|
||||
import language.experimental.macros
|
||||
def settingKey[T](description: String): SettingKey[T] = macro std.KeyMacro.settingKeyImpl[T]
|
||||
def taskKey[T](description: String): TaskKey[T] = macro std.KeyMacro.taskKeyImpl[T]
|
||||
def inputKey[T](description: String): InputKey[T] = macro std.KeyMacro.inputKeyImpl[T]
|
||||
|
||||
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)
|
||||
// avoid conflict with `sbt.Keys.aggregate`
|
||||
def aggregateProjects(refs: Eval[ProjectReference]*): DslEntry = DslEntry.DslAggregate(refs)
|
||||
}
|
||||
private[sbt] object BuildSyntax extends BuildSyntax
|
||||
|
|
@ -26,7 +26,6 @@ import sbt.internal.librarymanagement._
|
|||
import sbt.internal.librarymanagement.syntax._
|
||||
import sbt.internal.util._
|
||||
import sbt.util.{ Level, Logger }
|
||||
import sys.error
|
||||
import scala.xml.NodeSeq
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
|
|
@ -134,7 +133,12 @@ object Defaults extends BuildCommon {
|
|||
includeFilter in unmanagedSources :== ("*.java" | "*.scala") && new SimpleFileFilter(_.isFile),
|
||||
includeFilter in unmanagedJars :== "*.jar" | "*.so" | "*.dll" | "*.jnilib" | "*.zip",
|
||||
includeFilter in unmanagedResources :== AllPassFilter,
|
||||
fileToStore :== DefaultFileToStore
|
||||
fileToStore :== DefaultFileToStore,
|
||||
bgList := { bgJobService.value.jobs },
|
||||
ps := psTask.value,
|
||||
bgStop := bgStopTask.evaluated,
|
||||
bgWaitFor := bgWaitForTask.evaluated,
|
||||
bgCopyClasspath :== true
|
||||
)
|
||||
|
||||
private[sbt] lazy val globalIvyCore: Seq[Setting[_]] =
|
||||
|
|
@ -325,7 +329,7 @@ object Defaults extends BuildCommon {
|
|||
}
|
||||
}
|
||||
|
||||
lazy val configTasks = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ compileAnalysisSettings ++ Seq(
|
||||
lazy val configTasks: Seq[Setting[_]] = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ compileAnalysisSettings ++ Seq(
|
||||
compile := compileTask.value,
|
||||
manipulateBytecode := compileIncremental.value,
|
||||
compileIncremental := (compileIncrementalTask tag (Tags.Compile, Tags.CPU)).value,
|
||||
|
|
@ -343,14 +347,18 @@ object Defaults extends BuildCommon {
|
|||
consoleQuick := consoleQuickTask.value,
|
||||
discoveredMainClasses := (compile map discoverMainClasses storeAs discoveredMainClasses xtriggeredBy compile).value,
|
||||
discoveredSbtPlugins := discoverSbtPluginNames.value,
|
||||
inTask(run)(runnerTask :: Nil).head,
|
||||
// This fork options, scoped to the configuration is used for tests
|
||||
forkOptions := forkOptionsTask.value,
|
||||
selectMainClass := mainClass.value orElse askForMainClass(discoveredMainClasses.value),
|
||||
mainClass in run := (selectMainClass in run).value,
|
||||
mainClass := pickMainClassOrWarn(discoveredMainClasses.value, streams.value.log),
|
||||
run := runTask(fullClasspath, mainClass in run, runner in run).evaluated,
|
||||
runMain := runMainTask(fullClasspath, runner in run).evaluated,
|
||||
copyResources := copyResourcesTask.value
|
||||
)
|
||||
run := runTask(fullClasspath, mainClass in run, runner in run).evaluated,
|
||||
copyResources := copyResourcesTask.value,
|
||||
// note that we use the same runner and mainClass as plain run
|
||||
bgRunMain := bgRunMainTask(exportedProductJars, fullClasspathAsJars, bgCopyClasspath in bgRunMain, runner in run).evaluated,
|
||||
bgRun := bgRunTask(exportedProductJars, fullClasspathAsJars, mainClass in run, bgCopyClasspath in bgRun, runner in run).evaluated
|
||||
) ++ inTask(run)(runnerSettings)
|
||||
|
||||
private[this] lazy val configGlobal = globalDefaults(Seq(
|
||||
initialCommands :== "",
|
||||
|
|
@ -531,11 +539,18 @@ object Defaults extends BuildCommon {
|
|||
val opts = forkOptions.value
|
||||
Seq(new Tests.Group("<default>", tests, if (fk) Tests.SubProcess(opts) else Tests.InProcess))
|
||||
}
|
||||
private[this] def forkOptions: Initialize[Task[ForkOptions]] =
|
||||
(baseDirectory, javaOptions, outputStrategy, envVars, javaHome, connectInput) map {
|
||||
(base, options, strategy, env, javaHomeDir, connectIn) =>
|
||||
def forkOptionsTask: Initialize[Task[ForkOptions]] =
|
||||
Def.task {
|
||||
ForkOptions(
|
||||
// bootJars is empty by default because only jars on the user's classpath should be on the boot classpath
|
||||
ForkOptions(bootJars = Nil, javaHome = javaHomeDir, connectInput = connectIn, outputStrategy = strategy, runJVMOptions = options, workingDirectory = Some(base), envVars = env)
|
||||
bootJars = Nil,
|
||||
javaHome = javaHome.value,
|
||||
connectInput = connectInput.value,
|
||||
outputStrategy = outputStrategy.value,
|
||||
runJVMOptions = javaOptions.value,
|
||||
workingDirectory = Some(baseDirectory.value),
|
||||
envVars = envVars.value
|
||||
)
|
||||
}
|
||||
|
||||
def testExecutionTask(task: Scoped): Initialize[Task[Tests.Execution]] =
|
||||
|
|
@ -781,10 +796,6 @@ object Defaults extends BuildCommon {
|
|||
new Package.Configuration(srcs, path, options)
|
||||
}
|
||||
|
||||
@deprecated("use Defaults.askForMainClass", "0.13.7")
|
||||
def selectRunMain(classes: Seq[String]): Option[String] = askForMainClass(classes)
|
||||
@deprecated("use Defaults.pickMainClass", "0.13.7")
|
||||
def selectPackageMain(classes: Seq[String]): Option[String] = pickMainClass(classes)
|
||||
def askForMainClass(classes: Seq[String]): Option[String] =
|
||||
sbt.SelectMainClass(Some(SimpleReader readLine _), classes)
|
||||
def pickMainClass(classes: Seq[String]): Option[String] =
|
||||
|
|
@ -806,35 +817,80 @@ object Defaults extends BuildCommon {
|
|||
IO.createDirectories(dirs) // recreate empty directories
|
||||
IO.move(mappings.map(_.swap))
|
||||
}
|
||||
|
||||
def bgRunMainTask(products: Initialize[Task[Classpath]], classpath: Initialize[Task[Classpath]],
|
||||
copyClasspath: Initialize[Boolean], scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[JobHandle]] =
|
||||
{
|
||||
val parser = Defaults.loadForParser(discoveredMainClasses)((s, names) => Defaults.runMainParser(s, names getOrElse Nil))
|
||||
Def.inputTask {
|
||||
val service = bgJobService.value
|
||||
val (mainClass, args) = parser.parsed
|
||||
service.runInBackground(resolvedScoped.value, state.value) { (logger, workingDir) =>
|
||||
val cp =
|
||||
if (copyClasspath.value) service.copyClasspath(products.value, classpath.value, workingDir)
|
||||
else classpath.value
|
||||
scalaRun.value.run(mainClass, data(cp), args, logger).get
|
||||
}
|
||||
}
|
||||
}
|
||||
def bgRunTask(products: Initialize[Task[Classpath]], classpath: Initialize[Task[Classpath]], mainClassTask: Initialize[Task[Option[String]]],
|
||||
copyClasspath: Initialize[Boolean], scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[JobHandle]] =
|
||||
{
|
||||
import Def.parserToInput
|
||||
val parser = Def.spaceDelimited()
|
||||
Def.inputTask {
|
||||
val service = bgJobService.value
|
||||
val mainClass = mainClassTask.value getOrElse sys.error("No main class detected.")
|
||||
service.runInBackground(resolvedScoped.value, state.value) { (logger, workingDir) =>
|
||||
val cp =
|
||||
if (copyClasspath.value) service.copyClasspath(products.value, classpath.value, workingDir)
|
||||
else classpath.value
|
||||
scalaRun.value.run(mainClass, data(cp), parser.parsed, logger).get
|
||||
}
|
||||
}
|
||||
}
|
||||
// runMain calls bgRunMain in the background and waits for the result.
|
||||
def foregroundRunMainTask: Initialize[InputTask[Unit]] =
|
||||
Def.inputTask {
|
||||
val handle = bgRunMain.evaluated
|
||||
val service = bgJobService.value
|
||||
service.waitFor(handle)
|
||||
}
|
||||
// run calls bgRun in the background and waits for the result.
|
||||
def foregroundRunTask: Initialize[InputTask[Unit]] =
|
||||
Def.inputTask {
|
||||
val handle = bgRun.evaluated
|
||||
val service = bgJobService.value
|
||||
service.waitFor(handle)
|
||||
}
|
||||
def runMainTask(classpath: Initialize[Task[Classpath]], scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[Unit]] =
|
||||
{
|
||||
val parser = loadForParser(discoveredMainClasses)((s, names) => runMainParser(s, names getOrElse Nil))
|
||||
Def.inputTask {
|
||||
val (mainClass, args) = parser.parsed
|
||||
toError(scalaRun.value.run(mainClass, data(classpath.value), args, streams.value.log))
|
||||
scalaRun.value.run(mainClass, data(classpath.value), args, streams.value.log).get
|
||||
}
|
||||
}
|
||||
|
||||
def runTask(classpath: Initialize[Task[Classpath]], mainClassTask: Initialize[Task[Option[String]]], scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[Unit]] =
|
||||
{
|
||||
import Def.parserToInput
|
||||
val parser = Def.spaceDelimited()
|
||||
Def.inputTask {
|
||||
val mainClass = mainClassTask.value getOrElse sys.error("No main class detected.")
|
||||
toError(scalaRun.value.run(mainClass, data(classpath.value), parser.parsed, streams.value.log))
|
||||
scalaRun.value.run(mainClass, data(classpath.value), parser.parsed, streams.value.log).get
|
||||
}
|
||||
}
|
||||
|
||||
def runnerTask = runner := runnerInit.value
|
||||
def runnerTask: Initialize[Task[ScalaRun]] = runnerInit
|
||||
def runnerInit: Initialize[Task[ScalaRun]] = Def.task {
|
||||
val tmp = taskTemporaryDirectory.value
|
||||
val resolvedScope = resolvedScoped.value.scope
|
||||
val si = scalaInstance.value
|
||||
val s = streams.value
|
||||
val opts = forkOptions.value
|
||||
val options = javaOptions.value
|
||||
if (fork.value) {
|
||||
s.log.debug(s"javaOptions: $options")
|
||||
new ForkRun(forkOptions.value)
|
||||
new ForkRun(opts)
|
||||
} else {
|
||||
if (options.nonEmpty) {
|
||||
val mask = ScopeMask(project = false)
|
||||
|
|
@ -846,6 +902,31 @@ object Defaults extends BuildCommon {
|
|||
}
|
||||
}
|
||||
|
||||
private def foreachJobTask(f: (BackgroundJobService, JobHandle) => Unit): Initialize[InputTask[Unit]] = {
|
||||
val parser: Initialize[State => Parser[Seq[JobHandle]]] = Def.setting { (s: State) =>
|
||||
val extracted = Project.extract(s)
|
||||
val service = extracted.get(bgJobService)
|
||||
// you might be tempted to use the jobList task here, but the problem
|
||||
// is that its result gets cached during execution and therefore stale
|
||||
BackgroundJobService.jobIdParser(s, service.jobs)
|
||||
}
|
||||
Def.inputTask {
|
||||
val handles = parser.parsed
|
||||
for (handle <- handles) {
|
||||
f(bgJobService.value, handle)
|
||||
}
|
||||
}
|
||||
}
|
||||
def psTask: Initialize[Task[Vector[JobHandle]]] =
|
||||
Def.task {
|
||||
val xs = bgList.value
|
||||
val s = streams.value
|
||||
xs foreach { x => s.log.info(x.toString) }
|
||||
xs
|
||||
}
|
||||
def bgStopTask: Initialize[InputTask[Unit]] = foreachJobTask { (manager, handle) => manager.stop(handle) }
|
||||
def bgWaitForTask: Initialize[InputTask[Unit]] = foreachJobTask { (manager, handle) => manager.waitFor(handle) }
|
||||
|
||||
@deprecated("Use `docTaskSettings` instead", "0.12.0")
|
||||
def docSetting(key: TaskKey[File]) = docTaskSettings(key)
|
||||
def docTaskSettings(key: TaskKey[File] = doc): Seq[Setting[_]] = inTask(key)(Seq(
|
||||
|
|
@ -882,8 +963,8 @@ object Defaults extends BuildCommon {
|
|||
}
|
||||
))
|
||||
|
||||
def mainRunTask = run := runTask(fullClasspath in Runtime, mainClass in run, runner in run).evaluated
|
||||
def mainRunMainTask = runMain := runMainTask(fullClasspath in Runtime, runner in run).evaluated
|
||||
def mainBgRunTask = bgRun := bgRunTask(exportedProductJars, fullClasspathAsJars in Runtime, mainClass in run, bgCopyClasspath in bgRun, runner in run).evaluated
|
||||
def mainBgRunMainTask = bgRunMain := bgRunMainTask(exportedProductJars, fullClasspathAsJars in Runtime, bgCopyClasspath in bgRunMain, runner in run).evaluated
|
||||
|
||||
def discoverMainClasses(analysis: CompileAnalysis): Seq[String] =
|
||||
Discovery.applications(Tests.allDefs(analysis)).collect({ case (definition, discovered) if discovered.hasMain => definition.name }).sorted
|
||||
|
|
@ -901,7 +982,7 @@ object Defaults extends BuildCommon {
|
|||
cs.scalac match {
|
||||
case ac: AnalyzingCompiler => ac.onArgs(exported(s, "scala"))
|
||||
}
|
||||
(new Console(compiler))(cpFiles, options, loader, initCommands, cleanup)()(s.log).foreach(msg => sys.error(msg))
|
||||
(new Console(compiler))(cpFiles, options, loader, initCommands, cleanup)()(s.log).get
|
||||
println()
|
||||
}
|
||||
|
||||
|
|
@ -1086,15 +1167,21 @@ object Defaults extends BuildCommon {
|
|||
|
||||
val CompletionsID = "completions"
|
||||
|
||||
def noAggregation: Seq[Scoped] = Seq(run, runMain, console, consoleQuick, consoleProject)
|
||||
def noAggregation: Seq[Scoped] = Seq(run, runMain, bgRun, bgRunMain, console, consoleQuick, consoleProject)
|
||||
lazy val disableAggregation = Defaults.globalDefaults(noAggregation map disableAggregate)
|
||||
def disableAggregate(k: Scoped) = aggregate in k :== false
|
||||
|
||||
lazy val runnerSettings: Seq[Setting[_]] = Seq(runnerTask)
|
||||
// 1. runnerSettings is added unscoped via JvmPlugin.
|
||||
// 2. In addition it's added scoped to run task.
|
||||
lazy val runnerSettings: Seq[Setting[_]] =
|
||||
Seq(
|
||||
runner := runnerInit.value,
|
||||
forkOptions := forkOptionsTask.value
|
||||
)
|
||||
lazy val baseTasks: Seq[Setting[_]] = projectTasks ++ packageBase
|
||||
lazy val configSettings: Seq[Setting[_]] = Classpaths.configSettings ++ configTasks ++ configPaths ++ packageConfig ++ Classpaths.compilerPluginConfig ++ deprecationSettings
|
||||
|
||||
lazy val compileSettings: Seq[Setting[_]] = configSettings ++ (mainRunMainTask +: mainRunTask +: addBaseSources) ++ Classpaths.addUnmanagedLibrary
|
||||
lazy val compileSettings: Seq[Setting[_]] = configSettings ++ (mainBgRunMainTask +: mainBgRunTask +: addBaseSources) ++ Classpaths.addUnmanagedLibrary
|
||||
lazy val testSettings: Seq[Setting[_]] = configSettings ++ testTasks
|
||||
|
||||
lazy val itSettings: Seq[Setting[_]] = inConfig(IntegrationTest)(testSettings)
|
||||
|
|
@ -1142,6 +1229,12 @@ object Classpaths {
|
|||
exportedProducts := trackedExportedProducts(TrackLevel.TrackAlways).value,
|
||||
exportedProductsIfMissing := trackedExportedProducts(TrackLevel.TrackIfMissing).value,
|
||||
exportedProductsNoTracking := trackedExportedProducts(TrackLevel.NoTracking).value,
|
||||
exportedProductJars := trackedExportedJarProducts(TrackLevel.TrackAlways).value,
|
||||
exportedProductJarsIfMissing := trackedExportedJarProducts(TrackLevel.TrackIfMissing).value,
|
||||
exportedProductJarsNoTracking := trackedExportedJarProducts(TrackLevel.NoTracking).value,
|
||||
internalDependencyAsJars := internalDependencyJarsTask.value,
|
||||
dependencyClasspathAsJars := concat(internalDependencyAsJars, externalDependencyClasspath).value,
|
||||
fullClasspathAsJars := concatDistinct(exportedProductJars, dependencyClasspathAsJars).value,
|
||||
unmanagedJars := findUnmanagedJars(configuration.value, unmanagedBase.value, includeFilter in unmanagedJars value, excludeFilter in unmanagedJars value)
|
||||
).map(exportClasspath)
|
||||
|
||||
|
|
@ -1816,17 +1909,26 @@ object Classpaths {
|
|||
copyResources.value
|
||||
classDirectory.value :: Nil
|
||||
}
|
||||
// This is a variant of exportProductsTask with tracking
|
||||
private[sbt] def trackedExportedProducts(track: TrackLevel): Initialize[Task[Classpath]] = Def.task {
|
||||
val art = (artifact in packageBin).value
|
||||
val module = projectID.value
|
||||
val config = configuration.value
|
||||
for { (f, analysis) <- trackedProductsImplTask(track).value } yield APIMappings.store(analyzed(f, analysis), apiURL.value).put(artifact.key, art).put(moduleID.key, module).put(configuration.key, config)
|
||||
for { (f, analysis) <- trackedExportedProductsImplTask(track).value } yield APIMappings.store(analyzed(f, analysis), apiURL.value).put(artifact.key, art).put(moduleID.key, module).put(configuration.key, config)
|
||||
}
|
||||
private[this] def trackedProductsImplTask(track: TrackLevel): Initialize[Task[Seq[(File, CompileAnalysis)]]] =
|
||||
private[sbt] def trackedExportedJarProducts(track: TrackLevel): Initialize[Task[Classpath]] = Def.task {
|
||||
val art = (artifact in packageBin).value
|
||||
val module = projectID.value
|
||||
val config = configuration.value
|
||||
for { (f, analysis) <- trackedJarProductsImplTask(track).value } yield APIMappings.store(analyzed(f, analysis), apiURL.value).put(artifact.key, art).put(moduleID.key, module).put(configuration.key, config)
|
||||
}
|
||||
private[this] def trackedExportedProductsImplTask(track: TrackLevel): Initialize[Task[Seq[(File, CompileAnalysis)]]] =
|
||||
Def.taskDyn {
|
||||
val useJars = exportJars.value
|
||||
val jar = (artifactPath in packageBin).value
|
||||
if (useJars) trackedJarProductsImplTask(track)
|
||||
else trackedNonJarProductsImplTask(track)
|
||||
}
|
||||
private[this] def trackedNonJarProductsImplTask(track: TrackLevel): Initialize[Task[Seq[(File, CompileAnalysis)]]] =
|
||||
Def.taskDyn {
|
||||
val dirs = productDirectories.value
|
||||
def containsClassFile(fs: List[File]): Boolean =
|
||||
(fs exists { dir =>
|
||||
|
|
@ -1835,41 +1937,46 @@ object Classpaths {
|
|||
}
|
||||
})
|
||||
TrackLevel.intersection(track, exportToInternal.value) match {
|
||||
case TrackLevel.TrackAlways if (useJars) =>
|
||||
Def.task {
|
||||
Seq((packageBin.value, compile.value))
|
||||
}
|
||||
case TrackLevel.TrackAlways =>
|
||||
Def.task {
|
||||
products.value map { (_, compile.value) }
|
||||
}
|
||||
case TrackLevel.TrackIfMissing if (useJars && !jar.exists) =>
|
||||
Def.task {
|
||||
Seq((packageBin.value, compile.value))
|
||||
}
|
||||
case TrackLevel.TrackIfMissing if (!useJars && !containsClassFile(dirs.toList)) =>
|
||||
case TrackLevel.TrackIfMissing if !containsClassFile(dirs.toList) =>
|
||||
Def.task {
|
||||
products.value map { (_, compile.value) }
|
||||
}
|
||||
case _ =>
|
||||
Def.task {
|
||||
val analysisOpt = previousCompile.value.analysis
|
||||
(if (useJars) Seq(jar)
|
||||
else dirs) map { x =>
|
||||
dirs map { x =>
|
||||
(x, if (analysisOpt.isDefined) analysisOpt.get
|
||||
else Analysis.empty(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private[this] def trackedJarProductsImplTask(track: TrackLevel): Initialize[Task[Seq[(File, CompileAnalysis)]]] =
|
||||
Def.taskDyn {
|
||||
val jar = (artifactPath in packageBin).value
|
||||
TrackLevel.intersection(track, exportToInternal.value) match {
|
||||
case TrackLevel.TrackAlways =>
|
||||
Def.task {
|
||||
Seq((packageBin.value, compile.value))
|
||||
}
|
||||
case TrackLevel.TrackIfMissing if !jar.exists =>
|
||||
Def.task {
|
||||
Seq((packageBin.value, compile.value))
|
||||
}
|
||||
case _ =>
|
||||
Def.task {
|
||||
val analysisOpt = previousCompile.value.analysis
|
||||
Seq(jar) map { x =>
|
||||
(x, if (analysisOpt.isDefined) analysisOpt.get
|
||||
else Analysis.empty(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@deprecated("This is no longer used.", "0.13.10")
|
||||
def exportProductsTask: Initialize[Task[Classpath]] = Def.task {
|
||||
val art = (artifact in packageBin).value
|
||||
val module = projectID.value
|
||||
val config = configuration.value
|
||||
for (f <- productsImplTask.value) yield APIMappings.store(analyzed(f, compile.value), apiURL.value).put(artifact.key, art).put(moduleID.key, module).put(configuration.key, config)
|
||||
}
|
||||
|
||||
private[this] def productsImplTask: Initialize[Task[Seq[File]]] = Def.task {
|
||||
if (exportJars.value) Seq(packageBin.value) else products.value
|
||||
|
|
@ -1878,7 +1985,10 @@ object Classpaths {
|
|||
def constructBuildDependencies: Initialize[BuildDependencies] = loadedBuild(lb => BuildUtil.dependencies(lb.units))
|
||||
|
||||
def internalDependencies: Initialize[Task[Classpath]] =
|
||||
(thisProjectRef, classpathConfiguration, configuration, settingsData, buildDependencies, trackInternalDependencies) flatMap internalDependencies0
|
||||
(thisProjectRef, classpathConfiguration, configuration, settingsData, buildDependencies, trackInternalDependencies) flatMap internalDependenciesImplTask
|
||||
def internalDependencyJarsTask: Initialize[Task[Classpath]] =
|
||||
(thisProjectRef, classpathConfiguration, configuration, settingsData, buildDependencies, trackInternalDependencies) flatMap internalDependencyJarsImplTask
|
||||
|
||||
def unmanagedDependencies: Initialize[Task[Classpath]] =
|
||||
(thisProjectRef, configuration, settingsData, buildDependencies) flatMap unmanagedDependencies0
|
||||
def mkIvyConfiguration: Initialize[Task[IvyConfiguration]] =
|
||||
|
|
@ -1917,16 +2027,10 @@ object Classpaths {
|
|||
}
|
||||
def unmanagedDependencies0(projectRef: ProjectRef, conf: Configuration, data: Settings[Scope], deps: BuildDependencies): Task[Classpath] =
|
||||
interDependencies(projectRef, deps, conf, conf, data, TrackLevel.TrackAlways, true, unmanagedLibs0)
|
||||
@deprecated("This is no longer public.", "0.13.10")
|
||||
def internalDependencies0(projectRef: ProjectRef, conf: Configuration, self: Configuration, data: Settings[Scope], deps: BuildDependencies): Task[Classpath] =
|
||||
interDependencies(projectRef, deps, conf, self, data, false, productsTask)
|
||||
private[sbt] def internalDependencies0(projectRef: ProjectRef, conf: Configuration, self: Configuration, data: Settings[Scope], deps: BuildDependencies, track: TrackLevel): Task[Classpath] =
|
||||
interDependencies(projectRef, deps, conf, self, data, track, false, productsTask0)
|
||||
@deprecated("This is no longer public.", "0.13.10")
|
||||
def interDependencies(projectRef: ProjectRef, deps: BuildDependencies, conf: Configuration, self: Configuration, data: Settings[Scope], includeSelf: Boolean,
|
||||
f: (ProjectRef, String, Settings[Scope]) => Task[Classpath]): Task[Classpath] =
|
||||
interDependencies(projectRef, deps, conf, self, data, TrackLevel.TrackAlways, includeSelf,
|
||||
{ (pr: ProjectRef, s: String, sc: Settings[Scope], tl: TrackLevel) => f(pr, s, sc) })
|
||||
private[sbt] def internalDependenciesImplTask(projectRef: ProjectRef, conf: Configuration, self: Configuration, data: Settings[Scope], deps: BuildDependencies, track: TrackLevel): Task[Classpath] =
|
||||
interDependencies(projectRef, deps, conf, self, data, track, false, productsTask)
|
||||
private[sbt] def internalDependencyJarsImplTask(projectRef: ProjectRef, conf: Configuration, self: Configuration, data: Settings[Scope], deps: BuildDependencies, track: TrackLevel): Task[Classpath] =
|
||||
interDependencies(projectRef, deps, conf, self, data, track, false, jarProductsTask)
|
||||
private[sbt] def interDependencies(projectRef: ProjectRef, deps: BuildDependencies, conf: Configuration, self: Configuration, data: Settings[Scope],
|
||||
track: TrackLevel, includeSelf: Boolean, f: (ProjectRef, String, Settings[Scope], TrackLevel) => Task[Classpath]): Task[Classpath] =
|
||||
{
|
||||
|
|
@ -1979,14 +2083,18 @@ object Classpaths {
|
|||
ivyConfigurations in p get data getOrElse Nil
|
||||
def confOpt(configurations: Seq[Configuration], conf: String): Option[Configuration] =
|
||||
configurations.find(_.name == conf)
|
||||
def productsTask(dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] =
|
||||
getClasspath(exportedProducts, dep, conf, data)
|
||||
def productsTask0(dep: ResolvedReference, conf: String, data: Settings[Scope], track: TrackLevel): Task[Classpath] =
|
||||
private[sbt] def productsTask(dep: ResolvedReference, conf: String, data: Settings[Scope], track: TrackLevel): Task[Classpath] =
|
||||
track match {
|
||||
case TrackLevel.NoTracking => getClasspath(exportedProductsNoTracking, dep, conf, data)
|
||||
case TrackLevel.TrackIfMissing => getClasspath(exportedProductsIfMissing, dep, conf, data)
|
||||
case TrackLevel.TrackAlways => getClasspath(exportedProducts, dep, conf, data)
|
||||
}
|
||||
private[sbt] def jarProductsTask(dep: ResolvedReference, conf: String, data: Settings[Scope], track: TrackLevel): Task[Classpath] =
|
||||
track match {
|
||||
case TrackLevel.NoTracking => getClasspath(exportedProductJarsNoTracking, dep, conf, data)
|
||||
case TrackLevel.TrackIfMissing => getClasspath(exportedProductJarsIfMissing, dep, conf, data)
|
||||
case TrackLevel.TrackAlways => getClasspath(exportedProductJars, dep, conf, data)
|
||||
}
|
||||
private[sbt] def unmanagedLibs0(dep: ResolvedReference, conf: String, data: Settings[Scope], track: TrackLevel): Task[Classpath] =
|
||||
unmanagedLibs(dep, conf, data)
|
||||
def unmanagedLibs(dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] =
|
||||
|
|
@ -2054,7 +2162,7 @@ object Classpaths {
|
|||
|
||||
private[this] lazy val internalCompilerPluginClasspath: Initialize[Task[Classpath]] =
|
||||
(thisProjectRef, settingsData, buildDependencies) flatMap { (ref, data, deps) =>
|
||||
internalDependencies0(ref, CompilerPlugin, CompilerPlugin, data, deps, TrackLevel.TrackAlways)
|
||||
internalDependenciesImplTask(ref, CompilerPlugin, CompilerPlugin, data, deps, TrackLevel.TrackAlways)
|
||||
}
|
||||
|
||||
lazy val compilerPluginConfig = Seq(
|
||||
|
|
@ -2255,30 +2363,41 @@ trait BuildExtra extends BuildCommon with DefExtra {
|
|||
val r = (runner in (config, run)).value
|
||||
val cp = (fullClasspath in config).value
|
||||
val args = spaceDelimited().parsed
|
||||
toError(r.run(mainClass, data(cp), baseArguments ++ args, streams.value.log))
|
||||
r.run(mainClass, data(cp), baseArguments ++ args, streams.value.log).get
|
||||
}
|
||||
def runTask(config: Configuration, mainClass: String, arguments: String*): Initialize[Task[Unit]] =
|
||||
(fullClasspath in config, runner in (config, run), streams) map { (cp, r, s) =>
|
||||
toError(r.run(mainClass, data(cp), arguments, s.log))
|
||||
Def.task {
|
||||
val cp = (fullClasspath in config).value
|
||||
val r = (runner in (config, run)).value
|
||||
val s = streams.value
|
||||
r.run(mainClass, data(cp), arguments, s.log).get
|
||||
}
|
||||
// public API
|
||||
/** Returns a vector of settings that create custom run input task. */
|
||||
def fullRunInputTask(scoped: InputKey[Unit], config: Configuration, mainClass: String, baseArguments: String*): Vector[Setting[_]] =
|
||||
Vector(
|
||||
scoped := (inputTask { result =>
|
||||
(initScoped(scoped.scopedKey, runnerInit) zipWith (fullClasspath in config, streams, result).identityMap) { (rTask, t) =>
|
||||
(t, rTask) map {
|
||||
case ((cp, s, args), r) =>
|
||||
r.run(mainClass, data(cp), baseArguments ++ args, s.log).get
|
||||
}
|
||||
}
|
||||
}).evaluated
|
||||
) ++ inTask(scoped)(forkOptions := forkOptionsTask.value)
|
||||
// public API
|
||||
/** Returns a vector of settings that create custom run task. */
|
||||
def fullRunTask(scoped: TaskKey[Unit], config: Configuration, mainClass: String, arguments: String*): Vector[Setting[_]] =
|
||||
Vector(
|
||||
scoped := ((initScoped(scoped.scopedKey, runnerInit) zipWith (fullClasspath in config, streams).identityMap) {
|
||||
case (rTask, t) =>
|
||||
(t, rTask) map {
|
||||
case ((cp, s), r) =>
|
||||
r.run(mainClass, data(cp), arguments, s.log).get
|
||||
}
|
||||
}).value
|
||||
) ++ inTask(scoped)(forkOptions := forkOptionsTask.value)
|
||||
|
||||
def fullRunInputTask(scoped: InputKey[Unit], config: Configuration, mainClass: String, baseArguments: String*): Setting[InputTask[Unit]] =
|
||||
scoped := (inputTask { result =>
|
||||
(initScoped(scoped.scopedKey, runnerInit) zipWith (fullClasspath in config, streams, result).identityMap) { (rTask, t) =>
|
||||
(t, rTask) map {
|
||||
case ((cp, s, args), r) =>
|
||||
toError(r.run(mainClass, data(cp), baseArguments ++ args, s.log))
|
||||
}
|
||||
}
|
||||
}).evaluated
|
||||
def fullRunTask(scoped: TaskKey[Unit], config: Configuration, mainClass: String, arguments: String*): Setting[Task[Unit]] =
|
||||
scoped := ((initScoped(scoped.scopedKey, runnerInit) zipWith (fullClasspath in config, streams).identityMap) {
|
||||
case (rTask, t) =>
|
||||
(t, rTask) map {
|
||||
case ((cp, s), r) =>
|
||||
toError(r.run(mainClass, data(cp), arguments, s.log))
|
||||
}
|
||||
}).value
|
||||
def initScoped[T](sk: ScopedKey[_], i: Initialize[T]): Initialize[T] = initScope(fillTaskAxis(sk.scope, sk.key), i)
|
||||
def initScope[T](s: Scope, i: Initialize[T]): Initialize[T] = i mapReferenced Project.mapScope(Scope.replaceThis(s))
|
||||
|
||||
|
|
@ -2323,7 +2442,6 @@ trait BuildCommon {
|
|||
/** Converts the `Seq[File]` to a Classpath, which is an alias for `Seq[Attributed[File]]`. */
|
||||
def classpath: Classpath = Attributed blankSeq s
|
||||
}
|
||||
def toError(o: Option[String]): Unit = o foreach error
|
||||
|
||||
def overrideConfigs(cs: Configuration*)(configurations: Seq[Configuration]): Seq[Configuration] =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ import sbt.internal.librarymanagement.{
|
|||
}
|
||||
import sbt.util.{ AbstractLogger, Level, Logger }
|
||||
import org.apache.logging.log4j.core.Appender
|
||||
import sbt.BuildSyntax._
|
||||
|
||||
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."
|
||||
|
|
@ -238,12 +239,22 @@ object Keys {
|
|||
val trapExit = SettingKey[Boolean]("trap-exit", "If true, enables exit trapping and thread management for 'run'-like tasks. This is currently only suitable for serially-executed 'run'-like tasks.", CSetting)
|
||||
|
||||
val fork = SettingKey[Boolean]("fork", "If true, forks a new JVM when running. If false, runs in the same JVM as the build.", ASetting)
|
||||
val forkOptions = TaskKey[ForkOptions]("fork-options", "Configures JVM forking.", DSetting)
|
||||
val outputStrategy = SettingKey[Option[sbt.OutputStrategy]]("output-strategy", "Selects how to log output when running a main class.", DSetting)
|
||||
val connectInput = SettingKey[Boolean]("connect-input", "If true, connects standard input when running a main class forked.", CSetting)
|
||||
val javaHome = SettingKey[Option[File]]("java-home", "Selects the Java installation used for compiling and forking. If None, uses the Java installation running the build.", ASetting)
|
||||
val javaOptions = TaskKey[Seq[String]]("java-options", "Options passed to a new JVM when forking.", BPlusTask)
|
||||
val envVars = TaskKey[Map[String, String]]("envVars", "Environment variables used when forking a new JVM", BTask)
|
||||
|
||||
val bgJobService = settingKey[BackgroundJobService]("Job manager used to run background jobs.")
|
||||
val bgList = taskKey[Vector[JobHandle]]("List running background jobs.")
|
||||
val ps = taskKey[Vector[JobHandle]]("bgList variant that displays on the log.")
|
||||
val bgStop = inputKey[Unit]("Stop a background job by providing its ID.")
|
||||
val bgWaitFor = inputKey[Unit]("Wait for a background job to finish by providing its ID.")
|
||||
val bgRun = inputKey[JobHandle]("Start an application's default main class as a background job")
|
||||
val bgRunMain = inputKey[JobHandle]("Start a provided main class as a background job")
|
||||
val bgCopyClasspath = settingKey[Boolean]("Copies classpath on bgRun to prevent conflict.")
|
||||
|
||||
// Test Keys
|
||||
val testLoader = TaskKey[ClassLoader]("test-loader", "Provides the class loader used for testing.", DTask)
|
||||
val loadedTestFrameworks = TaskKey[Map[TestFramework, Framework]]("loaded-test-frameworks", "Loads Framework definitions from the test loader.", DTask)
|
||||
|
|
@ -285,8 +296,6 @@ object Keys {
|
|||
val defaultConfiguration = SettingKey[Option[Configuration]]("default-configuration", "Defines the configuration used when none is specified for a dependency in ivyXML.", CSetting)
|
||||
|
||||
val products = TaskKey[Seq[File]]("products", "Build products that get packaged.", BMinusTask)
|
||||
// TODO: This is used by exportedProducts, exportedProductsIfMissing, exportedProductsNoTracking..
|
||||
@deprecated("This task is unused by the default project and will be removed.", "0.13.0")
|
||||
val productDirectories = TaskKey[Seq[File]]("product-directories", "Base directories of build products.", CTask)
|
||||
val exportJars = SettingKey[Boolean]("export-jars", "Determines whether the exported classpath for this project contains classes (false) or a packaged jar (true).", BSetting)
|
||||
val exportedProducts = TaskKey[Classpath]("exported-products", "Build products that go on the exported classpath.", CTask)
|
||||
|
|
@ -301,6 +310,12 @@ object Keys {
|
|||
val fullClasspath = TaskKey[Classpath]("full-classpath", "The exported classpath, consisting of build products and unmanaged and managed, internal and external dependencies.", BPlusTask)
|
||||
val trackInternalDependencies = SettingKey[TrackLevel]("track-internal-dependencies", "The level of tracking for the internal (inter-project) dependency.", BSetting)
|
||||
val exportToInternal = SettingKey[TrackLevel]("export-to-internal", "The level of tracking for this project by the internal callers.", BSetting)
|
||||
val exportedProductJars = taskKey[Classpath]("Build products that go on the exported classpath as JARs.")
|
||||
val exportedProductJarsIfMissing = taskKey[Classpath]("Build products that go on the exported classpath as JARs if missing.")
|
||||
val exportedProductJarsNoTracking = taskKey[Classpath]("Just the exported classpath as JARs without triggering the compilation.")
|
||||
val internalDependencyAsJars = taskKey[Classpath]("The internal (inter-project) classpath as JARs.")
|
||||
val dependencyClasspathAsJars = taskKey[Classpath]("The classpath consisting of internal and external, managed and unmanaged dependencies, all as JARs.")
|
||||
val fullClasspathAsJars = taskKey[Classpath]("The exported classpath, consisting of build products and unmanaged and managed, internal and external dependencies, all as JARs.")
|
||||
|
||||
val internalConfigurationMap = SettingKey[Configuration => Configuration]("internal-configuration-map", "Maps configurations to the actual configuration used to define the classpath.", CSetting)
|
||||
val classpathConfiguration = TaskKey[Configuration]("classpath-configuration", "The configuration used to define the classpath.", CTask)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import sbt.internal.{
|
|||
Script,
|
||||
SessionSettings,
|
||||
SettingCompletions,
|
||||
LogManager
|
||||
LogManager,
|
||||
DefaultBackgroundJobService
|
||||
}
|
||||
import sbt.internal.util.{ AttributeKey, AttributeMap, complete, ConsoleOut, GlobalLogging, LineRange, MainAppender, SimpleReader, Types }
|
||||
import sbt.util.{ Level, Logger }
|
||||
|
|
@ -84,8 +85,11 @@ object StandardMain {
|
|||
def runManaged(s: State): xsbti.MainResult =
|
||||
{
|
||||
val previous = TrapExit.installManager()
|
||||
try MainLoop.runLogged(s)
|
||||
finally TrapExit.uninstallManager(previous)
|
||||
try {
|
||||
try {
|
||||
MainLoop.runLogged(s)
|
||||
} finally DefaultBackgroundJobService.backgroundJobService.shutdown()
|
||||
} finally TrapExit.uninstallManager(previous)
|
||||
}
|
||||
|
||||
/** The common interface to standard output, used for all built-in ConsoleLoggers. */
|
||||
|
|
|
|||
|
|
@ -214,7 +214,9 @@ object BuildStreams {
|
|||
|
||||
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)
|
||||
(s get Keys.stateStreams) getOrElse {
|
||||
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 =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,306 @@
|
|||
package sbt
|
||||
package internal
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.io.Closeable
|
||||
import Def.{ ScopedKey, Setting, Classpath }
|
||||
import scala.concurrent.ExecutionContext
|
||||
import Scope.GlobalScope
|
||||
import java.io.File
|
||||
import sbt.io.{ IO, Hash }
|
||||
import sbt.io.syntax._
|
||||
import sbt.util.{ Logger, LogExchange }
|
||||
import sbt.internal.util.{ Attributed, ManagedLogger }
|
||||
|
||||
/**
|
||||
* Interface between sbt and a thing running in the background.
|
||||
*/
|
||||
private[sbt] abstract class BackgroundJob {
|
||||
def humanReadableName: String
|
||||
def awaitTermination(): Unit
|
||||
def shutdown(): Unit
|
||||
// this should be true on construction and stay true until
|
||||
// the job is complete
|
||||
def isRunning(): Boolean
|
||||
// called after stop or on spontaneous exit, closing the result
|
||||
// removes the listener
|
||||
def onStop(listener: () => Unit)(implicit ex: ExecutionContext): Closeable
|
||||
// do we need this or is the spawning task good enough?
|
||||
// def tags: SomeType
|
||||
}
|
||||
|
||||
private[sbt] abstract class AbstractJobHandle extends JobHandle {
|
||||
override def toString = s"JobHandle(${id}, ${humanReadableName}, ${Def.showFullKey(spawningTask)})"
|
||||
}
|
||||
|
||||
private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobService {
|
||||
private val nextId = new AtomicLong(1)
|
||||
private val pool = new BackgroundThreadPool()
|
||||
private val serviceTempDir = IO.createTemporaryDirectory
|
||||
|
||||
// hooks for sending start/stop events
|
||||
protected def onAddJob(job: JobHandle): Unit = {}
|
||||
protected def onRemoveJob(job: JobHandle): Unit = {}
|
||||
|
||||
// this mutable state could conceptually go on State except
|
||||
// that then every task that runs a background job would have
|
||||
// to be a command, so not sure what to do here.
|
||||
@volatile
|
||||
private final var jobSet = Set.empty[ThreadJobHandle]
|
||||
private def addJob(job: ThreadJobHandle): Unit = synchronized {
|
||||
onAddJob(job)
|
||||
jobSet += job
|
||||
}
|
||||
|
||||
private def removeJob(job: ThreadJobHandle): Unit = synchronized {
|
||||
onRemoveJob(job)
|
||||
jobSet -= job
|
||||
}
|
||||
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
|
||||
) extends AbstractJobHandle {
|
||||
def humanReadableName: String = job.humanReadableName
|
||||
// EC for onStop handler below
|
||||
import ExecutionContext.Implicits.global
|
||||
job.onStop { () =>
|
||||
// TODO: Fix this
|
||||
// logger.close()
|
||||
removeJob(this)
|
||||
IO.delete(workingDirectory)
|
||||
LogExchange.unbindLoggerAppenders(logger.name)
|
||||
}
|
||||
addJob(this)
|
||||
override final def equals(other: Any): Boolean = other match {
|
||||
case handle: JobHandle if handle.id == id => true
|
||||
case _ => false
|
||||
}
|
||||
override final def hashCode(): Int = id.hashCode
|
||||
}
|
||||
|
||||
private val unknownTask = TaskKey[Unit]("unknownTask", "Dummy value")
|
||||
// we use this if we deserialize a handle for a job that no longer exists
|
||||
private final class DeadHandle(override val id: Long, override val humanReadableName: String)
|
||||
extends AbstractJobHandle {
|
||||
override val spawningTask: ScopedKey[_] = unknownTask
|
||||
}
|
||||
|
||||
protected def makeContext(id: Long, spawningTask: ScopedKey[_], state: State): ManagedLogger
|
||||
|
||||
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"
|
||||
IO.createDirectory(workingDir)
|
||||
val job = try {
|
||||
new ThreadJobHandle(id, spawningTask, logger, workingDir, start(logger, workingDir))
|
||||
} catch {
|
||||
case e: Throwable =>
|
||||
// TODO: Fix this
|
||||
// logger.close()
|
||||
throw e
|
||||
}
|
||||
job
|
||||
}
|
||||
|
||||
override def runInBackground(spawningTask: ScopedKey[_], state: State)(start: (Logger, File) => Unit): JobHandle = {
|
||||
pool.run(this, spawningTask, state)(start)
|
||||
}
|
||||
|
||||
override final def close(): Unit = shutdown()
|
||||
override def shutdown(): Unit = {
|
||||
while (jobSet.nonEmpty) {
|
||||
jobSet.headOption.foreach {
|
||||
case handle: ThreadJobHandle @unchecked =>
|
||||
handle.job.shutdown()
|
||||
handle.job.awaitTermination()
|
||||
case _ => //
|
||||
}
|
||||
}
|
||||
pool.close()
|
||||
IO.delete(serviceTempDir)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
override def stop(job: JobHandle): Unit =
|
||||
withHandle(job)(_.job.shutdown())
|
||||
|
||||
override def waitFor(job: JobHandle): Unit =
|
||||
withHandle(job)(_.job.awaitTermination())
|
||||
|
||||
override def toString(): String = s"BackgroundJobService(jobs=${jobs.map(_.id).mkString})"
|
||||
|
||||
/**
|
||||
* Copies products to the workind directory, and the rest to the serviceTempDir of this service,
|
||||
* both wrapped in SHA-1 hash of the file contents.
|
||||
* This is intended to mimize the file copying and accumulation of the unused JAR file.
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] object BackgroundThreadPool {
|
||||
sealed trait Status
|
||||
case object Waiting extends Status
|
||||
final case class Running(thread: Thread) extends Status
|
||||
// the oldThread is None if we never ran
|
||||
final case class Stopped(oldThread: Option[Thread]) extends Status
|
||||
}
|
||||
|
||||
private[sbt] class BackgroundThreadPool extends java.io.Closeable {
|
||||
|
||||
private val nextThreadId = new java.util.concurrent.atomic.AtomicInteger(1)
|
||||
private val threadGroup = Thread.currentThread.getThreadGroup()
|
||||
|
||||
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}")
|
||||
// Do NOT setDaemon because then the code in TaskExit.scala in sbt will insta-kill
|
||||
// the backgrounded process, at least for the case of the run task.
|
||||
thread
|
||||
}
|
||||
}
|
||||
|
||||
private val executor = new 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) */
|
||||
new java.util.concurrent.SynchronousQueue[Runnable](),
|
||||
threadFactory
|
||||
)
|
||||
|
||||
private class BackgroundRunnable(val taskName: String, body: () => Unit)
|
||||
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
|
||||
@volatile
|
||||
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")
|
||||
}
|
||||
}
|
||||
try { if (go) body() }
|
||||
finally cleanup()
|
||||
} finally finishedLatch.countDown()
|
||||
|
||||
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 {
|
||||
case r: AnyRef => this eq r
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
// access is synchronized
|
||||
private var stopListeners = Set.empty[StopListener]
|
||||
|
||||
private def removeListener(listener: StopListener): Unit = synchronized {
|
||||
stopListeners -= listener
|
||||
}
|
||||
|
||||
def cleanup(): Unit = {
|
||||
// avoid holding any lock while invoking callbacks, and
|
||||
// handle callbacks being added by other callbacks, just
|
||||
// to be all fancy.
|
||||
while (synchronized { stopListeners.nonEmpty }) {
|
||||
val listeners = synchronized {
|
||||
val list = stopListeners.toList
|
||||
stopListeners = Set.empty
|
||||
list
|
||||
}
|
||||
listeners.foreach { l =>
|
||||
l.executionContext.execute(new Runnable { override def run = l.callback() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def onStop(listener: () => Unit)(implicit ex: ExecutionContext): Closeable =
|
||||
synchronized {
|
||||
val result = new StopListener(listener, ex)
|
||||
stopListeners += result
|
||||
result
|
||||
}
|
||||
override def awaitTermination(): Unit = finishedLatch.await()
|
||||
override def humanReadableName: String = taskName
|
||||
override def isRunning(): Boolean =
|
||||
status match {
|
||||
case Waiting => true // we start as running from BackgroundJob perspective
|
||||
case Running(thread) => thread.isAlive()
|
||||
case Stopped(threadOption) => threadOption.map(_.isAlive()).getOrElse(false)
|
||||
}
|
||||
override def shutdown(): Unit =
|
||||
synchronized {
|
||||
status match {
|
||||
case Waiting =>
|
||||
status = Stopped(None) // makes run() not run the body
|
||||
case Running(thread) =>
|
||||
status = Stopped(Some(thread))
|
||||
thread.interrupt()
|
||||
case Stopped(threadOption) =>
|
||||
// try to interrupt again! woot!
|
||||
threadOption.foreach(_.interrupt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
executor.execute(runnable)
|
||||
runnable
|
||||
}
|
||||
manager.doRunInBackground(spawningTask, state, start _)
|
||||
}
|
||||
|
||||
override def close(): Unit = {
|
||||
executor.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] class DefaultBackgroundJobService extends AbstractBackgroundJobService {
|
||||
override def makeContext(id: Long, spawningTask: ScopedKey[_], state: State): ManagedLogger = {
|
||||
val extracted = Project.extract(state)
|
||||
LogManager.constructBackgroundLog(extracted.structure.data, state)(spawningTask)
|
||||
}
|
||||
}
|
||||
private[sbt] object DefaultBackgroundJobService {
|
||||
lazy val backgroundJobService: DefaultBackgroundJobService = new DefaultBackgroundJobService
|
||||
lazy val backgroundJobServiceSetting: Setting[_] =
|
||||
((Keys.bgJobService in GlobalScope) :== backgroundJobService)
|
||||
}
|
||||
|
|
@ -89,6 +89,7 @@ private[sbt] object Load {
|
|||
def injectGlobal(state: State): Seq[Setting[_]] =
|
||||
(appConfiguration in GlobalScope :== state.configuration) +:
|
||||
LogManager.settingsLogger(state) +:
|
||||
DefaultBackgroundJobService.backgroundJobServiceSetting +:
|
||||
EvaluateTask.injectSettings
|
||||
def defaultWithGlobal(state: State, base: File, rawConfig: LoadBuildConfiguration, globalBase: File, log: Logger): LoadBuildConfiguration =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,10 +12,12 @@ import scala.Console.{ BLUE, RESET }
|
|||
import sbt.internal.util.{ AttributeKey, ConsoleOut, Settings, SuppressedTraceContext, MainAppender }
|
||||
import MainAppender._
|
||||
import sbt.util.{ AbstractLogger, 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): Logger
|
||||
def backgroundLog(data: Settings[Scope], state: State, task: ScopedKey[_]): ManagedLogger
|
||||
}
|
||||
|
||||
object LogManager {
|
||||
|
|
@ -23,12 +25,18 @@ object LogManager {
|
|||
private val generateId: AtomicInteger = new AtomicInteger
|
||||
|
||||
// This is called by mkStreams
|
||||
def construct(data: Settings[Scope], state: State) = (task: ScopedKey[_], to: PrintWriter) =>
|
||||
def construct(data: Settings[Scope], state: State): (ScopedKey[_], PrintWriter) => Logger = (task: ScopedKey[_], to: PrintWriter) =>
|
||||
{
|
||||
val manager = logManager in task.scope get data getOrElse defaultManager(state.globalLogging.console)
|
||||
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) }
|
||||
manager.backgroundLog(data, state, task)
|
||||
}
|
||||
|
||||
def defaultManager(console: ConsoleOut): LogManager = withLoggers((sk, s) => defaultScreen(console))
|
||||
|
||||
// This is called by Defaults.
|
||||
|
|
@ -52,6 +60,9 @@ object LogManager {
|
|||
) extends LogManager {
|
||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], to: PrintWriter): Logger =
|
||||
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)
|
||||
}
|
||||
|
||||
// This is the main function that is used to generate the logger for tasks.
|
||||
|
|
@ -71,20 +82,22 @@ object LogManager {
|
|||
val screenTrace = getOr(traceLevel.key, defaultTraceLevel(state))
|
||||
val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue)
|
||||
val extraBacked = state.globalLogging.backed :: relay :: Nil
|
||||
val consoleOpt =
|
||||
execOpt match {
|
||||
case Some(x: Exec) =>
|
||||
x.source match {
|
||||
// TODO: Fix this stringliness
|
||||
case Some(x: CommandSource) if x.channelName == "console0" => Option(console)
|
||||
case Some(x: CommandSource) => None
|
||||
case _ => Option(console)
|
||||
}
|
||||
case _ => Option(console)
|
||||
}
|
||||
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 {
|
||||
case Some(x: Exec) =>
|
||||
x.source match {
|
||||
// TODO: Fix this stringliness
|
||||
case Some(x: CommandSource) if x.channelName == "console0" => Option(console)
|
||||
case Some(x: CommandSource) => None
|
||||
case _ => Option(console)
|
||||
}
|
||||
case _ => Option(console)
|
||||
}
|
||||
def defaultTraceLevel(state: State): Int =
|
||||
if (state.interactive) -1 else Int.MaxValue
|
||||
def suppressedMessage(key: ScopedKey[_], state: State): SuppressedTraceContext => Option[String] =
|
||||
|
|
@ -99,6 +112,20 @@ object LogManager {
|
|||
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
|
||||
}
|
||||
|
||||
// TODO: Fix this
|
||||
// if global logging levels are not explicitly set, set them from project settings
|
||||
// private[sbt] def setGlobalLogLevels(s: State, data: Settings[Scope]): State =
|
||||
|
|
|
|||
|
|
@ -12,19 +12,20 @@ import sbt.internal.inc.ScalaInstance
|
|||
import sbt.io.Path
|
||||
|
||||
import sbt.util.Logger
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
import scala.util.control.NonFatal
|
||||
import scala.sys.process.Process
|
||||
|
||||
trait ScalaRun {
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Option[String]
|
||||
sealed trait ScalaRun {
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Try[Unit]
|
||||
}
|
||||
class ForkRun(config: ForkOptions) extends ScalaRun {
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Option[String] =
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Try[Unit] =
|
||||
{
|
||||
log.info("Running " + mainClass + " " + options.mkString(" "))
|
||||
|
||||
val scalaOptions = classpathOption(classpath) ::: mainClass :: options.toList
|
||||
val configLogged = if (config.outputStrategy.isDefined) config else config.copy(outputStrategy = Some(LoggedOutput(log)))
|
||||
// fork with Java because Scala introduces an extra class loader (#702)
|
||||
val process = Fork.java.fork(configLogged, scalaOptions)
|
||||
def processExitCode(exitCode: Int, label: String): Try[Unit] =
|
||||
if (exitCode == 0) Success(())
|
||||
else Failure(new RuntimeException(s"""Nonzero exit code returned from $label: $exitCode""".stripMargin))
|
||||
val process = fork(mainClass, classpath, options, log)
|
||||
def cancel() = {
|
||||
log.warn("Run canceled.")
|
||||
process.destroy()
|
||||
|
|
@ -33,27 +34,40 @@ class ForkRun(config: ForkOptions) extends ScalaRun {
|
|||
val exitCode = try process.exitValue() catch { case e: InterruptedException => cancel() }
|
||||
processExitCode(exitCode, "runner")
|
||||
}
|
||||
private def classpathOption(classpath: Seq[File]) = "-classpath" :: Path.makeString(classpath) :: Nil
|
||||
private def processExitCode(exitCode: Int, label: String) =
|
||||
|
||||
def fork(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Process =
|
||||
{
|
||||
if (exitCode == 0)
|
||||
None
|
||||
else
|
||||
Some("Nonzero exit code returned from " + label + ": " + exitCode)
|
||||
log.info("Running (fork) " + mainClass + " " + options.mkString(" "))
|
||||
|
||||
val scalaOptions = classpathOption(classpath) ::: mainClass :: options.toList
|
||||
val configLogged =
|
||||
if (config.outputStrategy.isDefined) config
|
||||
else config.copy(outputStrategy = Some(LoggedOutput(log)))
|
||||
// fork with Java because Scala introduces an extra class loader (#702)
|
||||
Fork.java.fork(configLogged, scalaOptions)
|
||||
}
|
||||
private def classpathOption(classpath: Seq[File]) = "-classpath" :: Path.makeString(classpath) :: Nil
|
||||
}
|
||||
class Run(instance: ScalaInstance, trapExit: Boolean, nativeTmp: File) extends ScalaRun {
|
||||
/** Runs the class 'mainClass' using the given classpath and options using the scala runner.*/
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger) =
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Try[Unit] =
|
||||
{
|
||||
log.info("Running " + mainClass + " " + options.mkString(" "))
|
||||
|
||||
def execute() =
|
||||
try { run0(mainClass, classpath, options, log) }
|
||||
catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
|
||||
def directExecute() = try { execute(); None } catch { case e: Exception => log.trace(e); Some(e.toString) }
|
||||
def directExecute(): Try[Unit] =
|
||||
Try(execute()) recover {
|
||||
case NonFatal(e) =>
|
||||
// bgStop should not print out stack trace
|
||||
// log.trace(e)
|
||||
throw e
|
||||
}
|
||||
// try { execute(); None } catch { case e: Exception => log.trace(e); Some(e.toString) }
|
||||
|
||||
if (trapExit) Run.executeTrapExit(execute(), log) else directExecute()
|
||||
if (trapExit) Run.executeTrapExit(execute(), log)
|
||||
else directExecute()
|
||||
}
|
||||
private def run0(mainClassName: String, classpath: Seq[File], options: Seq[String], log: Logger): Unit = {
|
||||
log.debug(" Classpath:\n\t" + classpath.mkString("\n\t"))
|
||||
|
|
@ -88,13 +102,12 @@ object Run {
|
|||
runner.run(mainClass, classpath, options, log)
|
||||
|
||||
/** Executes the given function, trapping calls to System.exit. */
|
||||
def executeTrapExit(f: => Unit, log: Logger): Option[String] =
|
||||
def executeTrapExit(f: => Unit, log: Logger): Try[Unit] =
|
||||
{
|
||||
val exitCode = TrapExit(f, log)
|
||||
if (exitCode == 0) {
|
||||
log.debug("Exited with code 0")
|
||||
None
|
||||
} else
|
||||
Some("Nonzero exit code: " + exitCode)
|
||||
Success(())
|
||||
} else Failure(new RuntimeException("Nonzero exit code: " + exitCode))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
package sbt
|
||||
|
||||
import sbt.internal.DslEntry
|
||||
import sbt.util.Eval
|
||||
|
||||
object syntax extends syntax
|
||||
|
||||
abstract class syntax extends IOSyntax0 with sbt.std.TaskExtra with sbt.internal.util.Types with sbt.ProcessExtra
|
||||
with sbt.internal.librarymanagement.impl.DependencyBuilders with sbt.ProjectExtra
|
||||
with sbt.internal.librarymanagement.DependencyFilterExtra with sbt.BuildExtra with sbt.TaskMacroExtra
|
||||
with sbt.ScopeFilter.Make {
|
||||
with sbt.ScopeFilter.Make
|
||||
with sbt.BuildSyntax {
|
||||
|
||||
// IO
|
||||
def uri(s: String): URI = new URI(s)
|
||||
|
|
@ -43,18 +41,6 @@ abstract class syntax extends IOSyntax0 with sbt.std.TaskExtra with sbt.internal
|
|||
// final val System = C.System
|
||||
final val Optional = C.Optional
|
||||
def config(s: String): Configuration = C.config(s)
|
||||
|
||||
import language.experimental.macros
|
||||
def settingKey[T](description: String): SettingKey[T] = macro std.KeyMacro.settingKeyImpl[T]
|
||||
def taskKey[T](description: String): TaskKey[T] = macro std.KeyMacro.taskKeyImpl[T]
|
||||
def inputKey[T](description: String): InputKey[T] = macro std.KeyMacro.inputKeyImpl[T]
|
||||
|
||||
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)
|
||||
// avoid conflict with `sbt.Keys.aggregate`
|
||||
def aggregateProjects(refs: Eval[ProjectReference]*): DslEntry = DslEntry.DslAggregate(refs)
|
||||
}
|
||||
|
||||
// Todo share this this io.syntax
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ def runTestTask(pre: Def.Initialize[Task[Unit]]) =
|
|||
val cp = (fullClasspath in Compile).value
|
||||
val main = (mainClass in Compile).value getOrElse sys.error("No main class found")
|
||||
val args = baseDirectory.value.getAbsolutePath :: Nil
|
||||
r.run(main, cp.files, args, streams.value.log) foreach sys.error
|
||||
r.run(main, cp.files, args, streams.value.log).get
|
||||
}
|
||||
|
||||
lazy val b = project.settings(
|
||||
|
|
|
|||
Loading…
Reference in New Issue