Merge pull request #3659 from eed3si9n/wip/reboot

Adds "reboot dev"
This commit is contained in:
eugene yokota 2017-10-25 04:06:59 -04:00 committed by GitHub
commit 8eb5879101
6 changed files with 77 additions and 13 deletions

View File

@ -309,6 +309,8 @@ lazy val commandProj = (project in file("main-command"))
exclude[ReversedMissingMethodProblem]("sbt.internal.server.ServerInstance.*"),
// Added method to CommandChannel. internal.
exclude[ReversedMissingMethodProblem]("sbt.internal.CommandChannel.*"),
// Added an overload to reboot. The overload is private[sbt].
exclude[ReversedMissingMethodProblem]("sbt.StateOps.reboot"),
)
)
.configure(

View File

@ -117,14 +117,17 @@ $HelpCommand <regular expression>
def RebootCommand = "reboot"
def RebootDetailed =
RebootCommand + """ [full]
RebootCommand + """ [dev | full]
This command is equivalent to exiting sbt, restarting, and running the
remaining commands with the exception that the JVM is not shut down.
If 'full' is specified, the boot directory (`~/.sbt/boot` by default)
is deleted before restarting. This forces an update of sbt and Scala
and is useful when working with development versions of sbt or Scala."""
If 'dev' is specified, the current sbt artifacts from the boot directory
(`~/.sbt/boot` by default) are deleted before restarting.
This forces an update of sbt and Scala, which is useful when working with development
versions of sbt.
If 'full' is specified, the boot directory is wiped out before restarting.
"""
def Multi = ";"
def MultiBrief =

View File

@ -175,10 +175,19 @@ object BasicCommands {
}
def reboot: Command =
Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(rebootParser)((s, full) =>
s reboot full)
Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(rebootOptionParser) {
case (s, (full, currentOnly)) =>
s.reboot(full, currentOnly)
}
def rebootParser(s: State): Parser[Boolean] = token(Space ~> "full" ^^^ true) ?? false
@deprecated("Use rebootOptionParser", "1.1.0")
def rebootParser(s: State): Parser[Boolean] =
rebootOptionParser(s) map { case (full, currentOnly) => full }
private[sbt] def rebootOptionParser(s: State): Parser[(Boolean, Boolean)] =
token(
Space ~> (("full" ^^^ ((true, false))) |
("dev" ^^^ ((false, true))))) ?? ((false, false))
def call: Command =
Command(ApplyCommand, Help.more(ApplyCommand, ApplyDetailed))(_ => callParser) {

View File

@ -89,6 +89,17 @@ trait StateOps {
*/
def reboot(full: Boolean): State
/**
* Reboots sbt. A reboot restarts execution from the entry point of the launcher.
* A reboot is designed to be as close as possible to actually restarting the JVM without actually doing so.
* Because the JVM is not restarted, JVM exit hooks are not run.
* State.exitHooks should be used instead and those will be run before rebooting.
* If `full` is true, the boot directory is deleted before starting again.
* If `currentOnly` is true, the artifacts for the current sbt version is deleted.
* This command is currently implemented to not return, but may be implemented in the future to only reboot at the next command processing step.
*/
private[sbt] def reboot(full: Boolean, currentOnly: Boolean): State
/** Sets the next command processing action to do.*/
def setNext(n: State.Next): State
@ -248,12 +259,18 @@ object State {
def baseDir: File = s.configuration.baseDirectory
def setNext(n: Next) = s.copy(next = n)
def continue = setNext(Continue)
def reboot(full: Boolean) = {
runExitHooks();
throw new xsbti.FullReload(
(s.remainingCommands map { case e: Exec => e.commandLine }).toArray,
full)
/** Implementation of reboot. */
def reboot(full: Boolean): State = reboot(full, false)
/** Implementation of reboot. */
private[sbt] def reboot(full: Boolean, currentOnly: Boolean): State = {
runExitHooks()
val rs = s.remainingCommands map { case e: Exec => e.commandLine }
if (currentOnly) throw new RebootCurrent(rs)
else throw new xsbti.FullReload(rs.toArray, full)
}
def reload = runExitHooks().setNext(new Return(defaultReload(s)))
def clearGlobalLog = setNext(ClearGlobalLog)
def keepLastLog = setNext(KeepLastLog)
@ -320,3 +337,5 @@ object State {
private[sbt] def getBoolean(s: State, key: AttributeKey[Boolean], default: Boolean): Boolean =
s.get(key) getOrElse default
}
private[sbt] final class RebootCurrent(val arguments: List[String]) extends RuntimeException

View File

@ -7,11 +7,12 @@
package sbt
import java.util.Properties
import scala.annotation.tailrec
import scala.util.control.NonFatal
import jline.TerminalFactory
import sbt.io.Using
import sbt.io.{ IO, Using }
import sbt.internal.util.{ ErrorHandling, GlobalLogBacking }
import sbt.internal.util.complete.DefaultParsers
import sbt.util.Logger
@ -58,6 +59,10 @@ object MainLoop {
case e: xsbti.FullReload =>
deleteLastLog(logBacking)
throw e // pass along a reboot request
case e: RebootCurrent =>
deleteLastLog(logBacking)
deleteCurrentArtifacts(state)
throw new xsbti.FullReload(e.arguments.toArray, false)
case NonFatal(e) =>
System.err.println(
"sbt appears to be exiting abnormally.\n The log file for this session is at " + logBacking.file)
@ -69,6 +74,28 @@ object MainLoop {
def deleteLastLog(logBacking: GlobalLogBacking): Unit =
logBacking.last.foreach(_.delete())
/** Deletes the current sbt artifacts from boot. */
private[sbt] def deleteCurrentArtifacts(state: State): Unit = {
import sbt.io.syntax._
val provider = state.configuration.provider
val appId = provider.id
// If we can obtain boot directory more accurately it'd be better.
val defaultBoot = BuildPaths.defaultGlobalBase / "boot"
val buildProps = state.baseDir / "project" / "build.properties"
// First try reading the sbt version from build.properties file.
val sbtVersionOpt = if (buildProps.exists) {
val buildProperties = new Properties()
IO.load(buildProperties, buildProps)
Option(buildProperties.getProperty("sbt.version"))
} else None
val sbtVersion = sbtVersionOpt.getOrElse(appId.version)
val currentArtDirs = defaultBoot * "*" / appId.groupID / appId.name / sbtVersion
currentArtDirs.get foreach { dir =>
state.log.info(s"Deleting $dir")
IO.delete(dir)
}
}
/** Runs the next sequence of commands with global logging in place. */
def runWithNewLog(state: State, logBacking: GlobalLogBacking): RunNext =
Using.fileWriter(append = true)(logBacking.file) { writer =>
@ -109,6 +136,7 @@ object MainLoop {
ErrorHandling.wideConvert { state.process(processCommand) } match {
case Right(s) => s
case Left(t: xsbti.FullReload) => throw t
case Left(t: RebootCurrent) => throw t
case Left(t) => state.handleError(t)
}

3
notes/1.1.0/reboot.md Normal file
View File

@ -0,0 +1,3 @@
### Improvements
- Adds `reboot dev` command, which deletes the current artifact from the boot directory. This is useful when working with development versions of sbt.