mirror of https://github.com/sbt/sbt.git
Merge pull request #8349 from eed3si9n/wip/scala-3.8
[1.x] Add Scala 3.8 REPL support
This commit is contained in:
commit
aee4229fd0
|
|
@ -11,7 +11,7 @@ import scala.util.Try
|
||||||
// ThisBuild settings take lower precedence,
|
// ThisBuild settings take lower precedence,
|
||||||
// but can be shared across the multi projects.
|
// but can be shared across the multi projects.
|
||||||
ThisBuild / version := {
|
ThisBuild / version := {
|
||||||
val v = "1.11.8-SNAPSHOT"
|
val v = "1.12.0-SNAPSHOT"
|
||||||
nightlyVersion.getOrElse(v)
|
nightlyVersion.getOrElse(v)
|
||||||
}
|
}
|
||||||
ThisBuild / version2_13 := "2.0.0-SNAPSHOT"
|
ThisBuild / version2_13 := "2.0.0-SNAPSHOT"
|
||||||
|
|
|
||||||
|
|
@ -671,89 +671,94 @@ object Defaults extends BuildCommon {
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is included into JvmPlugin.projectSettings
|
// This is included into JvmPlugin.projectSettings
|
||||||
def compileBase = inTask(console)(compilersSetting :: Nil) ++ compileBaseGlobal ++ Seq(
|
def compileBase =
|
||||||
useScalaReplJLine :== false,
|
inTask(console)(
|
||||||
scalaInstanceTopLoader := {
|
Seq(
|
||||||
val topLoader = if (!useScalaReplJLine.value) {
|
scalaInstance := Compiler.scalaInstanceTask(Some(Configurations.ScalaReplTool)).value,
|
||||||
// the JLineLoader contains the SbtInterfaceClassLoader
|
) ++ compilersSetting
|
||||||
classOf[org.jline.terminal.Terminal].getClassLoader
|
) ++ compileBaseGlobal ++ Seq(
|
||||||
} else classOf[Compilers].getClassLoader // the SbtInterfaceClassLoader
|
useScalaReplJLine :== false,
|
||||||
|
scalaInstanceTopLoader := {
|
||||||
|
val topLoader = if (!useScalaReplJLine.value) {
|
||||||
|
// the JLineLoader contains the SbtInterfaceClassLoader
|
||||||
|
classOf[org.jline.terminal.Terminal].getClassLoader
|
||||||
|
} else classOf[Compilers].getClassLoader // the SbtInterfaceClassLoader
|
||||||
|
|
||||||
// Scala 2.10 shades jline in the console so we need to make sure that it loads a compatible
|
// Scala 2.10 shades jline in the console so we need to make sure that it loads a compatible
|
||||||
// jansi version. Because of the shading, console does not work with the thin client for 2.10.x.
|
// jansi version. Because of the shading, console does not work with the thin client for 2.10.x.
|
||||||
if (scalaVersion.value.startsWith("2.10.")) new ClassLoader(topLoader) {
|
if (scalaVersion.value.startsWith("2.10.")) new ClassLoader(topLoader) {
|
||||||
override protected def loadClass(name: String, resolve: Boolean): Class[_] = {
|
override protected def loadClass(name: String, resolve: Boolean): Class[_] = {
|
||||||
if (name.startsWith("org.fusesource")) throw new ClassNotFoundException(name)
|
if (name.startsWith("org.fusesource")) throw new ClassNotFoundException(name)
|
||||||
super.loadClass(name, resolve)
|
super.loadClass(name, resolve)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else topLoader
|
||||||
else topLoader
|
},
|
||||||
},
|
scalaInstance := Compiler.scalaInstanceTask(None).value,
|
||||||
scalaInstance := scalaInstanceTask.value,
|
crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled),
|
||||||
crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled),
|
pluginCrossBuild / sbtBinaryVersion := binarySbtVersion(
|
||||||
pluginCrossBuild / sbtBinaryVersion := binarySbtVersion(
|
(pluginCrossBuild / sbtVersion).value
|
||||||
(pluginCrossBuild / sbtVersion).value
|
),
|
||||||
),
|
// Use (sbtVersion in pluginCrossBuild) to pick the sbt module to depend from the plugin.
|
||||||
// Use (sbtVersion in pluginCrossBuild) to pick the sbt module to depend from the plugin.
|
// Because `sbtVersion in pluginCrossBuild` can be scoped to project level,
|
||||||
// Because `sbtVersion in pluginCrossBuild` can be scoped to project level,
|
// this setting needs to be set here too.
|
||||||
// this setting needs to be set here too.
|
pluginCrossBuild / sbtDependency := {
|
||||||
pluginCrossBuild / sbtDependency := {
|
val app = appConfiguration.value
|
||||||
val app = appConfiguration.value
|
val id = app.provider.id
|
||||||
val id = app.provider.id
|
val sv = (pluginCrossBuild / sbtVersion).value
|
||||||
val sv = (pluginCrossBuild / sbtVersion).value
|
val scalaV = (pluginCrossBuild / scalaVersion).value
|
||||||
val scalaV = (pluginCrossBuild / scalaVersion).value
|
val binVersion = (pluginCrossBuild / scalaBinaryVersion).value
|
||||||
val binVersion = (pluginCrossBuild / scalaBinaryVersion).value
|
val cross = id.crossVersionedValue match {
|
||||||
val cross = id.crossVersionedValue match {
|
case CrossValue.Disabled => Disabled()
|
||||||
case CrossValue.Disabled => Disabled()
|
case CrossValue.Full => CrossVersion.full
|
||||||
case CrossValue.Full => CrossVersion.full
|
case CrossValue.Binary => CrossVersion.binary
|
||||||
case CrossValue.Binary => CrossVersion.binary
|
}
|
||||||
}
|
val base = ModuleID(id.groupID, id.name, sv).withCrossVersion(cross)
|
||||||
val base = ModuleID(id.groupID, id.name, sv).withCrossVersion(cross)
|
CrossVersion(scalaV, binVersion)(base).withCrossVersion(Disabled())
|
||||||
CrossVersion(scalaV, binVersion)(base).withCrossVersion(Disabled())
|
},
|
||||||
},
|
crossSbtVersions := Vector((pluginCrossBuild / sbtVersion).value),
|
||||||
crossSbtVersions := Vector((pluginCrossBuild / sbtVersion).value),
|
crossTarget := makeCrossTarget(
|
||||||
crossTarget := makeCrossTarget(
|
target.value,
|
||||||
target.value,
|
scalaVersion.value,
|
||||||
scalaVersion.value,
|
scalaBinaryVersion.value,
|
||||||
scalaBinaryVersion.value,
|
(pluginCrossBuild / sbtBinaryVersion).value,
|
||||||
(pluginCrossBuild / sbtBinaryVersion).value,
|
sbtPlugin.value,
|
||||||
sbtPlugin.value,
|
crossPaths.value
|
||||||
crossPaths.value
|
),
|
||||||
),
|
cleanIvy := IvyActions.cleanCachedResolutionCache(ivyModule.value, streams.value.log),
|
||||||
cleanIvy := IvyActions.cleanCachedResolutionCache(ivyModule.value, streams.value.log),
|
clean := {
|
||||||
clean := {
|
val _ = cleanIvy.value
|
||||||
val _ = cleanIvy.value
|
try {
|
||||||
try {
|
val store = AnalysisUtil.staticCachedStore(
|
||||||
val store = AnalysisUtil.staticCachedStore(
|
analysisFile = (Compile / compileAnalysisFile).value.toPath,
|
||||||
analysisFile = (Compile / compileAnalysisFile).value.toPath,
|
useTextAnalysis = !(Compile / enableBinaryCompileAnalysis).value,
|
||||||
useTextAnalysis = !(Compile / enableBinaryCompileAnalysis).value,
|
useConsistent = (Compile / enableConsistentCompileAnalysis).value,
|
||||||
useConsistent = (Compile / enableConsistentCompileAnalysis).value,
|
)
|
||||||
)
|
store.clearCache()
|
||||||
store.clearCache()
|
} catch {
|
||||||
} catch {
|
case NonFatal(_) => ()
|
||||||
case NonFatal(_) => ()
|
}
|
||||||
}
|
clean.value
|
||||||
clean.value
|
},
|
||||||
},
|
scalaCompilerBridgeBinaryJar := Def.settingDyn {
|
||||||
scalaCompilerBridgeBinaryJar := Def.settingDyn {
|
val sv = scalaVersion.value
|
||||||
val sv = scalaVersion.value
|
if (ScalaArtifacts.isScala3(sv) || VersionNumber(sv)
|
||||||
if (ScalaArtifacts.isScala3(sv) || VersionNumber(sv)
|
.matchesSemVer(SemanticSelector(s"=2.13 >=${ZincLmUtil.scala2SbtBridgeStart}")))
|
||||||
.matchesSemVer(SemanticSelector(s"=2.13 >=${ZincLmUtil.scala2SbtBridgeStart}")))
|
fetchBridgeBinaryJarTask(sv)
|
||||||
fetchBridgeBinaryJarTask(sv)
|
else Def.task[Option[File]](None)
|
||||||
else Def.task[Option[File]](None)
|
}.value,
|
||||||
}.value,
|
scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule(scalaVersion.value),
|
||||||
scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule(scalaVersion.value),
|
auxiliaryClassFiles ++= {
|
||||||
auxiliaryClassFiles ++= {
|
if (ScalaArtifacts.isScala3(scalaVersion.value)) List(TastyFiles.instance)
|
||||||
if (ScalaArtifacts.isScala3(scalaVersion.value)) List(TastyFiles.instance)
|
else Nil
|
||||||
else Nil
|
},
|
||||||
},
|
consoleProject / scalaCompilerBridgeBinaryJar := None,
|
||||||
consoleProject / scalaCompilerBridgeBinaryJar := None,
|
consoleProject / scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule(
|
||||||
consoleProject / scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule(
|
appConfiguration.value.provider.scalaProvider.version
|
||||||
appConfiguration.value.provider.scalaProvider.version
|
),
|
||||||
),
|
classpathOptions := ClasspathOptionsUtil.noboot(scalaVersion.value),
|
||||||
classpathOptions := ClasspathOptionsUtil.noboot(scalaVersion.value),
|
console / classpathOptions := ClasspathOptionsUtil.replNoboot(scalaVersion.value),
|
||||||
console / classpathOptions := ClasspathOptionsUtil.replNoboot(scalaVersion.value),
|
)
|
||||||
)
|
|
||||||
// must be a val: duplication detected by object identity
|
// must be a val: duplication detected by object identity
|
||||||
private[this] lazy val compileBaseGlobal: Seq[Setting[_]] = globalDefaults(
|
private[this] lazy val compileBaseGlobal: Seq[Setting[_]] = globalDefaults(
|
||||||
Seq(
|
Seq(
|
||||||
|
|
@ -1139,34 +1144,8 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def scalaInstanceTask: Initialize[Task[ScalaInstance]] = Def.taskDyn {
|
@deprecated("Use Compiler.scalaInstanceTask", "1.12.0")
|
||||||
// if this logic changes, ensure that `unmanagedScalaInstanceOnly` and `update` are changed
|
def scalaInstanceTask: Initialize[Task[ScalaInstance]] = Compiler.scalaInstanceTask(None)
|
||||||
// appropriately to avoid cycles
|
|
||||||
scalaHome.value match {
|
|
||||||
case Some(h) => scalaInstanceFromHome(h)
|
|
||||||
case None =>
|
|
||||||
val scalaProvider = appConfiguration.value.provider.scalaProvider
|
|
||||||
val version = scalaVersion.value
|
|
||||||
if (version == scalaProvider.version) // use the same class loader as the Scala classes used by sbt
|
|
||||||
Def.task {
|
|
||||||
val allJars = scalaProvider.jars
|
|
||||||
val libraryJars = allJars.filter(_.getName == "scala-library.jar")
|
|
||||||
allJars.filter(_.getName == "scala-compiler.jar") match {
|
|
||||||
case Array(compilerJar) if libraryJars.nonEmpty =>
|
|
||||||
makeScalaInstance(
|
|
||||||
version,
|
|
||||||
libraryJars,
|
|
||||||
allJars,
|
|
||||||
Seq.empty,
|
|
||||||
state.value,
|
|
||||||
scalaInstanceTopLoader.value
|
|
||||||
)
|
|
||||||
case _ => ScalaInstance(version, scalaProvider)
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
scalaInstanceFromUpdate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the ScalaInstance only if it was not constructed via `update`
|
// Returns the ScalaInstance only if it was not constructed via `update`
|
||||||
// This is necessary to prevent cycles between `update` and `scalaInstance`
|
// This is necessary to prevent cycles between `update` and `scalaInstance`
|
||||||
|
|
@ -1175,108 +1154,10 @@ object Defaults extends BuildCommon {
|
||||||
if (scalaHome.value.isDefined) Def.task(Some(scalaInstance.value)) else Def.task(None)
|
if (scalaHome.value.isDefined) Def.task(Some(scalaInstance.value)) else Def.task(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def noToolConfiguration(autoInstance: Boolean): String = {
|
@deprecated("Use Compiler.scalaInstanceFromUpdate", "1.12.0")
|
||||||
val pre = "Missing Scala tool configuration from the 'update' report. "
|
def scalaInstanceFromUpdate: Initialize[Task[ScalaInstance]] =
|
||||||
val post =
|
Compiler.scalaInstanceFromUpdate(None)
|
||||||
if (autoInstance)
|
|
||||||
"'scala-tool' is normally added automatically, so this may indicate a bug in sbt or you may be removing it from ivyConfigurations, for example."
|
|
||||||
else
|
|
||||||
"Explicitly define scalaInstance or scalaHome or include Scala dependencies in the 'scala-tool' configuration."
|
|
||||||
pre + post
|
|
||||||
}
|
|
||||||
|
|
||||||
def scalaInstanceFromUpdate: Initialize[Task[ScalaInstance]] = Def.task {
|
|
||||||
val sv = scalaVersion.value
|
|
||||||
val fullReport = update.value
|
|
||||||
val s = streams.value
|
|
||||||
|
|
||||||
// For Scala 3, update scala-library.jar in `scala-tool` and `scala-doc-tool` in case a newer version
|
|
||||||
// is present in the `compile` configuration. This is needed once forwards binary compatibility is dropped
|
|
||||||
// to avoid NoSuchMethod exceptions when expanding macros.
|
|
||||||
def updateLibraryToCompileConfiguration(report: ConfigurationReport) =
|
|
||||||
if (!ScalaArtifacts.isScala3(sv)) report
|
|
||||||
else
|
|
||||||
(for {
|
|
||||||
compileConf <- fullReport.configuration(Configurations.Compile)
|
|
||||||
compileLibMod <- compileConf.modules.find(_.module.name == ScalaArtifacts.LibraryID)
|
|
||||||
reportLibMod <- report.modules.find(_.module.name == ScalaArtifacts.LibraryID)
|
|
||||||
if VersionNumber(reportLibMod.module.revision)
|
|
||||||
.matchesSemVer(SemanticSelector(s"<${compileLibMod.module.revision}"))
|
|
||||||
} yield {
|
|
||||||
val newMods = report.modules
|
|
||||||
.filterNot(_.module.name == ScalaArtifacts.LibraryID) :+ compileLibMod
|
|
||||||
report.withModules(newMods)
|
|
||||||
}).getOrElse(report)
|
|
||||||
|
|
||||||
val toolReport = updateLibraryToCompileConfiguration(
|
|
||||||
fullReport
|
|
||||||
.configuration(Configurations.ScalaTool)
|
|
||||||
.getOrElse(sys.error(noToolConfiguration(managedScalaInstance.value)))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (Classpaths.isScala213(sv)) {
|
|
||||||
val scalaDeps = for {
|
|
||||||
compileReport <- fullReport.configuration(Configurations.Compile).iterator
|
|
||||||
libName <- ScalaArtifacts.Artifacts.iterator
|
|
||||||
lib <- compileReport.modules.find(_.module.name == libName)
|
|
||||||
} yield lib
|
|
||||||
for (lib <- scalaDeps.take(1)) {
|
|
||||||
val libVer = lib.module.revision
|
|
||||||
val libName = lib.module.name
|
|
||||||
val proj = Def.displayBuildRelative(thisProjectRef.value.build, thisProjectRef.value)
|
|
||||||
if (VersionNumber(sv).matchesSemVer(SemanticSelector(s"<$libVer"))) {
|
|
||||||
val err = !allowUnsafeScalaLibUpgrade.value
|
|
||||||
val fix =
|
|
||||||
if (err)
|
|
||||||
"""Upgrade the `scalaVersion` to fix the build. If upgrading the Scala compiler version is
|
|
||||||
|not possible (for example due to a regression in the compiler or a missing dependency),
|
|
||||||
|this error can be demoted by setting `allowUnsafeScalaLibUpgrade := true`.""".stripMargin
|
|
||||||
else
|
|
||||||
s"""Note that the dependency classpath and the runtime classpath of your project
|
|
||||||
|contain the newer $libName $libVer, even if the scalaVersion is $sv.
|
|
||||||
|Compilation (macro expansion) or using the Scala REPL in sbt may fail with a LinkageError.""".stripMargin
|
|
||||||
|
|
||||||
val msg =
|
|
||||||
s"""Expected `$proj scalaVersion` to be $libVer or later, but found $sv.
|
|
||||||
|To support backwards-only binary compatibility (SIP-51), the Scala 2.13 compiler
|
|
||||||
|should not be older than $libName on the dependency classpath.
|
|
||||||
|
|
|
||||||
|$fix
|
|
||||||
|
|
|
||||||
|See `$proj evicted` to know why $libName $libVer is getting pulled in.
|
|
||||||
|""".stripMargin
|
|
||||||
if (err) sys.error(msg)
|
|
||||||
else s.log.warn(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def file(id: String): File = {
|
|
||||||
val files = for {
|
|
||||||
m <- toolReport.modules if m.module.name.startsWith(id)
|
|
||||||
(art, file) <- m.artifacts if art.`type` == Artifact.DefaultType
|
|
||||||
} yield file
|
|
||||||
files.headOption getOrElse sys.error(s"Missing $id jar file")
|
|
||||||
}
|
|
||||||
|
|
||||||
val allCompilerJars = toolReport.modules.flatMap(_.artifacts.map(_._2))
|
|
||||||
val allDocJars =
|
|
||||||
fullReport
|
|
||||||
.configuration(Configurations.ScalaDocTool)
|
|
||||||
.map(updateLibraryToCompileConfiguration)
|
|
||||||
.toSeq
|
|
||||||
.flatMap(_.modules)
|
|
||||||
.flatMap(_.artifacts.map(_._2))
|
|
||||||
val libraryJars = ScalaArtifacts.libraryIds(sv).map(file)
|
|
||||||
|
|
||||||
makeScalaInstance(
|
|
||||||
sv,
|
|
||||||
libraryJars,
|
|
||||||
allCompilerJars,
|
|
||||||
allDocJars,
|
|
||||||
state.value,
|
|
||||||
scalaInstanceTopLoader.value,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
def makeScalaInstance(
|
def makeScalaInstance(
|
||||||
version: String,
|
version: String,
|
||||||
libraryJars: Array[File],
|
libraryJars: Array[File],
|
||||||
|
|
@ -1284,46 +1165,11 @@ object Defaults extends BuildCommon {
|
||||||
allDocJars: Seq[File],
|
allDocJars: Seq[File],
|
||||||
state: State,
|
state: State,
|
||||||
topLoader: ClassLoader,
|
topLoader: ClassLoader,
|
||||||
): ScalaInstance = {
|
): ScalaInstance =
|
||||||
val classLoaderCache = state.extendedClassLoaderCache
|
Compiler.makeScalaInstance(version, libraryJars, allCompilerJars, allDocJars, state, topLoader)
|
||||||
val compilerJars = allCompilerJars.filterNot(libraryJars.contains).distinct.toArray
|
|
||||||
val docJars = allDocJars
|
|
||||||
.filterNot(jar => libraryJars.contains(jar) || compilerJars.contains(jar))
|
|
||||||
.distinct
|
|
||||||
.toArray
|
|
||||||
val allJars = libraryJars ++ compilerJars ++ docJars
|
|
||||||
|
|
||||||
val libraryLoader = classLoaderCache(libraryJars.toList, topLoader)
|
def scalaInstanceFromHome(dir: File): Initialize[Task[ScalaInstance]] =
|
||||||
val compilerLoader = classLoaderCache(compilerJars.toList, libraryLoader)
|
Compiler.scalaInstanceFromHome(dir)
|
||||||
val fullLoader =
|
|
||||||
if (docJars.isEmpty) compilerLoader
|
|
||||||
else classLoaderCache(docJars.distinct.toList, compilerLoader)
|
|
||||||
new ScalaInstance(
|
|
||||||
version = version,
|
|
||||||
loader = fullLoader,
|
|
||||||
loaderCompilerOnly = compilerLoader,
|
|
||||||
loaderLibraryOnly = libraryLoader,
|
|
||||||
libraryJars = libraryJars,
|
|
||||||
compilerJars = compilerJars,
|
|
||||||
allJars = allJars,
|
|
||||||
explicitActual = Some(version)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
def scalaInstanceFromHome(dir: File): Initialize[Task[ScalaInstance]] = Def.task {
|
|
||||||
val dummy = ScalaInstance(dir)(state.value.classLoaderCache.apply)
|
|
||||||
Seq(dummy.loader, dummy.loaderLibraryOnly).foreach {
|
|
||||||
case a: AutoCloseable => a.close()
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
makeScalaInstance(
|
|
||||||
dummy.version,
|
|
||||||
dummy.libraryJars,
|
|
||||||
dummy.compilerJars,
|
|
||||||
dummy.allJars,
|
|
||||||
state.value,
|
|
||||||
scalaInstanceTopLoader.value,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private[this] def testDefaults =
|
private[this] def testDefaults =
|
||||||
Defaults.globalDefaults(
|
Defaults.globalDefaults(
|
||||||
|
|
@ -2222,6 +2068,7 @@ object Defaults extends BuildCommon {
|
||||||
def docTaskSettings(key: TaskKey[File] = doc): Seq[Setting[_]] =
|
def docTaskSettings(key: TaskKey[File] = doc): Seq[Setting[_]] =
|
||||||
inTask(key)(
|
inTask(key)(
|
||||||
Seq(
|
Seq(
|
||||||
|
scalaInstance := Compiler.scalaInstanceTask(Some(Configurations.ScalaDocTool)).value,
|
||||||
apiMappings ++= {
|
apiMappings ++= {
|
||||||
val dependencyCp = dependencyClasspath.value
|
val dependencyCp = dependencyClasspath.value
|
||||||
val log = streams.value.log
|
val log = streams.value.log
|
||||||
|
|
@ -2299,7 +2146,7 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
)
|
) ++ compilersSetting
|
||||||
)
|
)
|
||||||
|
|
||||||
def mainBgRunTask = mainBgRunTaskForConfig(Select(Runtime))
|
def mainBgRunTask = mainBgRunTaskForConfig(Select(Runtime))
|
||||||
|
|
@ -3349,7 +3196,7 @@ object Classpaths {
|
||||||
ivyConfigurations ++= Configurations.auxiliary,
|
ivyConfigurations ++= Configurations.auxiliary,
|
||||||
ivyConfigurations ++= {
|
ivyConfigurations ++= {
|
||||||
if (managedScalaInstance.value && scalaHome.value.isEmpty)
|
if (managedScalaInstance.value && scalaHome.value.isEmpty)
|
||||||
Configurations.ScalaTool :: Configurations.ScalaDocTool :: Nil
|
Configurations.ScalaTool :: Configurations.ScalaDocTool :: Configurations.ScalaReplTool :: Nil
|
||||||
else Nil
|
else Nil
|
||||||
},
|
},
|
||||||
// Coursier needs these
|
// Coursier needs these
|
||||||
|
|
@ -3545,17 +3392,18 @@ object Classpaths {
|
||||||
val pluginAdjust =
|
val pluginAdjust =
|
||||||
if (isPlugin) sbtdeps +: base
|
if (isPlugin) sbtdeps +: base
|
||||||
else base
|
else base
|
||||||
val sbtOrg = scalaOrganization.value
|
val scalaOrg = scalaOrganization.value
|
||||||
val version = scalaVersion.value
|
val version = scalaVersion.value
|
||||||
val extResolvers = externalResolvers.value
|
|
||||||
val isScala3M123 = ScalaArtifacts.isScala3M123(version)
|
val isScala3M123 = ScalaArtifacts.isScala3M123(version)
|
||||||
val allToolDeps =
|
val allToolDeps =
|
||||||
if (scalaHome.value.isDefined || scalaModuleInfo.value.isEmpty || !managedScalaInstance.value)
|
if (scalaHome.value.isDefined || scalaModuleInfo.value.isEmpty || !managedScalaInstance.value)
|
||||||
Nil
|
Nil
|
||||||
else if (!isScala3M123 || extResolvers.contains(Resolver.JCenterRepository)) {
|
else if (isScala3M123)
|
||||||
ScalaArtifacts.toolDependencies(sbtOrg, version) ++
|
ScalaArtifacts.toolDependencies(scalaOrg, version)
|
||||||
ScalaArtifacts.docToolDependencies(sbtOrg, version)
|
else
|
||||||
} else ScalaArtifacts.toolDependencies(sbtOrg, version)
|
ScalaArtifacts.toolDependencies(scalaOrg, version) ++
|
||||||
|
ScalaArtifacts.docToolDependencies(scalaOrg, version) ++
|
||||||
|
ScalaArtifacts.replToolDependencies(scalaOrg, version)
|
||||||
allToolDeps ++ pluginAdjust
|
allToolDeps ++ pluginAdjust
|
||||||
},
|
},
|
||||||
// in case of meta build, exclude all sbt modules from the dependency graph, so we can use the sbt resolved by the launcher
|
// in case of meta build, exclude all sbt modules from the dependency graph, so we can use the sbt resolved by the launcher
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import sbt.internal.inc.ScalaInstance
|
||||||
|
import sbt.librarymanagement.{
|
||||||
|
Artifact,
|
||||||
|
Configuration,
|
||||||
|
Configurations,
|
||||||
|
ConfigurationReport,
|
||||||
|
ScalaArtifacts,
|
||||||
|
SemanticSelector,
|
||||||
|
UpdateReport,
|
||||||
|
VersionNumber
|
||||||
|
}
|
||||||
|
import xsbti.ScalaProvider
|
||||||
|
|
||||||
|
private[sbt] object Compiler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a ScalaInstance.
|
||||||
|
* extraToolConf is used for Scala 3 since it started splitting up scaladoc and repl.
|
||||||
|
*/
|
||||||
|
def scalaInstanceTask(extraToolConf: Option[Configuration]): Def.Initialize[Task[ScalaInstance]] =
|
||||||
|
Def.taskDyn {
|
||||||
|
val sh = Keys.scalaHome.value
|
||||||
|
val app = Keys.appConfiguration.value
|
||||||
|
val managed = Keys.managedScalaInstance.value
|
||||||
|
val sv = Keys.scalaVersion.value
|
||||||
|
// if this logic changes, ensure that `unmanagedScalaInstanceOnly` and `update` are changed
|
||||||
|
// appropriately to avoid cycles
|
||||||
|
sh match {
|
||||||
|
case Some(h) => scalaInstanceFromHome(h)
|
||||||
|
case _ =>
|
||||||
|
val scalaProvider = app.provider.scalaProvider
|
||||||
|
if (!managed) emptyScalaInstance
|
||||||
|
else if (sv == scalaProvider.version) optimizedScalaInstance(sv, scalaProvider)
|
||||||
|
else scalaInstanceFromUpdate(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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the same class loader as the Scala classes used by sbt
|
||||||
|
def optimizedScalaInstance(
|
||||||
|
sv: String,
|
||||||
|
scalaProvider: ScalaProvider
|
||||||
|
): Def.Initialize[Task[ScalaInstance]] = Def.task {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
compilerJar match {
|
||||||
|
case Array(compilerJar) if libraryJars.nonEmpty =>
|
||||||
|
makeScalaInstance(
|
||||||
|
sv,
|
||||||
|
libraryJars,
|
||||||
|
allJars.toSeq,
|
||||||
|
Seq.empty,
|
||||||
|
Keys.state.value,
|
||||||
|
Keys.scalaInstanceTopLoader.value,
|
||||||
|
)
|
||||||
|
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 _ =>
|
||||||
|
}
|
||||||
|
makeScalaInstance(
|
||||||
|
dummy.version,
|
||||||
|
dummy.libraryJars,
|
||||||
|
dummy.compilerJars.toSeq,
|
||||||
|
dummy.allJars.toSeq,
|
||||||
|
Keys.state.value,
|
||||||
|
Keys.scalaInstanceTopLoader.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a ScalaInstance.
|
||||||
|
* extraToolConf is used for Scala 3 since it started splitting up scaladoc and repl.
|
||||||
|
*/
|
||||||
|
def scalaInstanceFromUpdate(
|
||||||
|
extraToolConf: Option[Configuration]
|
||||||
|
): Def.Initialize[Task[ScalaInstance]] =
|
||||||
|
Def.task {
|
||||||
|
val sv = Keys.scalaVersion.value
|
||||||
|
val fullReport = Keys.update.value
|
||||||
|
val s = Keys.streams.value
|
||||||
|
|
||||||
|
val toolReport = updateLibraryToCompileConfiguration(sv, fullReport)(
|
||||||
|
fullReport
|
||||||
|
.configuration(Configurations.ScalaTool)
|
||||||
|
.getOrElse(sys.error(noToolConfiguration(Keys.managedScalaInstance.value)))
|
||||||
|
)
|
||||||
|
|
||||||
|
if (Classpaths.isScala213(sv)) {
|
||||||
|
val scalaDeps = for {
|
||||||
|
compileReport <- fullReport.configuration(Configurations.Compile).iterator
|
||||||
|
libName <- ScalaArtifacts.Artifacts.iterator
|
||||||
|
lib <- compileReport.modules.find(_.module.name == libName)
|
||||||
|
} yield lib
|
||||||
|
for (lib <- scalaDeps.take(1)) {
|
||||||
|
val libVer = lib.module.revision
|
||||||
|
val libName = lib.module.name
|
||||||
|
val proj =
|
||||||
|
Def.displayBuildRelative(Keys.thisProjectRef.value.build, Keys.thisProjectRef.value)
|
||||||
|
if (VersionNumber(sv).matchesSemVer(SemanticSelector(s"<$libVer"))) {
|
||||||
|
val err = !Keys.allowUnsafeScalaLibUpgrade.value
|
||||||
|
val fix =
|
||||||
|
if (err)
|
||||||
|
"""Upgrade the `scalaVersion` to fix the build. If upgrading the Scala compiler version is
|
||||||
|
|not possible (for example due to a regression in the compiler or a missing dependency),
|
||||||
|
|this error can be demoted by setting `allowUnsafeScalaLibUpgrade := true`.""".stripMargin
|
||||||
|
else
|
||||||
|
s"""Note that the dependency classpath and the runtime classpath of your project
|
||||||
|
|contain the newer $libName $libVer, even if the scalaVersion is $sv.
|
||||||
|
|Compilation (macro expansion) or using the Scala REPL in sbt may fail with a LinkageError.""".stripMargin
|
||||||
|
|
||||||
|
val msg =
|
||||||
|
s"""Expected `$proj scalaVersion` to be $libVer or later, but found $sv.
|
||||||
|
|To support backwards-only binary compatibility (SIP-51), the Scala 2.13 compiler
|
||||||
|
|should not be older than $libName on the dependency classpath.
|
||||||
|
|
|
||||||
|
|$fix
|
||||||
|
|
|
||||||
|
|See `$proj evicted` to know why $libName $libVer is getting pulled in.
|
||||||
|
|""".stripMargin
|
||||||
|
if (err) sys.error(msg)
|
||||||
|
else s.log.warn(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def file(id: String): File = {
|
||||||
|
val files = for {
|
||||||
|
m <- toolReport.modules if m.module.name.startsWith(id)
|
||||||
|
(art, file) <- m.artifacts if art.`type` == Artifact.DefaultType
|
||||||
|
} yield file
|
||||||
|
files.headOption getOrElse sys.error(s"Missing $id jar file")
|
||||||
|
}
|
||||||
|
|
||||||
|
val allCompilerJars = toolReport.modules.flatMap(_.artifacts.map(_._2))
|
||||||
|
val extraToolJars =
|
||||||
|
extraToolConf match {
|
||||||
|
case Some(extra) =>
|
||||||
|
fullReport
|
||||||
|
.configuration(extra)
|
||||||
|
.map(updateLibraryToCompileConfiguration(sv, fullReport))
|
||||||
|
.toSeq
|
||||||
|
.flatMap(_.modules)
|
||||||
|
.flatMap(_.artifacts.map(_._2))
|
||||||
|
case None => Nil
|
||||||
|
}
|
||||||
|
val libraryJars = ScalaArtifacts.libraryIds(sv).map(file)
|
||||||
|
|
||||||
|
makeScalaInstance(
|
||||||
|
sv,
|
||||||
|
libraryJars,
|
||||||
|
allCompilerJars,
|
||||||
|
extraToolJars,
|
||||||
|
Keys.state.value,
|
||||||
|
Keys.scalaInstanceTopLoader.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Scala 3, update scala-library.jar in `scala-tool` and `scala-doc-tool` in case a newer version
|
||||||
|
// is present in the `compile` configuration. This is needed once forwards binary compatibility is dropped
|
||||||
|
// to avoid NoSuchMethod exceptions when expanding macros.
|
||||||
|
private def updateLibraryToCompileConfiguration(sv: String, fullReport: UpdateReport)(
|
||||||
|
report: ConfigurationReport
|
||||||
|
) =
|
||||||
|
if (!ScalaArtifacts.isScala3(sv)) report
|
||||||
|
else
|
||||||
|
(for {
|
||||||
|
compileConf <- fullReport.configuration(Configurations.Compile)
|
||||||
|
compileLibMod <- compileConf.modules.find(_.module.name == ScalaArtifacts.LibraryID)
|
||||||
|
reportLibMod <- report.modules.find(_.module.name == ScalaArtifacts.LibraryID)
|
||||||
|
if VersionNumber(reportLibMod.module.revision)
|
||||||
|
.matchesSemVer(SemanticSelector(s"<${compileLibMod.module.revision}"))
|
||||||
|
} yield {
|
||||||
|
val newMods = report.modules
|
||||||
|
.filterNot(_.module.name == ScalaArtifacts.LibraryID) :+ compileLibMod
|
||||||
|
report.withModules(newMods)
|
||||||
|
}).getOrElse(report)
|
||||||
|
|
||||||
|
def makeScalaInstance(
|
||||||
|
version: String,
|
||||||
|
libraryJars: Array[File],
|
||||||
|
allCompilerJars: Seq[File],
|
||||||
|
extraToolJars: Seq[File],
|
||||||
|
state: State,
|
||||||
|
topLoader: ClassLoader,
|
||||||
|
): ScalaInstance = {
|
||||||
|
val classLoaderCache = state.extendedClassLoaderCache
|
||||||
|
val compilerJars = allCompilerJars.filterNot(libraryJars.contains).distinct.toArray
|
||||||
|
val toolJars = extraToolJars
|
||||||
|
.filterNot(jar => libraryJars.contains(jar) || compilerJars.contains(jar))
|
||||||
|
.distinct
|
||||||
|
.toArray
|
||||||
|
val allJars = libraryJars ++ compilerJars ++ toolJars
|
||||||
|
|
||||||
|
val libraryLoader = classLoaderCache(libraryJars.toList, topLoader)
|
||||||
|
val compilerLoader = classLoaderCache(compilerJars.toList, libraryLoader)
|
||||||
|
val fullLoader =
|
||||||
|
if (toolJars.isEmpty) compilerLoader
|
||||||
|
else classLoaderCache(toolJars.distinct.toList, compilerLoader)
|
||||||
|
new ScalaInstance(
|
||||||
|
version = version,
|
||||||
|
loader = fullLoader,
|
||||||
|
loaderCompilerOnly = compilerLoader,
|
||||||
|
loaderLibraryOnly = libraryLoader,
|
||||||
|
libraryJars = libraryJars,
|
||||||
|
compilerJars = compilerJars,
|
||||||
|
allJars = allJars,
|
||||||
|
explicitActual = Some(version)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def noToolConfiguration(autoInstance: Boolean): String = {
|
||||||
|
val pre = "Missing Scala tool configuration from the 'update' report. "
|
||||||
|
val post =
|
||||||
|
if (autoInstance)
|
||||||
|
"'scala-tool' is normally added automatically, so this may indicate a bug in sbt or you may be removing it from ivyConfigurations, for example."
|
||||||
|
else
|
||||||
|
"Explicitly define scalaInstance or scalaHome or include Scala dependencies in the 'scala-tool' configuration."
|
||||||
|
pre + post
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,8 +14,8 @@ object Dependencies {
|
||||||
// sbt modules
|
// sbt modules
|
||||||
private val ioVersion = nightlyVersion.getOrElse("1.10.5")
|
private val ioVersion = nightlyVersion.getOrElse("1.10.5")
|
||||||
private val lmVersion =
|
private val lmVersion =
|
||||||
sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.11.6")
|
sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.12.0-M1")
|
||||||
val zincVersion = nightlyVersion.getOrElse("1.11.0")
|
val zincVersion = nightlyVersion.getOrElse("1.12.0-M1")
|
||||||
|
|
||||||
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue