diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 1a2856a73..73057a5a9 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -17,6 +17,7 @@ import sbt.Project.LoadAction import sbt.compiler.EvalImports import sbt.internal.Aggregation.AnyKeys import sbt.internal.CommandStrings.BootCommand +import sbt.internal.FileManagement.CopiedFileTreeRepository import sbt.internal._ import sbt.internal.inc.ScalaInstance import sbt.internal.util.Types.{ const, idFun } @@ -847,15 +848,18 @@ object BuiltinCommands { } 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 = try { val extracted = Project.extract(s) val cleanedUp = new AtomicBoolean(false) def cleanup(): Unit = { - s.get(Keys.globalFileTreeRepository).foreach(_.close()) - s.attributes.remove(Keys.globalFileTreeRepository) + s.get(rawGlobalFileTreeRepository).foreach(_.close()) s.get(Keys.taskRepository).foreach(_.close()) - s.attributes.remove(Keys.taskRepository) () } cleanup() @@ -863,7 +867,8 @@ object BuiltinCommands { val newState = s.addExitHook(if (cleanedUp.compareAndSet(false, true)) cleanup()) newState .put(Keys.taskRepository, new TaskRepository.Repr) - .put(Keys.globalFileTreeRepository, fileTreeRepository) + .put(rawGlobalFileTreeRepository, fileTreeRepository) + .put(Keys.globalFileTreeRepository, new CopiedFileTreeRepository(fileTreeRepository)) } catch { case NonFatal(_) => s } diff --git a/main/src/main/scala/sbt/internal/Continuous.scala b/main/src/main/scala/sbt/internal/Continuous.scala index 613c3c3a7..d332bfb86 100644 --- a/main/src/main/scala/sbt/internal/Continuous.scala +++ b/main/src/main/scala/sbt/internal/Continuous.scala @@ -20,7 +20,6 @@ import sbt.BasicCommandStrings.{ import sbt.BasicCommands.otherCommandParser import sbt.Def._ import sbt.Scope.Global -import sbt.internal.FileManagement.FileTreeRepositoryOps import sbt.internal.LabeledFunctions._ import sbt.internal.io.WatchState import sbt.internal.util.complete.Parser._ @@ -191,7 +190,7 @@ object Continuous extends DeprecatedContinuous { new IllegalStateException("Tried to access FileTreeRepository for uninitialized state") state .get(Keys.globalFileTreeRepository) - .map(FileManagement.toMonitoringRepository(_).copy()) + .map(FileManagement.toMonitoringRepository) .getOrElse(throw exception) } diff --git a/main/src/main/scala/sbt/internal/FileManagement.scala b/main/src/main/scala/sbt/internal/FileManagement.scala index 77c2c0624..88c61ed95 100644 --- a/main/src/main/scala/sbt/internal/FileManagement.scala +++ b/main/src/main/scala/sbt/internal/FileManagement.scala @@ -13,9 +13,7 @@ import java.util.concurrent.ConcurrentHashMap import sbt.BasicCommandStrings.ContinuousExecutePrefix import sbt.internal.io.HybridPollingFileTreeRepository -import sbt.internal.util.Util import sbt.io.FileTreeDataView.{ Entry, Observable, Observer, Observers } -import sbt.io.Glob.TraversableGlobOps import sbt.io.{ FileTreeRepository, _ } import sbt.util.{ Level, Logger } @@ -55,11 +53,8 @@ private[sbt] object FileManagement { watchLogger ) } else { - if (Util.isWindows) new PollingFileRepository(FileAttributes.default) - else { - val service = Watched.createWatchService(pollInterval) - 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() } } - 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]( underlying: HybridPollingFileTreeRepository[T], delay: FiniteDuration, @@ -174,11 +120,10 @@ private[sbt] object FileManagement { private[sbt] def toMonitoringRepository[T]( repository: FileTreeRepository[T] ): FileTreeRepository[T] = repository match { - case p: PollingFileRepository[T] => p.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] { def addObserver(observer: Observer[T]) = underlying.addObserver(observer) 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 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) - } - } - } }