diff --git a/main-command/src/main/scala/sbt/BasicCommandStrings.scala b/main-command/src/main/scala/sbt/BasicCommandStrings.scala index 386208441..0034d48de 100644 --- a/main-command/src/main/scala/sbt/BasicCommandStrings.scala +++ b/main-command/src/main/scala/sbt/BasicCommandStrings.scala @@ -149,7 +149,9 @@ $HelpCommand remaining commands with the exception that the JVM is not shut down. If 'dev' is specified, the current sbt artifacts from the boot directory - (`~/.sbt/boot` by default) are deleted before restarting. + (under the default global base; `sbt.global.base` selects that location) are deleted + before restarting, and the compiler bridge secondary cache at `zinc/org.scala-sbt` + (honoring `sbt.global.base` and `sbt.global.zinc`) is removed. 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. diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index f2ebab9ba..1d98aeb51 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -22,6 +22,8 @@ import sbt.internal.util.{ Terminal as ITerminal } import sbt.io.{ IO, Using } +import sbt.io.syntax.* +import sbt.librarymanagement.SbtArtifacts import sbt.protocol.* import sbt.util.{ Logger, LoggerContext } @@ -75,6 +77,10 @@ private[sbt] object MainLoop: case e: RebootCurrent => deleteLastLog(logBacking) deleteCurrentArtifacts(state) + deleteZincBridgeSecondaryCache( + state.log, + BuildPaths.getZincDirectory(state, BuildPaths.getGlobalBase(state)), + ) throw new xsbti.FullReload(e.arguments.toArray, false) case NonFatal(e) => System.err.println( @@ -89,7 +95,6 @@ private[sbt] object MainLoop: /** 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. @@ -109,6 +114,13 @@ private[sbt] object MainLoop: } } + /** Removes the Zinc compiler bridge secondary cache (`zincDir/org.scala-sbt`). */ + private[sbt] def deleteZincBridgeSecondaryCache(log: Logger, zincDir: File): Unit = + val bridgeCache = zincDir / SbtArtifacts.Organization + if bridgeCache.exists() then + log.info(s"deleting $bridgeCache") + IO.delete(bridgeCache) + /** 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 => diff --git a/main/src/test/scala/sbt/MainLoopZincCacheTest.scala b/main/src/test/scala/sbt/MainLoopZincCacheTest.scala new file mode 100644 index 000000000..c3f6d1247 --- /dev/null +++ b/main/src/test/scala/sbt/MainLoopZincCacheTest.scala @@ -0,0 +1,49 @@ +/* + * sbt + * Copyright 2023, Scala center + * Copyright 2011 - 2022, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt + +import java.io.File + +import sbt.internal.util.{ ConsoleOut, GlobalLogging, MainAppender } +import sbt.io.IO +import sbt.io.syntax.* +import sbt.librarymanagement.SbtArtifacts +import sbt.util.Logger + +object MainLoopZincCacheTest extends verify.BasicTestSuite: + + private def withTestLog[A](f: Logger => A): A = + val logFile = File.createTempFile("sbt-mlz", ".log") + try + val gl = GlobalLogging.initial( + MainAppender.globalDefault(ConsoleOut.globalProxy), + logFile, + ConsoleOut.globalProxy + ) + f(gl.full) + finally IO.delete(logFile) + + test("deleteZincBridgeSecondaryCache removes org.scala-sbt under zincDir"): + IO.withTemporaryDirectory: tmp => + val zincRoot = tmp / "zinc" + val bridge = zincRoot / SbtArtifacts.Organization + IO.write(bridge / "marker.txt", "cached") + withTestLog: log => + MainLoop.deleteZincBridgeSecondaryCache(log, zincRoot) + assert(!bridge.exists(), s"expected $bridge deleted") + + test("deleteZincBridgeSecondaryCache is a no-op when org.scala-sbt is absent"): + IO.withTemporaryDirectory: tmp => + val zincRoot = tmp / "zinc" + IO.createDirectory(zincRoot) + withTestLog: log => + MainLoop.deleteZincBridgeSecondaryCache(log, zincRoot) + assert(zincRoot.exists()) + +end MainLoopZincCacheTest