mirror of https://github.com/sbt/sbt.git
commit
7b61fcb44f
|
|
@ -9,10 +9,9 @@ package sbt
|
|||
|
||||
import java.io.File
|
||||
|
||||
import sbt.internal.util.AttributeKey
|
||||
import sbt.internal.inc.classpath.ClassLoaderCache
|
||||
import sbt.internal.server.ServerHandler
|
||||
import sbt.io.FileTreeDataView
|
||||
import sbt.internal.util.AttributeKey
|
||||
import sbt.librarymanagement.ModuleID
|
||||
import sbt.util.Level
|
||||
|
||||
|
|
@ -102,11 +101,6 @@ object BasicKeys {
|
|||
"List of template resolver infos.",
|
||||
1000
|
||||
)
|
||||
private[sbt] val globalFileTreeView = AttributeKey[FileTreeDataView[StampedFile]](
|
||||
"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)
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@
|
|||
|
||||
package sbt
|
||||
import sbt.Watched.WatchSource
|
||||
import sbt.internal.FileCacheEntry
|
||||
import sbt.internal.io.{ HybridPollingFileTreeRepository, WatchServiceBackedObservable, WatchState }
|
||||
import sbt.io.FileTreeDataView.{ Observable, Observer }
|
||||
import sbt.io._
|
||||
import FileTreeDataView.{ Observable, Observer }
|
||||
import sbt.util.Logger
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -18,15 +19,15 @@ import scala.concurrent.duration._
|
|||
* Configuration for viewing and monitoring the file system.
|
||||
*/
|
||||
final class FileTreeViewConfig private (
|
||||
val newDataView: () => FileTreeDataView[StampedFile],
|
||||
val newDataView: () => FileTreeDataView[FileCacheEntry],
|
||||
val newMonitor: (
|
||||
FileTreeDataView[StampedFile],
|
||||
FileTreeDataView[FileCacheEntry],
|
||||
Seq[WatchSource],
|
||||
Logger
|
||||
) => FileEventMonitor[StampedFile]
|
||||
) => FileEventMonitor[FileCacheEntry]
|
||||
)
|
||||
object FileTreeViewConfig {
|
||||
private implicit class RepositoryOps(val repository: FileTreeRepository[StampedFile]) {
|
||||
private implicit class RepositoryOps(val repository: FileTreeRepository[FileCacheEntry]) {
|
||||
def register(sources: Seq[WatchSource]): Unit = sources foreach { s =>
|
||||
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
|
||||
* by {{{sbt.io.FileTreeDataView[StampedFile]}}}. 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
|
||||
* passed into [[FileTreeViewConfig.newMonitor]] without constraining the type of view to be
|
||||
* {{{sbt.io.FileTreeDataView[StampedFile]}}}.
|
||||
* {{{sbt.io.FileTreeDataView[FileCacheEntry]}}}.
|
||||
* @param newDataView create a new sbt.io.FileTreeDataView. This value may be cached in a global
|
||||
* attribute
|
||||
* @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]]
|
||||
* @return a [[FileTreeViewConfig]] instance.
|
||||
*/
|
||||
def apply[T <: FileTreeDataView[StampedFile]](
|
||||
def apply[T <: FileTreeDataView[FileCacheEntry]](
|
||||
newDataView: () => T,
|
||||
newMonitor: (T, Seq[WatchSource], Logger) => FileEventMonitor[StampedFile]
|
||||
newMonitor: (T, Seq[WatchSource], Logger) => FileEventMonitor[FileCacheEntry]
|
||||
): FileTreeViewConfig =
|
||||
new FileTreeViewConfig(
|
||||
newDataView,
|
||||
(view: FileTreeDataView[StampedFile], sources: Seq[WatchSource], logger: Logger) =>
|
||||
(view: FileTreeDataView[FileCacheEntry], sources: Seq[WatchSource], logger: Logger) =>
|
||||
newMonitor(view.asInstanceOf[T], sources, logger)
|
||||
)
|
||||
|
||||
|
|
@ -70,14 +71,14 @@ object FileTreeViewConfig {
|
|||
antiEntropy: FiniteDuration
|
||||
): FileTreeViewConfig =
|
||||
FileTreeViewConfig(
|
||||
() => FileTreeView.DEFAULT.asDataView(StampedFile.converter),
|
||||
(_: FileTreeDataView[StampedFile], sources, logger) => {
|
||||
() => FileTreeView.DEFAULT.asDataView(FileCacheEntry.default),
|
||||
(_: FileTreeDataView[FileCacheEntry], sources, logger) => {
|
||||
val ioLogger: sbt.io.WatchLogger = msg => logger.debug(msg.toString)
|
||||
FileEventMonitor.antiEntropy(
|
||||
new WatchServiceBackedObservable(
|
||||
WatchState.empty(Watched.createWatchService(), sources),
|
||||
delay,
|
||||
StampedFile.converter,
|
||||
FileCacheEntry.default,
|
||||
closeService = true,
|
||||
ioLogger
|
||||
),
|
||||
|
|
@ -97,11 +98,15 @@ object FileTreeViewConfig {
|
|||
*/
|
||||
def default(antiEntropy: FiniteDuration): FileTreeViewConfig =
|
||||
FileTreeViewConfig(
|
||||
() => FileTreeRepository.default(StampedFile.converter),
|
||||
(repository: FileTreeRepository[StampedFile], sources: Seq[WatchSource], logger: Logger) => {
|
||||
() => FileTreeRepository.default(FileCacheEntry.default),
|
||||
(
|
||||
repository: FileTreeRepository[FileCacheEntry],
|
||||
sources: Seq[WatchSource],
|
||||
logger: Logger
|
||||
) => {
|
||||
repository.register(sources)
|
||||
val copied = new Observable[StampedFile] {
|
||||
override def addObserver(observer: Observer[StampedFile]): Int =
|
||||
val copied = new Observable[FileCacheEntry] {
|
||||
override def addObserver(observer: Observer[FileCacheEntry]): Int =
|
||||
repository.addObserver(observer)
|
||||
override def removeObserver(handle: Int): Unit = repository.removeObserver(handle)
|
||||
override def close(): Unit = {} // Don't close the underlying observable
|
||||
|
|
@ -154,9 +159,9 @@ object FileTreeViewConfig {
|
|||
pollingInterval: FiniteDuration,
|
||||
pollingSources: Seq[WatchSource],
|
||||
): FileTreeViewConfig = FileTreeViewConfig(
|
||||
() => FileTreeRepository.hybrid(StampedFile.converter, pollingSources: _*),
|
||||
() => FileTreeRepository.hybrid(FileCacheEntry.default, pollingSources: _*),
|
||||
(
|
||||
repository: HybridPollingFileTreeRepository[StampedFile],
|
||||
repository: HybridPollingFileTreeRepository[FileCacheEntry],
|
||||
sources: Seq[WatchSource],
|
||||
logger: Logger
|
||||
) => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt
|
||||
|
||||
import java.io.{ File => JFile }
|
||||
import java.nio.file.Path
|
||||
|
||||
import sbt.internal.FileCacheEntry
|
||||
import sbt.internal.inc.Stamper
|
||||
import sbt.io.TypedPath
|
||||
import xsbti.compile.analysis.Stamp
|
||||
|
||||
/**
|
||||
* A File that has a compile analysis Stamp value associated with it. In general, the stamp method
|
||||
* should be a cached value that can be read without doing any io. This can be used to improve
|
||||
* performance anywhere where we need to check if files have changed before doing potentially
|
||||
* expensive work.
|
||||
*/
|
||||
private[sbt] trait Stamped {
|
||||
private[sbt] def stamp: Stamp
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides converter functions from TypedPath to [[Stamped]].
|
||||
*/
|
||||
private[sbt] object Stamped {
|
||||
type File = JFile with Stamped with TypedPath
|
||||
def file(typedPath: TypedPath, entry: FileCacheEntry): JFile with Stamped with TypedPath =
|
||||
new StampedFileImpl(typedPath, entry.stamp)
|
||||
|
||||
/**
|
||||
* Converts a TypedPath instance to a [[Stamped]] by calculating the file hash.
|
||||
*/
|
||||
val sourceConverter: TypedPath => Stamp = tp => Stamper.forHash(tp.toPath.toFile)
|
||||
|
||||
/**
|
||||
* Converts a TypedPath instance to a [[Stamped]] using the last modified time.
|
||||
*/
|
||||
val binaryConverter: TypedPath => Stamp = tp => Stamper.forLastModified(tp.toPath.toFile)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
val converter: TypedPath => Stamp = (tp: TypedPath) =>
|
||||
if (tp.isDirectory) binaryConverter(tp)
|
||||
else {
|
||||
tp.toPath.toString match {
|
||||
case s if s.endsWith(".jar") => binaryConverter(tp)
|
||||
case s if s.endsWith(".class") => binaryConverter(tp)
|
||||
case _ => sourceConverter(tp)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a default ordering that just delegates to the java.io.File.compareTo method.
|
||||
*/
|
||||
implicit case object ordering extends Ordering[Stamped.File] {
|
||||
override def compare(left: Stamped.File, right: Stamped.File): Int = left.compareTo(right)
|
||||
}
|
||||
|
||||
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)
|
||||
with Stamped
|
||||
with TypedPath {
|
||||
override def exists: Boolean = typedPath.exists
|
||||
override def isDirectory: Boolean = typedPath.isDirectory
|
||||
override def isFile: Boolean = typedPath.isFile
|
||||
override def isSymbolicLink: Boolean = typedPath.isSymbolicLink
|
||||
override def toPath: Path = typedPath.toPath
|
||||
}
|
||||
}
|
||||
|
|
@ -18,16 +18,14 @@ import sbt.BasicCommandStrings.{
|
|||
}
|
||||
import sbt.BasicCommands.otherCommandParser
|
||||
import sbt.internal.LabeledFunctions._
|
||||
import sbt.internal.LegacyWatched
|
||||
import sbt.internal.inc.Stamper
|
||||
import sbt.internal.io.{ EventMonitor, Source, WatchState }
|
||||
import sbt.internal.util.Types.const
|
||||
import sbt.internal.util.complete.{ DefaultParsers, Parser }
|
||||
import sbt.internal.util.{ AttributeKey, JLine }
|
||||
import sbt.internal.{ FileCacheEntry, LegacyWatched }
|
||||
import sbt.io.FileEventMonitor.{ Creation, Deletion, Event, Update }
|
||||
import sbt.io._
|
||||
import sbt.util.{ Level, Logger }
|
||||
import xsbti.compile.analysis.Stamp
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -148,13 +146,13 @@ object Watched {
|
|||
private[sbt] def onEvent(
|
||||
sources: Seq[WatchSource],
|
||||
projectSources: Seq[WatchSource]
|
||||
): Event[StampedFile] => Watched.Action =
|
||||
): Event[FileCacheEntry] => Watched.Action =
|
||||
event =>
|
||||
if (sources.exists(_.accept(event.entry.typedPath.toPath))) Watched.Trigger
|
||||
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 _: Creation[_] | _: Deletion[_] => Reload
|
||||
case _ => Ignore
|
||||
case Update(prev, cur, _) if prev.value != cur.value => Reload
|
||||
case _: Creation[_] | _: Deletion[_] => Reload
|
||||
case _ => Ignore
|
||||
} else Ignore
|
||||
|
||||
private[this] val reRun = if (isWin) "" else " or 'r' to re-run the command"
|
||||
|
|
@ -460,7 +458,7 @@ trait WatchConfig {
|
|||
*
|
||||
* @return an sbt.io.FileEventMonitor instance.
|
||||
*/
|
||||
def fileEventMonitor: FileEventMonitor[StampedFile]
|
||||
def fileEventMonitor: FileEventMonitor[FileCacheEntry]
|
||||
|
||||
/**
|
||||
* A function that is periodically invoked to determine whether the watch should stop or
|
||||
|
|
@ -483,7 +481,7 @@ trait WatchConfig {
|
|||
* @param event the detected sbt.io.FileEventMonitor.Event.
|
||||
* @return the next [[Watched.Action Action]] to run.
|
||||
*/
|
||||
def onWatchEvent(event: Event[StampedFile]): Watched.Action
|
||||
def onWatchEvent(event: Event[FileCacheEntry]): Watched.Action
|
||||
|
||||
/**
|
||||
* Transforms the state after the watch terminates.
|
||||
|
|
@ -539,10 +537,10 @@ object WatchConfig {
|
|||
*/
|
||||
def default(
|
||||
logger: Logger,
|
||||
fileEventMonitor: FileEventMonitor[StampedFile],
|
||||
fileEventMonitor: FileEventMonitor[FileCacheEntry],
|
||||
handleInput: InputStream => Watched.Action,
|
||||
preWatch: (Int, Boolean) => Watched.Action,
|
||||
onWatchEvent: Event[StampedFile] => Watched.Action,
|
||||
onWatchEvent: Event[FileCacheEntry] => Watched.Action,
|
||||
onWatchTerminated: (Watched.Action, String, State) => State,
|
||||
triggeredMessage: (TypedPath, Int) => Option[String],
|
||||
watchingMessage: Int => Option[String]
|
||||
|
|
@ -557,11 +555,11 @@ object WatchConfig {
|
|||
val wm = watchingMessage
|
||||
new WatchConfig {
|
||||
override def logger: Logger = l
|
||||
override def fileEventMonitor: FileEventMonitor[StampedFile] = fem
|
||||
override def fileEventMonitor: FileEventMonitor[FileCacheEntry] = fem
|
||||
override def handleInput(inputStream: InputStream): Watched.Action = hi(inputStream)
|
||||
override def preWatch(count: Int, lastResult: Boolean): Watched.Action =
|
||||
pw(count, lastResult)
|
||||
override def onWatchEvent(event: Event[StampedFile]): 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 =
|
||||
owt(action, command, state)
|
||||
override def triggeredMessage(typedPath: TypedPath, count: Int): Option[String] =
|
||||
|
|
@ -570,28 +568,3 @@ object WatchConfig {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait StampedFile extends File {
|
||||
def stamp: Stamp
|
||||
}
|
||||
object StampedFile {
|
||||
val sourceConverter: TypedPath => StampedFile =
|
||||
new StampedFileImpl(_: TypedPath, forceLastModified = false)
|
||||
val binaryConverter: TypedPath => StampedFile =
|
||||
new StampedFileImpl(_: TypedPath, forceLastModified = true)
|
||||
val converter: TypedPath => StampedFile = (tp: TypedPath) =>
|
||||
tp.toPath.toString match {
|
||||
case s if s.endsWith(".jar") => binaryConverter(tp)
|
||||
case s if s.endsWith(".class") => binaryConverter(tp)
|
||||
case _ => sourceConverter(tp)
|
||||
}
|
||||
|
||||
private class StampedFileImpl(typedPath: TypedPath, forceLastModified: Boolean)
|
||||
extends java.io.File(typedPath.toPath.toString)
|
||||
with StampedFile {
|
||||
override val stamp: Stamp =
|
||||
if (forceLastModified || typedPath.isDirectory)
|
||||
Stamper.forLastModified(typedPath.toPath.toFile)
|
||||
else Stamper.forHash(typedPath.toPath.toFile)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,6 +14,7 @@ import java.util.concurrent.atomic.AtomicBoolean
|
|||
import org.scalatest.{ FlatSpec, Matchers }
|
||||
import sbt.Watched._
|
||||
import sbt.WatchedSpec._
|
||||
import sbt.internal.FileCacheEntry
|
||||
import sbt.io.FileEventMonitor.Event
|
||||
import sbt.io.{ FileEventMonitor, IO, TypedPath }
|
||||
import sbt.util.Logger
|
||||
|
|
@ -26,11 +27,11 @@ class WatchedSpec extends FlatSpec with Matchers {
|
|||
private val fileTreeViewConfig = FileTreeViewConfig.default(50.millis)
|
||||
def config(
|
||||
sources: Seq[WatchSource],
|
||||
fileEventMonitor: Option[FileEventMonitor[StampedFile]] = None,
|
||||
fileEventMonitor: Option[FileEventMonitor[FileCacheEntry]] = None,
|
||||
logger: Logger = NullLogger,
|
||||
handleInput: InputStream => Action = _ => Ignore,
|
||||
preWatch: (Int, Boolean) => Action = (_, _) => CancelWatch,
|
||||
onWatchEvent: Event[StampedFile] => Action = _ => Ignore,
|
||||
onWatchEvent: Event[FileCacheEntry] => Action = _ => Ignore,
|
||||
triggeredMessage: (TypedPath, Int) => Option[String] = (_, _) => None,
|
||||
watchingMessage: Int => Option[String] = _ => None
|
||||
): WatchConfig = {
|
||||
|
|
|
|||
|
|
@ -273,8 +273,8 @@ object Defaults extends BuildCommon {
|
|||
watchStartMessage := Watched.defaultStartWatch,
|
||||
fileTreeViewConfig := FileManagement.defaultFileTreeView.value,
|
||||
fileTreeView := state.value
|
||||
.get(BasicKeys.globalFileTreeView)
|
||||
.getOrElse(FileTreeView.DEFAULT.asDataView(StampedFile.converter)),
|
||||
.get(Keys.globalFileTreeView)
|
||||
.getOrElse(FileTreeView.DEFAULT.asDataView(FileCacheEntry.default)),
|
||||
externalHooks := {
|
||||
val view = fileTreeView.value
|
||||
compileOptions =>
|
||||
|
|
|
|||
|
|
@ -93,14 +93,14 @@ object Keys {
|
|||
@deprecated("This is no longer used for continuous execution", "1.3.0")
|
||||
val watch = SettingKey(BasicKeys.watch)
|
||||
val suppressSbtShellNotification = settingKey[Boolean]("""True to suppress the "Executing in batch mode.." message.""").withRank(CSetting)
|
||||
val fileTreeView = taskKey[FileTreeDataView[StampedFile]]("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 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 watchConfig = taskKey[WatchConfig]("The configuration for continuous execution.").withRank(BMinusSetting)
|
||||
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 watchOnEvent = taskKey[Event[StampedFile] => 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 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)
|
||||
|
|
@ -456,6 +456,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 state = Def.stateKey
|
||||
val streamsManager = Def.streamsManagerKey
|
||||
private[sbt] val globalFileTreeView = AttributeKey[FileTreeDataView[FileCacheEntry]](
|
||||
"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 resolvedScoped = Def.resolvedScoped
|
||||
|
|
|
|||
|
|
@ -855,18 +855,18 @@ object BuiltinCommands {
|
|||
val extracted = Project.extract(s)
|
||||
try {
|
||||
def cleanup(): Unit = {
|
||||
s.get(BasicKeys.globalFileTreeView).foreach(_.close())
|
||||
s.attributes.remove(BasicKeys.globalFileTreeView)
|
||||
s.get(Keys.globalFileTreeView).foreach(_.close())
|
||||
s.attributes.remove(Keys.globalFileTreeView)
|
||||
s.get(Keys.taskRepository).foreach(_.close())
|
||||
s.attributes.remove(Keys.taskRepository)
|
||||
()
|
||||
}
|
||||
val (_, config: FileTreeViewConfig) = extracted.runTask(Keys.fileTreeViewConfig, s)
|
||||
val view: FileTreeDataView[StampedFile] = config.newDataView()
|
||||
val view: FileTreeDataView[FileCacheEntry] = config.newDataView()
|
||||
val newState = s.addExitHook(cleanup())
|
||||
cleanup()
|
||||
newState
|
||||
.put(BasicKeys.globalFileTreeView, view)
|
||||
.put(Keys.globalFileTreeView, view)
|
||||
.put(Keys.taskRepository, new TaskRepository.Repr)
|
||||
} catch {
|
||||
case NonFatal(_) => s
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ package sbt.internal
|
|||
import java.nio.file.Paths
|
||||
import java.util.Optional
|
||||
|
||||
import sbt.StampedFile
|
||||
import sbt.Stamped
|
||||
import sbt.internal.inc.ExternalLookup
|
||||
import sbt.io.syntax.File
|
||||
import sbt.io.{ FileTreeRepository, FileTreeDataView, TypedPath }
|
||||
|
|
@ -20,17 +20,20 @@ import scala.collection.mutable
|
|||
|
||||
private[sbt] object ExternalHooks {
|
||||
private val javaHome = Option(System.getProperty("java.home")).map(Paths.get(_))
|
||||
def apply(options: CompileOptions, view: FileTreeDataView[StampedFile]): DefaultExternalHooks = {
|
||||
def apply(
|
||||
options: CompileOptions,
|
||||
view: FileTreeDataView[FileCacheEntry]
|
||||
): DefaultExternalHooks = {
|
||||
import scala.collection.JavaConverters._
|
||||
val sources = options.sources()
|
||||
val cachedSources = new java.util.HashMap[File, Stamp]
|
||||
val converter: File => Stamp = f => StampedFile.sourceConverter(TypedPath(f.toPath)).stamp
|
||||
val converter: File => Stamp = f => Stamped.sourceConverter(TypedPath(f.toPath))
|
||||
sources.foreach {
|
||||
case sf: StampedFile => cachedSources.put(sf, sf.stamp)
|
||||
case f: File => cachedSources.put(f, converter(f))
|
||||
case sf: Stamped => cachedSources.put(sf, sf.stamp)
|
||||
case f: File => cachedSources.put(f, converter(f))
|
||||
}
|
||||
view match {
|
||||
case r: FileTreeRepository[StampedFile] =>
|
||||
case r: FileTreeRepository[FileCacheEntry] =>
|
||||
r.register(options.classesDirectory.toPath, Integer.MAX_VALUE)
|
||||
options.classpath.foreach { f =>
|
||||
r.register(f.toPath, Integer.MAX_VALUE)
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ package sbt.internal
|
|||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
import sbt.BasicCommandStrings.ContinuousExecutePrefix
|
||||
import sbt.Keys._
|
||||
import sbt._
|
||||
import sbt.io.FileTreeDataView.Entry
|
||||
import sbt.io.syntax.File
|
||||
import sbt.io.{ FileFilter, FileTreeDataView, FileTreeRepository }
|
||||
import sbt._
|
||||
import BasicCommandStrings.ContinuousExecutePrefix
|
||||
|
||||
private[sbt] object FileManagement {
|
||||
private[sbt] def defaultFileTreeView: Def.Initialize[Task[FileTreeViewConfig]] = Def.task {
|
||||
|
|
@ -51,17 +51,19 @@ private[sbt] object FileManagement {
|
|||
val view = fileTreeView.value
|
||||
val include = filter.toTask.value
|
||||
val ex = excludes.toTask.value
|
||||
val sourceFilter: Entry[StampedFile] => Boolean = (entry: Entry[StampedFile]) => {
|
||||
entry.value match {
|
||||
case Right(sf) => include.accept(sf) && !ex.accept(sf)
|
||||
case _ => false
|
||||
val sourceFilter: Entry[FileCacheEntry] => Boolean = (entry: Entry[FileCacheEntry]) => {
|
||||
val typedPath = entry.typedPath
|
||||
val file = new java.io.File(typedPath.toPath.toString) {
|
||||
override def isDirectory: Boolean = typedPath.isDirectory
|
||||
override def isFile: Boolean = typedPath.isFile
|
||||
}
|
||||
include.accept(file) && !ex.accept(file)
|
||||
}
|
||||
sourceDirs.flatMap { dir =>
|
||||
view.register(dir.toPath, maxDepth = Integer.MAX_VALUE)
|
||||
view
|
||||
.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 +95,7 @@ private[sbt] object FileManagement {
|
|||
f.accept(file) && !excl.accept(file)
|
||||
}
|
||||
)
|
||||
.flatMap(_.value.toOption)
|
||||
.flatMap(e => e.value.toOption.map(Stamped.file(e.typedPath, _)))
|
||||
} else sources
|
||||
}
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue