From 2ed6c7e7d67657e349fe245d93659a659f23615e Mon Sep 17 00:00:00 2001 From: bitloi Date: Sun, 12 Apr 2026 07:33:38 +0200 Subject: [PATCH] refactor: pass zincDir and Logger to deleteZincBridgeSecondaryCache Addresses review: avoid heavy State stubs in tests; resolve zinc directory at the reboot-dev call site only. --- main/src/main/scala/sbt/MainLoop.scala | 12 +- .../scala/sbt/MainLoopZincCacheTest.scala | 159 ++---------------- 2 files changed, 23 insertions(+), 148 deletions(-) diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index 0108160cb..1d98aeb51 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -77,7 +77,10 @@ private[sbt] object MainLoop: case e: RebootCurrent => deleteLastLog(logBacking) deleteCurrentArtifacts(state) - deleteZincBridgeSecondaryCache(state) + deleteZincBridgeSecondaryCache( + state.log, + BuildPaths.getZincDirectory(state, BuildPaths.getGlobalBase(state)), + ) throw new xsbti.FullReload(e.arguments.toArray, false) case NonFatal(e) => System.err.println( @@ -111,12 +114,11 @@ private[sbt] object MainLoop: } } - /** Removes the Zinc compiler bridge secondary cache (`…/zinc/org.scala-sbt`). */ - private[sbt] def deleteZincBridgeSecondaryCache(state: State): Unit = - val zincDir = BuildPaths.getZincDirectory(state, BuildPaths.getGlobalBase(state)) + /** 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 - state.log.info(s"deleting $bridgeCache") + log.info(s"deleting $bridgeCache") IO.delete(bridgeCache) /** Runs the next sequence of commands with global logging in place. */ diff --git a/main/src/test/scala/sbt/MainLoopZincCacheTest.scala b/main/src/test/scala/sbt/MainLoopZincCacheTest.scala index 1e0d44b3f..c3f6d1247 100644 --- a/main/src/test/scala/sbt/MainLoopZincCacheTest.scala +++ b/main/src/test/scala/sbt/MainLoopZincCacheTest.scala @@ -9,168 +9,41 @@ package sbt import java.io.File -import java.util.concurrent.Callable -import sbt.internal.util.{ AttributeMap, ConsoleOut, GlobalLogging, MainAppender } +import sbt.internal.util.{ ConsoleOut, GlobalLogging, MainAppender } import sbt.io.IO import sbt.io.syntax.* import sbt.librarymanagement.SbtArtifacts - -import xsbti.{ - AppConfiguration, - AppMain, - AppProvider, - ApplicationID as XApplicationID, - ComponentProvider, - CrossValue, - GlobalLock, - Launcher, - Repository, - ScalaProvider, -} +import sbt.util.Logger object MainLoopZincCacheTest extends verify.BasicTestSuite: - private object Stubs: - val NoGlobalLock: GlobalLock = new GlobalLock: - def apply[T](lockFile: File, run: Callable[T]): T = run.call() - - lazy val componentProvider: ComponentProvider = new ComponentProvider: - def componentLocation(id: String): File = new File(id) - def component(id: String): Array[File] = Array.empty - def defineComponent(id: String, jars: Array[File]): Unit = () - def addToComponent(id: String, jars: Array[File]): Boolean = false - def lockFile(): File = new File(System.getProperty("java.io.tmpdir"), "stub-components.lock") - - lazy val launcher: Launcher = new Launcher: - def getScala(version: String): ScalaProvider = Stubs.scalaProvider - def getScala(version: String, reason: String): ScalaProvider = Stubs.scalaProvider - def getScala(version: String, reason: String, scalaOrg: String): ScalaProvider = - Stubs.scalaProvider - def app(id: XApplicationID, version: String): AppProvider = Stubs.appProvider - def topLoader(): ClassLoader = classOf[String].getClassLoader - def globalLock(): GlobalLock = NoGlobalLock - def bootDirectory(): File = new File(System.getProperty("java.io.tmpdir")) - def ivyRepositories(): Array[Repository] = Array.empty - def appRepositories(): Array[Repository] = Array.empty - def isOverrideRepositories: Boolean = false - def ivyHome(): File = new File(System.getProperty("java.io.tmpdir")) - def checksums(): Array[String] = Array.empty - - lazy val scalaProvider: ScalaProvider = new ScalaProvider: - def launcher(): Launcher = Stubs.launcher - def version(): String = "3.8.3" - def loader(): ClassLoader = classOf[String].getClassLoader - def jars(): Array[File] = Array.empty - def libraryJar(): File = new File("scala-library.jar") - def compilerJar(): File = new File("scala-compiler.jar") - def app(id: XApplicationID): AppProvider = Stubs.appProvider - - val appId: sbt.ApplicationID = sbt.ApplicationID( - "org.scala-sbt", - "sbt", - "2.0.0", - "sbt.xMain", - Seq.empty, - CrossValue.Disabled, - Seq.empty, - ) - - lazy val appProvider: AppProvider = new AppProvider: - def scalaProvider(): ScalaProvider = Stubs.scalaProvider - def id(): XApplicationID = appId - def loader(): ClassLoader = classOf[String].getClassLoader - def mainClass(): Class[? <: AppMain] = classOf[xMain] - def entryPoint(): Class[?] = classOf[xMain] - def newMain(): AppMain = new xMain() - def mainClasspath(): Array[File] = Array.empty - def components(): ComponentProvider = Stubs.componentProvider - - def appConfiguration(baseDir: File): AppConfiguration = new AppConfiguration: - def arguments(): Array[String] = Array.empty - def baseDirectory(): File = baseDir - def provider(): AppProvider = Stubs.appProvider - - end Stubs - - private def mkState(zincRoot: File, logFile: File, baseDir: File) = - val attrs = AttributeMap.empty - .put(BuildPaths.globalBaseDirectory, zincRoot.getParentFile) - .put(BuildPaths.globalZincDirectory, zincRoot) - State( - configuration = Stubs.appConfiguration(baseDir), - definedCommands = Nil, - exitHooks = Set.empty, - onFailure = None, - remainingCommands = Nil, - history = State.newHistory, - attributes = attrs, - globalLogging = GlobalLogging.initial( + 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 - ), - currentCommand = None, - next = State.Continue - ) + ) + f(gl.full) + finally IO.delete(logFile) - test("deleteZincBridgeSecondaryCache removes org.scala-sbt under global zinc"): + 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") - val logFile = File.createTempFile("sbt-mlz", ".log") - try - MainLoop.deleteZincBridgeSecondaryCache(mkState(zincRoot, logFile, tmp)) - assert(!bridge.exists(), s"expected $bridge deleted") - finally IO.delete(logFile) + 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) - val logFile = File.createTempFile("sbt-mlz", ".log") - try - MainLoop.deleteZincBridgeSecondaryCache(mkState(zincRoot, logFile, tmp)) - assert(zincRoot.exists()) - finally IO.delete(logFile) - - test("deleteZincBridgeSecondaryCache respects sbt.global.zinc system property"): - IO.withTemporaryDirectory: customZinc => - val prop = BuildPaths.GlobalZincProperty - val prev = sys.props.get(prop) - try - sys.props(prop) = customZinc.getAbsolutePath - val bridge = customZinc / SbtArtifacts.Organization - IO.write(bridge / "x.jar", Array.emptyByteArray) - val logFile = File.createTempFile("sbt-mlz", ".log") - try - val attrs = AttributeMap.empty.put( - BuildPaths.globalBaseDirectory, - customZinc.getParentFile / "unused-base" - ) - val state = State( - configuration = Stubs.appConfiguration(customZinc.getParentFile), - definedCommands = Nil, - exitHooks = Set.empty, - onFailure = None, - remainingCommands = Nil, - history = State.newHistory, - attributes = attrs, - globalLogging = GlobalLogging.initial( - MainAppender.globalDefault(ConsoleOut.globalProxy), - logFile, - ConsoleOut.globalProxy - ), - currentCommand = None, - next = State.Continue - ) - MainLoop.deleteZincBridgeSecondaryCache(state) - assert(!bridge.exists()) - finally IO.delete(logFile) - finally - prev match - case Some(v) => sys.props(prop) = v - case None => sys.props.remove(prop) + withTestLog: log => + MainLoop.deleteZincBridgeSecondaryCache(log, zincRoot) + assert(zincRoot.exists()) end MainLoopZincCacheTest