Skip to content

Commit

Permalink
Merge pull request #455 from scalacenter/topic/non-existing-dir-file-…
Browse files Browse the repository at this point in the history
…watching

Filter out directories that don't exist
  • Loading branch information
jvican authored Apr 27, 2018
2 parents 80a1ec1 + 23ebfbc commit cc15fd0
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 12 deletions.
2 changes: 1 addition & 1 deletion frontend/src/main/scala/bloop/engine/Interpreter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ object Interpreter {
private[bloop] def watch(project: Project, state: State, f: State => Task[State]): Task[State] = {
val reachable = Dag.dfs(state.build.getDagFor(project))
val allSources = reachable.iterator.flatMap(_.sources.toList).map(_.underlying)
val watcher = new SourceWatcher(project, allSources.toList, state.logger)
val watcher = SourceWatcher(project, allSources.toList, state.logger)
val fg = (state: State) =>
f(state).map { state =>
watcher.notifyWatch()
Expand Down
30 changes: 20 additions & 10 deletions frontend/src/main/scala/bloop/io/SourceWatcher.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package bloop.io

import java.nio.file.Path
import java.nio.file.{Files, Path}

import bloop.Project
import bloop.bsp.BspServer
Expand All @@ -15,17 +15,15 @@ import monix.eval.Task
import monix.execution.Cancelable
import monix.reactive.{MulticastStrategy, Observable}

final class SourceWatcher(project: Project, paths0: Seq[Path], logger: Logger) {
final class SourceWatcher private (
project: Project,
dirs: Seq[Path],
files: Seq[Path],
logger: Logger
) {
import java.nio.file.Files
private val paths = paths0.distinct
private val dirs = paths.filter(p => Files.isDirectory(p))
private val dirsCount = dirs.size
private val filesCount = paths.filter(p => Files.isRegularFile(p)).size
private val slf4jLogger = new Slf4jAdapter(logger)

// Create source directories if they don't exist, otherwise the watcher fails.
dirs.foreach(p => if (!Files.exists(p)) Files.createDirectories(p) else ())

def watch(state0: State, action: State => Task[State]): Task[State] = {
val ngout = state0.commonOptions.ngout
def runAction(state: State, event: DirectoryChangeEvent): Task[State] = {
Expand All @@ -40,9 +38,10 @@ final class SourceWatcher(project: Project, paths0: Seq[Path], logger: Logger) {
Observable.multicast[DirectoryChangeEvent](MulticastStrategy.publish)(
ExecutionContext.ioScheduler)

val allPaths = (files ++ dirs).asJava
var watchingEnabled: Boolean = true
val watcher = DirectoryWatcher.create(
paths.asJava,
allPaths,
new DirectoryChangeListener {
// Define `isWatching` just for correctness
override def isWatching: Boolean = watchingEnabled
Expand Down Expand Up @@ -102,7 +101,18 @@ final class SourceWatcher(project: Project, paths0: Seq[Path], logger: Logger) {
}

def notifyWatch(): Unit = {
val filesCount = files.size
val dirsCount = dirs.size
val andFiles = if (filesCount == 0) "" else s" and $filesCount files"
logger.info(s"Watching $dirsCount directories$andFiles... (press C-c to interrupt)")
}
}

object SourceWatcher {
def apply(project: Project, paths0: Seq[Path], logger: Logger): SourceWatcher = {
val existingPaths = paths0.distinct.filter(p => Files.exists(p))
val dirs = existingPaths.filter(p => Files.isDirectory(p))
val files = existingPaths.filter(p => Files.isRegularFile(p))
new SourceWatcher(project, dirs, files, logger)
}
}
14 changes: 13 additions & 1 deletion frontend/src/test/scala/bloop/engine/FileWatchingSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ class FileWatchingSpec {
val `C.scala` = "package p2\nclass C extends p0.A with p1.B"
}

def addNonExistingSources(project: Project): Project = {
val currentSources = project.sources
currentSources.headOption match {
case Some(source) =>
val fakeSource = source.getParent.resolve("fake-source-dir-scala")
project.copy(sources = currentSources ++ Array(fakeSource))
case None => project
}
}

type FileWatchingContext = (State, Project, ByteArrayOutputStream)
def testFileWatcher(state0: State, projectName: String)(
workerAction: FileWatchingContext => Unit,
Expand All @@ -46,9 +56,11 @@ class FileWatchingSpec {
import scala.concurrent.duration
implicit val scheduler = ExecutionContext.scheduler
val projects0 = state0.build.projects
val rootProject = projects0
val rootProject0 = projects0
.find(_.name == projectName)
.getOrElse(sys.error(s"Project $projectName could not be found!"))
// Add non-existing sources on purpose to the project to ensure it doesn't crash
val rootProject = addNonExistingSources(rootProject0)
val cleanAction = Run(Commands.Clean(rootProject.name :: Nil), Exit(ExitStatus.Ok))
val state = TestUtil.blockingExecute(cleanAction, state0)
val projects = state.build.projects
Expand Down

0 comments on commit cc15fd0

Please sign in to comment.