mirror of https://github.com/sbt/sbt.git
Merge pull request #4145 from eed3si9n/wip/bumpzinc
IO 1.1.7, Zinc 1.1.6, dotty plugin, and watch
This commit is contained in:
commit
20ca5c8f8a
|
|
@ -12,7 +12,7 @@ import java.nio.file.FileSystems
|
||||||
|
|
||||||
import sbt.BasicCommandStrings.ClearOnFailure
|
import sbt.BasicCommandStrings.ClearOnFailure
|
||||||
import sbt.State.FailureWall
|
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.AttributeKey
|
||||||
import sbt.internal.util.Types.const
|
import sbt.internal.util.Types.const
|
||||||
import sbt.io._
|
import sbt.io._
|
||||||
|
|
@ -33,6 +33,12 @@ trait Watched {
|
||||||
*/
|
*/
|
||||||
def pollInterval: FiniteDuration = Watched.PollDelay
|
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.*/
|
/** The message to show when triggered execution waits for sources to change.*/
|
||||||
private[sbt] def watchingMessage(s: WatchState): String = Watched.defaultWatchingMessage(s)
|
private[sbt] def watchingMessage(s: WatchState): String = Watched.defaultWatchingMessage(s)
|
||||||
|
|
||||||
|
|
@ -81,52 +87,65 @@ object Watched {
|
||||||
override def watchSources(s: State) = (base.watchSources(s) /: paths)(_ ++ _.watchSources(s))
|
override def watchSources(s: State) = (base.watchSources(s) /: paths)(_ ++ _.watchSources(s))
|
||||||
override def terminateWatch(key: Int): Boolean = base.terminateWatch(key)
|
override def terminateWatch(key: Int): Boolean = base.terminateWatch(key)
|
||||||
override val pollInterval = (base +: paths).map(_.pollInterval).min
|
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 watchingMessage(s: WatchState) = base.watchingMessage(s)
|
||||||
override def triggeredMessage(s: WatchState) = base.triggeredMessage(s)
|
override def triggeredMessage(s: WatchState) = base.triggeredMessage(s)
|
||||||
}
|
}
|
||||||
def empty: Watched = new AWatched
|
def empty: Watched = new AWatched
|
||||||
|
|
||||||
val PollDelay: FiniteDuration = 500.milliseconds
|
val PollDelay: FiniteDuration = 500.milliseconds
|
||||||
|
val AntiEntropy: FiniteDuration = 40.milliseconds
|
||||||
def isEnter(key: Int): Boolean = key == 10 || key == 13
|
def isEnter(key: Int): Boolean = key == 10 || key == 13
|
||||||
def printIfDefined(msg: String) = if (!msg.isEmpty) System.out.println(msg)
|
def printIfDefined(msg: String) = if (!msg.isEmpty) System.out.println(msg)
|
||||||
|
|
||||||
def executeContinuously(watched: Watched, s: State, next: String, repeat: String): State = {
|
def executeContinuously(watched: Watched, s: State, next: String, repeat: String): State = {
|
||||||
@tailrec def shouldTerminate: Boolean =
|
@tailrec def shouldTerminate: Boolean =
|
||||||
(System.in.available > 0) && (watched.terminateWatch(System.in.read()) || shouldTerminate)
|
watched.terminateWatch(System.in.read()) || shouldTerminate
|
||||||
val sources = watched.watchSources(s)
|
val log = s.log
|
||||||
val service = s get ContinuousWatchService getOrElse watched.watchService()
|
val logger = new EventMonitor.Logger {
|
||||||
val watchState = s get ContinuousState getOrElse WatchState.empty(service, sources)
|
override def debug(msg: => Any): Unit = log.debug(msg.toString)
|
||||||
|
}
|
||||||
if (watchState.count > 0)
|
s get ContinuousEventMonitor match {
|
||||||
printIfDefined(watched watchingMessage watchState)
|
case None =>
|
||||||
|
// This is the first iteration, so run the task and create a new EventMonitor
|
||||||
val (triggered, newWatchState) =
|
(ClearOnFailure :: next :: FailureWall :: repeat :: s)
|
||||||
try {
|
.put(
|
||||||
val (triggered, newWatchState) =
|
ContinuousEventMonitor,
|
||||||
SourceModificationWatch.watch(watched.pollInterval, watchState)(shouldTerminate)
|
EventMonitor(WatchState.empty(watched.watchService(), watched.watchSources(s)),
|
||||||
(triggered, newWatchState)
|
watched.pollInterval,
|
||||||
} catch {
|
watched.antiEntropy,
|
||||||
case e: Exception =>
|
shouldTerminate,
|
||||||
val log = s.log
|
logger)
|
||||||
log.error("Error occurred obtaining files to watch. Terminating continuous execution...")
|
)
|
||||||
State.handleException(e, s, log)
|
case Some(eventMonitor) =>
|
||||||
(false, watchState)
|
printIfDefined(watched watchingMessage eventMonitor.state)
|
||||||
}
|
val triggered = try eventMonitor.awaitEvent()
|
||||||
|
catch {
|
||||||
if (triggered) {
|
case e: Exception =>
|
||||||
printIfDefined(watched triggeredMessage newWatchState)
|
log.error(
|
||||||
(ClearOnFailure :: next :: FailureWall :: repeat :: s)
|
"Error occurred obtaining files to watch. Terminating continuous execution...")
|
||||||
.put(ContinuousState, newWatchState)
|
State.handleException(e, s, log)
|
||||||
.put(ContinuousWatchService, service)
|
false
|
||||||
} else {
|
}
|
||||||
while (System.in.available() > 0) System.in.read()
|
if (triggered) {
|
||||||
service.close()
|
printIfDefined(watched triggeredMessage eventMonitor.state)
|
||||||
s.remove(ContinuousState).remove(ContinuousWatchService)
|
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 =
|
val ContinuousState =
|
||||||
AttributeKey[WatchState]("watch state", "Internal: tracks state for continuous execution.")
|
AttributeKey[WatchState]("watch state", "Internal: tracks state for continuous execution.")
|
||||||
|
|
||||||
|
@deprecated("Superseded by ContinuousEventMonitor", "1.1.5")
|
||||||
val ContinuousWatchService =
|
val ContinuousWatchService =
|
||||||
AttributeKey[WatchService]("watch service",
|
AttributeKey[WatchService]("watch service",
|
||||||
"Internal: tracks watch service for continuous execution.")
|
"Internal: tracks watch service for continuous execution.")
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,7 @@ object Defaults extends BuildCommon {
|
||||||
concurrentRestrictions := defaultRestrictions.value,
|
concurrentRestrictions := defaultRestrictions.value,
|
||||||
parallelExecution :== true,
|
parallelExecution :== true,
|
||||||
pollInterval :== new FiniteDuration(500, TimeUnit.MILLISECONDS),
|
pollInterval :== new FiniteDuration(500, TimeUnit.MILLISECONDS),
|
||||||
|
watchAntiEntropy :== new FiniteDuration(40, TimeUnit.MILLISECONDS),
|
||||||
watchService :== { () =>
|
watchService :== { () =>
|
||||||
Watched.createWatchService()
|
Watched.createWatchService()
|
||||||
},
|
},
|
||||||
|
|
@ -555,13 +556,15 @@ object Defaults extends BuildCommon {
|
||||||
Def.setting {
|
Def.setting {
|
||||||
val getService = watchService.value
|
val getService = watchService.value
|
||||||
val interval = pollInterval.value
|
val interval = pollInterval.value
|
||||||
|
val _antiEntropy = watchAntiEntropy.value
|
||||||
val base = thisProjectRef.value
|
val base = thisProjectRef.value
|
||||||
val msg = watchingMessage.value
|
val msg = watchingMessage.value
|
||||||
val trigMsg = triggeredMessage.value
|
val trigMsg = triggeredMessage.value
|
||||||
new Watched {
|
new Watched {
|
||||||
val scoped = watchTransitiveSources in base
|
val scoped = watchTransitiveSources in base
|
||||||
val key = scoped.scopedKey
|
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 watchingMessage(s: WatchState) = msg(s)
|
||||||
override def triggeredMessage(s: WatchState) = trigMsg(s)
|
override def triggeredMessage(s: WatchState) = trigMsg(s)
|
||||||
override def watchService() = getService()
|
override def watchService() = getService()
|
||||||
|
|
@ -2884,10 +2887,18 @@ object Classpaths {
|
||||||
filter: FileFilter,
|
filter: FileFilter,
|
||||||
excl: FileFilter): Classpath =
|
excl: FileFilter): Classpath =
|
||||||
(base * (filter -- excl) +++ (base / config.name).descendantsExcept(filter, excl)).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 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
|
plugins.map("-Xplugin:" + _.getAbsolutePath).toSeq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2907,7 +2918,9 @@ object Classpaths {
|
||||||
lazy val compilerPluginConfig = Seq(
|
lazy val compilerPluginConfig = Seq(
|
||||||
scalacOptions := {
|
scalacOptions := {
|
||||||
val options = scalacOptions.value
|
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
|
val existing = options.toSet
|
||||||
if (autoCompilerPlugins.value) options ++ newPlugins.filterNot(existing) else options
|
if (autoCompilerPlugins.value) options ++ newPlugins.filterNot(existing) else options
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@ object Keys {
|
||||||
val analysis = AttributeKey[CompileAnalysis]("analysis", "Analysis of compilation, including dependencies and generated outputs.", DSetting)
|
val analysis = AttributeKey[CompileAnalysis]("analysis", "Analysis of compilation, including dependencies and generated outputs.", DSetting)
|
||||||
val watch = SettingKey(BasicKeys.watch)
|
val watch = SettingKey(BasicKeys.watch)
|
||||||
val suppressSbtShellNotification = settingKey[Boolean]("""True to suppress the "Executing in batch mode.." message.""").withRank(CSetting)
|
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 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 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)
|
val watchSources = taskKey[Seq[Watched.WatchSource]]("Defines the sources in this project for continuous execution to watch for changes.").withRank(BMinusSetting)
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
@ -8,10 +8,10 @@ object Dependencies {
|
||||||
val baseScalaVersion = scala212
|
val baseScalaVersion = scala212
|
||||||
|
|
||||||
// sbt modules
|
// sbt modules
|
||||||
private val ioVersion = "1.1.6"
|
private val ioVersion = "1.1.7"
|
||||||
private val utilVersion = "1.1.3"
|
private val utilVersion = "1.1.3"
|
||||||
private val lmVersion = "1.1.4"
|
private val lmVersion = "1.1.4"
|
||||||
private val zincVersion = "1.1.5"
|
private val zincVersion = "1.1.6"
|
||||||
|
|
||||||
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"),
|
||||||
|
)
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
pluginClass=dividezero.DivideZero
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.2.0")
|
||||||
|
|
@ -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!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
> plugin/publishLocal
|
||||||
|
> app/run
|
||||||
Loading…
Reference in New Issue