diff --git a/main-actions/src/main/scala/sbt/Package.scala b/main-actions/src/main/scala/sbt/Package.scala index e0c29e95c..9ce8735b4 100644 --- a/main-actions/src/main/scala/sbt/Package.scala +++ b/main-actions/src/main/scala/sbt/Package.scala @@ -22,7 +22,7 @@ import sbt.internal.util.HNil import sbt.internal.util.HListFormats._ import sbt.util.FileInfo.{ exists, lastModified } import sbt.util.CacheImplicits._ -import sbt.util.Tracked.inputChanged +import sbt.util.Tracked.{ inputChanged, outputChanged } sealed trait PackageOption object Package { @@ -72,17 +72,18 @@ object Package { (inChanged, inputs: Inputs) => import exists.format val sources :+: _ :+: manifest :+: HNil = inputs - inputChanged(cacheStoreFactory make "output") { (outChanged, jar: PlainFileInfo) => - if (inChanged || outChanged) + outputChanged(cacheStoreFactory make "output") { (outChanged, jar: PlainFileInfo) => + if (inChanged || outChanged) { makeJar(sources.toSeq, jar.file, manifest, log) - else + jar.file + } else log.debug("Jar uptodate: " + jar.file) } } val map = conf.sources.toMap val inputs = map :+: lastModified(map.keySet) :+: manifest :+: HNil - cachedMakeJar(inputs)(exists(conf.jar)) + cachedMakeJar(inputs)(() => exists(conf.jar)) } def setVersion(main: Attributes): Unit = { val version = Attributes.Name.MANIFEST_VERSION diff --git a/main-command/src/main/scala/sbt/BasicCommandStrings.scala b/main-command/src/main/scala/sbt/BasicCommandStrings.scala index b819f77a5..482612f6f 100644 --- a/main-command/src/main/scala/sbt/BasicCommandStrings.scala +++ b/main-command/src/main/scala/sbt/BasicCommandStrings.scala @@ -69,7 +69,7 @@ $HelpCommand This will be used as the default level for logging from commands, settings, and tasks. Any explicit `logLevel` configuration in a project overrides this setting. --$level +-$level OR --$level Sets the global logging level as described above, but does so before any other commands are executed on startup, including project loading. This is useful as a startup option: @@ -79,7 +79,9 @@ $HelpCommand def runEarly(command: String) = s"$EarlyCommand($command)" private[sbt] def isEarlyCommand(s: String): Boolean = { - val levelOptions = Level.values.toSeq map { "-" + _ } + val levelOptions = Level.values.toSeq flatMap { elem => + List("-" + elem, "--" + elem) + } (s.startsWith(EarlyCommand + "(") && s.endsWith(")")) || (levelOptions contains s) } diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index 439821a6d..eee6311ac 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -69,7 +69,8 @@ object BasicCommands { private[this] def earlyParser: State => Parser[String] = (s: State) => { val p1 = token(EarlyCommand + "(") flatMap (_ => otherCommandParser(s) <~ token(")")) val p2 = token("-") flatMap (_ => levelParser) - p1 | p2 + val p3 = token("--") flatMap (_ => levelParser) + p1 | p2 | p3 } private[this] def earlyHelp = Help(EarlyCommand, EarlyCommandBrief, EarlyCommandDetailed) diff --git a/main-command/src/main/scala/sbt/Watched.scala b/main-command/src/main/scala/sbt/Watched.scala index eed2b60a0..053e15082 100644 --- a/main-command/src/main/scala/sbt/Watched.scala +++ b/main-command/src/main/scala/sbt/Watched.scala @@ -12,7 +12,7 @@ import java.nio.file.FileSystems import sbt.BasicCommandStrings.ClearOnFailure import sbt.State.FailureWall -import sbt.internal.io.{ Source, SourceModificationWatch, WatchState } +import sbt.internal.io.{ EventMonitor, Source, WatchState } import sbt.internal.util.AttributeKey import sbt.internal.util.Types.const import sbt.io._ @@ -33,6 +33,12 @@ trait Watched { */ def pollInterval: FiniteDuration = Watched.PollDelay + /** + * The duration for which the EventMonitor while ignore file events after a file triggers + * a new build. + */ + def antiEntropy: FiniteDuration = Watched.AntiEntropy + /** The message to show when triggered execution waits for sources to change.*/ private[sbt] def watchingMessage(s: WatchState): String = Watched.defaultWatchingMessage(s) @@ -86,53 +92,70 @@ object Watched { override def watchSources(s: State) = (base.watchSources(s) /: paths)(_ ++ _.watchSources(s)) override def terminateWatch(key: Int): Boolean = base.terminateWatch(key) override val pollInterval = (base +: paths).map(_.pollInterval).min + override val antiEntropy = (base +: paths).map(_.antiEntropy).min override def watchingMessage(s: WatchState) = base.watchingMessage(s) override def triggeredMessage(s: WatchState) = base.triggeredMessage(s) } def empty: Watched = new AWatched val PollDelay: FiniteDuration = 500.milliseconds + val AntiEntropy: FiniteDuration = 40.milliseconds def isEnter(key: Int): Boolean = key == 10 || key == 13 def printIfDefined(msg: String) = if (!msg.isEmpty) System.out.println(msg) def executeContinuously(watched: Watched, s: State, next: String, repeat: String): State = { @tailrec def shouldTerminate: Boolean = (System.in.available > 0) && (watched.terminateWatch(System.in.read()) || shouldTerminate) - val sources = watched.watchSources(s) - val service = s get ContinuousWatchService getOrElse watched.watchService() - val watchState = s get ContinuousState getOrElse WatchState.empty(service, sources) - - if (watchState.count > 0) - printIfDefined(watched watchingMessage watchState) - - val (triggered, newWatchState) = - try { - val (triggered, newWatchState) = - SourceModificationWatch.watch(watched.pollInterval, watchState)(shouldTerminate) - (triggered, newWatchState) - } catch { - case e: Exception => - s.log.error( - "Error occurred obtaining files to watch. Terminating continuous execution..." + val log = s.log + val logger = new EventMonitor.Logger { + override def debug(msg: => Any): Unit = log.debug(msg.toString) + } + s get ContinuousEventMonitor match { + case None => + // This is the first iteration, so run the task and create a new EventMonitor + (ClearOnFailure :: next :: FailureWall :: repeat :: s) + .put( + ContinuousEventMonitor, + EventMonitor( + WatchState.empty(watched.watchService(), watched.watchSources(s)), + watched.pollInterval, + watched.antiEntropy, + shouldTerminate, + logger + ) ) - s.handleError(e) - (false, watchState) - } - - if (triggered) { - printIfDefined(watched triggeredMessage newWatchState) - (ClearOnFailure :: next :: FailureWall :: repeat :: s) - .put(ContinuousState, newWatchState) - .put(ContinuousWatchService, service) - } else { - while (System.in.available() > 0) System.in.read() - service.close() - s.remove(ContinuousState).remove(ContinuousWatchService) + case Some(eventMonitor) => + printIfDefined(watched watchingMessage eventMonitor.state) + val triggered = try eventMonitor.awaitEvent() + catch { + case e: Exception => + log.error( + "Error occurred obtaining files to watch. Terminating continuous execution..." + ) + s.handleError(e) + false + } + if (triggered) { + printIfDefined(watched triggeredMessage eventMonitor.state) + ClearOnFailure :: next :: FailureWall :: repeat :: s + } else { + while (System.in.available() > 0) System.in.read() + eventMonitor.close() + s.remove(ContinuousEventMonitor) + } } } + + val ContinuousEventMonitor = + AttributeKey[EventMonitor]( + "watch event monitor", + "Internal: maintains watch state and monitor threads." + ) + @deprecated("Superseded by ContinuousEventMonitor", "1.1.5") val ContinuousState = AttributeKey[WatchState]("watch state", "Internal: tracks state for continuous execution.") + @deprecated("Superseded by ContinuousEventMonitor", "1.1.5") val ContinuousWatchService = AttributeKey[WatchService]( "watch service", diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index f3e550205..d8c072cae 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -259,6 +259,7 @@ object Defaults extends BuildCommon { concurrentRestrictions := defaultRestrictions.value, parallelExecution :== true, pollInterval :== new FiniteDuration(500, TimeUnit.MILLISECONDS), + watchAntiEntropy :== new FiniteDuration(40, TimeUnit.MILLISECONDS), watchService :== { () => Watched.createWatchService() }, @@ -352,7 +353,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 @@ -364,7 +376,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", @@ -593,13 +605,15 @@ object Defaults extends BuildCommon { Def.setting { val getService = watchService.value val interval = pollInterval.value + val _antiEntropy = watchAntiEntropy.value val base = thisProjectRef.value val msg = watchingMessage.value val trigMsg = triggeredMessage.value new Watched { val scoped = watchTransitiveSources in base val key = scoped.scopedKey - override def pollInterval = interval + override def antiEntropy: FiniteDuration = _antiEntropy + override def pollInterval: FiniteDuration = interval override def watchingMessage(s: WatchState) = msg(s) override def triggeredMessage(s: WatchState) = trigMsg(s) override def watchService() = getService() @@ -3052,10 +3066,21 @@ object Classpaths { excl: FileFilter ): Classpath = (base * (filter -- excl) +++ (base / config.name).descendantsExcept(filter, excl)).classpath + @deprecated( + "The method only works for Scala 2, use the overloaded version to support both Scala 2 and Scala 3", + "1.1.5" + ) + def autoPlugins(report: UpdateReport, internalPluginClasspath: Seq[File]): Seq[String] = + autoPlugins(report, internalPluginClasspath, isDotty = false) - def autoPlugins(report: UpdateReport, internalPluginClasspath: Seq[File]): Seq[String] = { + def autoPlugins( + report: UpdateReport, + internalPluginClasspath: Seq[File], + isDotty: Boolean + ): Seq[String] = { val pluginClasspath = report.matching(configurationFilter(CompilerPlugin.name)) ++ internalPluginClasspath - val plugins = sbt.internal.inc.classpath.ClasspathUtilities.compilerPlugins(pluginClasspath) + val plugins = + sbt.internal.inc.classpath.ClasspathUtilities.compilerPlugins(pluginClasspath, isDotty) plugins.map("-Xplugin:" + _.getAbsolutePath).toSeq } @@ -3077,7 +3102,11 @@ object Classpaths { lazy val compilerPluginConfig = Seq( scalacOptions := { val options = scalacOptions.value - val newPlugins = autoPlugins(update.value, internalCompilerPluginClasspath.value.files) + val newPlugins = autoPlugins( + update.value, + internalCompilerPluginClasspath.value.files, + ScalaInstance.isDotty(scalaVersion.value) + ) val existing = options.toSet if (autoCompilerPlugins.value) options ++ newPlugins.filterNot(existing) else options } diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index bf8c9549f..45d9c2d96 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -143,6 +143,7 @@ object Keys { val analysis = AttributeKey[CompileAnalysis]("analysis", "Analysis of compilation, including dependencies and generated outputs.", DSetting) val watch = SettingKey(BasicKeys.watch) val suppressSbtShellNotification = settingKey[Boolean]("""True to suppress the "Executing in batch mode.." message.""").withRank(CSetting) + 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 pollInterval = settingKey[FiniteDuration]("Interval between checks for modified sources by the continuous execution command.").withRank(BMinusSetting) val watchService = settingKey[() => WatchService]("Service to use to monitor file system changes.").withRank(BMinusSetting) val watchSources = taskKey[Seq[Watched.WatchSource]]("Defines the sources in this project for continuous execution to watch for changes.").withRank(BMinusSetting) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index d6185d41e..86a02db6b 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -328,8 +328,14 @@ object BuiltinCommands { 'v'.id.+.map(_.size + 1) | ("V" ^^^ Int.MaxValue) )) + + def taskDetail(keys: Seq[AttributeKey[_]], firstOnly: Boolean): Seq[(String, String)] = + sortByLabel(withDescription(keys)) flatMap { t => + taskStrings(t, firstOnly) + } + def taskDetail(keys: Seq[AttributeKey[_]]): Seq[(String, String)] = - sortByLabel(withDescription(keys)) flatMap taskStrings + taskDetail(keys, false) def allTaskAndSettingKeys(s: State): Seq[AttributeKey[_]] = { val extracted = Project.extract(s) @@ -367,16 +373,19 @@ object BuiltinCommands { filter: Seq[AttributeKey[_]] => Seq[AttributeKey[_]], arg: Option[String] ): String = { - val commandAndDescription = taskDetail(filter(allTaskAndSettingKeys(s))) + val commandAndDescription = taskDetail(filter(allTaskAndSettingKeys(s)), true) arg match { case Some(selected) => detail(selected, commandAndDescription.toMap) case None => aligned(" ", " ", commandAndDescription) mkString ("\n", "\n", "") } } - def taskStrings(key: AttributeKey[_]): Option[(String, String)] = key.description map { d => - (key.label, d) - } + def taskStrings(key: AttributeKey[_], firstOnly: Boolean): Option[(String, String)] = + key.description map { d => + if (firstOnly) (key.label, d.split("\r?\n")(0)) else (key.label, d) + } + + def taskStrings(key: AttributeKey[_]): Option[(String, String)] = taskStrings(key, false) def defaults = Command.command(DefaultsCommand) { s => s.copy(definedCommands = DefaultCommands) diff --git a/main/src/main/scala/sbt/internal/parser/SbtParser.scala b/main/src/main/scala/sbt/internal/parser/SbtParser.scala index fa427ec92..2b170b570 100644 --- a/main/src/main/scala/sbt/internal/parser/SbtParser.scala +++ b/main/src/main/scala/sbt/internal/parser/SbtParser.scala @@ -151,7 +151,9 @@ private[sbt] object SbtParser { reporter.reset() val wrapperFile = new BatchSourceFile(reporterId, code) val unit = new CompilationUnit(wrapperFile) - val parser = new syntaxAnalyzer.UnitParser(unit) + val parser = SbtParser.synchronized { // see https://github.com/sbt/sbt/issues/4148 + new syntaxAnalyzer.UnitParser(unit) + } val parsedTrees = SbtParser.synchronized { // see https://github.com/scala/bug/issues/10605 parser.templateStats() } diff --git a/notes/1.1.5/dotty-plugin.md b/notes/1.1.5/dotty-plugin.md new file mode 100644 index 000000000..6250c30dd --- /dev/null +++ b/notes/1.1.5/dotty-plugin.md @@ -0,0 +1,9 @@ +[@liufengyun]: https://github.com/liufengyun + +[4073]: https://github.com/sbt/sbt/issues/4073 +[4084]: https://github.com/sbt/sbt/pull/4084 + + +### Improvements + +- Detect dotty plugins which have descriptor file named `plugin.properties` instead of `scalac-plugin.xml`. [#4073][4073]/[#4084][4084] by [@liufengyun][@liufengyun] diff --git a/project/Dependencies.scala b/project/Dependencies.scala index b1e9fd0ab..bb2a81649 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -8,10 +8,10 @@ object Dependencies { val baseScalaVersion = scala212 // sbt modules - private val ioVersion = "1.1.6" + private val ioVersion = "1.1.10" private val utilVersion = "1.1.3" - private val lmVersion = "1.1.4" - private val zincVersion = "1.1.5" + private val lmVersion = "1.1.5" + private val zincVersion = "1.1.7" private val sbtIO = "org.scala-sbt" %% "io" % ioVersion diff --git a/sbt/src/sbt-test/actions/early-command/test b/sbt/src/sbt-test/actions/early-command/test index 532cef013..66e241ad4 100644 --- a/sbt/src/sbt-test/actions/early-command/test +++ b/sbt/src/sbt-test/actions/early-command/test @@ -1,2 +1,3 @@ > -error > early(error) +> --error \ No newline at end of file diff --git a/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/build.sbt b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/build.sbt new file mode 100644 index 000000000..099cd9f71 --- /dev/null +++ b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/build.sbt @@ -0,0 +1,16 @@ +// hardcode dottyVersion to make test deterministic +lazy val dottyVersion = "0.8.0-bin-20180424-e77604d-NIGHTLY" + +lazy val plugin = (project in file("plugin")) + .settings( + name := "dividezero", + version := "0.0.1", + organization := "ch.epfl.lamp", + scalaVersion := dottyVersion, + ) + +lazy val app = (project in file(".")) + .settings( + scalaVersion := dottyVersion, + libraryDependencies += compilerPlugin("ch.epfl.lamp" %% "dividezero" % "0.0.1"), + ) diff --git a/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/plugin/DivideZero.scala b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/plugin/DivideZero.scala new file mode 100644 index 000000000..c110a7cee --- /dev/null +++ b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/plugin/DivideZero.scala @@ -0,0 +1,40 @@ +package dividezero + +import dotty.tools.dotc._ +import core._ +import Contexts.Context +import plugins._ +import Phases.Phase +import ast.tpd +import transform.MegaPhase.MiniPhase +import Decorators._ +import Symbols.Symbol +import Constants.Constant +import transform.{LinkAll, Pickler} + +class DivideZero extends PluginPhase with StandardPlugin { + val name: String = "divideZero" + override val description: String = "divide zero check" + + val phaseName = name + + override val runsAfter = Set(Pickler.name) + override val runsBefore = Set(LinkAll.name) + + override def init(options: List[String]): List[PluginPhase] = this :: Nil + + private def isNumericDivide(sym: Symbol)(implicit ctx: Context): Boolean = { + def test(tpe: String): Boolean = + (sym.owner eq ctx.requiredClass(tpe.toTermName)) && sym.name.show == "/" + + test("scala.Int") || test("scala.Long") || test("scala.Short") || test("scala.Float") || test("scala.Double") + } + + override def transformApply(tree: tpd.Apply)(implicit ctx: Context): tpd.Tree = tree match { + case tpd.Apply(fun, tpd.Literal(Constants.Constant(v)) :: Nil) if isNumericDivide(fun.symbol) && v == 0 => + ctx.warning("divide by zero", tree.pos) + tpd.Literal(Constant(0)) + case _ => + tree + } +} diff --git a/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/plugin/src/main/resources/plugin.properties b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/plugin/src/main/resources/plugin.properties new file mode 100644 index 000000000..db215842c --- /dev/null +++ b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/plugin/src/main/resources/plugin.properties @@ -0,0 +1 @@ +pluginClass=dividezero.DivideZero \ No newline at end of file diff --git a/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/project/plugins.sbt b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/project/plugins.sbt new file mode 100644 index 000000000..ba89aa2bb --- /dev/null +++ b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.2.0") diff --git a/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/src/main/scala/hello/Hello.scala b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/src/main/scala/hello/Hello.scala new file mode 100644 index 000000000..99ae2d0d7 --- /dev/null +++ b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/src/main/scala/hello/Hello.scala @@ -0,0 +1,13 @@ +package hello +object Hello { + def main(args: Array[String]): Unit = { + val dotty: Int | String = "dotty" + + val y = 5 / 0 // error + 100 + 6 / 0 // error + 6L / 0L // error + val z = 7 / 0.0 // error + + println(s"Hello $dotty!") + } +} diff --git a/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/test b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/test new file mode 100644 index 000000000..28c06d4d6 --- /dev/null +++ b/sbt/src/sbt-test/compiler-project/dotty-compiler-plugin/test @@ -0,0 +1,2 @@ +> plugin/publishLocal +> app/run diff --git a/sbt/src/sbt-test/tests/watch-loop/build.sbt b/sbt/src/sbt-test/tests/watch-loop/build.sbt new file mode 100644 index 000000000..effea2c79 --- /dev/null +++ b/sbt/src/sbt-test/tests/watch-loop/build.sbt @@ -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) +} diff --git a/sbt/src/sbt-test/tests/watch-loop/project/SourceWrapper.scala b/sbt/src/sbt-test/tests/watch-loop/project/SourceWrapper.scala new file mode 100644 index 000000000..ff351168a --- /dev/null +++ b/sbt/src/sbt-test/tests/watch-loop/project/SourceWrapper.scala @@ -0,0 +1,6 @@ +package sbt + +object SourceWrapper { + def accept(sources: Seq[sbt.internal.io.Source], file: File): Boolean = + sources.exists(_.accept(file.toPath)) +} diff --git a/sbt/src/sbt-test/tests/watch-loop/src/main/scala/Bar.scala b/sbt/src/sbt-test/tests/watch-loop/src/main/scala/Bar.scala new file mode 100644 index 000000000..3b43dece6 --- /dev/null +++ b/sbt/src/sbt-test/tests/watch-loop/src/main/scala/Bar.scala @@ -0,0 +1 @@ +object Bar diff --git a/sbt/src/sbt-test/tests/watch-loop/src/main/scala/Foo.scala b/sbt/src/sbt-test/tests/watch-loop/src/main/scala/Foo.scala new file mode 100644 index 000000000..d37c10456 --- /dev/null +++ b/sbt/src/sbt-test/tests/watch-loop/src/main/scala/Foo.scala @@ -0,0 +1 @@ +object Foo \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/watch-loop/test b/sbt/src/sbt-test/tests/watch-loop/test new file mode 100644 index 000000000..41d621982 --- /dev/null +++ b/sbt/src/sbt-test/tests/watch-loop/test @@ -0,0 +1 @@ +> watchLoopTest diff --git a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index 77e3b7467..50883790e 100644 --- a/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted/sbt/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -167,6 +167,7 @@ final class ScriptedTests( case "actions/external-doc" => LauncherBased // sbt/Package$ case "actions/input-task" => LauncherBased // sbt/Package$ case "actions/input-task-dyn" => LauncherBased // sbt/Package$ + case "compiler-project/dotty-compiler-plugin" => LauncherBased // sbt/Package$ case "compiler-project/run-test" => LauncherBased // sbt/Package$ case "compiler-project/src-dep-plugin" => LauncherBased // sbt/Package$ case "dependency-management/artifact" => LauncherBased // tbd