mirror of https://github.com/sbt/sbt.git
Clean up file repository management
I had needed to add proxy classes for the global FileTreeRepository so that tasks that called the close method wouldn't actually stop the monitoring done by the global repository. I realized that it makes a lot more sense to just not provide direct access to the underlying file tree repository and let the registerGlobalCaches manage its life cycle instead.
This commit is contained in:
parent
9cdeb7120e
commit
7c2607b1ae
|
|
@ -17,6 +17,7 @@ import sbt.Project.LoadAction
|
||||||
import sbt.compiler.EvalImports
|
import sbt.compiler.EvalImports
|
||||||
import sbt.internal.Aggregation.AnyKeys
|
import sbt.internal.Aggregation.AnyKeys
|
||||||
import sbt.internal.CommandStrings.BootCommand
|
import sbt.internal.CommandStrings.BootCommand
|
||||||
|
import sbt.internal.FileManagement.CopiedFileTreeRepository
|
||||||
import sbt.internal._
|
import sbt.internal._
|
||||||
import sbt.internal.inc.ScalaInstance
|
import sbt.internal.inc.ScalaInstance
|
||||||
import sbt.internal.util.Types.{ const, idFun }
|
import sbt.internal.util.Types.{ const, idFun }
|
||||||
|
|
@ -847,15 +848,18 @@ object BuiltinCommands {
|
||||||
}
|
}
|
||||||
s.put(Keys.stateCompilerCache, cache)
|
s.put(Keys.stateCompilerCache, cache)
|
||||||
}
|
}
|
||||||
|
private[this] val rawGlobalFileTreeRepository = AttributeKey[FileTreeRepository[FileAttributes]](
|
||||||
|
"raw-global-file-tree-repository",
|
||||||
|
"Provides a view into the file system that may or may not cache the tree in memory",
|
||||||
|
1000
|
||||||
|
)
|
||||||
private[sbt] def registerGlobalCaches(s: State): State =
|
private[sbt] def registerGlobalCaches(s: State): State =
|
||||||
try {
|
try {
|
||||||
val extracted = Project.extract(s)
|
val extracted = Project.extract(s)
|
||||||
val cleanedUp = new AtomicBoolean(false)
|
val cleanedUp = new AtomicBoolean(false)
|
||||||
def cleanup(): Unit = {
|
def cleanup(): Unit = {
|
||||||
s.get(Keys.globalFileTreeRepository).foreach(_.close())
|
s.get(rawGlobalFileTreeRepository).foreach(_.close())
|
||||||
s.attributes.remove(Keys.globalFileTreeRepository)
|
|
||||||
s.get(Keys.taskRepository).foreach(_.close())
|
s.get(Keys.taskRepository).foreach(_.close())
|
||||||
s.attributes.remove(Keys.taskRepository)
|
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|
@ -863,7 +867,8 @@ object BuiltinCommands {
|
||||||
val newState = s.addExitHook(if (cleanedUp.compareAndSet(false, true)) cleanup())
|
val newState = s.addExitHook(if (cleanedUp.compareAndSet(false, true)) cleanup())
|
||||||
newState
|
newState
|
||||||
.put(Keys.taskRepository, new TaskRepository.Repr)
|
.put(Keys.taskRepository, new TaskRepository.Repr)
|
||||||
.put(Keys.globalFileTreeRepository, fileTreeRepository)
|
.put(rawGlobalFileTreeRepository, fileTreeRepository)
|
||||||
|
.put(Keys.globalFileTreeRepository, new CopiedFileTreeRepository(fileTreeRepository))
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(_) => s
|
case NonFatal(_) => s
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import sbt.BasicCommandStrings.{
|
||||||
import sbt.BasicCommands.otherCommandParser
|
import sbt.BasicCommands.otherCommandParser
|
||||||
import sbt.Def._
|
import sbt.Def._
|
||||||
import sbt.Scope.Global
|
import sbt.Scope.Global
|
||||||
import sbt.internal.FileManagement.FileTreeRepositoryOps
|
|
||||||
import sbt.internal.LabeledFunctions._
|
import sbt.internal.LabeledFunctions._
|
||||||
import sbt.internal.io.WatchState
|
import sbt.internal.io.WatchState
|
||||||
import sbt.internal.util.complete.Parser._
|
import sbt.internal.util.complete.Parser._
|
||||||
|
|
@ -191,7 +190,7 @@ object Continuous extends DeprecatedContinuous {
|
||||||
new IllegalStateException("Tried to access FileTreeRepository for uninitialized state")
|
new IllegalStateException("Tried to access FileTreeRepository for uninitialized state")
|
||||||
state
|
state
|
||||||
.get(Keys.globalFileTreeRepository)
|
.get(Keys.globalFileTreeRepository)
|
||||||
.map(FileManagement.toMonitoringRepository(_).copy())
|
.map(FileManagement.toMonitoringRepository)
|
||||||
.getOrElse(throw exception)
|
.getOrElse(throw exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,7 @@ import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
import sbt.BasicCommandStrings.ContinuousExecutePrefix
|
import sbt.BasicCommandStrings.ContinuousExecutePrefix
|
||||||
import sbt.internal.io.HybridPollingFileTreeRepository
|
import sbt.internal.io.HybridPollingFileTreeRepository
|
||||||
import sbt.internal.util.Util
|
|
||||||
import sbt.io.FileTreeDataView.{ Entry, Observable, Observer, Observers }
|
import sbt.io.FileTreeDataView.{ Entry, Observable, Observer, Observers }
|
||||||
import sbt.io.Glob.TraversableGlobOps
|
|
||||||
import sbt.io.{ FileTreeRepository, _ }
|
import sbt.io.{ FileTreeRepository, _ }
|
||||||
import sbt.util.{ Level, Logger }
|
import sbt.util.{ Level, Logger }
|
||||||
|
|
||||||
|
|
@ -55,11 +53,8 @@ private[sbt] object FileManagement {
|
||||||
watchLogger
|
watchLogger
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if (Util.isWindows) new PollingFileRepository(FileAttributes.default)
|
val service = Watched.createWatchService(pollInterval)
|
||||||
else {
|
FileTreeRepository.legacy(FileAttributes.default _, (_: Any) => {}, service)
|
||||||
val service = Watched.createWatchService(pollInterval)
|
|
||||||
FileTreeRepository.legacy(FileAttributes.default _, (_: Any) => {}, service)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,55 +95,6 @@ private[sbt] object FileManagement {
|
||||||
override def close(): Unit = monitor.close()
|
override def close(): Unit = monitor.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private[sbt] implicit class FileTreeRepositoryOps[T](val repo: FileTreeRepository[T])
|
|
||||||
extends AnyVal {
|
|
||||||
def copy(): FileTreeRepository[T] =
|
|
||||||
copy(ConcurrentHashMap.newKeySet[Glob].asScala, closeUnderlying = false)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a copied FileTreeRepository that keeps track of all of the globs that are explicitly
|
|
||||||
* registered with it.
|
|
||||||
*
|
|
||||||
* @param registered the registered globs
|
|
||||||
* @param closeUnderlying toggles whether or not close should actually close the delegate
|
|
||||||
* repository
|
|
||||||
*
|
|
||||||
* @return the copied FileTreeRepository
|
|
||||||
*/
|
|
||||||
def copy(registered: mutable.Set[Glob], closeUnderlying: Boolean): FileTreeRepository[T] =
|
|
||||||
new FileTreeRepository[T] {
|
|
||||||
private val entryFilter: FileTreeDataView.Entry[T] => Boolean =
|
|
||||||
(entry: FileTreeDataView.Entry[T]) => registered.toEntryFilter(entry)
|
|
||||||
private[this] val observers = new Observers[T] {
|
|
||||||
override def onCreate(newEntry: FileTreeDataView.Entry[T]): Unit =
|
|
||||||
if (entryFilter(newEntry)) super.onCreate(newEntry)
|
|
||||||
override def onDelete(oldEntry: FileTreeDataView.Entry[T]): Unit =
|
|
||||||
if (entryFilter(oldEntry)) super.onDelete(oldEntry)
|
|
||||||
override def onUpdate(
|
|
||||||
oldEntry: FileTreeDataView.Entry[T],
|
|
||||||
newEntry: FileTreeDataView.Entry[T]
|
|
||||||
): Unit = if (entryFilter(newEntry)) super.onUpdate(oldEntry, newEntry)
|
|
||||||
}
|
|
||||||
private[this] val handle = repo.addObserver(observers)
|
|
||||||
override def register(glob: Glob): Either[IOException, Boolean] = {
|
|
||||||
registered.add(glob)
|
|
||||||
repo.register(glob)
|
|
||||||
}
|
|
||||||
override def unregister(glob: Glob): Unit = repo.unregister(glob)
|
|
||||||
override def addObserver(observer: FileTreeDataView.Observer[T]): Int =
|
|
||||||
observers.addObserver(observer)
|
|
||||||
override def removeObserver(handle: Int): Unit = observers.removeObserver(handle)
|
|
||||||
override def close(): Unit = {
|
|
||||||
repo.removeObserver(handle)
|
|
||||||
if (closeUnderlying) repo.close()
|
|
||||||
}
|
|
||||||
override def toString: String = s"CopiedFileTreeRepository(base = $repo)"
|
|
||||||
override def list(glob: Glob): Seq[TypedPath] = repo.list(glob)
|
|
||||||
override def listEntries(glob: Glob): Seq[FileTreeDataView.Entry[T]] =
|
|
||||||
repo.listEntries(glob)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private[sbt] class HybridMonitoringRepository[T](
|
private[sbt] class HybridMonitoringRepository[T](
|
||||||
underlying: HybridPollingFileTreeRepository[T],
|
underlying: HybridPollingFileTreeRepository[T],
|
||||||
delay: FiniteDuration,
|
delay: FiniteDuration,
|
||||||
|
|
@ -174,11 +120,10 @@ private[sbt] object FileManagement {
|
||||||
private[sbt] def toMonitoringRepository[T](
|
private[sbt] def toMonitoringRepository[T](
|
||||||
repository: FileTreeRepository[T]
|
repository: FileTreeRepository[T]
|
||||||
): FileTreeRepository[T] = repository match {
|
): FileTreeRepository[T] = repository match {
|
||||||
case p: PollingFileRepository[T] => p.toMonitoringRepository
|
|
||||||
case h: HybridMonitoringRepository[T] => h.toMonitoringRepository
|
case h: HybridMonitoringRepository[T] => h.toMonitoringRepository
|
||||||
case r: FileTreeRepository[T] => new CopiedFileRepository(r)
|
case r: FileTreeRepository[T] => r
|
||||||
}
|
}
|
||||||
private class CopiedFileRepository[T](underlying: FileTreeRepository[T])
|
private[sbt] class CopiedFileTreeRepository[T](underlying: FileTreeRepository[T])
|
||||||
extends FileTreeRepository[T] {
|
extends FileTreeRepository[T] {
|
||||||
def addObserver(observer: Observer[T]) = underlying.addObserver(observer)
|
def addObserver(observer: Observer[T]) = underlying.addObserver(observer)
|
||||||
def close(): Unit = {} // Don't close the underlying observable
|
def close(): Unit = {} // Don't close the underlying observable
|
||||||
|
|
@ -188,41 +133,4 @@ private[sbt] object FileManagement {
|
||||||
def register(glob: Glob): Either[IOException, Boolean] = underlying.register(glob)
|
def register(glob: Glob): Either[IOException, Boolean] = underlying.register(glob)
|
||||||
def unregister(glob: Glob): Unit = underlying.unregister(glob)
|
def unregister(glob: Glob): Unit = underlying.unregister(glob)
|
||||||
}
|
}
|
||||||
private[sbt] class PollingFileRepository[T](converter: TypedPath => T)
|
|
||||||
extends FileTreeRepository[T] { self =>
|
|
||||||
private val registered: mutable.Set[Glob] = ConcurrentHashMap.newKeySet[Glob].asScala
|
|
||||||
private[this] val view = FileTreeView.DEFAULT
|
|
||||||
private[this] val dataView = view.asDataView(converter)
|
|
||||||
private[this] val handles: mutable.Map[FileTreeRepository[T], Int] =
|
|
||||||
new ConcurrentHashMap[FileTreeRepository[T], Int].asScala
|
|
||||||
private val observers: Observers[T] = new Observers
|
|
||||||
override def addObserver(observer: Observer[T]): Int = observers.addObserver(observer)
|
|
||||||
override def close(): Unit = {
|
|
||||||
handles.foreach { case (repo, handle) => repo.removeObserver(handle) }
|
|
||||||
observers.close()
|
|
||||||
}
|
|
||||||
override def list(glob: Glob): Seq[TypedPath] = view.list(glob)
|
|
||||||
override def listEntries(glob: Glob): Seq[Entry[T]] = dataView.listEntries(glob)
|
|
||||||
override def removeObserver(handle: Int): Unit = observers.removeObserver(handle)
|
|
||||||
override def register(glob: Glob): Either[IOException, Boolean] = Right(registered.add(glob))
|
|
||||||
override def unregister(glob: Glob): Unit = registered -= glob
|
|
||||||
|
|
||||||
private[sbt] def toMonitoringRepository: FileTreeRepository[T] = {
|
|
||||||
val legacy = FileTreeRepository.legacy(converter)
|
|
||||||
registered.foreach(legacy.register)
|
|
||||||
handles += legacy -> legacy.addObserver(observers)
|
|
||||||
new FileTreeRepository[T] {
|
|
||||||
override def listEntries(glob: Glob): Seq[Entry[T]] = legacy.listEntries(glob)
|
|
||||||
override def list(glob: Glob): Seq[TypedPath] = legacy.list(glob)
|
|
||||||
def addObserver(observer: Observer[T]): Int = legacy.addObserver(observer)
|
|
||||||
override def removeObserver(handle: Int): Unit = legacy.removeObserver(handle)
|
|
||||||
override def close(): Unit = legacy.close()
|
|
||||||
override def register(glob: Glob): Either[IOException, Boolean] = {
|
|
||||||
self.register(glob)
|
|
||||||
legacy.register(glob)
|
|
||||||
}
|
|
||||||
override def unregister(glob: Glob): Unit = legacy.unregister(glob)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue