From a921a86440a57ca3e96554dc48d9132f3dae138d Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 10 Jan 2026 01:56:27 -0500 Subject: [PATCH] [2.x] consoleProject **Problem** consoleProject doesn't work. REPL doesn't even start. **Solution** I made some progress into consoleProject. At least Scala 3.7 repl session will now start. The problem is that compiler bridge has not implemented binding, so we can't forward the sbt build information into the repl. --- build.sbt | 1 + main/src/main/scala/sbt/Defaults.scala | 29 ++++++-- .../scala/sbt/internal/ConsoleProject.scala | 66 +++++++++++++------ 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/build.sbt b/build.sbt index 762b05261..10824cd8b 100644 --- a/build.sbt +++ b/build.sbt @@ -717,6 +717,7 @@ lazy val mainProj = (project in file("main")) mimaSettings, mimaBinaryIssueFilters ++= Vector( exclude[ReversedMissingMethodProblem]("sbt.ProjectMatrix.*"), + exclude[DirectMissingMethodProblem]("sbt.internal.ConsoleProject.*"), ), ) .dependsOn(lmCore, lmIvy, lmCoursierShadedPublishing) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 96747f085..5f90ebbe2 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -774,6 +774,27 @@ object Defaults extends BuildCommon { javacOptions :== Nil, scalacOptions :== Nil, scalaVersion := appConfiguration.value.provider.scalaProvider.version, + consoleProject := ConsoleProject.consoleProjectTask.value, + consoleProject / scalaInstance := { + val topLoader = classOf[org.jline.terminal.Terminal].getClassLoader + val scalaProvider = appConfiguration.value.provider.scalaProvider + val allJars = scalaProvider.jars + val libraryJars = allJars.filter { jar => + jar.getName == "scala-library.jar" || jar.getName.startsWith("scala3-library_3") + } + val compilerJar = allJars.filter { jar => + jar.getName == "scala-compiler.jar" || jar.getName.startsWith("scala3-compiler_3") + } + ScalaInstance(scalaProvider.version, scalaProvider.launcher) + Compiler.makeScalaInstance( + scalaProvider.version, + libraryJars, + allJars.toSeq, + Seq.empty, + state.value, + topLoader, + ) + }, derive(crossScalaVersions := Seq(scalaVersion.value)), derive(compilersSetting), derive(scalaBinaryVersion := binaryScalaVersion(scalaVersion.value)), @@ -1067,7 +1088,6 @@ object Defaults extends BuildCommon { cleanKeepGlobs ++= historyPath.value.map(_.toGlob).toVector, // clean := Def.taskDyn(Clean.task(resolvedScoped.value.scope, full = true)).value, clean := Clean.scopedTask.value, - consoleProject := consoleProjectTask.value, transitiveDynamicInputs := Def.uncached(WatchTransitiveDependencies.task.value), ) @@ -2053,12 +2073,7 @@ object Defaults extends BuildCommon { analysis.infos.allInfos.values.map(_.getMainClasses).flatten.toSeq.sorted } - def consoleProjectTask = - Def.task { - ConsoleProject(state.value, (consoleProject / initialCommands).value)(using streams.value.log) - println() - } - + def consoleProjectTask = ConsoleProject.consoleProjectTask def consoleTask: Initialize[Task[Unit]] = consoleTask(fullClasspath, console) def consoleQuickTask = consoleTask(externalDependencyClasspath, consoleQuick) def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[?]): Initialize[Task[Unit]] = diff --git a/main/src/main/scala/sbt/internal/ConsoleProject.scala b/main/src/main/scala/sbt/internal/ConsoleProject.scala index ee0c333c0..35fd97906 100644 --- a/main/src/main/scala/sbt/internal/ConsoleProject.scala +++ b/main/src/main/scala/sbt/internal/ConsoleProject.scala @@ -12,58 +12,84 @@ package internal import sbt.ProjectExtra.extract import sbt.internal.classpath.AlternativeZincUtil import sbt.internal.inc.{ ScalaInstance, ZincLmUtil } +import sbt.internal.inc.classpath.ClasspathUtil import sbt.internal.util.Terminal +import sbt.io.IO +import sbt.librarymanagement.DependencyResolution import sbt.util.Logger +import xsbti.HashedVirtualFileRef import xsbti.compile.ClasspathOptionsUtil -object ConsoleProject { - def apply(state: State, extra: String, cleanupCommands: String = "", options: Seq[String] = Nil)( - using log: Logger +object ConsoleProject: + def consoleProjectTask = + Def.task { + val st = Keys.state.value + val si = (Keys.consoleProject / Keys.scalaInstance).value + val dr = (LocalRootProject / Keys.dependencyResolution).value + val compilerBridgeBinaryBin = + (LocalRootProject / Keys.consoleProject / Keys.scalaCompilerBridgeBin).value + ConsoleProject( + st, + si, + dr, + compilerBridgeBinaryBin, + (Keys.consoleProject / Keys.initialCommands).value + )(using + Keys.streams.value.log + ) + println() + } + + def apply( + state: State, + si: ScalaInstance, + dr: DependencyResolution, + compilerBridgeBinaryBin: Seq[HashedVirtualFileRef], + extra: String, + cleanupCommands: String = "", + options: Seq[String] = Nil + )(using + log: Logger ): Unit = { val extracted = Project.extract(state) val cpImports = new Imports(extracted, state) + // Bindings are blocked by https://github.com/scala/scala3/issues/5069 val bindings = ("currentState" -> state) :: ("extracted" -> extracted) :: ("cpHelpers" -> cpImports) :: Nil val unit = extracted.currentUnit - val (state1, dependencyResolution) = - extracted.runTask(Keys.dependencyResolution, state) - val (_, scalaCompilerBridgeBinaryBin) = - extracted.runTask(Keys.consoleProject / Keys.scalaCompilerBridgeBin, state1) + val tempDir0 = extracted.get(Keys.consoleProject / Keys.taskTemporaryDirectory) + val tempDir = IO.createUniqueDirectory(tempDir0).toPath() val conv = extracted.get(Keys.fileConverter) - val scalaInstance = { - val scalaProvider = state.configuration.provider.scalaProvider - ScalaInstance(scalaProvider.version, scalaProvider) - } val g = BuildPaths.getGlobalBase(state) val zincDir = BuildPaths.getZincDirectory(state, g) val app = state.configuration val launcher = app.provider.scalaProvider.launcher - val compiler = scalaCompilerBridgeBinaryBin.toList match + val compiler = compilerBridgeBinaryBin.toList match case jar :: xs => AlternativeZincUtil.scalaCompiler( - scalaInstance = scalaInstance, + scalaInstance = si, classpathOptions = ClasspathOptionsUtil.repl, compilerBridgeJar = conv.toPath(jar).toFile(), - classLoaderCache = state1.get(BasicKeys.classLoaderCache) + classLoaderCache = state.get(BasicKeys.classLoaderCache) ) case Nil => ZincLmUtil.scalaCompiler( - scalaInstance = scalaInstance, + scalaInstance = si, classpathOptions = ClasspathOptionsUtil.repl, globalLock = launcher.globalLock, componentProvider = app.provider.components, secondaryCacheDir = Option(zincDir), - dependencyResolution = dependencyResolution, + dependencyResolution = dr, compilerBridgeSource = extracted.get(Keys.consoleProject / Keys.scalaCompilerBridgeSource), scalaJarsTarget = zincDir, - classLoaderCache = state1.get(BasicKeys.classLoaderCache), + classLoaderCache = state.get(BasicKeys.classLoaderCache), log = log ) val imports = BuildUtil.getImports(unit.unit) ++ BuildUtil.importAll(bindings.map(_._1)) val importString = imports.mkString("", ";\n", ";\n\n") val initCommands = importString + extra - + val loader = ClasspathUtil.makeLoader(unit.classpath, si, tempDir) val terminal = Terminal.get // TODO - Hook up dsl classpath correctly... (new Console(compiler))( @@ -72,7 +98,7 @@ object ConsoleProject { initCommands, cleanupCommands, terminal - )(Some(unit.loader), bindings).get + )(Some(loader), bindings).get () } @@ -84,4 +110,4 @@ object ConsoleProject { implicit def settingKeyEvaluate[T](s: SettingKey[T]): Evaluate[T] = new Evaluate(get(s)) } final class Evaluate[T] private[sbt] (val eval: T) -} +end ConsoleProject