Remove watch loops

When source generators write into the unmanaged source directory, bad
things can happen. Continuous builds will loop indefinitely and
compiling will fail because the generated sources get added to the
source list twice, causing the incremental compiler to complain about
compiling classes it has already seen. My two-pronged solution is to
de-duplicate the sources task and to filter out managed source files in
watch sources. The drawback to the latter is that it causes the source
generation task to be executed twice per compile.
This commit is contained in:
Ethan Atkins 2018-04-13 14:11:07 -07:00
parent bf8df381f5
commit 0de8345e33
6 changed files with 39 additions and 2 deletions

View File

@ -329,7 +329,18 @@ object Defaults extends BuildCommon {
val baseDir = baseDirectory.value
val bases = unmanagedSourceDirectories.value
val include = (includeFilter in unmanagedSources).value
val exclude = (excludeFilter in unmanagedSources).value
val exclude = (excludeFilter in unmanagedSources).value match {
case e =>
(managedSources in ThisScope).value match {
case l if l.nonEmpty =>
e || new FileFilter {
private val files = l.toSet
override def accept(pathname: File): Boolean = files.contains(pathname)
override def toString = s"ManagedSourcesFilter($files)"
}
case _ => e
}
}
val baseSources =
if (sourcesInBase.value) Seq(new Source(baseDir, include, exclude, recursive = false))
else Nil
@ -341,7 +352,7 @@ object Defaults extends BuildCommon {
sourceDirectories := Classpaths
.concatSettings(unmanagedSourceDirectories, managedSourceDirectories)
.value,
sources := Classpaths.concat(unmanagedSources, managedSources).value
sources := Classpaths.concatDistinct(unmanagedSources, managedSources).value
)
lazy val resourceConfigPaths = Seq(
resourceDirectory := sourceDirectory.value / "resources",

View File

@ -0,0 +1,17 @@
import java.nio.file.Files
lazy val watchLoopTest = taskKey[Unit]("Check that managed sources are filtered")
sourceGenerators in Compile += Def.task {
val path = baseDirectory.value.toPath.resolve("src/main/scala/Foo.scala")
Files.write(path, "object Foo".getBytes).toFile :: Nil
}
watchLoopTest := {
val watched = watchSources.value
val managedSource = (managedSources in Compile).value.head
assert(!SourceWrapper.accept(watched, managedSource))
assert((sources in Compile).value.foldLeft((true, Set.empty[File])) {
case ((res, set), f) => (res && !set.contains(f), set + f)
}._1)
}

View File

@ -0,0 +1,6 @@
package sbt
object SourceWrapper {
def accept(sources: Seq[sbt.internal.io.Source], file: File): Boolean =
sources.exists(_.accept(file.toPath))
}

View File

@ -0,0 +1 @@
object Bar

View File

@ -0,0 +1 @@
object Foo

View File

@ -0,0 +1 @@
> watchLoopTest