mirror of https://github.com/sbt/sbt.git
Add FileCacheEntry
Previously, we were leaking the internal details of incremental compilation to users by defining FileTree(DataView|Repository)[Stamp]. To avoid this, I introduce the new class FileCacheEntry that is quite similar to Stamp except defined using scala Options rather than java Optionals. The implementation class just delegates to an actual Stamp and I provided a private[sbt] ops class that adds a method `stamp` to FileCacheEntry. This will usually just extract the stamp from the implementation class. This allows us to use FileCacheEntry almost interchangeably with Stamp while still avoiding exposing users to Stamp.
This commit is contained in:
parent
ba0494df14
commit
e8af828c73
|
|
@ -9,13 +9,11 @@ package sbt
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
import sbt.internal.util.AttributeKey
|
|
||||||
import sbt.internal.inc.classpath.ClassLoaderCache
|
import sbt.internal.inc.classpath.ClassLoaderCache
|
||||||
import sbt.internal.server.ServerHandler
|
import sbt.internal.server.ServerHandler
|
||||||
import sbt.io.FileTreeDataView
|
import sbt.internal.util.AttributeKey
|
||||||
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]](
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@
|
||||||
|
|
||||||
package sbt
|
package sbt
|
||||||
import sbt.Watched.WatchSource
|
import sbt.Watched.WatchSource
|
||||||
|
import sbt.internal.FileCacheEntry
|
||||||
import sbt.internal.io.{ HybridPollingFileTreeRepository, WatchServiceBackedObservable, WatchState }
|
import sbt.internal.io.{ HybridPollingFileTreeRepository, WatchServiceBackedObservable, WatchState }
|
||||||
|
import sbt.io.FileTreeDataView.{ Observable, Observer }
|
||||||
import sbt.io._
|
import sbt.io._
|
||||||
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._
|
||||||
|
|
||||||
|
|
@ -19,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[Stamp],
|
val newDataView: () => FileTreeDataView[FileCacheEntry],
|
||||||
val newMonitor: (
|
val newMonitor: (
|
||||||
FileTreeDataView[Stamp],
|
FileTreeDataView[FileCacheEntry],
|
||||||
Seq[WatchSource],
|
Seq[WatchSource],
|
||||||
Logger
|
Logger
|
||||||
) => FileEventMonitor[Stamp]
|
) => FileEventMonitor[FileCacheEntry]
|
||||||
)
|
)
|
||||||
object FileTreeViewConfig {
|
object FileTreeViewConfig {
|
||||||
private implicit class RepositoryOps(val repository: FileTreeRepository[Stamp]) {
|
private implicit class RepositoryOps(val repository: FileTreeRepository[FileCacheEntry]) {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -35,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[Stamp]}}}. The reason for this is to ensure that a
|
* by {{{sbt.io.FileTreeDataView[FileCacheEntry]}}}. 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[Stamp]}}}.
|
* {{{sbt.io.FileTreeDataView[FileCacheEntry]}}}.
|
||||||
* @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
|
||||||
|
|
@ -46,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[Stamp]](
|
def apply[T <: FileTreeDataView[FileCacheEntry]](
|
||||||
newDataView: () => T,
|
newDataView: () => T,
|
||||||
newMonitor: (T, Seq[WatchSource], Logger) => FileEventMonitor[Stamp]
|
newMonitor: (T, Seq[WatchSource], Logger) => FileEventMonitor[FileCacheEntry]
|
||||||
): FileTreeViewConfig =
|
): FileTreeViewConfig =
|
||||||
new FileTreeViewConfig(
|
new FileTreeViewConfig(
|
||||||
newDataView,
|
newDataView,
|
||||||
(view: FileTreeDataView[Stamp], sources: Seq[WatchSource], logger: Logger) =>
|
(view: FileTreeDataView[FileCacheEntry], sources: Seq[WatchSource], logger: Logger) =>
|
||||||
newMonitor(view.asInstanceOf[T], sources, logger)
|
newMonitor(view.asInstanceOf[T], sources, logger)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -71,14 +71,14 @@ object FileTreeViewConfig {
|
||||||
antiEntropy: FiniteDuration
|
antiEntropy: FiniteDuration
|
||||||
): FileTreeViewConfig =
|
): FileTreeViewConfig =
|
||||||
FileTreeViewConfig(
|
FileTreeViewConfig(
|
||||||
() => FileTreeView.DEFAULT.asDataView(Stamped.converter),
|
() => FileTreeView.DEFAULT.asDataView(FileCacheEntry.default),
|
||||||
(_: FileTreeDataView[Stamp], sources, logger) => {
|
(_: FileTreeDataView[FileCacheEntry], 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(
|
||||||
WatchState.empty(Watched.createWatchService(), sources),
|
WatchState.empty(Watched.createWatchService(), sources),
|
||||||
delay,
|
delay,
|
||||||
Stamped.converter,
|
FileCacheEntry.default,
|
||||||
closeService = true,
|
closeService = true,
|
||||||
ioLogger
|
ioLogger
|
||||||
),
|
),
|
||||||
|
|
@ -98,11 +98,15 @@ object FileTreeViewConfig {
|
||||||
*/
|
*/
|
||||||
def default(antiEntropy: FiniteDuration): FileTreeViewConfig =
|
def default(antiEntropy: FiniteDuration): FileTreeViewConfig =
|
||||||
FileTreeViewConfig(
|
FileTreeViewConfig(
|
||||||
() => FileTreeRepository.default(Stamped.converter),
|
() => FileTreeRepository.default(FileCacheEntry.default),
|
||||||
(repository: FileTreeRepository[Stamp], sources: Seq[WatchSource], logger: Logger) => {
|
(
|
||||||
|
repository: FileTreeRepository[FileCacheEntry],
|
||||||
|
sources: Seq[WatchSource],
|
||||||
|
logger: Logger
|
||||||
|
) => {
|
||||||
repository.register(sources)
|
repository.register(sources)
|
||||||
val copied = new Observable[Stamp] {
|
val copied = new Observable[FileCacheEntry] {
|
||||||
override def addObserver(observer: Observer[Stamp]): Int =
|
override def addObserver(observer: Observer[FileCacheEntry]): 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
|
||||||
|
|
@ -155,9 +159,9 @@ object FileTreeViewConfig {
|
||||||
pollingInterval: FiniteDuration,
|
pollingInterval: FiniteDuration,
|
||||||
pollingSources: Seq[WatchSource],
|
pollingSources: Seq[WatchSource],
|
||||||
): FileTreeViewConfig = FileTreeViewConfig(
|
): FileTreeViewConfig = FileTreeViewConfig(
|
||||||
() => FileTreeRepository.hybrid(Stamped.converter, pollingSources: _*),
|
() => FileTreeRepository.hybrid(FileCacheEntry.default, pollingSources: _*),
|
||||||
(
|
(
|
||||||
repository: HybridPollingFileTreeRepository[Stamp],
|
repository: HybridPollingFileTreeRepository[FileCacheEntry],
|
||||||
sources: Seq[WatchSource],
|
sources: Seq[WatchSource],
|
||||||
logger: Logger
|
logger: Logger
|
||||||
) => {
|
) => {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ package sbt
|
||||||
import java.io.{ File => JFile }
|
import java.io.{ File => JFile }
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
import sbt.internal.FileCacheEntry
|
||||||
import sbt.internal.inc.Stamper
|
import sbt.internal.inc.Stamper
|
||||||
import sbt.io.TypedPath
|
import sbt.io.TypedPath
|
||||||
import xsbti.compile.analysis.Stamp
|
import xsbti.compile.analysis.Stamp
|
||||||
|
|
@ -20,17 +21,17 @@ import xsbti.compile.analysis.Stamp
|
||||||
* performance anywhere where we need to check if files have changed before doing potentially
|
* performance anywhere where we need to check if files have changed before doing potentially
|
||||||
* expensive work.
|
* expensive work.
|
||||||
*/
|
*/
|
||||||
trait Stamped {
|
private[sbt] trait Stamped {
|
||||||
def stamp: Stamp
|
private[sbt] def stamp: Stamp
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides converter functions from TypedPath to [[Stamped]].
|
* Provides converter functions from TypedPath to [[Stamped]].
|
||||||
*/
|
*/
|
||||||
object Stamped {
|
private[sbt] object Stamped {
|
||||||
type File = JFile with Stamped with TypedPath
|
type File = JFile with Stamped with TypedPath
|
||||||
def file(typedPath: TypedPath, stamp: Stamp): JFile with Stamped with TypedPath =
|
def file(typedPath: TypedPath, entry: FileCacheEntry): JFile with Stamped with TypedPath =
|
||||||
new StampedFileImpl(typedPath, stamp)
|
new StampedFileImpl(typedPath, entry.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.
|
||||||
|
|
@ -47,11 +48,14 @@ object Stamped {
|
||||||
* 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 => Stamp = (tp: TypedPath) =>
|
val converter: TypedPath => Stamp = (tp: TypedPath) =>
|
||||||
|
if (tp.isDirectory) binaryConverter(tp)
|
||||||
|
else {
|
||||||
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)
|
||||||
case _ => sourceConverter(tp)
|
case _ => sourceConverter(tp)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,14 @@ import sbt.BasicCommandStrings.{
|
||||||
}
|
}
|
||||||
import sbt.BasicCommands.otherCommandParser
|
import sbt.BasicCommands.otherCommandParser
|
||||||
import sbt.internal.LabeledFunctions._
|
import sbt.internal.LabeledFunctions._
|
||||||
import sbt.internal.LegacyWatched
|
|
||||||
import sbt.internal.io.{ EventMonitor, Source, WatchState }
|
import sbt.internal.io.{ EventMonitor, Source, WatchState }
|
||||||
import sbt.internal.util.Types.const
|
import sbt.internal.util.Types.const
|
||||||
import sbt.internal.util.complete.{ DefaultParsers, Parser }
|
import sbt.internal.util.complete.{ DefaultParsers, Parser }
|
||||||
import sbt.internal.util.{ AttributeKey, JLine }
|
import sbt.internal.util.{ AttributeKey, JLine }
|
||||||
|
import sbt.internal.{ FileCacheEntry, LegacyWatched }
|
||||||
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._
|
||||||
|
|
@ -147,7 +146,7 @@ object Watched {
|
||||||
private[sbt] def onEvent(
|
private[sbt] def onEvent(
|
||||||
sources: Seq[WatchSource],
|
sources: Seq[WatchSource],
|
||||||
projectSources: Seq[WatchSource]
|
projectSources: Seq[WatchSource]
|
||||||
): Event[Stamp] => Watched.Action =
|
): Event[FileCacheEntry] => 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 {
|
||||||
|
|
@ -459,7 +458,7 @@ trait WatchConfig {
|
||||||
*
|
*
|
||||||
* @return an sbt.io.FileEventMonitor instance.
|
* @return an sbt.io.FileEventMonitor instance.
|
||||||
*/
|
*/
|
||||||
def fileEventMonitor: FileEventMonitor[Stamp]
|
def fileEventMonitor: FileEventMonitor[FileCacheEntry]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
|
@ -482,7 +481,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[Stamp]): Watched.Action
|
def onWatchEvent(event: Event[FileCacheEntry]): Watched.Action
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms the state after the watch terminates.
|
* Transforms the state after the watch terminates.
|
||||||
|
|
@ -538,10 +537,10 @@ object WatchConfig {
|
||||||
*/
|
*/
|
||||||
def default(
|
def default(
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
fileEventMonitor: FileEventMonitor[Stamp],
|
fileEventMonitor: FileEventMonitor[FileCacheEntry],
|
||||||
handleInput: InputStream => Watched.Action,
|
handleInput: InputStream => Watched.Action,
|
||||||
preWatch: (Int, Boolean) => Watched.Action,
|
preWatch: (Int, Boolean) => Watched.Action,
|
||||||
onWatchEvent: Event[Stamp] => Watched.Action,
|
onWatchEvent: Event[FileCacheEntry] => 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]
|
||||||
|
|
@ -556,11 +555,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[Stamp] = fem
|
override def fileEventMonitor: FileEventMonitor[FileCacheEntry] = 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[Stamp]): Watched.Action = owe(event)
|
override def onWatchEvent(event: Event[FileCacheEntry]): 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] =
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt
|
||||||
|
package internal
|
||||||
|
import java.lang
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
|
import sbt.internal.inc.{ EmptyStamp, LastModified, Stamp }
|
||||||
|
import sbt.io.TypedPath
|
||||||
|
import xsbti.compile.analysis.{ Stamp => XStamp }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a cache entry for a FileTreeRepository. It can be extended to add user defined
|
||||||
|
* data to the FileTreeRepository cache.
|
||||||
|
*/
|
||||||
|
trait FileCacheEntry {
|
||||||
|
def hash: Option[String]
|
||||||
|
def lastModified: Option[Long]
|
||||||
|
}
|
||||||
|
object FileCacheEntry {
|
||||||
|
def default(typedPath: TypedPath): FileCacheEntry =
|
||||||
|
DelegateFileCacheEntry(Stamped.converter(typedPath))
|
||||||
|
private[sbt] implicit class FileCacheEntryOps(val e: FileCacheEntry) extends AnyVal {
|
||||||
|
private[sbt] def stamp: XStamp = e match {
|
||||||
|
case DelegateFileCacheEntry(s) => s
|
||||||
|
case _ =>
|
||||||
|
e.hash
|
||||||
|
.map(Stamp.fromString)
|
||||||
|
.orElse(e.lastModified.map(new LastModified(_)))
|
||||||
|
.getOrElse(EmptyStamp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private case class DelegateFileCacheEntry(private val stamp: XStamp)
|
||||||
|
extends FileCacheEntry
|
||||||
|
with XStamp {
|
||||||
|
override def getValueId: Int = stamp.getValueId
|
||||||
|
override def writeStamp(): String = stamp.writeStamp()
|
||||||
|
override def getHash: Optional[String] = stamp.getHash
|
||||||
|
override def getLastModified: Optional[lang.Long] = stamp.getLastModified
|
||||||
|
override def hash: Option[String] = getHash match {
|
||||||
|
case h if h.isPresent => Some(h.get)
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
override def lastModified: Option[Long] = getLastModified match {
|
||||||
|
case l if l.isPresent => Some(l.get)
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
override def equals(o: Any): Boolean = o match {
|
||||||
|
case that: DelegateFileCacheEntry => this.stamp == that.stamp
|
||||||
|
case that: XStamp => this.stamp == that
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
override def hashCode: Int = stamp.hashCode
|
||||||
|
override def toString: String = s"FileCacheEntry(hash = $hash, lastModified = $lastModified)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,10 +14,10 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import org.scalatest.{ FlatSpec, Matchers }
|
import org.scalatest.{ FlatSpec, Matchers }
|
||||||
import sbt.Watched._
|
import sbt.Watched._
|
||||||
import sbt.WatchedSpec._
|
import sbt.WatchedSpec._
|
||||||
|
import sbt.internal.FileCacheEntry
|
||||||
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._
|
||||||
|
|
@ -27,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[Stamp]] = None,
|
fileEventMonitor: Option[FileEventMonitor[FileCacheEntry]] = 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[Stamp] => Action = _ => Ignore,
|
onWatchEvent: Event[FileCacheEntry] => 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 = {
|
||||||
|
|
|
||||||
|
|
@ -274,7 +274,7 @@ object Defaults extends BuildCommon {
|
||||||
fileTreeViewConfig := FileManagement.defaultFileTreeView.value,
|
fileTreeViewConfig := FileManagement.defaultFileTreeView.value,
|
||||||
fileTreeView := state.value
|
fileTreeView := state.value
|
||||||
.get(Keys.globalFileTreeView)
|
.get(Keys.globalFileTreeView)
|
||||||
.getOrElse(FileTreeView.DEFAULT.asDataView(Stamped.converter)),
|
.getOrElse(FileTreeView.DEFAULT.asDataView(FileCacheEntry.default)),
|
||||||
externalHooks := {
|
externalHooks := {
|
||||||
val view = fileTreeView.value
|
val view = fileTreeView.value
|
||||||
compileOptions =>
|
compileOptions =>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ 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 }
|
||||||
|
|
@ -94,14 +93,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[Stamp]]("A view of the file system")
|
val fileTreeView = taskKey[FileTreeDataView[FileCacheEntry]]("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[Stamp] => Watched.Action]("Determines how to handle a file event").withRank(BMinusSetting)
|
val watchOnEvent = taskKey[Event[FileCacheEntry] => 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)
|
||||||
|
|
@ -457,7 +456,7 @@ 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]](
|
private[sbt] val globalFileTreeView = AttributeKey[FileTreeDataView[FileCacheEntry]](
|
||||||
"globalFileTreeView",
|
"globalFileTreeView",
|
||||||
"Provides a view into the file system that may or may not cache the tree in memory",
|
"Provides a view into the file system that may or may not cache the tree in memory",
|
||||||
1000
|
1000
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ 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
|
||||||
|
|
@ -863,7 +862,7 @@ object BuiltinCommands {
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
val (_, config: FileTreeViewConfig) = extracted.runTask(Keys.fileTreeViewConfig, s)
|
val (_, config: FileTreeViewConfig) = extracted.runTask(Keys.fileTreeViewConfig, s)
|
||||||
val view: FileTreeDataView[Stamp] = config.newDataView()
|
val view: FileTreeDataView[FileCacheEntry] = config.newDataView()
|
||||||
val newState = s.addExitHook(cleanup())
|
val newState = s.addExitHook(cleanup())
|
||||||
cleanup()
|
cleanup()
|
||||||
newState
|
newState
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,10 @@ 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[Stamp]): DefaultExternalHooks = {
|
def apply(
|
||||||
|
options: CompileOptions,
|
||||||
|
view: FileTreeDataView[FileCacheEntry]
|
||||||
|
): 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]
|
||||||
|
|
@ -30,7 +33,7 @@ private[sbt] object ExternalHooks {
|
||||||
case f: File => cachedSources.put(f, converter(f))
|
case f: File => cachedSources.put(f, converter(f))
|
||||||
}
|
}
|
||||||
view match {
|
view match {
|
||||||
case r: FileTreeRepository[Stamp] =>
|
case r: FileTreeRepository[FileCacheEntry] =>
|
||||||
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 +44,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)
|
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value.stamp)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +52,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)
|
case Right(value) => allBinaries.put(e.typedPath.toPath.toFile, value.stamp)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,12 @@ package sbt.internal
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
import sbt.BasicCommandStrings.ContinuousExecutePrefix
|
||||||
import sbt.Keys._
|
import sbt.Keys._
|
||||||
|
import sbt._
|
||||||
import sbt.io.FileTreeDataView.Entry
|
import sbt.io.FileTreeDataView.Entry
|
||||||
import sbt.io.syntax.File
|
import sbt.io.syntax.File
|
||||||
import sbt.io.{ FileFilter, FileTreeDataView, FileTreeRepository }
|
import sbt.io.{ FileFilter, FileTreeDataView, FileTreeRepository }
|
||||||
import sbt._
|
|
||||||
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 {
|
||||||
|
|
@ -52,7 +51,7 @@ 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[Stamp] => Boolean = (entry: Entry[Stamp]) => {
|
val sourceFilter: Entry[FileCacheEntry] => Boolean = (entry: Entry[FileCacheEntry]) => {
|
||||||
val typedPath = entry.typedPath
|
val typedPath = entry.typedPath
|
||||||
val file = new java.io.File(typedPath.toPath.toString) {
|
val file = new java.io.File(typedPath.toPath.toString) {
|
||||||
override def isDirectory: Boolean = typedPath.isDirectory
|
override def isDirectory: Boolean = typedPath.isDirectory
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue