mirror of https://github.com/sbt/sbt.git
Merge pull request #3517 from dwijnand/run-cp-dirs
Fix run's support of directories in the classpath
This commit is contained in:
commit
da40288d6f
|
|
@ -500,15 +500,8 @@ object Defaults extends BuildCommon {
|
||||||
run := foregroundRunTask.evaluated,
|
run := foregroundRunTask.evaluated,
|
||||||
copyResources := copyResourcesTask.value,
|
copyResources := copyResourcesTask.value,
|
||||||
// note that we use the same runner and mainClass as plain run
|
// note that we use the same runner and mainClass as plain run
|
||||||
bgRunMain := bgRunMainTask(exportedProductJars,
|
mainBgRunMainTaskForConfig(This),
|
||||||
fullClasspathAsJars,
|
mainBgRunTaskForConfig(This)
|
||||||
bgCopyClasspath in bgRunMain,
|
|
||||||
runner in run).evaluated,
|
|
||||||
bgRun := bgRunTask(exportedProductJars,
|
|
||||||
fullClasspathAsJars,
|
|
||||||
mainClass in run,
|
|
||||||
bgCopyClasspath in bgRun,
|
|
||||||
runner in run).evaluated
|
|
||||||
) ++ inTask(run)(runnerSettings)
|
) ++ inTask(run)(runnerSettings)
|
||||||
|
|
||||||
private[this] lazy val configGlobal = globalDefaults(
|
private[this] lazy val configGlobal = globalDefaults(
|
||||||
|
|
@ -1119,10 +1112,12 @@ object Defaults extends BuildCommon {
|
||||||
toClean
|
toClean
|
||||||
}
|
}
|
||||||
|
|
||||||
def bgRunMainTask(products: Initialize[Task[Classpath]],
|
def bgRunMainTask(
|
||||||
classpath: Initialize[Task[Classpath]],
|
products: Initialize[Task[Classpath]],
|
||||||
copyClasspath: Initialize[Boolean],
|
classpath: Initialize[Task[Classpath]],
|
||||||
scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[JobHandle]] = {
|
copyClasspath: Initialize[Boolean],
|
||||||
|
scalaRun: Initialize[Task[ScalaRun]]
|
||||||
|
): Initialize[InputTask[JobHandle]] = {
|
||||||
val parser = Defaults.loadForParser(discoveredMainClasses)((s, names) =>
|
val parser = Defaults.loadForParser(discoveredMainClasses)((s, names) =>
|
||||||
Defaults.runMainParser(s, names getOrElse Nil))
|
Defaults.runMainParser(s, names getOrElse Nil))
|
||||||
Def.inputTask {
|
Def.inputTask {
|
||||||
|
|
@ -1137,11 +1132,14 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def bgRunTask(products: Initialize[Task[Classpath]],
|
|
||||||
classpath: Initialize[Task[Classpath]],
|
def bgRunTask(
|
||||||
mainClassTask: Initialize[Task[Option[String]]],
|
products: Initialize[Task[Classpath]],
|
||||||
copyClasspath: Initialize[Boolean],
|
classpath: Initialize[Task[Classpath]],
|
||||||
scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[JobHandle]] = {
|
mainClassTask: Initialize[Task[Option[String]]],
|
||||||
|
copyClasspath: Initialize[Boolean],
|
||||||
|
scalaRun: Initialize[Task[ScalaRun]]
|
||||||
|
): Initialize[InputTask[JobHandle]] = {
|
||||||
import Def.parserToInput
|
import Def.parserToInput
|
||||||
val parser = Def.spaceDelimited()
|
val parser = Def.spaceDelimited()
|
||||||
Def.inputTask {
|
Def.inputTask {
|
||||||
|
|
@ -1156,6 +1154,7 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// runMain calls bgRunMain in the background and waits for the result.
|
// runMain calls bgRunMain in the background and waits for the result.
|
||||||
def foregroundRunMainTask: Initialize[InputTask[Unit]] =
|
def foregroundRunMainTask: Initialize[InputTask[Unit]] =
|
||||||
Def.inputTask {
|
Def.inputTask {
|
||||||
|
|
@ -1163,6 +1162,7 @@ object Defaults extends BuildCommon {
|
||||||
val service = bgJobService.value
|
val service = bgJobService.value
|
||||||
service.waitForTry(handle).get
|
service.waitForTry(handle).get
|
||||||
}
|
}
|
||||||
|
|
||||||
// run calls bgRun in the background and waits for the result.
|
// run calls bgRun in the background and waits for the result.
|
||||||
def foregroundRunTask: Initialize[InputTask[Unit]] =
|
def foregroundRunTask: Initialize[InputTask[Unit]] =
|
||||||
Def.inputTask {
|
Def.inputTask {
|
||||||
|
|
@ -1170,8 +1170,11 @@ object Defaults extends BuildCommon {
|
||||||
val service = bgJobService.value
|
val service = bgJobService.value
|
||||||
service.waitForTry(handle).get
|
service.waitForTry(handle).get
|
||||||
}
|
}
|
||||||
def runMainTask(classpath: Initialize[Task[Classpath]],
|
|
||||||
scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[Unit]] = {
|
def runMainTask(
|
||||||
|
classpath: Initialize[Task[Classpath]],
|
||||||
|
scalaRun: Initialize[Task[ScalaRun]]
|
||||||
|
): Initialize[InputTask[Unit]] = {
|
||||||
val parser =
|
val parser =
|
||||||
loadForParser(discoveredMainClasses)((s, names) => runMainParser(s, names getOrElse Nil))
|
loadForParser(discoveredMainClasses)((s, names) => runMainParser(s, names getOrElse Nil))
|
||||||
Def.inputTask {
|
Def.inputTask {
|
||||||
|
|
@ -1179,9 +1182,12 @@ object Defaults extends BuildCommon {
|
||||||
scalaRun.value.run(mainClass, data(classpath.value), args, streams.value.log).get
|
scalaRun.value.run(mainClass, data(classpath.value), args, streams.value.log).get
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def runTask(classpath: Initialize[Task[Classpath]],
|
|
||||||
mainClassTask: Initialize[Task[Option[String]]],
|
def runTask(
|
||||||
scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[Unit]] = {
|
classpath: Initialize[Task[Classpath]],
|
||||||
|
mainClassTask: Initialize[Task[Option[String]]],
|
||||||
|
scalaRun: Initialize[Task[ScalaRun]]
|
||||||
|
): Initialize[InputTask[Unit]] = {
|
||||||
import Def.parserToInput
|
import Def.parserToInput
|
||||||
val parser = Def.spaceDelimited()
|
val parser = Def.spaceDelimited()
|
||||||
Def.inputTask {
|
Def.inputTask {
|
||||||
|
|
@ -1189,7 +1195,9 @@ object Defaults extends BuildCommon {
|
||||||
scalaRun.value.run(mainClass, data(classpath.value), parser.parsed, streams.value.log).get
|
scalaRun.value.run(mainClass, data(classpath.value), parser.parsed, streams.value.log).get
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def runnerTask: Setting[Task[ScalaRun]] = runner := runnerInit.value
|
def runnerTask: Setting[Task[ScalaRun]] = runner := runnerInit.value
|
||||||
|
|
||||||
def runnerInit: Initialize[Task[ScalaRun]] = Def.task {
|
def runnerInit: Initialize[Task[ScalaRun]] = Def.task {
|
||||||
val tmp = taskTemporaryDirectory.value
|
val tmp = taskTemporaryDirectory.value
|
||||||
val resolvedScope = resolvedScoped.value.scope
|
val resolvedScope = resolvedScoped.value.scope
|
||||||
|
|
@ -1217,7 +1225,8 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
|
|
||||||
private def foreachJobTask(
|
private def foreachJobTask(
|
||||||
f: (BackgroundJobService, JobHandle) => Unit): Initialize[InputTask[Unit]] = {
|
f: (BackgroundJobService, JobHandle) => Unit
|
||||||
|
): Initialize[InputTask[Unit]] = {
|
||||||
val parser: Initialize[State => Parser[Seq[JobHandle]]] = Def.setting { (s: State) =>
|
val parser: Initialize[State => Parser[Seq[JobHandle]]] = Def.setting { (s: State) =>
|
||||||
val extracted = Project.extract(s)
|
val extracted = Project.extract(s)
|
||||||
val service = extracted.get(bgJobService)
|
val service = extracted.get(bgJobService)
|
||||||
|
|
@ -1232,6 +1241,7 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def psTask: Initialize[Task[Seq[JobHandle]]] =
|
def psTask: Initialize[Task[Seq[JobHandle]]] =
|
||||||
Def.task {
|
Def.task {
|
||||||
val xs = bgList.value
|
val xs = bgList.value
|
||||||
|
|
@ -1241,9 +1251,11 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
xs
|
xs
|
||||||
}
|
}
|
||||||
|
|
||||||
def bgStopTask: Initialize[InputTask[Unit]] = foreachJobTask { (manager, handle) =>
|
def bgStopTask: Initialize[InputTask[Unit]] = foreachJobTask { (manager, handle) =>
|
||||||
manager.stop(handle)
|
manager.stop(handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
def bgWaitForTask: Initialize[InputTask[Unit]] = foreachJobTask { (manager, handle) =>
|
def bgWaitForTask: Initialize[InputTask[Unit]] = foreachJobTask { (manager, handle) =>
|
||||||
manager.waitFor(handle)
|
manager.waitFor(handle)
|
||||||
}
|
}
|
||||||
|
|
@ -1294,17 +1306,25 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
def mainBgRunTask =
|
def mainBgRunTask = mainBgRunTaskForConfig(Select(Runtime))
|
||||||
bgRun := bgRunTask(exportedProductJars,
|
def mainBgRunMainTask = mainBgRunMainTaskForConfig(Select(Runtime))
|
||||||
fullClasspathAsJars in Runtime,
|
|
||||||
mainClass in run,
|
private[this] def mainBgRunTaskForConfig(c: ScopeAxis[ConfigKey]) =
|
||||||
bgCopyClasspath in bgRun,
|
bgRun := bgRunTask(
|
||||||
runner in run).evaluated
|
exportedProductJars,
|
||||||
def mainBgRunMainTask =
|
fullClasspathAsJars in (This, c, This),
|
||||||
bgRunMain := bgRunMainTask(exportedProductJars,
|
mainClass in run,
|
||||||
fullClasspathAsJars in Runtime,
|
bgCopyClasspath in bgRun,
|
||||||
bgCopyClasspath in bgRunMain,
|
runner in run
|
||||||
runner in run).evaluated
|
).evaluated
|
||||||
|
|
||||||
|
private[this] def mainBgRunMainTaskForConfig(c: ScopeAxis[ConfigKey]) =
|
||||||
|
bgRunMain := bgRunMainTask(
|
||||||
|
exportedProductJars,
|
||||||
|
fullClasspathAsJars in (This, c, This),
|
||||||
|
bgCopyClasspath in bgRunMain,
|
||||||
|
runner in run
|
||||||
|
).evaluated
|
||||||
|
|
||||||
def discoverMainClasses(analysis: CompileAnalysis): Seq[String] = analysis match {
|
def discoverMainClasses(analysis: CompileAnalysis): Seq[String] = analysis match {
|
||||||
case analysis: Analysis =>
|
case analysis: Analysis =>
|
||||||
|
|
@ -1316,6 +1336,7 @@ object Defaults extends BuildCommon {
|
||||||
ConsoleProject(state.value, (initialCommands in consoleProject).value)(streams.value.log)
|
ConsoleProject(state.value, (initialCommands in consoleProject).value)(streams.value.log)
|
||||||
println()
|
println()
|
||||||
}
|
}
|
||||||
|
|
||||||
def consoleTask: Initialize[Task[Unit]] = consoleTask(fullClasspath, console)
|
def consoleTask: Initialize[Task[Unit]] = consoleTask(fullClasspath, console)
|
||||||
def consoleQuickTask = consoleTask(externalDependencyClasspath, consoleQuick)
|
def consoleQuickTask = consoleTask(externalDependencyClasspath, consoleQuick)
|
||||||
def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[_]): Initialize[Task[Unit]] =
|
def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[_]): Initialize[Task[Unit]] =
|
||||||
|
|
@ -1340,6 +1361,7 @@ object Defaults extends BuildCommon {
|
||||||
|
|
||||||
private[this] def exported(w: PrintWriter, command: String): Seq[String] => Unit =
|
private[this] def exported(w: PrintWriter, command: String): Seq[String] => Unit =
|
||||||
args => w.println((command +: args).mkString(" "))
|
args => w.println((command +: args).mkString(" "))
|
||||||
|
|
||||||
private[this] def exported(s: TaskStreams, command: String): Seq[String] => Unit = args => {
|
private[this] def exported(s: TaskStreams, command: String): Seq[String] => Unit = args => {
|
||||||
val w = s.text(ExportStream)
|
val w = s.text(ExportStream)
|
||||||
try exported(w, command)
|
try exported(w, command)
|
||||||
|
|
@ -1530,8 +1552,10 @@ object Defaults extends BuildCommon {
|
||||||
lazy val runnerSettings: Seq[Setting[_]] = Seq(runnerTask, forkOptions := forkOptionsTask.value)
|
lazy val runnerSettings: Seq[Setting[_]] = Seq(runnerTask, forkOptions := forkOptionsTask.value)
|
||||||
|
|
||||||
lazy val baseTasks: Seq[Setting[_]] = projectTasks ++ packageBase
|
lazy val baseTasks: Seq[Setting[_]] = projectTasks ++ packageBase
|
||||||
lazy val configSettings
|
|
||||||
: Seq[Setting[_]] = Classpaths.configSettings ++ configTasks ++ configPaths ++ packageConfig ++ Classpaths.compilerPluginConfig ++ deprecationSettings
|
lazy val configSettings: Seq[Setting[_]] =
|
||||||
|
Classpaths.configSettings ++ configTasks ++ configPaths ++ packageConfig ++
|
||||||
|
Classpaths.compilerPluginConfig ++ deprecationSettings
|
||||||
|
|
||||||
lazy val compileSettings: Seq[Setting[_]] =
|
lazy val compileSettings: Seq[Setting[_]] =
|
||||||
configSettings ++
|
configSettings ++
|
||||||
|
|
@ -1541,8 +1565,8 @@ object Defaults extends BuildCommon {
|
||||||
lazy val testSettings: Seq[Setting[_]] = configSettings ++ testTasks
|
lazy val testSettings: Seq[Setting[_]] = configSettings ++ testTasks
|
||||||
|
|
||||||
lazy val itSettings: Seq[Setting[_]] = inConfig(IntegrationTest)(testSettings)
|
lazy val itSettings: Seq[Setting[_]] = inConfig(IntegrationTest)(testSettings)
|
||||||
lazy val defaultConfigs: Seq[Setting[_]] = inConfig(Compile)(compileSettings) ++ inConfig(Test)(
|
lazy val defaultConfigs: Seq[Setting[_]] = inConfig(Compile)(compileSettings) ++
|
||||||
testSettings) ++ inConfig(Runtime)(Classpaths.configSettings)
|
inConfig(Test)(testSettings) ++ inConfig(Runtime)(Classpaths.configSettings)
|
||||||
|
|
||||||
// These are project level settings that MUST be on every project.
|
// These are project level settings that MUST be on every project.
|
||||||
lazy val coreDefaultSettings: Seq[Setting[_]] =
|
lazy val coreDefaultSettings: Seq[Setting[_]] =
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,17 @@ package sbt
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
import java.io.Closeable
|
import java.io.{ Closeable, File, FileInputStream, IOException }
|
||||||
import Def.{ ScopedKey, Setting, Classpath }
|
import java.nio.file.attribute.BasicFileAttributes
|
||||||
|
import java.nio.file.{ FileVisitResult, Files, Path, SimpleFileVisitor }
|
||||||
|
import java.security.{ DigestInputStream, MessageDigest }
|
||||||
|
import Def.{ Classpath, ScopedKey, Setting }
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
import Scope.GlobalScope
|
import Scope.GlobalScope
|
||||||
import java.io.File
|
import sbt.io.{ Hash, IO }
|
||||||
import sbt.io.{ IO, Hash }
|
|
||||||
import sbt.io.syntax._
|
import sbt.io.syntax._
|
||||||
import sbt.util.{ Logger, LogExchange }
|
import sbt.util.{ LogExchange, Logger }
|
||||||
import sbt.internal.util.{ Attributed, ManagedLogger }
|
import sbt.internal.util.{ Attributed, ManagedLogger }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -167,20 +169,25 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
||||||
override def toString(): String = s"BackgroundJobService(jobs=${jobs.map(_.id).mkString})"
|
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,
|
* Copies products to the working directory, and the rest to the serviceTempDir of this service,
|
||||||
* both wrapped in SHA-1 hash of the file contents.
|
* 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.
|
* This is intended to minimize 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.
|
* 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.
|
* Meanwhile, the rest of the dependencies are cached for the duration of this service.
|
||||||
*/
|
*/
|
||||||
override def copyClasspath(products: Classpath,
|
override def copyClasspath(
|
||||||
full: Classpath,
|
products: Classpath,
|
||||||
workingDirectory: File): Classpath = {
|
full: Classpath,
|
||||||
|
workingDirectory: File
|
||||||
|
): Classpath = {
|
||||||
def syncTo(dir: File)(source0: Attributed[File]): Attributed[File] = {
|
def syncTo(dir: File)(source0: Attributed[File]): Attributed[File] = {
|
||||||
val source = source0.data
|
val source = source0.data
|
||||||
val hash8 = Hash.toHex(Hash(source)).take(8)
|
val hash8 = Hash.toHex(hash(source)).take(8)
|
||||||
val dest = dir / hash8 / source.getName
|
val dest = dir / hash8 / source.getName
|
||||||
if (!dest.exists) { IO.copyFile(source, dest) }
|
if (!dest.exists) {
|
||||||
|
if (source.isDirectory) IO.copyDirectory(source, dest)
|
||||||
|
else IO.copyFile(source, dest)
|
||||||
|
}
|
||||||
Attributed.blank(dest)
|
Attributed.blank(dest)
|
||||||
}
|
}
|
||||||
val xs = (products.toVector map { syncTo(workingDirectory / "target") }) ++
|
val xs = (products.toVector map { syncTo(workingDirectory / "target") }) ++
|
||||||
|
|
@ -188,6 +195,27 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
||||||
Thread.sleep(100)
|
Thread.sleep(100)
|
||||||
xs
|
xs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** An alternative to sbt.io.Hash that handles java.io.File being a directory. */
|
||||||
|
private def hash(f: File) = {
|
||||||
|
val digest = MessageDigest.getInstance("SHA")
|
||||||
|
val buffer = new Array[Byte](8192)
|
||||||
|
Files.walkFileTree(
|
||||||
|
f.toPath,
|
||||||
|
new SimpleFileVisitor[Path]() {
|
||||||
|
override def visitFile(file: Path, attrs: BasicFileAttributes) = {
|
||||||
|
val dis = new DigestInputStream(new FileInputStream(file.toFile), digest)
|
||||||
|
try {
|
||||||
|
while (dis.read(buffer) >= 0) ()
|
||||||
|
FileVisitResult.CONTINUE
|
||||||
|
} catch {
|
||||||
|
case _: IOException => FileVisitResult.TERMINATE
|
||||||
|
} finally dis.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
digest.digest
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] object BackgroundThreadPool {
|
private[sbt] object BackgroundThreadPool {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package t
|
||||||
|
|
||||||
|
import java.nio._, charset._, file._
|
||||||
|
|
||||||
|
object Main {
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
println(new String(Files.readAllBytes(Paths.get(getClass().getResource("/a.txt").toURI()))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
externalDependencyClasspath in Runtime += file("conf")
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
foo
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
> run
|
||||||
Loading…
Reference in New Issue