[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.
This commit is contained in:
Eugene Yokota 2026-01-10 01:56:27 -05:00
parent ba43e99b93
commit a921a86440
3 changed files with 69 additions and 27 deletions

View File

@ -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)

View File

@ -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]] =

View File

@ -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