mirror of https://github.com/sbt/sbt.git
Stop Stamped from inheriting File and TypedPath
In the FileTreeDataView use case, we were previously working with FileTreeDataView[Stamped], which actually contained a lot of redundant information because FileTreeDataView.Entry[_] has a toTypedPath method that could be used to read the path related fields in Stamped. Instead, we can just return the Stamp itself in FileTreeDataView.list* methods and convert to Stamped.File where needed (i.e. in ExternalHooks). Also move BasicKeys.globalFileTreeView to Keys since it isn't actually used in the main-command project.
This commit is contained in:
parent
0bdc30b60b
commit
ba0494df14
|
|
@ -15,6 +15,7 @@ import sbt.internal.server.ServerHandler
|
||||||
import sbt.io.FileTreeDataView
|
import sbt.io.FileTreeDataView
|
||||||
import sbt.librarymanagement.ModuleID
|
import sbt.librarymanagement.ModuleID
|
||||||
import sbt.util.Level
|
import sbt.util.Level
|
||||||
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
||||||
object BasicKeys {
|
object BasicKeys {
|
||||||
val historyPath = AttributeKey[Option[File]](
|
val historyPath = AttributeKey[Option[File]](
|
||||||
|
|
@ -102,11 +103,6 @@ object BasicKeys {
|
||||||
"List of template resolver infos.",
|
"List of template resolver infos.",
|
||||||
1000
|
1000
|
||||||
)
|
)
|
||||||
private[sbt] val globalFileTreeView = AttributeKey[FileTreeDataView[Stamped]](
|
|
||||||
"globalFileTreeView",
|
|
||||||
"provides a view into the file system that may or may not cache the tree in memory",
|
|
||||||
1000
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case class TemplateResolverInfo(module: ModuleID, implementationClass: String)
|
case class TemplateResolverInfo(module: ModuleID, implementationClass: String)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import sbt.internal.io.{ HybridPollingFileTreeRepository, WatchServiceBackedObse
|
||||||
import sbt.io._
|
import sbt.io._
|
||||||
import FileTreeDataView.{ Observable, Observer }
|
import FileTreeDataView.{ Observable, Observer }
|
||||||
import sbt.util.Logger
|
import sbt.util.Logger
|
||||||
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
|
@ -18,15 +19,15 @@ import scala.concurrent.duration._
|
||||||
* Configuration for viewing and monitoring the file system.
|
* Configuration for viewing and monitoring the file system.
|
||||||
*/
|
*/
|
||||||
final class FileTreeViewConfig private (
|
final class FileTreeViewConfig private (
|
||||||
val newDataView: () => FileTreeDataView[Stamped],
|
val newDataView: () => FileTreeDataView[Stamp],
|
||||||
val newMonitor: (
|
val newMonitor: (
|
||||||
FileTreeDataView[Stamped],
|
FileTreeDataView[Stamp],
|
||||||
Seq[WatchSource],
|
Seq[WatchSource],
|
||||||
Logger
|
Logger
|
||||||
) => FileEventMonitor[Stamped]
|
) => FileEventMonitor[Stamp]
|
||||||
)
|
)
|
||||||
object FileTreeViewConfig {
|
object FileTreeViewConfig {
|
||||||
private implicit class RepositoryOps(val repository: FileTreeRepository[Stamped]) {
|
private implicit class RepositoryOps(val repository: FileTreeRepository[Stamp]) {
|
||||||
def register(sources: Seq[WatchSource]): Unit = sources foreach { s =>
|
def register(sources: Seq[WatchSource]): Unit = sources foreach { s =>
|
||||||
repository.register(s.base.toPath, if (s.recursive) Integer.MAX_VALUE else 0)
|
repository.register(s.base.toPath, if (s.recursive) Integer.MAX_VALUE else 0)
|
||||||
}
|
}
|
||||||
|
|
@ -34,10 +35,10 @@ object FileTreeViewConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new FileTreeViewConfig. This factory takes a generic parameter, T, that is bounded
|
* Create a new FileTreeViewConfig. This factory takes a generic parameter, T, that is bounded
|
||||||
* by {{{sbt.io.FileTreeDataView[Stamped]}}}. The reason for this is to ensure that a
|
* by {{{sbt.io.FileTreeDataView[Stamp]}}}. The reason for this is to ensure that a
|
||||||
* sbt.io.FileTreeDataView that is instantiated by [[FileTreeViewConfig.newDataView]] can be
|
* sbt.io.FileTreeDataView that is instantiated by [[FileTreeViewConfig.newDataView]] can be
|
||||||
* passed into [[FileTreeViewConfig.newMonitor]] without constraining the type of view to be
|
* passed into [[FileTreeViewConfig.newMonitor]] without constraining the type of view to be
|
||||||
* {{{sbt.io.FileTreeDataView[Stamped]}}}.
|
* {{{sbt.io.FileTreeDataView[Stamp]}}}.
|
||||||
* @param newDataView create a new sbt.io.FileTreeDataView. This value may be cached in a global
|
* @param newDataView create a new sbt.io.FileTreeDataView. This value may be cached in a global
|
||||||
* attribute
|
* attribute
|
||||||
* @param newMonitor create a new sbt.io.FileEventMonitor using the sbt.io.FileTreeDataView
|
* @param newMonitor create a new sbt.io.FileEventMonitor using the sbt.io.FileTreeDataView
|
||||||
|
|
@ -45,13 +46,13 @@ object FileTreeViewConfig {
|
||||||
* @tparam T the subtype of sbt.io.FileTreeDataView that is returned by [[FileTreeViewConfig.newDataView]]
|
* @tparam T the subtype of sbt.io.FileTreeDataView that is returned by [[FileTreeViewConfig.newDataView]]
|
||||||
* @return a [[FileTreeViewConfig]] instance.
|
* @return a [[FileTreeViewConfig]] instance.
|
||||||
*/
|
*/
|
||||||
def apply[T <: FileTreeDataView[Stamped]](
|
def apply[T <: FileTreeDataView[Stamp]](
|
||||||
newDataView: () => T,
|
newDataView: () => T,
|
||||||
newMonitor: (T, Seq[WatchSource], Logger) => FileEventMonitor[Stamped]
|
newMonitor: (T, Seq[WatchSource], Logger) => FileEventMonitor[Stamp]
|
||||||
): FileTreeViewConfig =
|
): FileTreeViewConfig =
|
||||||
new FileTreeViewConfig(
|
new FileTreeViewConfig(
|
||||||
newDataView,
|
newDataView,
|
||||||
(view: FileTreeDataView[Stamped], sources: Seq[WatchSource], logger: Logger) =>
|
(view: FileTreeDataView[Stamp], sources: Seq[WatchSource], logger: Logger) =>
|
||||||
newMonitor(view.asInstanceOf[T], sources, logger)
|
newMonitor(view.asInstanceOf[T], sources, logger)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -71,7 +72,7 @@ object FileTreeViewConfig {
|
||||||
): FileTreeViewConfig =
|
): FileTreeViewConfig =
|
||||||
FileTreeViewConfig(
|
FileTreeViewConfig(
|
||||||
() => FileTreeView.DEFAULT.asDataView(Stamped.converter),
|
() => FileTreeView.DEFAULT.asDataView(Stamped.converter),
|
||||||
(_: FileTreeDataView[Stamped], sources, logger) => {
|
(_: FileTreeDataView[Stamp], sources, logger) => {
|
||||||
val ioLogger: sbt.io.WatchLogger = msg => logger.debug(msg.toString)
|
val ioLogger: sbt.io.WatchLogger = msg => logger.debug(msg.toString)
|
||||||
FileEventMonitor.antiEntropy(
|
FileEventMonitor.antiEntropy(
|
||||||
new WatchServiceBackedObservable(
|
new WatchServiceBackedObservable(
|
||||||
|
|
@ -98,10 +99,10 @@ object FileTreeViewConfig {
|
||||||
def default(antiEntropy: FiniteDuration): FileTreeViewConfig =
|
def default(antiEntropy: FiniteDuration): FileTreeViewConfig =
|
||||||
FileTreeViewConfig(
|
FileTreeViewConfig(
|
||||||
() => FileTreeRepository.default(Stamped.converter),
|
() => FileTreeRepository.default(Stamped.converter),
|
||||||
(repository: FileTreeRepository[Stamped], sources: Seq[WatchSource], logger: Logger) => {
|
(repository: FileTreeRepository[Stamp], sources: Seq[WatchSource], logger: Logger) => {
|
||||||
repository.register(sources)
|
repository.register(sources)
|
||||||
val copied = new Observable[Stamped] {
|
val copied = new Observable[Stamp] {
|
||||||
override def addObserver(observer: Observer[Stamped]): Int =
|
override def addObserver(observer: Observer[Stamp]): Int =
|
||||||
repository.addObserver(observer)
|
repository.addObserver(observer)
|
||||||
override def removeObserver(handle: Int): Unit = repository.removeObserver(handle)
|
override def removeObserver(handle: Int): Unit = repository.removeObserver(handle)
|
||||||
override def close(): Unit = {} // Don't close the underlying observable
|
override def close(): Unit = {} // Don't close the underlying observable
|
||||||
|
|
@ -156,7 +157,7 @@ object FileTreeViewConfig {
|
||||||
): FileTreeViewConfig = FileTreeViewConfig(
|
): FileTreeViewConfig = FileTreeViewConfig(
|
||||||
() => FileTreeRepository.hybrid(Stamped.converter, pollingSources: _*),
|
() => FileTreeRepository.hybrid(Stamped.converter, pollingSources: _*),
|
||||||
(
|
(
|
||||||
repository: HybridPollingFileTreeRepository[Stamped],
|
repository: HybridPollingFileTreeRepository[Stamp],
|
||||||
sources: Seq[WatchSource],
|
sources: Seq[WatchSource],
|
||||||
logger: Logger
|
logger: Logger
|
||||||
) => {
|
) => {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
import java.io.File
|
import java.io.{ File => JFile }
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
import sbt.internal.inc.Stamper
|
import sbt.internal.inc.Stamper
|
||||||
|
|
@ -15,11 +15,12 @@ import sbt.io.TypedPath
|
||||||
import xsbti.compile.analysis.Stamp
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A File that has a [[Stamp]] value associated with it. In general, the stamp method should be
|
* A File that has a compile analysis Stamp value associated with it. In general, the stamp method
|
||||||
* a cached value that can be read without doing any io. This can be used to improve performance
|
* should be a cached value that can be read without doing any io. This can be used to improve
|
||||||
* anywhere where we need to check if files have changed before doing potentially expensive work.
|
* performance anywhere where we need to check if files have changed before doing potentially
|
||||||
|
* expensive work.
|
||||||
*/
|
*/
|
||||||
trait Stamped extends File with TypedPath {
|
trait Stamped {
|
||||||
def stamp: Stamp
|
def stamp: Stamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,24 +28,25 @@ trait Stamped extends File with TypedPath {
|
||||||
* Provides converter functions from TypedPath to [[Stamped]].
|
* Provides converter functions from TypedPath to [[Stamped]].
|
||||||
*/
|
*/
|
||||||
object Stamped {
|
object Stamped {
|
||||||
|
type File = JFile with Stamped with TypedPath
|
||||||
|
def file(typedPath: TypedPath, stamp: Stamp): JFile with Stamped with TypedPath =
|
||||||
|
new StampedFileImpl(typedPath, stamp)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a TypedPath instance to a [[Stamped]] by calculating the file hash.
|
* Converts a TypedPath instance to a [[Stamped]] by calculating the file hash.
|
||||||
*/
|
*/
|
||||||
val sourceConverter: TypedPath => Stamped =
|
val sourceConverter: TypedPath => Stamp = tp => Stamper.forHash(tp.toPath.toFile)
|
||||||
new StampedImpl(_: TypedPath, forceLastModified = false)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a TypedPath instance to a [[Stamped]] using the last modified time.
|
* Converts a TypedPath instance to a [[Stamped]] using the last modified time.
|
||||||
*/
|
*/
|
||||||
val binaryConverter: TypedPath => Stamped =
|
val binaryConverter: TypedPath => Stamp = tp => Stamper.forLastModified(tp.toPath.toFile)
|
||||||
new StampedImpl(_: TypedPath, forceLastModified = true)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A combined convert that converts TypedPath instances representing *.jar and *.class files
|
* A combined convert that converts TypedPath instances representing *.jar and *.class files
|
||||||
* using the last modified time and all other files using the file hash.
|
* using the last modified time and all other files using the file hash.
|
||||||
*/
|
*/
|
||||||
val converter: TypedPath => Stamped = (tp: TypedPath) =>
|
val converter: TypedPath => Stamp = (tp: TypedPath) =>
|
||||||
tp.toPath.toString match {
|
tp.toPath.toString match {
|
||||||
case s if s.endsWith(".jar") => binaryConverter(tp)
|
case s if s.endsWith(".jar") => binaryConverter(tp)
|
||||||
case s if s.endsWith(".class") => binaryConverter(tp)
|
case s if s.endsWith(".class") => binaryConverter(tp)
|
||||||
|
|
@ -54,16 +56,15 @@ object Stamped {
|
||||||
/**
|
/**
|
||||||
* Adds a default ordering that just delegates to the java.io.File.compareTo method.
|
* Adds a default ordering that just delegates to the java.io.File.compareTo method.
|
||||||
*/
|
*/
|
||||||
implicit case object ordering extends Ordering[Stamped] {
|
implicit case object ordering extends Ordering[Stamped.File] {
|
||||||
override def compare(left: Stamped, right: Stamped): Int = left.compareTo(right)
|
override def compare(left: Stamped.File, right: Stamped.File): Int = left.compareTo(right)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StampedImpl(typedPath: TypedPath, forceLastModified: Boolean)
|
private final class StampedImpl(override val stamp: Stamp) extends Stamped
|
||||||
|
private final class StampedFileImpl(typedPath: TypedPath, override val stamp: Stamp)
|
||||||
extends java.io.File(typedPath.toPath.toString)
|
extends java.io.File(typedPath.toPath.toString)
|
||||||
with Stamped {
|
with Stamped
|
||||||
override val stamp: Stamp =
|
with TypedPath {
|
||||||
if (forceLastModified || typedPath.isDirectory) Stamper.forLastModified(this)
|
|
||||||
else Stamper.forHash(this)
|
|
||||||
override def exists: Boolean = typedPath.exists
|
override def exists: Boolean = typedPath.exists
|
||||||
override def isDirectory: Boolean = typedPath.isDirectory
|
override def isDirectory: Boolean = typedPath.isDirectory
|
||||||
override def isFile: Boolean = typedPath.isFile
|
override def isFile: Boolean = typedPath.isFile
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import sbt.internal.util.{ AttributeKey, JLine }
|
||||||
import sbt.io.FileEventMonitor.{ Creation, Deletion, Event, Update }
|
import sbt.io.FileEventMonitor.{ Creation, Deletion, Event, Update }
|
||||||
import sbt.io._
|
import sbt.io._
|
||||||
import sbt.util.{ Level, Logger }
|
import sbt.util.{ Level, Logger }
|
||||||
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -146,11 +147,11 @@ object Watched {
|
||||||
private[sbt] def onEvent(
|
private[sbt] def onEvent(
|
||||||
sources: Seq[WatchSource],
|
sources: Seq[WatchSource],
|
||||||
projectSources: Seq[WatchSource]
|
projectSources: Seq[WatchSource]
|
||||||
): Event[Stamped] => Watched.Action =
|
): Event[Stamp] => Watched.Action =
|
||||||
event =>
|
event =>
|
||||||
if (sources.exists(_.accept(event.entry.typedPath.toPath))) Watched.Trigger
|
if (sources.exists(_.accept(event.entry.typedPath.toPath))) Watched.Trigger
|
||||||
else if (projectSources.exists(_.accept(event.entry.typedPath.toPath))) event match {
|
else if (projectSources.exists(_.accept(event.entry.typedPath.toPath))) event match {
|
||||||
case Update(prev, cur, _) if prev.value.map(_.stamp) != cur.value.map(_.stamp) => Reload
|
case Update(prev, cur, _) if prev.value != cur.value => Reload
|
||||||
case _: Creation[_] | _: Deletion[_] => Reload
|
case _: Creation[_] | _: Deletion[_] => Reload
|
||||||
case _ => Ignore
|
case _ => Ignore
|
||||||
} else Ignore
|
} else Ignore
|
||||||
|
|
@ -458,7 +459,7 @@ trait WatchConfig {
|
||||||
*
|
*
|
||||||
* @return an sbt.io.FileEventMonitor instance.
|
* @return an sbt.io.FileEventMonitor instance.
|
||||||
*/
|
*/
|
||||||
def fileEventMonitor: FileEventMonitor[Stamped]
|
def fileEventMonitor: FileEventMonitor[Stamp]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that is periodically invoked to determine whether the watch should stop or
|
* A function that is periodically invoked to determine whether the watch should stop or
|
||||||
|
|
@ -481,7 +482,7 @@ trait WatchConfig {
|
||||||
* @param event the detected sbt.io.FileEventMonitor.Event.
|
* @param event the detected sbt.io.FileEventMonitor.Event.
|
||||||
* @return the next [[Watched.Action Action]] to run.
|
* @return the next [[Watched.Action Action]] to run.
|
||||||
*/
|
*/
|
||||||
def onWatchEvent(event: Event[Stamped]): Watched.Action
|
def onWatchEvent(event: Event[Stamp]): Watched.Action
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms the state after the watch terminates.
|
* Transforms the state after the watch terminates.
|
||||||
|
|
@ -537,10 +538,10 @@ object WatchConfig {
|
||||||
*/
|
*/
|
||||||
def default(
|
def default(
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
fileEventMonitor: FileEventMonitor[Stamped],
|
fileEventMonitor: FileEventMonitor[Stamp],
|
||||||
handleInput: InputStream => Watched.Action,
|
handleInput: InputStream => Watched.Action,
|
||||||
preWatch: (Int, Boolean) => Watched.Action,
|
preWatch: (Int, Boolean) => Watched.Action,
|
||||||
onWatchEvent: Event[Stamped] => Watched.Action,
|
onWatchEvent: Event[Stamp] => Watched.Action,
|
||||||
onWatchTerminated: (Watched.Action, String, State) => State,
|
onWatchTerminated: (Watched.Action, String, State) => State,
|
||||||
triggeredMessage: (TypedPath, Int) => Option[String],
|
triggeredMessage: (TypedPath, Int) => Option[String],
|
||||||
watchingMessage: Int => Option[String]
|
watchingMessage: Int => Option[String]
|
||||||
|
|
@ -555,11 +556,11 @@ object WatchConfig {
|
||||||
val wm = watchingMessage
|
val wm = watchingMessage
|
||||||
new WatchConfig {
|
new WatchConfig {
|
||||||
override def logger: Logger = l
|
override def logger: Logger = l
|
||||||
override def fileEventMonitor: FileEventMonitor[Stamped] = fem
|
override def fileEventMonitor: FileEventMonitor[Stamp] = fem
|
||||||
override def handleInput(inputStream: InputStream): Watched.Action = hi(inputStream)
|
override def handleInput(inputStream: InputStream): Watched.Action = hi(inputStream)
|
||||||
override def preWatch(count: Int, lastResult: Boolean): Watched.Action =
|
override def preWatch(count: Int, lastResult: Boolean): Watched.Action =
|
||||||
pw(count, lastResult)
|
pw(count, lastResult)
|
||||||
override def onWatchEvent(event: Event[Stamped]): Watched.Action = owe(event)
|
override def onWatchEvent(event: Event[Stamp]): Watched.Action = owe(event)
|
||||||
override def onWatchTerminated(action: Watched.Action, command: String, state: State): State =
|
override def onWatchTerminated(action: Watched.Action, command: String, state: State): State =
|
||||||
owt(action, command, state)
|
owt(action, command, state)
|
||||||
override def triggeredMessage(typedPath: TypedPath, count: Int): Option[String] =
|
override def triggeredMessage(typedPath: TypedPath, count: Int): Option[String] =
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import sbt.WatchedSpec._
|
||||||
import sbt.io.FileEventMonitor.Event
|
import sbt.io.FileEventMonitor.Event
|
||||||
import sbt.io.{ FileEventMonitor, IO, TypedPath }
|
import sbt.io.{ FileEventMonitor, IO, TypedPath }
|
||||||
import sbt.util.Logger
|
import sbt.util.Logger
|
||||||
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -26,11 +27,11 @@ class WatchedSpec extends FlatSpec with Matchers {
|
||||||
private val fileTreeViewConfig = FileTreeViewConfig.default(50.millis)
|
private val fileTreeViewConfig = FileTreeViewConfig.default(50.millis)
|
||||||
def config(
|
def config(
|
||||||
sources: Seq[WatchSource],
|
sources: Seq[WatchSource],
|
||||||
fileEventMonitor: Option[FileEventMonitor[Stamped]] = None,
|
fileEventMonitor: Option[FileEventMonitor[Stamp]] = None,
|
||||||
logger: Logger = NullLogger,
|
logger: Logger = NullLogger,
|
||||||
handleInput: InputStream => Action = _ => Ignore,
|
handleInput: InputStream => Action = _ => Ignore,
|
||||||
preWatch: (Int, Boolean) => Action = (_, _) => CancelWatch,
|
preWatch: (Int, Boolean) => Action = (_, _) => CancelWatch,
|
||||||
onWatchEvent: Event[Stamped] => Action = _ => Ignore,
|
onWatchEvent: Event[Stamp] => Action = _ => Ignore,
|
||||||
triggeredMessage: (TypedPath, Int) => Option[String] = (_, _) => None,
|
triggeredMessage: (TypedPath, Int) => Option[String] = (_, _) => None,
|
||||||
watchingMessage: Int => Option[String] = _ => None
|
watchingMessage: Int => Option[String] = _ => None
|
||||||
): WatchConfig = {
|
): WatchConfig = {
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,7 @@ object Defaults extends BuildCommon {
|
||||||
watchStartMessage := Watched.defaultStartWatch,
|
watchStartMessage := Watched.defaultStartWatch,
|
||||||
fileTreeViewConfig := FileManagement.defaultFileTreeView.value,
|
fileTreeViewConfig := FileManagement.defaultFileTreeView.value,
|
||||||
fileTreeView := state.value
|
fileTreeView := state.value
|
||||||
.get(BasicKeys.globalFileTreeView)
|
.get(Keys.globalFileTreeView)
|
||||||
.getOrElse(FileTreeView.DEFAULT.asDataView(Stamped.converter)),
|
.getOrElse(FileTreeView.DEFAULT.asDataView(Stamped.converter)),
|
||||||
externalHooks := {
|
externalHooks := {
|
||||||
val view = fileTreeView.value
|
val view = fileTreeView.value
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import sbt.librarymanagement.ivy.{ Credentials, IvyConfiguration, IvyPaths, Upda
|
||||||
import sbt.testing.Framework
|
import sbt.testing.Framework
|
||||||
import sbt.util.{ Level, Logger }
|
import sbt.util.{ Level, Logger }
|
||||||
import xsbti.compile._
|
import xsbti.compile._
|
||||||
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
||||||
import scala.concurrent.duration.{ Duration, FiniteDuration }
|
import scala.concurrent.duration.{ Duration, FiniteDuration }
|
||||||
import scala.xml.{ NodeSeq, Node => XNode }
|
import scala.xml.{ NodeSeq, Node => XNode }
|
||||||
|
|
@ -93,14 +94,14 @@ object Keys {
|
||||||
@deprecated("This is no longer used for continuous execution", "1.3.0")
|
@deprecated("This is no longer used for continuous execution", "1.3.0")
|
||||||
val watch = SettingKey(BasicKeys.watch)
|
val watch = SettingKey(BasicKeys.watch)
|
||||||
val suppressSbtShellNotification = settingKey[Boolean]("""True to suppress the "Executing in batch mode.." message.""").withRank(CSetting)
|
val suppressSbtShellNotification = settingKey[Boolean]("""True to suppress the "Executing in batch mode.." message.""").withRank(CSetting)
|
||||||
val fileTreeView = taskKey[FileTreeDataView[Stamped]]("A view of the file system")
|
val fileTreeView = taskKey[FileTreeDataView[Stamp]]("A view of the file system")
|
||||||
val pollInterval = settingKey[FiniteDuration]("Interval between checks for modified sources by the continuous execution command.").withRank(BMinusSetting)
|
val pollInterval = settingKey[FiniteDuration]("Interval between checks for modified sources by the continuous execution command.").withRank(BMinusSetting)
|
||||||
val pollingDirectories = settingKey[Seq[Watched.WatchSource]]("Directories that cannot be cached and must always be rescanned. Typically these will be NFS mounted or something similar.").withRank(DSetting)
|
val pollingDirectories = settingKey[Seq[Watched.WatchSource]]("Directories that cannot be cached and must always be rescanned. Typically these will be NFS mounted or something similar.").withRank(DSetting)
|
||||||
val watchAntiEntropy = settingKey[FiniteDuration]("Duration for which the watch EventMonitor will ignore events for a file after that file has triggered a build.").withRank(BMinusSetting)
|
val watchAntiEntropy = settingKey[FiniteDuration]("Duration for which the watch EventMonitor will ignore events for a file after that file has triggered a build.").withRank(BMinusSetting)
|
||||||
val watchConfig = taskKey[WatchConfig]("The configuration for continuous execution.").withRank(BMinusSetting)
|
val watchConfig = taskKey[WatchConfig]("The configuration for continuous execution.").withRank(BMinusSetting)
|
||||||
val watchLogger = taskKey[Logger]("A logger that reports watch events.").withRank(DSetting)
|
val watchLogger = taskKey[Logger]("A logger that reports watch events.").withRank(DSetting)
|
||||||
val watchHandleInput = settingKey[InputStream => Watched.Action]("Function that is periodically invoked to determine if the continous build should be stopped or if a build should be triggered. It will usually read from stdin to respond to user commands.").withRank(BMinusSetting)
|
val watchHandleInput = settingKey[InputStream => Watched.Action]("Function that is periodically invoked to determine if the continous build should be stopped or if a build should be triggered. It will usually read from stdin to respond to user commands.").withRank(BMinusSetting)
|
||||||
val watchOnEvent = taskKey[Event[Stamped] => Watched.Action]("Determines how to handle a file event").withRank(BMinusSetting)
|
val watchOnEvent = taskKey[Event[Stamp] => Watched.Action]("Determines how to handle a file event").withRank(BMinusSetting)
|
||||||
val watchOnTermination = taskKey[(Watched.Action, String, State) => State]("Transforms the input state after the continuous build completes.").withRank(BMinusSetting)
|
val watchOnTermination = taskKey[(Watched.Action, String, State) => State]("Transforms the input state after the continuous build completes.").withRank(BMinusSetting)
|
||||||
val watchService = settingKey[() => WatchService]("Service to use to monitor file system changes.").withRank(BMinusSetting)
|
val watchService = settingKey[() => WatchService]("Service to use to monitor file system changes.").withRank(BMinusSetting)
|
||||||
val watchProjectSources = taskKey[Seq[Watched.WatchSource]]("Defines the sources for the sbt meta project to watch to trigger a reload.").withRank(CSetting)
|
val watchProjectSources = taskKey[Seq[Watched.WatchSource]]("Defines the sources for the sbt meta project to watch to trigger a reload.").withRank(CSetting)
|
||||||
|
|
@ -456,6 +457,11 @@ object Keys {
|
||||||
val (executionRoots, dummyRoots) = Def.dummy[Seq[ScopedKey[_]]]("executionRoots", "The list of root tasks for this task execution. Roots are the top-level tasks that were directly requested to be run.")
|
val (executionRoots, dummyRoots) = Def.dummy[Seq[ScopedKey[_]]]("executionRoots", "The list of root tasks for this task execution. Roots are the top-level tasks that were directly requested to be run.")
|
||||||
val state = Def.stateKey
|
val state = Def.stateKey
|
||||||
val streamsManager = Def.streamsManagerKey
|
val streamsManager = Def.streamsManagerKey
|
||||||
|
private[sbt] val globalFileTreeView = AttributeKey[FileTreeDataView[Stamp]](
|
||||||
|
"globalFileTreeView",
|
||||||
|
"Provides a view into the file system that may or may not cache the tree in memory",
|
||||||
|
1000
|
||||||
|
)
|
||||||
|
|
||||||
val stateStreams = AttributeKey[Streams]("stateStreams", "Streams manager, which provides streams for different contexts. Setting this on State will override the default Streams implementation.")
|
val stateStreams = AttributeKey[Streams]("stateStreams", "Streams manager, which provides streams for different contexts. Setting this on State will override the default Streams implementation.")
|
||||||
val resolvedScoped = Def.resolvedScoped
|
val resolvedScoped = Def.resolvedScoped
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import sbt.io.syntax._
|
||||||
import sbt.io.{ FileTreeDataView, IO }
|
import sbt.io.{ FileTreeDataView, IO }
|
||||||
import sbt.util.{ Level, Logger, Show }
|
import sbt.util.{ Level, Logger, Show }
|
||||||
import xsbti.compile.CompilerCache
|
import xsbti.compile.CompilerCache
|
||||||
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
|
|
@ -855,18 +856,18 @@ object BuiltinCommands {
|
||||||
val extracted = Project.extract(s)
|
val extracted = Project.extract(s)
|
||||||
try {
|
try {
|
||||||
def cleanup(): Unit = {
|
def cleanup(): Unit = {
|
||||||
s.get(BasicKeys.globalFileTreeView).foreach(_.close())
|
s.get(Keys.globalFileTreeView).foreach(_.close())
|
||||||
s.attributes.remove(BasicKeys.globalFileTreeView)
|
s.attributes.remove(Keys.globalFileTreeView)
|
||||||
s.get(Keys.taskRepository).foreach(_.close())
|
s.get(Keys.taskRepository).foreach(_.close())
|
||||||
s.attributes.remove(Keys.taskRepository)
|
s.attributes.remove(Keys.taskRepository)
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
val (_, config: FileTreeViewConfig) = extracted.runTask(Keys.fileTreeViewConfig, s)
|
val (_, config: FileTreeViewConfig) = extracted.runTask(Keys.fileTreeViewConfig, s)
|
||||||
val view: FileTreeDataView[Stamped] = config.newDataView()
|
val view: FileTreeDataView[Stamp] = config.newDataView()
|
||||||
val newState = s.addExitHook(cleanup())
|
val newState = s.addExitHook(cleanup())
|
||||||
cleanup()
|
cleanup()
|
||||||
newState
|
newState
|
||||||
.put(BasicKeys.globalFileTreeView, view)
|
.put(Keys.globalFileTreeView, view)
|
||||||
.put(Keys.taskRepository, new TaskRepository.Repr)
|
.put(Keys.taskRepository, new TaskRepository.Repr)
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(_) => s
|
case NonFatal(_) => s
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,17 @@ import scala.collection.mutable
|
||||||
|
|
||||||
private[sbt] object ExternalHooks {
|
private[sbt] object ExternalHooks {
|
||||||
private val javaHome = Option(System.getProperty("java.home")).map(Paths.get(_))
|
private val javaHome = Option(System.getProperty("java.home")).map(Paths.get(_))
|
||||||
def apply(options: CompileOptions, view: FileTreeDataView[Stamped]): DefaultExternalHooks = {
|
def apply(options: CompileOptions, view: FileTreeDataView[Stamp]): DefaultExternalHooks = {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
val sources = options.sources()
|
val sources = options.sources()
|
||||||
val cachedSources = new java.util.HashMap[File, Stamp]
|
val cachedSources = new java.util.HashMap[File, Stamp]
|
||||||
val converter: File => Stamp = f => Stamped.sourceConverter(TypedPath(f.toPath)).stamp
|
val converter: File => Stamp = f => Stamped.sourceConverter(TypedPath(f.toPath))
|
||||||
sources.foreach {
|
sources.foreach {
|
||||||
case sf: Stamped => cachedSources.put(sf, sf.stamp)
|
case sf: Stamped => cachedSources.put(sf, sf.stamp)
|
||||||
case f: File => cachedSources.put(f, converter(f))
|
case f: File => cachedSources.put(f, converter(f))
|
||||||
}
|
}
|
||||||
view match {
|
view match {
|
||||||
case r: FileTreeRepository[Stamped] =>
|
case r: FileTreeRepository[Stamp] =>
|
||||||
r.register(options.classesDirectory.toPath, Integer.MAX_VALUE)
|
r.register(options.classesDirectory.toPath, Integer.MAX_VALUE)
|
||||||
options.classpath.foreach { f =>
|
options.classpath.foreach { f =>
|
||||||
r.register(f.toPath, Integer.MAX_VALUE)
|
r.register(f.toPath, Integer.MAX_VALUE)
|
||||||
|
|
@ -41,7 +41,7 @@ private[sbt] object ExternalHooks {
|
||||||
options.classpath.foreach { f =>
|
options.classpath.foreach { f =>
|
||||||
view.listEntries(f.toPath, Integer.MAX_VALUE, _ => true) foreach { e =>
|
view.listEntries(f.toPath, Integer.MAX_VALUE, _ => true) foreach { e =>
|
||||||
e.value match {
|
e.value match {
|
||||||
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value.stamp)
|
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ private[sbt] object ExternalHooks {
|
||||||
// rather than a directory.
|
// rather than a directory.
|
||||||
view.listEntries(f.toPath, -1, _ => true) foreach { e =>
|
view.listEntries(f.toPath, -1, _ => true) foreach { e =>
|
||||||
e.value match {
|
e.value match {
|
||||||
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value.stamp)
|
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import sbt.io.syntax.File
|
||||||
import sbt.io.{ FileFilter, FileTreeDataView, FileTreeRepository }
|
import sbt.io.{ FileFilter, FileTreeDataView, FileTreeRepository }
|
||||||
import sbt._
|
import sbt._
|
||||||
import BasicCommandStrings.ContinuousExecutePrefix
|
import BasicCommandStrings.ContinuousExecutePrefix
|
||||||
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
||||||
private[sbt] object FileManagement {
|
private[sbt] object FileManagement {
|
||||||
private[sbt] def defaultFileTreeView: Def.Initialize[Task[FileTreeViewConfig]] = Def.task {
|
private[sbt] def defaultFileTreeView: Def.Initialize[Task[FileTreeViewConfig]] = Def.task {
|
||||||
|
|
@ -51,17 +52,19 @@ private[sbt] object FileManagement {
|
||||||
val view = fileTreeView.value
|
val view = fileTreeView.value
|
||||||
val include = filter.toTask.value
|
val include = filter.toTask.value
|
||||||
val ex = excludes.toTask.value
|
val ex = excludes.toTask.value
|
||||||
val sourceFilter: Entry[Stamped] => Boolean = (entry: Entry[Stamped]) => {
|
val sourceFilter: Entry[Stamp] => Boolean = (entry: Entry[Stamp]) => {
|
||||||
entry.value match {
|
val typedPath = entry.typedPath
|
||||||
case Right(sf) => include.accept(sf) && !ex.accept(sf)
|
val file = new java.io.File(typedPath.toPath.toString) {
|
||||||
case _ => false
|
override def isDirectory: Boolean = typedPath.isDirectory
|
||||||
|
override def isFile: Boolean = typedPath.isFile
|
||||||
}
|
}
|
||||||
|
include.accept(file) && !ex.accept(file)
|
||||||
}
|
}
|
||||||
sourceDirs.flatMap { dir =>
|
sourceDirs.flatMap { dir =>
|
||||||
view.register(dir.toPath, maxDepth = Integer.MAX_VALUE)
|
view.register(dir.toPath, maxDepth = Integer.MAX_VALUE)
|
||||||
view
|
view
|
||||||
.listEntries(dir.toPath, maxDepth = Integer.MAX_VALUE, sourceFilter)
|
.listEntries(dir.toPath, maxDepth = Integer.MAX_VALUE, sourceFilter)
|
||||||
.map(e => e.value.getOrElse(e.typedPath.toPath.toFile))
|
.flatMap(e => e.value.toOption.map(Stamped.file(e.typedPath, _)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +96,7 @@ private[sbt] object FileManagement {
|
||||||
f.accept(file) && !excl.accept(file)
|
f.accept(file) && !excl.accept(file)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.flatMap(_.value.toOption)
|
.flatMap(e => e.value.toOption.map(Stamped.file(e.typedPath, _)))
|
||||||
} else sources
|
} else sources
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue