Merge branch '1.10.x' into wip/merge-1.10.x

This commit is contained in:
Eugene Yokota 2025-02-10 00:59:52 -05:00
commit cb63de6574
40 changed files with 532 additions and 182 deletions

View File

@ -32,10 +32,10 @@ jobs:
java: 21
distribution: temurin
jobtype: 5
- os: ubuntu-latest
java: 8
distribution: zulu
jobtype: 6
# - os: ubuntu-latest
# java: 8
# distribution: zulu
# jobtype: 6
- os: ubuntu-latest
java: 8
distribution: zulu

View File

@ -4,21 +4,7 @@ jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check CLA
env:
AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
echo "Pull request submitted by $AUTHOR";
signed=$(curl -s "https://contribute.akka.io/contribute/cla/scala/check/$AUTHOR" | jq -r ".signed");
if [ "$signed" = "true" ] ; then
echo "CLA check for $AUTHOR successful";
else
echo "CLA check for $AUTHOR failed";
echo "Please sign the Scala CLA to contribute to the Scala compiler.";
echo "Go to https://contribute.akka.io/contribute/cla/scala and then";
echo "comment on the pull request to ask for a new check.";
echo "";
echo "Check if CLA is signed: https://contribute.akka.io/contribute/cla/scala/check/$AUTHOR";
exit 1;
fi;
- name: Check CLA
uses: scala/cla-checker@v1
with:
author: ${{ github.event.pull_request.user.login }}

View File

@ -12,6 +12,8 @@ import java.nio.file.{ Path, Paths }
import java.util.Locale
import scala.collection.concurrent.TrieMap
import scala.reflect.Selectable.reflectiveSelectable
import scala.util.control.NonFatal
object Util:
def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value)
@ -86,4 +88,46 @@ object Util:
case None =>
if sys.props("java.home").endsWith("jre") then Paths.get(sys.props("java.home")).getParent()
else Paths.get(sys.props("java.home"))
/**
* Given a list of event handlers expressed partial functions, combine them
* together using orElse from the left.
*/
def reduceIntents[A1, A2](intents: PartialFunction[A1, A2]*): PartialFunction[A1, A2] =
intents.toList.reduceLeft(_ orElse _)
lazy val majorJavaVersion: Int =
try {
val javaVersion = sys.props.get("java.version").getOrElse("1.0")
if (javaVersion.startsWith("1.")) {
javaVersion.split("\\.")(1).toInt
} else {
javaVersion.split("\\.")(0).toInt
}
} catch {
case NonFatal(_) => 0
}
private type GetId = {
def getId: Long
}
private type ThreadId = {
def threadId: Long
}
/**
* Returns current thread id.
* Thread.threadId was added in JDK 19, and deprecated Thread#getId
* https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Thread.html#threadId()
*/
def threadId: Long =
if (majorJavaVersion < 19) {
(Thread.currentThread(): AnyRef) match {
case g: GetId @unchecked => g.getId
}
} else {
(Thread.currentThread(): AnyRef) match {
case g: ThreadId @unchecked => g.threadId
}
}
end Util

View File

@ -22,7 +22,7 @@ abstract class SourcePositionImpl {
}
final class SourcePositionMacro(val c: blackbox.Context) {
import c.universe.{ NoPosition => _, _ }
import c.universe.{ NoPosition as _, * }
def fromEnclosingImpl(): Expr[SourcePosition] = {
val pos = c.enclosingPosition

View File

@ -56,6 +56,10 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler {
def fromStrings(paths: List[String]) = paths.map(fromString)
def fromString(path: String) = new File(baseDirectory, path)
def filterFromStrings(exprs: List[String]): List[PathFilter] =
def globs(exprs: List[String]): List[PathFilter] =
exprs.map: g =>
if g.startsWith("/") then (Glob(g): PathFilter)
else (Glob(baseDirectory, g): PathFilter)
def orGlobs =
val exprs1 = exprs
.mkString("")
@ -63,16 +67,16 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler {
.filter(_ != OR)
.toList
.map(_.trim)
val combined = exprs1.map(Glob(baseDirectory, _)) match
val combined = globs(exprs1) match
case Nil => sys.error("unexpected Nil")
case g :: Nil => (g: PathFilter)
case g :: Nil => g
case g :: gs =>
gs.foldLeft(g: PathFilter) { case (acc, g) =>
acc || (g: PathFilter)
gs.foldLeft(g) { case (acc, g) =>
acc || g
}
List(combined)
if exprs.contains("||") then orGlobs
else exprs.map(Glob(baseDirectory, _): PathFilter)
else globs(exprs)
def touch(paths: List[String]): Unit = IO.touch(fromStrings(paths))
def delete(paths: List[String]): Unit =
@ -130,10 +134,10 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler {
}
}
private type NamedCommand = (String, List[String] => Unit)
type NamedCommand = (String, List[String] => Unit)
// these are for readability of the command list
extension (commandName: String) {
extension (commandName: String)
def nonEmpty(action: List[String] => Unit): NamedCommand =
commandName -> { paths =>
if (paths.isEmpty)
@ -177,5 +181,4 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler {
"Wrong number of arguments to " + commandName + " command. " +
requiredArgs + " required, found: '" + spaced(args) + "'."
)
}
}

View File

@ -4,6 +4,7 @@ import minitest._
import scala.sys.process._
import java.io.File
import java.util.Locale
import sbt.io.IO
object SbtRunnerTest extends SimpleTestSuite with PowerAssertions {
// 1.3.0, 1.3.0-M4
@ -20,6 +21,10 @@ object SbtRunnerTest extends SimpleTestSuite with PowerAssertions {
sbt.internal.Process(Seq(sbtScript.getAbsolutePath) ++ args, new File("citest"),
"JAVA_OPTS" -> javaOpts,
"SBT_OPTS" -> sbtOpts)
def sbtProcessInDir(dir: File)(args: String*) =
sbt.internal.Process(Seq(sbtScript.getAbsolutePath) ++ args, dir,
"JAVA_OPTS" -> "",
"SBT_OPTS" -> "")
test("sbt runs") {
assert(sbtScript.exists)
@ -68,6 +73,27 @@ object SbtRunnerTest extends SimpleTestSuite with PowerAssertions {
}
}
test("sbt in empty directory") {
IO.withTemporaryDirectory { tmp =>
val out = sbtProcessInDir(tmp)("about").!
assert(out == 1)
}
IO.withTemporaryDirectory { tmp =>
val out = sbtProcessInDir(tmp)("about", "--allow-empty").!
assert(out == 0)
}
()
}
test("sbt --script-version in empty directory") {
IO.withTemporaryDirectory { tmp =>
val out = sbtProcessInDir(tmp)("--script-version").!!.trim
val expectedVersion = "^"+versionRegEx+"$"
assert(out.matches(expectedVersion))
}
()
}
/*
test("sbt --client") {
val out = sbtProcess("--client", "--no-colors", "compile").!!.linesIterator.toList

View File

@ -45,7 +45,7 @@ set sbt_args_timings=
set sbt_args_traces=
set sbt_args_sbt_boot=
set sbt_args_sbt_cache=
set sbt_args_sbt_create=
set sbt_args_allow_empty=
set sbt_args_sbt_dir=
set sbt_args_sbt_version=
set sbt_args_mem=
@ -72,11 +72,13 @@ rem TODO: remove/deprecate sbtconfig.txt and parse the sbtopts files
rem FIRST we load the config file of extra options.
set SBT_CONFIG=!SBT_HOME!\conf\sbtconfig.txt
set SBT_CFG_OPTS=
for /F "tokens=* eol=# usebackq delims=" %%i in ("!SBT_CONFIG!") do (
set DO_NOT_REUSE_ME=%%i
rem ZOMG (Part #2) WE use !! here to delay the expansion of
rem SBT_CFG_OPTS, otherwise it remains "" for this loop.
set SBT_CFG_OPTS=!SBT_CFG_OPTS! !DO_NOT_REUSE_ME!
if exist "!SBT_CONFIG!" (
for /F "tokens=* eol=# usebackq delims=" %%i in ("!SBT_CONFIG!") do (
set DO_NOT_REUSE_ME=%%i
rem ZOMG (Part #2) WE use !! here to delay the expansion of
rem SBT_CFG_OPTS, otherwise it remains "" for this loop.
set SBT_CFG_OPTS=!SBT_CFG_OPTS! !DO_NOT_REUSE_ME!
)
)
rem poor man's jenv (which is not available on Windows)
@ -235,12 +237,14 @@ if defined _traces_arg (
goto args_loop
)
if "%~0" == "-sbt-create" set _sbt_create_arg=true
if "%~0" == "--sbt-create" set _sbt_create_arg=true
if "%~0" == "-sbt-create" set _allow_empty_arg=true
if "%~0" == "--sbt-create" set _allow_empty_arg=true
if "%~0" == "-allow-empty" set _allow_empty_arg=true
if "%~0" == "--allow-empty" set _allow_empty_arg=true
if defined _sbt_create_arg (
set _sbt_create_arg=
set sbt_args_sbt_create=1
if defined _allow_empty_arg (
set _allow_empty_arg=
set sbt_args_allow_empty=1
goto args_loop
)
@ -526,25 +530,12 @@ goto args_loop
rem Confirm a user's intent if the current directory does not look like an sbt
rem top-level directory and the "new" command was not given.
if not defined sbt_args_sbt_create if not defined sbt_args_print_version if not defined sbt_args_print_sbt_version if not defined sbt_args_print_sbt_script_version if not defined shutdownall if not exist build.sbt (
if not defined sbt_args_allow_empty if not defined sbt_args_print_version if not defined sbt_args_print_sbt_version if not defined sbt_args_print_sbt_script_version if not defined shutdownall if not exist build.sbt (
if not exist project\ (
if not defined sbt_new (
echo [warn] Neither build.sbt nor a 'project' directory in the current directory: "%CD%"
setlocal
:confirm
echo c^) continue
echo q^) quit
set /P reply=^?
if /I "!reply!" == "c" (
goto confirm_end
) else if /I "!reply!" == "q" (
exit /B 1
)
goto confirm
:confirm_end
endlocal
echo [error] Neither build.sbt nor a 'project' directory in the current directory: "%CD%"
echo [error] run 'sbt new', touch build.sbt, or run 'sbt --allow-empty'.
goto error
)
)
)

View File

@ -9,7 +9,7 @@
# Starts sbt even if the current directory contains no sbt project.
#
-sbt-create
#--allow-empty
# Path to global settings/plugins directory (default: ~/.sbt)
#
@ -31,11 +31,11 @@
#
#-no-share
# Put SBT in offline mode.
# Put sbt in offline mode.
#
#-offline
# Sets the SBT version to use.
# Sets the sbt version to use.
#-sbt-version 0.11.3
# Scala version (default: latest release)

View File

@ -530,60 +530,76 @@ class NetworkClient(
.getOrElse(1)
case _ => 1
}
private def completeExec(execId: String, exitCode: => Int): Unit =
private val onAttachResponse: PartialFunction[JsonRpcResponseMessage, Unit] = {
case msg if attachUUID.get == msg.id =>
attachUUID.set(null)
attached.set(true)
Option(inputThread.get).foreach(_.drain())
()
}
def completeExec(execId: String, exitCode: Int) = {
pendingResults.remove(execId) match {
case null =>
case null => ()
case (q, startTime, name) =>
val now = System.currentTimeMillis
val message = NetworkClient.timing(startTime, now)
val ec = exitCode
if (batchMode.get || !attached.get) {
if (ec == 0) console.success(message)
if (exitCode == 0) console.success(message)
else console.appendLog(Level.Error, message)
}
Util.ignoreResult(q.offer(ec))
}
def onResponse(msg: JsonRpcResponseMessage): Unit = {
completeExec(msg.id, getExitCode(msg.result))
pendingCancellations.remove(msg.id) match {
case null =>
case q => q.offer(msg.toString.contains("Task cancelled"))
}
msg.id match {
case execId =>
if (attachUUID.get == msg.id) {
attachUUID.set(null)
attached.set(true)
Option(inputThread.get).foreach(_.drain())
}
pendingCompletions.remove(execId) match {
case null =>
case completions =>
completions(msg.result match {
case Some(o: JObject) =>
o.value
.foldLeft(CompletionResponse(Vector.empty[String])) { case (resp, i) =>
if (i.field == "items")
resp.withItems(
Converter
.fromJson[Vector[String]](i.value)
.getOrElse(Vector.empty[String])
)
else if (i.field == "cachedTestNames")
resp.withCachedTestNames(
Converter.fromJson[Boolean](i.value).getOrElse(true)
)
else if (i.field == "cachedMainClassNames")
resp.withCachedMainClassNames(
Converter.fromJson[Boolean](i.value).getOrElse(true)
)
else resp
}
case _ => CompletionResponse(Vector.empty[String])
})
}
Util.ignoreResult(q.offer(exitCode))
}
}
private val onExecResponse: PartialFunction[JsonRpcResponseMessage, Unit] = {
case msg if pendingResults.containsKey(msg.id) =>
completeExec(msg.id, getExitCode(msg.result))
}
private val onCancellationResponse: PartialFunction[JsonRpcResponseMessage, Unit] = {
case msg if pendingCancellations.containsKey(msg.id) =>
pendingCancellations.remove(msg.id) match {
case null => ()
case q => Util.ignoreResult(q.offer(msg.toString.contains("Task cancelled")))
}
}
private val onCompletionResponse: PartialFunction[JsonRpcResponseMessage, Unit] = {
case msg if pendingCompletions.containsKey(msg.id) =>
pendingCompletions.remove(msg.id) match {
case null => ()
case completions =>
completions(msg.result match {
case Some(o: JObject) =>
o.value
.foldLeft(CompletionResponse(Vector.empty[String])) { case (resp, i) =>
if (i.field == "items")
resp.withItems(
Converter
.fromJson[Vector[String]](i.value)
.getOrElse(Vector.empty[String])
)
else if (i.field == "cachedTestNames")
resp.withCachedTestNames(
Converter.fromJson[Boolean](i.value).getOrElse(true)
)
else if (i.field == "cachedMainClassNames")
resp.withCachedMainClassNames(
Converter.fromJson[Boolean](i.value).getOrElse(true)
)
else resp
}
case _ => CompletionResponse(Vector.empty[String])
})
}
}
// cache the composed plan
private val responsePlan = Util.reduceIntents[JsonRpcResponseMessage, Unit](
onExecResponse,
onCancellationResponse,
onAttachResponse,
onCompletionResponse,
{ case _ => () },
)
def onResponse(msg: JsonRpcResponseMessage): Unit = responsePlan(msg)
def onNotification(msg: JsonRpcNotificationMessage): Unit = {
def splitToMessage: Vector[(Level.Value, String)] =

View File

@ -191,6 +191,7 @@ object Defaults extends BuildCommon {
apiMappings := Map.empty,
autoScalaLibrary :== true,
managedScalaInstance :== true,
allowUnsafeScalaLibUpgrade :== false,
classpathEntryDefinesClass := { (file: File) =>
sys.error("use classpathEntryDefinesClassVF instead")
},
@ -296,6 +297,7 @@ object Defaults extends BuildCommon {
csrLogger := LMCoursier.coursierLoggerTask.value,
csrMavenProfiles :== Set.empty,
csrReconciliations :== LMCoursier.relaxedForAllModules,
csrMavenDependencyOverride :== false,
csrSameVersions := Seq(
ScalaArtifacts.Artifacts.map(a => InclExclRule(scalaOrganization.value, a)).toSet
),

View File

@ -499,6 +499,7 @@ object Keys {
val csrPublications = taskKey[Seq[(lmcoursier.definitions.Configuration, lmcoursier.definitions.Publication)]]("")
val csrReconciliations = settingKey[Seq[(ModuleMatchers, Reconciliation)]]("Strategy to reconcile version conflicts.")
val csrSameVersions = settingKey[Seq[Set[InclExclRule]]]("Modules to keep at the same version.")
val csrMavenDependencyOverride = settingKey[Boolean]("Enables Maven dependency override (bill of materials) support")
val internalConfigurationMap = settingKey[Configuration => Configuration]("Maps configurations to the actual configuration used to define the classpath.").withRank(CSetting)
val classpathConfiguration = taskKey[Configuration]("The configuration used to define the classpath.").withRank(CTask)
@ -615,6 +616,7 @@ object Keys {
val conflictManager = settingKey[ConflictManager]("Selects the conflict manager to use for dependency management.").withRank(CSetting)
val autoScalaLibrary = settingKey[Boolean]("Adds a dependency on scala-library if true.").withRank(ASetting)
val managedScalaInstance = settingKey[Boolean]("Automatically obtains Scala tools as managed dependencies if true.").withRank(BSetting)
val allowUnsafeScalaLibUpgrade = settingKey[Boolean]("Allow the Scala library on the compilation classpath to be newer than the scalaVersion (see Scala SIP-51).").withRank(CSetting)
val sbtResolver = settingKey[Resolver]("Provides a resolver for obtaining sbt as a dependency.").withRank(BMinusSetting)
val sbtResolvers = settingKey[Seq[Resolver]]("The external resolvers for sbt and plugin dependencies.").withRank(BMinusSetting)
val sbtDependency = settingKey[ModuleID]("Provides a definition for declaring the current version of sbt.").withRank(BMinusSetting)

View File

@ -95,6 +95,7 @@ object LMCoursier {
depsOverrides: Seq[ModuleID],
updateConfig: Option[UpdateConfiguration],
sameVersions: Seq[Set[InclExclRule]],
enableDependencyOverrides: Option[Boolean],
log: Logger
): CoursierConfiguration = {
val coursierExcludeDeps = Inputs
@ -146,6 +147,7 @@ object LMCoursier {
.withForceVersions(userForceVersions.toVector)
.withMissingOk(missingOk)
.withSameVersions(sameVersions)
// .withEnableDependencyOverrides(enableDependencyOverrides)
}
def coursierConfigurationTask: Def.Initialize[Task[CoursierConfiguration]] = Def.task {
@ -172,6 +174,7 @@ object LMCoursier {
dependencyOverrides.value,
Some(updateConfiguration.value),
csrSameVersions.value,
Some(csrMavenDependencyOverride.value),
streams.value.log
)
}
@ -207,6 +210,7 @@ object LMCoursier {
dependencyOverrides.value,
Some(updateConfiguration.value),
csrSameVersions.value,
Some(csrMavenDependencyOverride.value),
streams.value.log
)
}
@ -235,6 +239,7 @@ object LMCoursier {
dependencyOverrides.value,
Some(updateConfiguration.value),
csrSameVersions.value,
Some(csrMavenDependencyOverride.value),
streams.value.log
)
}

View File

@ -9,6 +9,7 @@
package sbt
package internal
import sbt.internal.util.Util
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong
import scala.jdk.CollectionConverters.*
@ -126,6 +127,7 @@ object AbstractTaskExecuteProgress {
private[sbt] class Timer() {
val startNanos: Long = System.nanoTime()
val threadName: String = Thread.currentThread().getName
val threadId: Long = Util.threadId
var endNanos: Long = 0L
def stop(): Unit = {
endNanos = System.nanoTime()

View File

@ -70,6 +70,7 @@ object Compiler:
def scalaInstanceFromUpdate: Def.Initialize[Task[ScalaInstance]] = Def.task {
val sv = Keys.scalaVersion.value
val fullReport = Keys.update.value
val s = Keys.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
@ -95,28 +96,39 @@ object Compiler:
.getOrElse(sys.error(noToolConfiguration(Keys.managedScalaInstance.value)))
)
if (Classpaths.isScala213(sv)) {
for {
compileReport <- fullReport.configuration(Configurations.Compile)
libName <- ScalaArtifacts.Artifacts
} {
for (lib <- compileReport.modules.find(_.module.name == libName)) {
val libVer = lib.module.revision
val n = Keys.name.value
if (VersionNumber(sv).matchesSemVer(SemanticSelector(s"<$libVer")))
sys.error(
s"""expected `$n/scalaVersion` to be "$libVer" or later,
|but found "$sv"; upgrade scalaVersion to fix the build.
|
|to support backwards-only binary compatibility (SIP-51),
|the Scala 2.13 compiler cannot be older than $libName on the
|dependency classpath.
|see `$n/evicted` to know why $libName $libVer is getting pulled in.
|""".stripMargin
)
}
}
}
if Classpaths.isScala213(sv) then
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) do
val libVer = lib.module.revision
val libName = lib.module.name
val n = Keys.name.value
if VersionNumber(sv).matchesSemVer(SemanticSelector(s"<$libVer")) then
val err = !Keys.allowUnsafeScalaLibUpgrade.value
val fix =
if err then
"""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 `$n/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 `$n/evicted` to know why $libName $libVer is getting pulled in.
|""".stripMargin
if err then sys.error(msg)
else s.log.warn(msg)
else ()
else ()
def file(id: String): File = {
val files = for {
m <- toolReport.modules if m.module.name.startsWith(id)

View File

@ -409,12 +409,18 @@ private[sbt] object CrossJava {
class MacOsDiscoverConfig extends JavaDiscoverConf {
val base: File = file("/Library") / "Java" / "JavaVirtualMachines"
// User-specific JDKs are installed, for example, by IntelliJ IDEA
private val baseInUserHome: File = Path.userHome / "Library" / "Java" / "JavaVirtualMachines"
def javaHomes: Vector[(String, File)] =
wrapNull(base.list())
.collect { case dir @ JavaHomeDir(version) =>
version -> (base / dir / "Contents" / "Home")
}
findAllHomes(base) ++
findAllHomes(baseInUserHome)
private def findAllHomes(root: File): Vector[(String, File)] = {
wrapNull(root.list()).collect { case dir @ JavaHomeDir(version) =>
version -> (root / dir / "Contents" / "Home")
}
}
}
class JabbaDiscoverConfig extends JavaDiscoverConf {

View File

@ -34,6 +34,7 @@ object LintUnused {
configuration,
crossScalaVersions,
crossSbtVersions,
allowUnsafeScalaLibUpgrade,
evictionWarningOptions,
initialize,
lintUnusedKeysOnLoad,

View File

@ -59,7 +59,7 @@ private[sbt] final class TaskTraceEvent extends AbstractTaskExecuteProgress with
def durationEvent(name: String, cat: String, t: Timer): String = {
val sb = new java.lang.StringBuilder(name.length + 2)
CompactPrinter.print(new JString(name), sb)
s"""{"name": ${sb.toString}, "cat": "$cat", "ph": "X", "ts": ${(t.startMicros)}, "dur": ${(t.durationMicros)}, "pid": 0, "tname": "${t.threadName}"}"""
s"""{"name": ${sb.toString}, "cat": "$cat", "ph": "X", "ts": ${(t.startMicros)}, "dur": ${(t.durationMicros)}, "pid": 0, "tid": "${t.threadId}"}"""
}
val entryIterator = currentTimings
while (entryIterator.hasNext) {

View File

@ -34,6 +34,7 @@ import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser as
import xsbti.CompileFailed
import java.io.File
import java.nio.file.Paths
import java.util.concurrent.atomic.AtomicBoolean
import scala.collection.mutable
@ -628,12 +629,18 @@ object BuildServerProtocol {
val thisProjectRef = Keys.thisProjectRef.value
val thisConfig = Keys.configuration.value
val scalaJars = Keys.scalaInstance.value.allJars.map(_.toURI.toString)
val (javaHomeForTarget, isForkedJava) = Keys.javaHome.value match
case Some(forkedJava) => (Some(forkedJava.toURI), true)
case None => (sys.props.get("java.home").map(Paths.get(_)).map(_.toUri), false)
val javaVersionForTarget = extractJavaVersion(javacOptions.value, isForkedJava)
val jvmBuildTarget = JvmBuildTarget(javaHomeForTarget, javaVersionForTarget)
val compileData = ScalaBuildTarget(
scalaOrganization = scalaOrganization.value,
scalaVersion = scalaVersion.value,
scalaBinaryVersion = scalaBinaryVersion.value,
platform = ScalaPlatform.JVM,
jars = scalaJars.toVector
jars = scalaJars.toVector,
jvmBuildTarget = jvmBuildTarget,
)
val configuration = Keys.configuration.value
val displayName = BuildTargetName.fromScope(thisProject.id, configuration.name)
@ -699,7 +706,11 @@ object BuildServerProtocol {
scalaVersion = scalaProvider.version(),
scalaBinaryVersion = binaryScalaVersion(scalaProvider.version()),
platform = ScalaPlatform.JVM,
jars = scalaJars.toVector.map(_.toURI.toString)
jars = scalaJars.toVector.map(_.toURI.toString),
jvmBuildTarget = JvmBuildTarget(
sys.props.get("java.home").map(Paths.get(_)).map(_.toUri),
sys.props.get("java.version")
),
)
val sbtVersionValue = sbtVersion.value
val sbtData = SbtBuildTarget(
@ -1057,6 +1068,26 @@ object BuildServerProtocol {
)
}
private def extractJavaVersion(
javacOptions: Seq[String],
isForkedJava: Boolean
): Option[String] = {
def getVersionAfterFlag(flag: String): Option[String] = {
val index = javacOptions.indexOf(flag)
if (index >= 0) javacOptions.lift(index + 1)
else None
}
val versionFromJavacOption = getVersionAfterFlag("--release")
.orElse(getVersionAfterFlag("--target"))
.orElse(getVersionAfterFlag("-target"))
versionFromJavacOption.orElse {
// TODO: extract java version from forked javac
if (isForkedJava) None else sys.props.get("java.version")
}
}
// naming convention still seems like the only reliable way to get IntelliJ to import this correctly
// https://github.com/JetBrains/intellij-scala/blob/a54c2a7c157236f35957049cbfd8c10587c9e60c/scala/scala-impl/src/org/jetbrains/sbt/language/SbtFileImpl.scala#L82-L84
private def toSbtTargetIdName(ref: LoadedBuildUnit): String = {

View File

@ -165,7 +165,7 @@ final class NetworkChannel(
}
}
val thread = new Thread(s"sbt-networkchannel-${connection.getPort}") {
private val thread = new Thread(s"sbt-networkchannel-${connection.getPort}") {
private val ct = "Content-Type: "
private val x1 = "application/sbt-x1"
override def run(): Unit = {
@ -243,7 +243,6 @@ final class NetworkChannel(
}
}
}
thread.start()
private[sbt] def isLanguageServerProtocol: Boolean = true
@ -370,7 +369,6 @@ final class NetworkChannel(
s"sbt-$name-write-thread"
)
writeThread.setDaemon(true)
writeThread.start()
def publishBytes(event: Array[Byte], delimit: Boolean): Unit =
try pendingWrites.put(event -> delimit)
@ -959,6 +957,9 @@ final class NetworkChannel(
}
}
private[sbt] def isAttached: Boolean = attached.get
thread.start()
writeThread.start()
}
object NetworkChannel {

View File

@ -27,7 +27,7 @@ object Giter8TemplatePlugin extends AutoPlugin {
ModuleID(
"org.scala-sbt.sbt-giter8-resolver",
"sbt-giter8-resolver",
"0.16.2"
"0.17.0"
).cross(CrossVersion.binary),
"sbtgiter8resolver.Giter8TemplateResolver"
)

View File

@ -3,7 +3,7 @@ import Keys.*
object Dependencies {
// WARNING: Please Scala update versions in PluginCross.scala too
val scala213 = "2.13.15"
val scala213 = "2.13.16"
val scala3 = "3.6.3"
val checkPluginCross = settingKey[Unit]("Make sure scalaVersion match up")
val baseScalaVersion = scala3
@ -77,7 +77,7 @@ object Dependencies {
// JLine 3 version must be coordinated together with JAnsi version
// and the JLine 2 fork version, which uses the same JAnsi
val jline =
"org.scala-sbt.jline" % "jline" % "2.14.7-sbt-9c3b6aca11c57e339441442bbf58e550cdfecb79"
"org.scala-sbt.jline" % "jline" % "2.14.7-sbt-9a88bc413e2b34a4580c001c654d1a7f4f65bf18"
val jline3Version = "3.27.1"
val jline3Terminal = "org.jline" % "jline-terminal" % jline3Version
val jline3JNI = "org.jline" % "jline-terminal-jni" % jline3Version
@ -119,7 +119,7 @@ object Dependencies {
// lm-coursier dependencies
val dataclassScalafixVersion = "0.1.0"
val coursierVersion = "2.1.19"
val coursierVersion = "2.1.23"
val coursier = ("io.get-coursier" %% "coursier" % coursierVersion)
.cross(CrossVersion.for3Use2_13)

View File

@ -1 +1 @@
sbt.version=1.10.5
sbt.version=1.10.7

View File

@ -0,0 +1,48 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* Contains jvm-specific metadata, specifically JDK reference
* @param javaHome Uri representing absolute path to jdk
* @param javaVersion The java version this target is supposed to use (can be set using javac `-target` flag)
*/
final class JvmBuildTarget private (
val javaHome: Option[java.net.URI],
val javaVersion: Option[String]) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: JvmBuildTarget => (this.javaHome == x.javaHome) && (this.javaVersion == x.javaVersion)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.JvmBuildTarget".##) + javaHome.##) + javaVersion.##)
}
override def toString: String = {
"JvmBuildTarget(" + javaHome + ", " + javaVersion + ")"
}
private def copy(javaHome: Option[java.net.URI] = javaHome, javaVersion: Option[String] = javaVersion): JvmBuildTarget = {
new JvmBuildTarget(javaHome, javaVersion)
}
def withJavaHome(javaHome: Option[java.net.URI]): JvmBuildTarget = {
copy(javaHome = javaHome)
}
def withJavaHome(javaHome: java.net.URI): JvmBuildTarget = {
copy(javaHome = Option(javaHome))
}
def withJavaVersion(javaVersion: Option[String]): JvmBuildTarget = {
copy(javaVersion = javaVersion)
}
def withJavaVersion(javaVersion: String): JvmBuildTarget = {
copy(javaVersion = Option(javaVersion))
}
}
object JvmBuildTarget {
def apply(javaHome: Option[java.net.URI], javaVersion: Option[String]): JvmBuildTarget = new JvmBuildTarget(javaHome, javaVersion)
def apply(javaHome: java.net.URI, javaVersion: String): JvmBuildTarget = new JvmBuildTarget(Option(javaHome), Option(javaVersion))
}

View File

@ -14,28 +14,30 @@ package sbt.internal.bsp
For example, 2.12 if scalaVersion is 2.12.4.
* @param platform The target platform for this target
* @param jars A sequence of Scala jars such as scala-library, scala-compiler and scala-reflect.
* @param jvmBuildTarget The jvm build target describing jdk to be used
*/
final class ScalaBuildTarget private (
val scalaOrganization: String,
val scalaVersion: String,
val scalaBinaryVersion: String,
val platform: Int,
val jars: Vector[String]) extends Serializable {
val jars: Vector[String],
val jvmBuildTarget: Option[sbt.internal.bsp.JvmBuildTarget]) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: ScalaBuildTarget => (this.scalaOrganization == x.scalaOrganization) && (this.scalaVersion == x.scalaVersion) && (this.scalaBinaryVersion == x.scalaBinaryVersion) && (this.platform == x.platform) && (this.jars == x.jars)
case x: ScalaBuildTarget => (this.scalaOrganization == x.scalaOrganization) && (this.scalaVersion == x.scalaVersion) && (this.scalaBinaryVersion == x.scalaBinaryVersion) && (this.platform == x.platform) && (this.jars == x.jars) && (this.jvmBuildTarget == x.jvmBuildTarget)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaBuildTarget".##) + scalaOrganization.##) + scalaVersion.##) + scalaBinaryVersion.##) + platform.##) + jars.##)
37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaBuildTarget".##) + scalaOrganization.##) + scalaVersion.##) + scalaBinaryVersion.##) + platform.##) + jars.##) + jvmBuildTarget.##)
}
override def toString: String = {
"ScalaBuildTarget(" + scalaOrganization + ", " + scalaVersion + ", " + scalaBinaryVersion + ", " + platform + ", " + jars + ")"
"ScalaBuildTarget(" + scalaOrganization + ", " + scalaVersion + ", " + scalaBinaryVersion + ", " + platform + ", " + jars + ", " + jvmBuildTarget + ")"
}
private def copy(scalaOrganization: String = scalaOrganization, scalaVersion: String = scalaVersion, scalaBinaryVersion: String = scalaBinaryVersion, platform: Int = platform, jars: Vector[String] = jars): ScalaBuildTarget = {
new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars)
private def copy(scalaOrganization: String = scalaOrganization, scalaVersion: String = scalaVersion, scalaBinaryVersion: String = scalaBinaryVersion, platform: Int = platform, jars: Vector[String] = jars, jvmBuildTarget: Option[sbt.internal.bsp.JvmBuildTarget] = jvmBuildTarget): ScalaBuildTarget = {
new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, jvmBuildTarget)
}
def withScalaOrganization(scalaOrganization: String): ScalaBuildTarget = {
copy(scalaOrganization = scalaOrganization)
@ -52,8 +54,15 @@ final class ScalaBuildTarget private (
def withJars(jars: Vector[String]): ScalaBuildTarget = {
copy(jars = jars)
}
def withJvmBuildTarget(jvmBuildTarget: Option[sbt.internal.bsp.JvmBuildTarget]): ScalaBuildTarget = {
copy(jvmBuildTarget = jvmBuildTarget)
}
def withJvmBuildTarget(jvmBuildTarget: sbt.internal.bsp.JvmBuildTarget): ScalaBuildTarget = {
copy(jvmBuildTarget = Option(jvmBuildTarget))
}
}
object ScalaBuildTarget {
def apply(scalaOrganization: String, scalaVersion: String, scalaBinaryVersion: String, platform: Int, jars: Vector[String]): ScalaBuildTarget = new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars)
def apply(scalaOrganization: String, scalaVersion: String, scalaBinaryVersion: String, platform: Int, jars: Vector[String], jvmBuildTarget: Option[sbt.internal.bsp.JvmBuildTarget]): ScalaBuildTarget = new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, jvmBuildTarget)
def apply(scalaOrganization: String, scalaVersion: String, scalaBinaryVersion: String, platform: Int, jars: Vector[String], jvmBuildTarget: sbt.internal.bsp.JvmBuildTarget): ScalaBuildTarget = new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, Option(jvmBuildTarget))
}

View File

@ -56,6 +56,7 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.bsp.codec.TestResultFormats
with sbt.internal.bsp.codec.RunParamsFormats
with sbt.internal.bsp.codec.RunResultFormats
with sbt.internal.bsp.codec.JvmBuildTargetFormats
with sbt.internal.bsp.codec.ScalaBuildTargetFormats
with sbt.internal.bsp.codec.ScalacOptionsParamsFormats
with sbt.internal.bsp.codec.ScalacOptionsItemFormats

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait JvmBuildTargetFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val JvmBuildTargetFormat: JsonFormat[sbt.internal.bsp.JvmBuildTarget] = new JsonFormat[sbt.internal.bsp.JvmBuildTarget] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JvmBuildTarget = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val javaHome = unbuilder.readField[Option[java.net.URI]]("javaHome")
val javaVersion = unbuilder.readField[Option[String]]("javaVersion")
unbuilder.endObject()
sbt.internal.bsp.JvmBuildTarget(javaHome, javaVersion)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.JvmBuildTarget, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("javaHome", obj.javaHome)
builder.addField("javaVersion", obj.javaVersion)
builder.endObject()
}
}
}

View File

@ -5,7 +5,7 @@
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait SbtBuildTargetFormats { self: sbt.internal.bsp.codec.ScalaBuildTargetFormats & sjsonnew.BasicJsonProtocol & sbt.internal.bsp.codec.BuildTargetIdentifierFormats =>
trait SbtBuildTargetFormats { self: sbt.internal.bsp.codec.ScalaBuildTargetFormats & sbt.internal.bsp.codec.JvmBuildTargetFormats & sjsonnew.BasicJsonProtocol & sbt.internal.bsp.codec.BuildTargetIdentifierFormats =>
implicit lazy val SbtBuildTargetFormat: JsonFormat[sbt.internal.bsp.SbtBuildTarget] = new JsonFormat[sbt.internal.bsp.SbtBuildTarget] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.SbtBuildTarget = {
__jsOpt match {

View File

@ -5,7 +5,7 @@
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait ScalaBuildTargetFormats { self: sjsonnew.BasicJsonProtocol =>
trait ScalaBuildTargetFormats { self: sbt.internal.bsp.codec.JvmBuildTargetFormats & sjsonnew.BasicJsonProtocol =>
implicit lazy val ScalaBuildTargetFormat: JsonFormat[sbt.internal.bsp.ScalaBuildTarget] = new JsonFormat[sbt.internal.bsp.ScalaBuildTarget] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaBuildTarget = {
__jsOpt match {
@ -16,8 +16,9 @@ implicit lazy val ScalaBuildTargetFormat: JsonFormat[sbt.internal.bsp.ScalaBuild
val scalaBinaryVersion = unbuilder.readField[String]("scalaBinaryVersion")
val platform = unbuilder.readField[Int]("platform")
val jars = unbuilder.readField[Vector[String]]("jars")
val jvmBuildTarget = unbuilder.readField[Option[sbt.internal.bsp.JvmBuildTarget]]("jvmBuildTarget")
unbuilder.endObject()
sbt.internal.bsp.ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars)
sbt.internal.bsp.ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, jvmBuildTarget)
case None =>
deserializationError("Expected JsObject but found None")
}
@ -29,6 +30,7 @@ implicit lazy val ScalaBuildTargetFormat: JsonFormat[sbt.internal.bsp.ScalaBuild
builder.addField("scalaBinaryVersion", obj.scalaBinaryVersion)
builder.addField("platform", obj.platform)
builder.addField("jars", obj.jars)
builder.addField("jvmBuildTarget", obj.jvmBuildTarget)
builder.endObject()
}
}

View File

@ -615,6 +615,17 @@ type RunResult {
}
# JVM extension
## Contains jvm-specific metadata, specifically JDK reference
type JvmBuildTarget {
## Uri representing absolute path to jdk
javaHome: java.net.URI
## The java version this target is supposed to use (can be set using javac `-target` flag)
javaVersion: String
}
# Scala Extension
## Contains scala-specific metadata for compiling a target containing Scala sources.
@ -636,6 +647,9 @@ type ScalaBuildTarget {
## A sequence of Scala jars such as scala-library, scala-compiler and scala-reflect.
jars: [String]!
## The jvm build target describing jdk to be used
jvmBuildTarget: sbt.internal.bsp.JvmBuildTarget
}
## Scalac options

67
sbt
View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
set +e
declare builtin_sbt_version="1.10.6"
declare builtin_sbt_version="1.10.7"
declare -a residual_args
declare -a java_args
declare -a scalac_args
@ -25,6 +25,7 @@ declare use_sbtn=
declare no_server=
declare sbtn_command="$SBTN_CMD"
declare sbtn_version="1.10.5"
declare use_colors=1
### ------------------------------- ###
### Helper methods for BASH scripts ###
@ -104,6 +105,15 @@ declare -r sbt_home="$(dirname "$sbt_bin_dir")"
echoerr () {
echo 1>&2 "$@"
}
RED='\033[0;31m'
NC='\033[0m' # No Color
echoerr_error () {
if [[ $use_colors == "1" ]]; then
echoerr -e "[${RED}error${NC}] $@"
else
echoerr "[error] $@"
fi
}
vlog () {
[[ $sbt_verbose || $sbt_debug ]] && echoerr "$@"
}
@ -486,6 +496,21 @@ copyRt() {
fi
}
# Confirm a user's intent if the current directory does not look like an sbt
# top-level directory and neither the --allow-empty option nor the "new" command was given.
checkWorkingDirectory() {
if [[ ! -n "$allow_empty" ]]; then
[[ -f ./build.sbt || -d ./project || -n "$sbt_new" ]] || {
echoerr_error "Neither build.sbt nor a 'project' directory in the current directory: $(pwd)"
echoerr_error "run 'sbt new', touch build.sbt, or run 'sbt --allow-empty'."
echoerr_error ""
echoerr_error "To opt out of this check, create ${config_home}/sbtopts with:"
echoerr_error "--allow-empty"
exit 1
}
fi
}
run() {
# Copy preloaded repo to user's preloaded directory
syncPreloaded
@ -522,6 +547,7 @@ run() {
done
echo "shutdown ${#sbt_processes[@]} sbt processes"
else
checkWorkingDirectory
# run sbt
execRunner "$java_cmd" \
"${java_args[@]}" \
@ -546,7 +572,10 @@ declare -r sbt_opts_file=".sbtopts"
declare -r build_props_file="$(pwd)/project/build.properties"
declare -r etc_sbt_opts_file="/etc/sbt/sbtopts"
# this allows /etc/sbt/sbtopts location to be changed
declare -r etc_file="${SBT_ETC_FILE:-$etc_sbt_opts_file}"
declare machine_sbt_opts_file="${etc_sbt_opts_file}"
declare config_home="${XDG_CONFIG_HOME:-$HOME/.config}/sbt"
[[ -f "${config_home}/sbtopts" ]] && machine_sbt_opts_file="${config_home}/sbtopts"
[[ -f "$SBT_ETC_FILE" ]] && machine_sbt_opts_file="$SBT_ETC_FILE"
declare -r dist_sbt_opts_file="${sbt_home}/conf/sbtopts"
declare -r win_sbt_opts_file="${sbt_home}/conf/sbtconfig.txt"
declare sbt_jar="$(jar_file)"
@ -571,7 +600,7 @@ Usage: `basename "$0"` [options]
enable or disable supershell (sbt 1.3 and above)
--traces generate Trace Event report on shutdown (sbt 1.3 and above)
--timings display task timings report on shutdown
--sbt-create start sbt even if current directory contains no sbt project
--allow-empty start sbt even if current directory contains no sbt project
--sbt-dir <path> path to global settings/plugins directory (default: ~/.sbt)
--sbt-boot <path> path to shared boot directory (default: ~/.sbt/boot in 0.11 series)
--sbt-cache <path> path to global cache directory (default: operating system specific)
@ -610,7 +639,7 @@ process_my_args () {
case "$1" in
-batch|--batch) exec </dev/null && shift ;; #>
-sbt-create|--sbt-create) sbt_create=true && shift ;;
-allow-empty|--allow-empty|-sbt-create|--sbt-create) allow_empty=true && shift ;;
new) sbt_new=true && addResidual "$1" && shift ;;
@ -620,23 +649,6 @@ process_my_args () {
# Now, ensure sbt version is used.
[[ "${sbt_version}XXX" != "XXX" ]] && addJava "-Dsbt.version=$sbt_version"
# Confirm a user's intent if the current directory does not look like an sbt
# top-level directory and neither the -sbt-create option nor the "new"
# command was given.
[[ -f ./build.sbt || -d ./project || -n "$sbt_create" || -n "$sbt_new" ]] || {
echo "[warn] Neither build.sbt nor a 'project' directory in the current directory: $(pwd)"
while true; do
echo 'c) continue'
echo 'q) quit'
read -p '? ' || exit 1
case "$REPLY" in
c|C) break ;;
q|Q) exit 1 ;;
esac
done
}
}
## map over argument array. this is used to process both command line arguments and SBT_OPTS
@ -697,6 +709,7 @@ process_args () {
export PATH="$2/bin:$PATH" &&
shift 2 ;;
-Dsbt.color=never|-Dsbt.log.noformat=true) addJava "$1" && use_colors=0 && shift ;;
"-D*"|-D*) addJava "$1" && shift ;;
-J*) addJava "${1:2}" && shift ;;
*) addResidual "$1" && shift ;;
@ -788,11 +801,13 @@ runNativeClient() {
original_args=("$@")
# Here we pull in the default settings configuration.
[[ -f "$dist_sbt_opts_file" ]] && set -- $(loadConfigFile "$dist_sbt_opts_file") "$@"
# Here we pull in the global settings configuration.
[[ -f "$etc_file" ]] && set -- $(loadConfigFile "$etc_file") "$@"
# Pull in the machine-wide settings configuration.
if [[ -f "$machine_sbt_opts_file" ]]; then
set -- $(loadConfigFile "$machine_sbt_opts_file") "$@"
else
# Otherwise pull in the default settings configuration.
[[ -f "$dist_sbt_opts_file" ]] && set -- $(loadConfigFile "$dist_sbt_opts_file") "$@"
fi
# Pull in the project-level config file, if it exists.
[[ -f "$sbt_opts_file" ]] && set -- $(loadConfigFile "$sbt_opts_file") "$@"

View File

@ -2,3 +2,5 @@
-> demo-failure
> +z
> z
$ absent /tmp/non-existent

View File

@ -4,6 +4,7 @@ libraryDependencies ++= Seq(
"org.slf4j" % "slf4j-api" % "1.7.2",
"ch.qos.logback" % "logback-classic" % "1.0.7"
)
csrMavenDependencyOverride := false
TaskKey[Unit]("check") := {
val report = updateFull.value

View File

@ -0,0 +1,27 @@
import scala.language.reflectiveCalls
package scala.collection.immutable {
object Exp {
// Access RedBlackTree.validate added in Scala 2.13.13
def v = RedBlackTree.validate(null)(null)
}
}
object A extends App {
println(scala.util.Properties.versionString)
}
object AMacro {
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
def m(x: Int): Int = macro impl
def impl(c: Context)(x: c.Expr[Int]): c.Expr[Int] = {
import c.universe._
println(scala.collection.immutable.Exp.v)
c.Expr(q"2 + $x")
}
}

View File

@ -0,0 +1,7 @@
import java.nio.file.{Paths, Files}
import java.nio.charset.StandardCharsets
object B extends App {
println(AMacro.m(33)) // fails
Files.write(Paths.get(s"s${scala.util.Properties.versionNumberString}.txt"), "nix".getBytes)
}

View File

@ -0,0 +1,39 @@
import sbt.librarymanagement.InclExclRule
lazy val a = project.settings(
scalaVersion := "2.13.13",
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value,
TaskKey[Unit]("checkLibs") := checkLibs("2.13.13", (Compile/dependencyClasspath).value, ".*scala-(library|reflect).*"),
)
lazy val b = project.dependsOn(a).settings(
allowUnsafeScalaLibUpgrade := true,
scalaVersion := "2.13.12",
// dependencies are upgraded to 2.13.13
TaskKey[Unit]("checkLibs") := checkLibs("2.13.13", (Compile/dependencyClasspath).value, ".*scala-(library|reflect).*"),
// check the compiler uses the 2.13.12 library on its runtime classpath
TaskKey[Unit]("checkScala") := {
val i = scalaInstance.value
i.libraryJars.filter(_.toString.contains("scala-library")).toList match {
case List(l) => assert(l.toString.contains("2.13.12"), i.toString)
}
assert(i.compilerJars.filter(_.toString.contains("scala-library")).isEmpty, i.toString)
assert(i.otherJars.filter(_.toString.contains("scala-library")).isEmpty, i.toString)
},
)
lazy val c = project.dependsOn(a).settings(
allowUnsafeScalaLibUpgrade := true,
scalaVersion := "2.13.12",
TaskKey[Unit]("checkLibs") := checkLibs("2.13.13", (Compile/dependencyClasspath).value, ".*scala-(library|reflect).*"),
)
def checkLibs(v: String, cp: Classpath, filter: String): Unit = {
for (p <- cp)
if (p.toString.matches(filter)) {
println(s"$p -- $v")
assert(p.toString.contains(v), p)
}
}

View File

@ -0,0 +1,7 @@
import java.nio.file.{Paths, Files}
import java.nio.charset.StandardCharsets
object C extends App {
assert(scala.collection.immutable.Exp.v == null)
Files.write(Paths.get(s"s${scala.util.Properties.versionNumberString}.txt"), "nix".getBytes)
}

View File

@ -0,0 +1,11 @@
> a/checkLibs
> b/checkLibs
> b/checkScala
> c/checkLibs
# macro expansion fails
-> b/compile
> c/runBlock
$ exists s2.13.13.txt
$ delete s2.13.13.txt

View File

@ -1,6 +1,6 @@
# This tests that this sbt scripted plugin can launch the previous one
> ^^1.10.5
> ^^1.10.7
$ copy-file changes/A.scala src/sbt-test/a/b/A.scala
> scripted

View File

@ -17,7 +17,7 @@ import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
import java.io.File
import java.net.URI
import java.nio.file.Files
import java.nio.file.{ Files, Paths }
import java.util.concurrent.atomic.AtomicInteger
import scala.concurrent.duration.*
@ -57,6 +57,16 @@ class BuildServerTest extends AbstractServerTest {
case None => sys.error(s"buildserver-root-build not in ${result.targets}")
assert(buildServerBuildTarget.id.uri.toString.endsWith("#buildserver-root-build"))
assert(!result.targets.exists(_.displayName.contains("badBuildTarget")))
// Check for JVM based Scala Project, built target should contain Java version information
val scalaBuildTarget =
Converter.fromJsonOptionUnsafe[ScalaBuildTarget](utilTarget.data)
val javaTarget = scalaBuildTarget.jvmBuildTarget
(javaTarget.flatMap(_.javaVersion), javaTarget.flatMap(_.javaHome)) match {
case (Some(javaVersion), Some(javaHome)) =>
assert(javaVersion.equals(sys.props("java.version")))
assert(javaHome.equals(Paths.get(sys.props("java.home")).toUri))
case _ => fail("JVM build target should contain javaVersion and javaHome")
}
}
test("buildTarget/sources") {