From cc0915fed94b218681a3ec68b9a34176e925850d Mon Sep 17 00:00:00 2001 From: eugene yokota Date: Wed, 28 Jan 2026 02:27:57 -0500 Subject: [PATCH] [2.x] refactor: Scala Instance (#8648) **Problem** Strings are used to pass paths around. **Solution** This cleans up the Scala instance construction code. --- build.sbt | 1 + .../main/scala/sbt/internal/ConsoleMain.scala | 17 ++- main/src/main/scala/sbt/Defaults.scala | 59 ++++---- main/src/main/scala/sbt/Keys.scala | 6 +- .../main/scala/sbt/internal/Compiler.scala | 131 +++++++++++++----- .../sbt/internal/worker/ConsoleConfig.scala | 18 +-- .../internal/worker/ScalaInstanceConfig.scala | 29 ++-- .../worker/codec/ConsoleConfigFormats.scala | 6 +- .../codec/ScalaInstanceConfigFormats.scala | 10 +- protocol/src/main/contraband/worker.contra | 8 +- .../scala/sbt/internal/inc/ZincLmUtil.scala | 18 +++ 11 files changed, 192 insertions(+), 111 deletions(-) diff --git a/build.sbt b/build.sbt index 33c3d6577..6df8ccafb 100644 --- a/build.sbt +++ b/build.sbt @@ -731,6 +731,7 @@ lazy val mainProj = (project in file("main")) exclude[DirectMissingMethodProblem]("sbt.ScriptedRun#RunInParallelV2.invoke"), exclude[DirectMissingMethodProblem]("sbt.ScriptedRun#RunV1.invoke"), exclude[DirectMissingMethodProblem]("sbt.ScriptedRun#RunV2.invoke"), + exclude[IncompatibleMethTypeProblem]("sbt.internal.Compiler.scalaInstanceTask"), ), ) .dependsOn(lmCore, lmIvy, lmCoursierShadedPublishing) diff --git a/main-actions/src/main/scala/sbt/internal/ConsoleMain.scala b/main-actions/src/main/scala/sbt/internal/ConsoleMain.scala index 0e2c68d58..64701b2f8 100644 --- a/main-actions/src/main/scala/sbt/internal/ConsoleMain.scala +++ b/main-actions/src/main/scala/sbt/internal/ConsoleMain.scala @@ -69,7 +69,12 @@ class ConsoleMain: ) def analyzingCompiler(config: ConsoleConfig, si: ScalaInstance): AnalyzingCompiler = - val bridgeProvider = ZincUtil.constantBridgeProvider(si, File(config.bridgeJar)) + val bridgeProvider = ZincUtil.constantBridgeProvider( + si, + config.bridgeJars.toList match + case x :: Nil => Paths.get(x) + case xs => sys.error(s"expected one bridge jar, but got $xs") + ) val classpathOptions = ClasspathOptionsUtil.repl() AnalyzingCompiler( si, @@ -87,18 +92,18 @@ class ConsoleMain: val jlineJars = allCompilerJars.filter(_.getFileName.toString.contains("jline")) val compilerJars = allCompilerJars.filterNot(x => libraryJars.contains(x) || jlineJars.contains(x)).distinct - val allDocJars = siConfig.allDocJars.map(Paths.get(_)).sortBy(_.getFileName.toString) - val docJars = allDocJars + val extraToolJars0 = siConfig.extraToolJars.map(Paths.get(_)).sortBy(_.getFileName.toString()) + val extraToolJars = extraToolJars0 .filterNot(jar => libraryJars.contains(jar) || compilerJars.contains(jar)) .distinct - val allJars = libraryJars ++ compilerJars ++ docJars + val allJars = libraryJars ++ compilerJars ++ extraToolJars // Use parent class loader for JLine to avoid conflicts val jlineLoader = classOf[org.jline.terminal.Terminal].getClassLoader val libraryLoader = ClasspathUtil.toLoader(libraryJars, jlineLoader) val compilerLoader = ClasspathUtil.toLoader(compilerJars, libraryLoader) val fullLoader = - if docJars.isEmpty then compilerLoader - else ClasspathUtil.toLoader(docJars, compilerLoader) + if extraToolJars.isEmpty then compilerLoader + else ClasspathUtil.toLoader(extraToolJars, compilerLoader) new ScalaInstance( version = siConfig.scalaVersion, loader = fullLoader, diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index e0ffeae2f..b0a634e05 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -672,7 +672,10 @@ object Defaults extends BuildCommon { def compileBase = inTask(console)( Seq( - scalaInstance := Compiler.scalaInstanceTask(Some(Configurations.ScalaReplTool)).value, + scalaInstanceConfig := Compiler + .scalaInstanceConfigTask(Some(Configurations.ScalaReplTool)) + .value, + scalaInstance := Compiler.scalaInstanceTask(console / scalaInstanceConfig).value, ) ++ compilersSetting ) ++ compileBaseGlobal ++ Seq( useScalaReplJLine :== false, @@ -691,7 +694,8 @@ object Defaults extends BuildCommon { } else topLoader }, - scalaInstance := Def.uncached(Compiler.scalaInstanceTask(None).value), + scalaInstanceConfig := Def.uncached(Compiler.scalaInstanceConfigTask(None).value), + scalaInstance := Def.uncached(Compiler.scalaInstanceTask(scalaInstanceConfig).value), crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled), pluginCrossBuild / sbtBinaryVersion := binarySbtVersion( (pluginCrossBuild / sbtVersion).value @@ -757,6 +761,12 @@ object Defaults extends BuildCommon { Vector(outVf: HashedVirtualFileRef) })(Def.task(Vector.empty)) .value, + scalaCompilerBridgeJars := (Def.taskDyn { + val s = streams.value + val b = scalaCompilerBridgeBin.value + if b.nonEmpty then Def.task { b } + else Compiler.scalaCompilerBridgeJarsTask(scalaCompilerBridgeSource, s.log) + }).value, scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule(scalaVersion.value), auxiliaryClassFiles ++= { if (ScalaArtifacts.isScala3(scalaVersion.value)) List(TastyFiles.instance) @@ -2008,7 +2018,10 @@ object Defaults extends BuildCommon { def docTaskSettings(key: TaskKey[File] = doc): Seq[Setting[?]] = inTask(key)( Seq( - scalaInstance := Compiler.scalaInstanceTask(Some(Configurations.ScalaDocTool)).value, + scalaInstanceConfig := Compiler + .scalaInstanceConfigTask(Some(Configurations.ScalaDocTool)) + .value, + scalaInstance := Compiler.scalaInstanceTask(key / scalaInstanceConfig).value, apiMappings ++= { val dependencyCp = dependencyClasspath.value val log = streams.value.log @@ -2123,36 +2136,17 @@ object Defaults extends BuildCommon { private def forkedConsoleTask: Initialize[Task[Unit]] = Def.task { - import sbt.internal.worker.{ ConsoleConfig, ScalaInstanceConfig } - val si = (console / scalaInstance).value + import sbt.internal.worker.ConsoleConfig + val s = streams.value val conv = fileConverter.value val depsJars = (console / externalDependencyClasspath).value.toVector .map(_.data) .map(conv.toPath) - val bridgeJars = scalaCompilerBridgeBin.value - val bridgeJar = - if bridgeJars.nonEmpty then conv.toPath(bridgeJars.head).toFile - else - // Fall back to fetching the bridge module - val dr = scalaCompilerBridgeDependencyResolution.value - val uc = (update / updateConfiguration).value - val uwc = (update / unresolvedWarningConfiguration).value - ZincLmUtil.fetchDefaultBridgeModule( - si.version, - dr, - uc, - uwc, - streams.value.log - ) - val siConfig = ScalaInstanceConfig( - scalaVersion = si.version, - libraryJars = si.libraryJars.map(_.toString).toVector, - allCompilerJars = si.compilerJars.map(_.toString).toVector, - allDocJars = Vector.empty, - ) + val siConfig = (console / scalaInstanceConfig).value + val bridgeJars = scalaCompilerBridgeJars.value val config = ConsoleConfig( scalaInstanceConfig = siConfig, - bridgeJar = bridgeJar.toString, + bridgeJars = bridgeJars.toVector.map(vf => conv.toPath(vf).toUri()), externalDependencyJars = depsJars.map(_.toString), scalacOptions = (console / scalacOptions).value.toVector, initialCommands = (console / initialCommands).value, @@ -2160,10 +2154,13 @@ object Defaults extends BuildCommon { ) val fo = (console / forkOptions).value val terminal = ITerminal.console - terminal.restore() - val exitCode = ForkConsole(config, fo) - terminal.restore() - if exitCode != 0 then throw MessageOnlyException(s"Forked console exited with code $exitCode") + s.log.info("running console (fork)") + try + terminal.restore() + val exitCode = ForkConsole(config, fo) + if exitCode != 0 then + throw MessageOnlyException(s"Forked console exited with code $exitCode") + finally terminal.restore() println() } diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 711cc2af8..d5a11ccf6 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -27,7 +27,7 @@ import sbt.internal.server.BuildServerProtocol.BspFullWorkspace import sbt.internal.server.{ BspCompileTask, BuildServerReporter, ServerHandler } import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition } import sbt.internal.util.StringAttributeKey -import sbt.internal.worker.ClientJobParams +import sbt.internal.worker.{ ClientJobParams, ScalaInstanceConfig } import sbt.io.* import sbt.librarymanagement.Configurations.CompilerPlugin import sbt.librarymanagement.LibraryManagementCodec.* @@ -216,6 +216,8 @@ object Keys { val scalaHome = settingKey[Option[File]]("If Some, defines the local Scala installation to use for compilation, running, and testing.").withRank(ASetting) @transient val scalaInstance = taskKey[ScalaInstance]("Defines the Scala instance to use for compilation, running, and testing.").withRank(DTask) + @transient + val scalaInstanceConfig = taskKey[ScalaInstanceConfig]("Defines the Scala instance configuration.").withRank(DTask) val scalaOrganization = settingKey[String]("Organization/group ID of the Scala used in the project. Default value is 'org.scala-lang'. This is an advanced setting used for clones of the Scala Language. It should be disregarded in standard use cases.").withRank(CSetting) val scalaVersion = settingKey[String]("The version of Scala used for building.").withRank(APlusSetting) val scalaDynVersion = taskKey[String]("Resolves dynamic Scala version strings like '3-latest.candidate' to concrete versions.").withRank(DSetting) @@ -236,6 +238,8 @@ object Keys { val scalaCompilerBridgeBin = taskKey[Seq[HashedVirtualFileRef]]("Optionally, the jar of the compiler bridge. When not None, this takes precedence over scalaCompilerBridgeSource").withRank(DTask) val scalaCompilerBridgeSource = settingKey[ModuleID]("Configures the module ID of the sources of the compiler bridge when scalaCompilerBridgeBinaryJar is None").withRank(CSetting) val scalaCompilerBridgeScope = taskKey[Unit]("The compiler bridge scope.").withRank(DTask) + @transient + val scalaCompilerBridgeJars = taskKey[Seq[HashedVirtualFileRef]]("The compiler bridge JAR.").withRank(DTask) val scalaArtifacts = settingKey[Seq[String]]("Configures the list of artifacts which should match the Scala binary version").withRank(CSetting) val crossJavaVersions = settingKey[Seq[String]]("The java versions used during JDK cross testing").withRank(BPlusSetting) val semanticdbEnabled = settingKey[Boolean]("Enables SemanticDB Scalac plugin").withRank(CSetting) diff --git a/main/src/main/scala/sbt/internal/Compiler.scala b/main/src/main/scala/sbt/internal/Compiler.scala index 03602ce5d..f2a65b288 100644 --- a/main/src/main/scala/sbt/internal/Compiler.scala +++ b/main/src/main/scala/sbt/internal/Compiler.scala @@ -10,45 +10,64 @@ package sbt package internal import java.io.File -import sbt.internal.inc.ScalaInstance +import sbt.internal.inc.{ ScalaInstance, ZincLmUtil } +import sbt.internal.worker.ScalaInstanceConfig import sbt.librarymanagement.{ Artifact, Configuration, Configurations, ConfigurationReport, + ModuleID, ScalaArtifacts, SemanticSelector, VersionNumber } -import xsbti.ScalaProvider +import sbt.util.Logger +import xsbti.{ HashedVirtualFileRef, ScalaProvider } object Compiler: - def scalaInstanceTask(extraToolConf: Option[Configuration]): Def.Initialize[Task[ScalaInstance]] = + def scalaInstanceTask( + configKey: TaskKey[ScalaInstanceConfig] + ): Def.Initialize[Task[ScalaInstance]] = + Def.task { + val config = configKey.value + makeScalaInstance( + config.scalaVersion, + config.libraryJars.map(File(_)).toArray, + config.allCompilerJars.map(File(_)), + config.extraToolJars.map(File(_)), + Keys.state.value, + Keys.scalaInstanceTopLoader.value, + ) + } + + def scalaInstanceConfigTask( + extraToolConf: Option[Configuration] + ): Def.Initialize[Task[ScalaInstanceConfig]] = Def.taskDyn { val sh = Keys.scalaHome.value val app = Keys.appConfiguration.value val managed = Keys.managedScalaInstance.value sh match - case Some(h) => scalaInstanceFromHome(h) + case Some(h) => scalaInstanceConfigFromHome(h) case _ => val scalaProvider = app.provider.scalaProvider - if !managed then emptyScalaInstance - else scalaInstanceFromUpdate(extraToolConf) + if !managed then emptyScalaInstanceConfig + else scalaInstanceConfigFromUpdate(extraToolConf) } /** * A dummy ScalaInstance for Java-only projects. */ - def emptyScalaInstance: Def.Initialize[Task[ScalaInstance]] = Def.task { - makeScalaInstance( - "0.0.0", - Array.empty, - Seq.empty, - Seq.empty, - Keys.state.value, - Keys.scalaInstanceTopLoader.value, - ) - } + def emptyScalaInstanceConfig: Def.Initialize[Task[ScalaInstanceConfig]] = + Def.task { + ScalaInstanceConfig( + "0.0.0", + Vector.empty, + Vector.empty, + Vector.empty, + ) + } // Use the same class loader as the Scala classes used by sbt // This will fail for "doc" task https://github.com/sbt/sbt/issues/7725 @@ -77,25 +96,39 @@ object Compiler: case _ => ScalaInstance(sv, scalaProvider) } - def scalaInstanceFromHome(dir: File): Def.Initialize[Task[ScalaInstance]] = Def.task { - val dummy = ScalaInstance(dir)(Keys.state.value.classLoaderCache.apply) - Seq(dummy.loader, dummy.loaderLibraryOnly).foreach { - case a: AutoCloseable => a.close() - case _ => + def scalaInstanceConfigFromHome(dir: File): Def.Initialize[Task[ScalaInstanceConfig]] = + Def.task { + val dummy = ScalaInstance(dir)(Keys.state.value.classLoaderCache.apply) + Seq(dummy.loader, dummy.loaderLibraryOnly).foreach { + case a: AutoCloseable => a.close() + case _ => + } + ScalaInstanceConfig( + dummy.version, + dummy.libraryJars.toVector.map(_.toPath().toUri()), + dummy.compilerJars.toVector.map(_.toPath().toUri()), + dummy.allJars.toVector.map(_.toPath().toUri()), + ) } - makeScalaInstance( - dummy.version, - dummy.libraryJars, - dummy.compilerJars.toSeq, - dummy.allJars.toSeq, - Keys.state.value, - Keys.scalaInstanceTopLoader.value, - ) - } def scalaInstanceFromUpdate( extraToolConf: Option[Configuration] - ): Def.Initialize[Task[ScalaInstance]] = Def.task { + ): Def.Initialize[Task[ScalaInstance]] = + Def.task { + val config = scalaInstanceConfigFromUpdate(extraToolConf).value + makeScalaInstance( + config.scalaVersion, + config.libraryJars.map(File(_)).toArray, + config.allCompilerJars.map(File(_)), + config.extraToolJars.map(File(_)), + Keys.state.value, + Keys.scalaInstanceTopLoader.value, + ) + } + + def scalaInstanceConfigFromUpdate( + extraToolConf: Option[Configuration] + ): Def.Initialize[Task[ScalaInstanceConfig]] = Def.task { val sv = Keys.scalaVersion.value val fullReport = Keys.update.value val s = Keys.streams.value @@ -175,14 +208,11 @@ object Compiler: .flatMap(_.artifacts.map(_._2)) case None => Nil val libraryJars = ScalaArtifacts.libraryIds(sv).flatMap(file) - - makeScalaInstance( + ScalaInstanceConfig( sv, - libraryJars, - allCompilerJars, - extraToolJars, - Keys.state.value, - Keys.scalaInstanceTopLoader.value, + libraryJars.toVector.map(_.toPath().toUri()), + allCompilerJars.map(_.toPath().toUri()), + extraToolJars.toVector.map(_.toPath().toUri()) ) } @@ -226,4 +256,29 @@ object Compiler: else "Explicitly define scalaInstance or scalaHome or include Scala dependencies in the 'scala-tool' configuration." pre + post + + def scalaCompilerBridgeJarsTask( + sourceKey: Def.Initialize[ModuleID], + log: Logger + ): Def.Initialize[Task[Seq[HashedVirtualFileRef]]] = + Def.task { + val st = Keys.state.value + val g = BuildPaths.getGlobalBase(st) + val zincDir = BuildPaths.getZincDirectory(st, g) + val app = Keys.appConfiguration.value + val launcher = app.provider.scalaProvider.launcher + val dr = Keys.scalaCompilerBridgeDependencyResolution.value + val jars = ZincLmUtil.scalaCompilerBridgeJars( + scalaInstance = Keys.scalaInstance.value, + globalLock = launcher.globalLock, + componentProvider = app.provider.components, + secondaryCacheDir = Option(zincDir), + dependencyResolution = dr, + compilerBridgeSource = Keys.scalaCompilerBridgeSource.value, + scalaJarsTarget = zincDir, + log = log + ) + val conv = Keys.fileConverter.value + jars.map(jar => (conv.toVirtualFile(jar.toPath()): HashedVirtualFileRef)) + } end Compiler diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/ConsoleConfig.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/ConsoleConfig.scala index 0b94cf55d..a7e5e888d 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/worker/ConsoleConfig.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/ConsoleConfig.scala @@ -7,7 +7,7 @@ package sbt.internal.worker /** Configuration for forked console. */ final class ConsoleConfig private ( val scalaInstanceConfig: sbt.internal.worker.ScalaInstanceConfig, - val bridgeJar: String, + val bridgeJars: Vector[java.net.URI], val externalDependencyJars: Vector[String], val scalacOptions: Vector[String], val initialCommands: String, @@ -16,23 +16,23 @@ final class ConsoleConfig private ( override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: ConsoleConfig => (this.scalaInstanceConfig == x.scalaInstanceConfig) && (this.bridgeJar == x.bridgeJar) && (this.externalDependencyJars == x.externalDependencyJars) && (this.scalacOptions == x.scalacOptions) && (this.initialCommands == x.initialCommands) && (this.cleanupCommands == x.cleanupCommands) + case x: ConsoleConfig => (this.scalaInstanceConfig == x.scalaInstanceConfig) && (this.bridgeJars == x.bridgeJars) && (this.externalDependencyJars == x.externalDependencyJars) && (this.scalacOptions == x.scalacOptions) && (this.initialCommands == x.initialCommands) && (this.cleanupCommands == x.cleanupCommands) case _ => false }) override def hashCode: Int = { - 37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.ConsoleConfig".##) + scalaInstanceConfig.##) + bridgeJar.##) + externalDependencyJars.##) + scalacOptions.##) + initialCommands.##) + cleanupCommands.##) + 37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.ConsoleConfig".##) + scalaInstanceConfig.##) + bridgeJars.##) + externalDependencyJars.##) + scalacOptions.##) + initialCommands.##) + cleanupCommands.##) } override def toString: String = { - "ConsoleConfig(" + scalaInstanceConfig + ", " + bridgeJar + ", " + externalDependencyJars + ", " + scalacOptions + ", " + initialCommands + ", " + cleanupCommands + ")" + "ConsoleConfig(" + scalaInstanceConfig + ", " + bridgeJars + ", " + externalDependencyJars + ", " + scalacOptions + ", " + initialCommands + ", " + cleanupCommands + ")" } - private def copy(scalaInstanceConfig: sbt.internal.worker.ScalaInstanceConfig = scalaInstanceConfig, bridgeJar: String = bridgeJar, externalDependencyJars: Vector[String] = externalDependencyJars, scalacOptions: Vector[String] = scalacOptions, initialCommands: String = initialCommands, cleanupCommands: String = cleanupCommands): ConsoleConfig = { - new ConsoleConfig(scalaInstanceConfig, bridgeJar, externalDependencyJars, scalacOptions, initialCommands, cleanupCommands) + private def copy(scalaInstanceConfig: sbt.internal.worker.ScalaInstanceConfig = scalaInstanceConfig, bridgeJars: Vector[java.net.URI] = bridgeJars, externalDependencyJars: Vector[String] = externalDependencyJars, scalacOptions: Vector[String] = scalacOptions, initialCommands: String = initialCommands, cleanupCommands: String = cleanupCommands): ConsoleConfig = { + new ConsoleConfig(scalaInstanceConfig, bridgeJars, externalDependencyJars, scalacOptions, initialCommands, cleanupCommands) } def withScalaInstanceConfig(scalaInstanceConfig: sbt.internal.worker.ScalaInstanceConfig): ConsoleConfig = { copy(scalaInstanceConfig = scalaInstanceConfig) } - def withBridgeJar(bridgeJar: String): ConsoleConfig = { - copy(bridgeJar = bridgeJar) + def withBridgeJars(bridgeJars: Vector[java.net.URI]): ConsoleConfig = { + copy(bridgeJars = bridgeJars) } def withExternalDependencyJars(externalDependencyJars: Vector[String]): ConsoleConfig = { copy(externalDependencyJars = externalDependencyJars) @@ -49,5 +49,5 @@ final class ConsoleConfig private ( } object ConsoleConfig { - def apply(scalaInstanceConfig: sbt.internal.worker.ScalaInstanceConfig, bridgeJar: String, externalDependencyJars: Vector[String], scalacOptions: Vector[String], initialCommands: String, cleanupCommands: String): ConsoleConfig = new ConsoleConfig(scalaInstanceConfig, bridgeJar, externalDependencyJars, scalacOptions, initialCommands, cleanupCommands) + def apply(scalaInstanceConfig: sbt.internal.worker.ScalaInstanceConfig, bridgeJars: Vector[java.net.URI], externalDependencyJars: Vector[String], scalacOptions: Vector[String], initialCommands: String, cleanupCommands: String): ConsoleConfig = new ConsoleConfig(scalaInstanceConfig, bridgeJars, externalDependencyJars, scalacOptions, initialCommands, cleanupCommands) } diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/ScalaInstanceConfig.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/ScalaInstanceConfig.scala index 118b5ee98..954586160 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/worker/ScalaInstanceConfig.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/ScalaInstanceConfig.scala @@ -7,39 +7,40 @@ package sbt.internal.worker /** Configuration for creating a ScalaInstance in forked process. */ final class ScalaInstanceConfig private ( val scalaVersion: String, - val libraryJars: Vector[String], - val allCompilerJars: Vector[String], - val allDocJars: Vector[String]) extends Serializable { - + val libraryJars: Vector[java.net.URI], + val allCompilerJars: Vector[java.net.URI], + val extraToolJars: Vector[java.net.URI]) extends Serializable { + private def this(scalaVersion: String, libraryJars: Vector[java.net.URI], allCompilerJars: Vector[java.net.URI]) = this(scalaVersion, libraryJars, allCompilerJars, Vector()) override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: ScalaInstanceConfig => (this.scalaVersion == x.scalaVersion) && (this.libraryJars == x.libraryJars) && (this.allCompilerJars == x.allCompilerJars) && (this.allDocJars == x.allDocJars) + case x: ScalaInstanceConfig => (this.scalaVersion == x.scalaVersion) && (this.libraryJars == x.libraryJars) && (this.allCompilerJars == x.allCompilerJars) && (this.extraToolJars == x.extraToolJars) case _ => false }) override def hashCode: Int = { - 37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.ScalaInstanceConfig".##) + scalaVersion.##) + libraryJars.##) + allCompilerJars.##) + allDocJars.##) + 37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.ScalaInstanceConfig".##) + scalaVersion.##) + libraryJars.##) + allCompilerJars.##) + extraToolJars.##) } override def toString: String = { - "ScalaInstanceConfig(" + scalaVersion + ", " + libraryJars + ", " + allCompilerJars + ", " + allDocJars + ")" + "ScalaInstanceConfig(" + scalaVersion + ", " + libraryJars + ", " + allCompilerJars + ", " + extraToolJars + ")" } - private def copy(scalaVersion: String = scalaVersion, libraryJars: Vector[String] = libraryJars, allCompilerJars: Vector[String] = allCompilerJars, allDocJars: Vector[String] = allDocJars): ScalaInstanceConfig = { - new ScalaInstanceConfig(scalaVersion, libraryJars, allCompilerJars, allDocJars) + private def copy(scalaVersion: String = scalaVersion, libraryJars: Vector[java.net.URI] = libraryJars, allCompilerJars: Vector[java.net.URI] = allCompilerJars, extraToolJars: Vector[java.net.URI] = extraToolJars): ScalaInstanceConfig = { + new ScalaInstanceConfig(scalaVersion, libraryJars, allCompilerJars, extraToolJars) } def withScalaVersion(scalaVersion: String): ScalaInstanceConfig = { copy(scalaVersion = scalaVersion) } - def withLibraryJars(libraryJars: Vector[String]): ScalaInstanceConfig = { + def withLibraryJars(libraryJars: Vector[java.net.URI]): ScalaInstanceConfig = { copy(libraryJars = libraryJars) } - def withAllCompilerJars(allCompilerJars: Vector[String]): ScalaInstanceConfig = { + def withAllCompilerJars(allCompilerJars: Vector[java.net.URI]): ScalaInstanceConfig = { copy(allCompilerJars = allCompilerJars) } - def withAllDocJars(allDocJars: Vector[String]): ScalaInstanceConfig = { - copy(allDocJars = allDocJars) + def withExtraToolJars(extraToolJars: Vector[java.net.URI]): ScalaInstanceConfig = { + copy(extraToolJars = extraToolJars) } } object ScalaInstanceConfig { - def apply(scalaVersion: String, libraryJars: Vector[String], allCompilerJars: Vector[String], allDocJars: Vector[String]): ScalaInstanceConfig = new ScalaInstanceConfig(scalaVersion, libraryJars, allCompilerJars, allDocJars) + def apply(scalaVersion: String, libraryJars: Vector[java.net.URI], allCompilerJars: Vector[java.net.URI]): ScalaInstanceConfig = new ScalaInstanceConfig(scalaVersion, libraryJars, allCompilerJars) + def apply(scalaVersion: String, libraryJars: Vector[java.net.URI], allCompilerJars: Vector[java.net.URI], extraToolJars: Vector[java.net.URI]): ScalaInstanceConfig = new ScalaInstanceConfig(scalaVersion, libraryJars, allCompilerJars, extraToolJars) } diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ConsoleConfigFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ConsoleConfigFormats.scala index 20714412f..04acf96bf 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ConsoleConfigFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ConsoleConfigFormats.scala @@ -12,13 +12,13 @@ given ConsoleConfigFormat: JsonFormat[sbt.internal.worker.ConsoleConfig] = new J case Some(__js) => unbuilder.beginObject(__js) val scalaInstanceConfig = unbuilder.readField[sbt.internal.worker.ScalaInstanceConfig]("scalaInstanceConfig") - val bridgeJar = unbuilder.readField[String]("bridgeJar") + val bridgeJars = unbuilder.readField[Vector[java.net.URI]]("bridgeJars") val externalDependencyJars = unbuilder.readField[Vector[String]]("externalDependencyJars") val scalacOptions = unbuilder.readField[Vector[String]]("scalacOptions") val initialCommands = unbuilder.readField[String]("initialCommands") val cleanupCommands = unbuilder.readField[String]("cleanupCommands") unbuilder.endObject() - sbt.internal.worker.ConsoleConfig(scalaInstanceConfig, bridgeJar, externalDependencyJars, scalacOptions, initialCommands, cleanupCommands) + sbt.internal.worker.ConsoleConfig(scalaInstanceConfig, bridgeJars, externalDependencyJars, scalacOptions, initialCommands, cleanupCommands) case None => deserializationError("Expected JsObject but found None") } @@ -26,7 +26,7 @@ given ConsoleConfigFormat: JsonFormat[sbt.internal.worker.ConsoleConfig] = new J override def write[J](obj: sbt.internal.worker.ConsoleConfig, builder: Builder[J]): Unit = { builder.beginObject() builder.addField("scalaInstanceConfig", obj.scalaInstanceConfig) - builder.addField("bridgeJar", obj.bridgeJar) + builder.addField("bridgeJars", obj.bridgeJars) builder.addField("externalDependencyJars", obj.externalDependencyJars) builder.addField("scalacOptions", obj.scalacOptions) builder.addField("initialCommands", obj.initialCommands) diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ScalaInstanceConfigFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ScalaInstanceConfigFormats.scala index 3c6a50d78..77179f632 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ScalaInstanceConfigFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ScalaInstanceConfigFormats.scala @@ -12,11 +12,11 @@ given ScalaInstanceConfigFormat: JsonFormat[sbt.internal.worker.ScalaInstanceCon case Some(__js) => unbuilder.beginObject(__js) val scalaVersion = unbuilder.readField[String]("scalaVersion") - val libraryJars = unbuilder.readField[Vector[String]]("libraryJars") - val allCompilerJars = unbuilder.readField[Vector[String]]("allCompilerJars") - val allDocJars = unbuilder.readField[Vector[String]]("allDocJars") + val libraryJars = unbuilder.readField[Vector[java.net.URI]]("libraryJars") + val allCompilerJars = unbuilder.readField[Vector[java.net.URI]]("allCompilerJars") + val extraToolJars = unbuilder.readField[Vector[java.net.URI]]("extraToolJars") unbuilder.endObject() - sbt.internal.worker.ScalaInstanceConfig(scalaVersion, libraryJars, allCompilerJars, allDocJars) + sbt.internal.worker.ScalaInstanceConfig(scalaVersion, libraryJars, allCompilerJars, extraToolJars) case None => deserializationError("Expected JsObject but found None") } @@ -26,7 +26,7 @@ given ScalaInstanceConfigFormat: JsonFormat[sbt.internal.worker.ScalaInstanceCon builder.addField("scalaVersion", obj.scalaVersion) builder.addField("libraryJars", obj.libraryJars) builder.addField("allCompilerJars", obj.allCompilerJars) - builder.addField("allDocJars", obj.allDocJars) + builder.addField("extraToolJars", obj.extraToolJars) builder.endObject() } } diff --git a/protocol/src/main/contraband/worker.contra b/protocol/src/main/contraband/worker.contra index 5090ec385..a534b39ee 100644 --- a/protocol/src/main/contraband/worker.contra +++ b/protocol/src/main/contraband/worker.contra @@ -54,15 +54,15 @@ type ClientJobParams { ## Configuration for creating a ScalaInstance in forked process. type ScalaInstanceConfig { scalaVersion: String! - libraryJars: [String] - allCompilerJars: [String] - allDocJars: [String] + libraryJars: [java.net.URI] + allCompilerJars: [java.net.URI] + extraToolJars: [java.net.URI] @since("0.1.0") } ## Configuration for forked console. type ConsoleConfig { scalaInstanceConfig: sbt.internal.worker.ScalaInstanceConfig! - bridgeJar: String! + bridgeJars: [java.net.URI] externalDependencyJars: [String] scalacOptions: [String] initialCommands: String! diff --git a/zinc-lm-integration/src/main/scala/sbt/internal/inc/ZincLmUtil.scala b/zinc-lm-integration/src/main/scala/sbt/internal/inc/ZincLmUtil.scala index d444cf2d2..3b9ddfae8 100644 --- a/zinc-lm-integration/src/main/scala/sbt/internal/inc/ZincLmUtil.scala +++ b/zinc-lm-integration/src/main/scala/sbt/internal/inc/ZincLmUtil.scala @@ -65,6 +65,24 @@ object ZincLmUtil { ) } + def scalaCompilerBridgeJars( + scalaInstance: XScalaInstance, + globalLock: GlobalLock, + componentProvider: ComponentProvider, + secondaryCacheDir: Option[File], + dependencyResolution: DependencyResolution, + compilerBridgeSource: ModuleID, + scalaJarsTarget: File, + log: Logger + ): Seq[File] = + val compilerBridgeProvider = ZincComponentCompiler.interfaceProvider( + compilerBridgeSource, + new ZincComponentManager(globalLock, componentProvider, secondaryCacheDir, log), + dependencyResolution, + scalaJarsTarget, + ) + compilerBridgeProvider.fetchCompiledBridge(scalaInstance, log) :: Nil + def fetchDefaultBridgeModule( scalaVersion: String, dependencyResolution: DependencyResolution,