Merge pull request #8036 from eed3si9n/wip/merge-1.10.x

[2.x] Merge 1.10.x branch
This commit is contained in:
eugene yokota 2025-02-10 03:02:47 -05:00 committed by GitHub
commit 2a47d4471b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 532 additions and 182 deletions

View File

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

View File

@ -4,21 +4,7 @@ jobs:
check: check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - name: Check CLA
- name: Check CLA uses: scala/cla-checker@v1
env: with:
AUTHOR: ${{ github.event.pull_request.user.login }} 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;

View File

@ -12,6 +12,8 @@ import java.nio.file.{ Path, Paths }
import java.util.Locale import java.util.Locale
import scala.collection.concurrent.TrieMap import scala.collection.concurrent.TrieMap
import scala.reflect.Selectable.reflectiveSelectable
import scala.util.control.NonFatal
object Util: object Util:
def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value) def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value)
@ -86,4 +88,46 @@ object Util:
case None => case None =>
if sys.props("java.home").endsWith("jre") then Paths.get(sys.props("java.home")).getParent() if sys.props("java.home").endsWith("jre") then Paths.get(sys.props("java.home")).getParent()
else Paths.get(sys.props("java.home")) 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 end Util

View File

@ -22,7 +22,7 @@ abstract class SourcePositionImpl {
} }
final class SourcePositionMacro(val c: blackbox.Context) { final class SourcePositionMacro(val c: blackbox.Context) {
import c.universe.{ NoPosition => _, _ } import c.universe.{ NoPosition as _, * }
def fromEnclosingImpl(): Expr[SourcePosition] = { def fromEnclosingImpl(): Expr[SourcePosition] = {
val pos = c.enclosingPosition 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 fromStrings(paths: List[String]) = paths.map(fromString)
def fromString(path: String) = new File(baseDirectory, path) def fromString(path: String) = new File(baseDirectory, path)
def filterFromStrings(exprs: List[String]): List[PathFilter] = 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 = def orGlobs =
val exprs1 = exprs val exprs1 = exprs
.mkString("") .mkString("")
@ -63,16 +67,16 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler {
.filter(_ != OR) .filter(_ != OR)
.toList .toList
.map(_.trim) .map(_.trim)
val combined = exprs1.map(Glob(baseDirectory, _)) match val combined = globs(exprs1) match
case Nil => sys.error("unexpected Nil") case Nil => sys.error("unexpected Nil")
case g :: Nil => (g: PathFilter) case g :: Nil => g
case g :: gs => case g :: gs =>
gs.foldLeft(g: PathFilter) { case (acc, g) => gs.foldLeft(g) { case (acc, g) =>
acc || (g: PathFilter) acc || g
} }
List(combined) List(combined)
if exprs.contains("||") then orGlobs 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 touch(paths: List[String]): Unit = IO.touch(fromStrings(paths))
def delete(paths: List[String]): Unit = 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 // these are for readability of the command list
extension (commandName: String) { extension (commandName: String)
def nonEmpty(action: List[String] => Unit): NamedCommand = def nonEmpty(action: List[String] => Unit): NamedCommand =
commandName -> { paths => commandName -> { paths =>
if (paths.isEmpty) if (paths.isEmpty)
@ -177,5 +181,4 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler {
"Wrong number of arguments to " + commandName + " command. " + "Wrong number of arguments to " + commandName + " command. " +
requiredArgs + " required, found: '" + spaced(args) + "'." requiredArgs + " required, found: '" + spaced(args) + "'."
) )
}
} }

View File

@ -4,6 +4,7 @@ import minitest._
import scala.sys.process._ import scala.sys.process._
import java.io.File import java.io.File
import java.util.Locale import java.util.Locale
import sbt.io.IO
object SbtRunnerTest extends SimpleTestSuite with PowerAssertions { object SbtRunnerTest extends SimpleTestSuite with PowerAssertions {
// 1.3.0, 1.3.0-M4 // 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"), sbt.internal.Process(Seq(sbtScript.getAbsolutePath) ++ args, new File("citest"),
"JAVA_OPTS" -> javaOpts, "JAVA_OPTS" -> javaOpts,
"SBT_OPTS" -> sbtOpts) "SBT_OPTS" -> sbtOpts)
def sbtProcessInDir(dir: File)(args: String*) =
sbt.internal.Process(Seq(sbtScript.getAbsolutePath) ++ args, dir,
"JAVA_OPTS" -> "",
"SBT_OPTS" -> "")
test("sbt runs") { test("sbt runs") {
assert(sbtScript.exists) 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") { test("sbt --client") {
val out = sbtProcess("--client", "--no-colors", "compile").!!.linesIterator.toList 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_traces=
set sbt_args_sbt_boot= set sbt_args_sbt_boot=
set sbt_args_sbt_cache= set sbt_args_sbt_cache=
set sbt_args_sbt_create= set sbt_args_allow_empty=
set sbt_args_sbt_dir= set sbt_args_sbt_dir=
set sbt_args_sbt_version= set sbt_args_sbt_version=
set sbt_args_mem= 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. rem FIRST we load the config file of extra options.
set SBT_CONFIG=!SBT_HOME!\conf\sbtconfig.txt set SBT_CONFIG=!SBT_HOME!\conf\sbtconfig.txt
set SBT_CFG_OPTS= set SBT_CFG_OPTS=
for /F "tokens=* eol=# usebackq delims=" %%i in ("!SBT_CONFIG!") do ( if exist "!SBT_CONFIG!" (
set DO_NOT_REUSE_ME=%%i for /F "tokens=* eol=# usebackq delims=" %%i in ("!SBT_CONFIG!") do (
rem ZOMG (Part #2) WE use !! here to delay the expansion of set DO_NOT_REUSE_ME=%%i
rem SBT_CFG_OPTS, otherwise it remains "" for this loop. rem ZOMG (Part #2) WE use !! here to delay the expansion of
set SBT_CFG_OPTS=!SBT_CFG_OPTS! !DO_NOT_REUSE_ME! 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) rem poor man's jenv (which is not available on Windows)
@ -235,12 +237,14 @@ if defined _traces_arg (
goto args_loop goto args_loop
) )
if "%~0" == "-sbt-create" set _sbt_create_arg=true if "%~0" == "-sbt-create" set _allow_empty_arg=true
if "%~0" == "--sbt-create" set _sbt_create_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 ( if defined _allow_empty_arg (
set _sbt_create_arg= set _allow_empty_arg=
set sbt_args_sbt_create=1 set sbt_args_allow_empty=1
goto args_loop 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 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. 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 exist project\ (
if not defined sbt_new ( if not defined sbt_new (
echo [warn] Neither build.sbt nor a 'project' directory in the current directory: "%CD%" echo [error] Neither build.sbt nor a 'project' directory in the current directory: "%CD%"
setlocal echo [error] run 'sbt new', touch build.sbt, or run 'sbt --allow-empty'.
:confirm goto error
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
) )
) )
) )

View File

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

View File

@ -530,60 +530,76 @@ class NetworkClient(
.getOrElse(1) .getOrElse(1)
case _ => 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 { pendingResults.remove(execId) match {
case null => case null => ()
case (q, startTime, name) => case (q, startTime, name) =>
val now = System.currentTimeMillis val now = System.currentTimeMillis
val message = NetworkClient.timing(startTime, now) val message = NetworkClient.timing(startTime, now)
val ec = exitCode
if (batchMode.get || !attached.get) { if (batchMode.get || !attached.get) {
if (ec == 0) console.success(message) if (exitCode == 0) console.success(message)
else console.appendLog(Level.Error, message) else console.appendLog(Level.Error, message)
} }
Util.ignoreResult(q.offer(ec)) Util.ignoreResult(q.offer(exitCode))
}
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])
})
}
} }
} }
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 onNotification(msg: JsonRpcNotificationMessage): Unit = {
def splitToMessage: Vector[(Level.Value, String)] = def splitToMessage: Vector[(Level.Value, String)] =

View File

@ -191,6 +191,7 @@ object Defaults extends BuildCommon {
apiMappings := Map.empty, apiMappings := Map.empty,
autoScalaLibrary :== true, autoScalaLibrary :== true,
managedScalaInstance :== true, managedScalaInstance :== true,
allowUnsafeScalaLibUpgrade :== false,
classpathEntryDefinesClass := { (file: File) => classpathEntryDefinesClass := { (file: File) =>
sys.error("use classpathEntryDefinesClassVF instead") sys.error("use classpathEntryDefinesClassVF instead")
}, },
@ -296,6 +297,7 @@ object Defaults extends BuildCommon {
csrLogger := LMCoursier.coursierLoggerTask.value, csrLogger := LMCoursier.coursierLoggerTask.value,
csrMavenProfiles :== Set.empty, csrMavenProfiles :== Set.empty,
csrReconciliations :== LMCoursier.relaxedForAllModules, csrReconciliations :== LMCoursier.relaxedForAllModules,
csrMavenDependencyOverride :== false,
csrSameVersions := Seq( csrSameVersions := Seq(
ScalaArtifacts.Artifacts.map(a => InclExclRule(scalaOrganization.value, a)).toSet 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 csrPublications = taskKey[Seq[(lmcoursier.definitions.Configuration, lmcoursier.definitions.Publication)]]("")
val csrReconciliations = settingKey[Seq[(ModuleMatchers, Reconciliation)]]("Strategy to reconcile version conflicts.") 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 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 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) 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 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 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 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 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 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) 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], depsOverrides: Seq[ModuleID],
updateConfig: Option[UpdateConfiguration], updateConfig: Option[UpdateConfiguration],
sameVersions: Seq[Set[InclExclRule]], sameVersions: Seq[Set[InclExclRule]],
enableDependencyOverrides: Option[Boolean],
log: Logger log: Logger
): CoursierConfiguration = { ): CoursierConfiguration = {
val coursierExcludeDeps = Inputs val coursierExcludeDeps = Inputs
@ -146,6 +147,7 @@ object LMCoursier {
.withForceVersions(userForceVersions.toVector) .withForceVersions(userForceVersions.toVector)
.withMissingOk(missingOk) .withMissingOk(missingOk)
.withSameVersions(sameVersions) .withSameVersions(sameVersions)
// .withEnableDependencyOverrides(enableDependencyOverrides)
} }
def coursierConfigurationTask: Def.Initialize[Task[CoursierConfiguration]] = Def.task { def coursierConfigurationTask: Def.Initialize[Task[CoursierConfiguration]] = Def.task {
@ -172,6 +174,7 @@ object LMCoursier {
dependencyOverrides.value, dependencyOverrides.value,
Some(updateConfiguration.value), Some(updateConfiguration.value),
csrSameVersions.value, csrSameVersions.value,
Some(csrMavenDependencyOverride.value),
streams.value.log streams.value.log
) )
} }
@ -207,6 +210,7 @@ object LMCoursier {
dependencyOverrides.value, dependencyOverrides.value,
Some(updateConfiguration.value), Some(updateConfiguration.value),
csrSameVersions.value, csrSameVersions.value,
Some(csrMavenDependencyOverride.value),
streams.value.log streams.value.log
) )
} }
@ -235,6 +239,7 @@ object LMCoursier {
dependencyOverrides.value, dependencyOverrides.value,
Some(updateConfiguration.value), Some(updateConfiguration.value),
csrSameVersions.value, csrSameVersions.value,
Some(csrMavenDependencyOverride.value),
streams.value.log streams.value.log
) )
} }

View File

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

View File

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

View File

@ -409,12 +409,18 @@ private[sbt] object CrossJava {
class MacOsDiscoverConfig extends JavaDiscoverConf { class MacOsDiscoverConfig extends JavaDiscoverConf {
val base: File = file("/Library") / "Java" / "JavaVirtualMachines" 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)] = def javaHomes: Vector[(String, File)] =
wrapNull(base.list()) findAllHomes(base) ++
.collect { case dir @ JavaHomeDir(version) => findAllHomes(baseInUserHome)
version -> (base / dir / "Contents" / "Home")
} 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 { class JabbaDiscoverConfig extends JavaDiscoverConf {

View File

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

View File

@ -59,7 +59,7 @@ private[sbt] final class TaskTraceEvent extends AbstractTaskExecuteProgress with
def durationEvent(name: String, cat: String, t: Timer): String = { def durationEvent(name: String, cat: String, t: Timer): String = {
val sb = new java.lang.StringBuilder(name.length + 2) val sb = new java.lang.StringBuilder(name.length + 2)
CompactPrinter.print(new JString(name), sb) 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 val entryIterator = currentTimings
while (entryIterator.hasNext) { while (entryIterator.hasNext) {

View File

@ -34,6 +34,7 @@ import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser as
import xsbti.CompileFailed import xsbti.CompileFailed
import java.io.File import java.io.File
import java.nio.file.Paths
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import scala.collection.mutable import scala.collection.mutable
@ -628,12 +629,18 @@ object BuildServerProtocol {
val thisProjectRef = Keys.thisProjectRef.value val thisProjectRef = Keys.thisProjectRef.value
val thisConfig = Keys.configuration.value val thisConfig = Keys.configuration.value
val scalaJars = Keys.scalaInstance.value.allJars.map(_.toURI.toString) 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( val compileData = ScalaBuildTarget(
scalaOrganization = scalaOrganization.value, scalaOrganization = scalaOrganization.value,
scalaVersion = scalaVersion.value, scalaVersion = scalaVersion.value,
scalaBinaryVersion = scalaBinaryVersion.value, scalaBinaryVersion = scalaBinaryVersion.value,
platform = ScalaPlatform.JVM, platform = ScalaPlatform.JVM,
jars = scalaJars.toVector jars = scalaJars.toVector,
jvmBuildTarget = jvmBuildTarget,
) )
val configuration = Keys.configuration.value val configuration = Keys.configuration.value
val displayName = BuildTargetName.fromScope(thisProject.id, configuration.name) val displayName = BuildTargetName.fromScope(thisProject.id, configuration.name)
@ -699,7 +706,11 @@ object BuildServerProtocol {
scalaVersion = scalaProvider.version(), scalaVersion = scalaProvider.version(),
scalaBinaryVersion = binaryScalaVersion(scalaProvider.version()), scalaBinaryVersion = binaryScalaVersion(scalaProvider.version()),
platform = ScalaPlatform.JVM, 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 sbtVersionValue = sbtVersion.value
val sbtData = SbtBuildTarget( 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 // 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 // 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 = { 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 ct = "Content-Type: "
private val x1 = "application/sbt-x1" private val x1 = "application/sbt-x1"
override def run(): Unit = { override def run(): Unit = {
@ -243,7 +243,6 @@ final class NetworkChannel(
} }
} }
} }
thread.start()
private[sbt] def isLanguageServerProtocol: Boolean = true private[sbt] def isLanguageServerProtocol: Boolean = true
@ -370,7 +369,6 @@ final class NetworkChannel(
s"sbt-$name-write-thread" s"sbt-$name-write-thread"
) )
writeThread.setDaemon(true) writeThread.setDaemon(true)
writeThread.start()
def publishBytes(event: Array[Byte], delimit: Boolean): Unit = def publishBytes(event: Array[Byte], delimit: Boolean): Unit =
try pendingWrites.put(event -> delimit) try pendingWrites.put(event -> delimit)
@ -959,6 +957,9 @@ final class NetworkChannel(
} }
} }
private[sbt] def isAttached: Boolean = attached.get private[sbt] def isAttached: Boolean = attached.get
thread.start()
writeThread.start()
} }
object NetworkChannel { object NetworkChannel {

View File

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

View File

@ -3,7 +3,7 @@ import Keys.*
object Dependencies { object Dependencies {
// WARNING: Please Scala update versions in PluginCross.scala too // 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 scala3 = "3.6.3"
val checkPluginCross = settingKey[Unit]("Make sure scalaVersion match up") val checkPluginCross = settingKey[Unit]("Make sure scalaVersion match up")
val baseScalaVersion = scala3 val baseScalaVersion = scala3
@ -77,7 +77,7 @@ object Dependencies {
// JLine 3 version must be coordinated together with JAnsi version // JLine 3 version must be coordinated together with JAnsi version
// and the JLine 2 fork version, which uses the same JAnsi // and the JLine 2 fork version, which uses the same JAnsi
val jline = 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 jline3Version = "3.27.1"
val jline3Terminal = "org.jline" % "jline-terminal" % jline3Version val jline3Terminal = "org.jline" % "jline-terminal" % jline3Version
val jline3JNI = "org.jline" % "jline-terminal-jni" % jline3Version val jline3JNI = "org.jline" % "jline-terminal-jni" % jline3Version
@ -119,7 +119,7 @@ object Dependencies {
// lm-coursier dependencies // lm-coursier dependencies
val dataclassScalafixVersion = "0.1.0" val dataclassScalafixVersion = "0.1.0"
val coursierVersion = "2.1.19" val coursierVersion = "2.1.23"
val coursier = ("io.get-coursier" %% "coursier" % coursierVersion) val coursier = ("io.get-coursier" %% "coursier" % coursierVersion)
.cross(CrossVersion.for3Use2_13) .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. For example, 2.12 if scalaVersion is 2.12.4.
* @param platform The target platform for this target * @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 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 ( final class ScalaBuildTarget private (
val scalaOrganization: String, val scalaOrganization: String,
val scalaVersion: String, val scalaVersion: String,
val scalaBinaryVersion: String, val scalaBinaryVersion: String,
val platform: Int, 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 { 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 case _ => false
}) })
override def hashCode: Int = { 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 = { 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 = { 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) new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, jvmBuildTarget)
} }
def withScalaOrganization(scalaOrganization: String): ScalaBuildTarget = { def withScalaOrganization(scalaOrganization: String): ScalaBuildTarget = {
copy(scalaOrganization = scalaOrganization) copy(scalaOrganization = scalaOrganization)
@ -52,8 +54,15 @@ final class ScalaBuildTarget private (
def withJars(jars: Vector[String]): ScalaBuildTarget = { def withJars(jars: Vector[String]): ScalaBuildTarget = {
copy(jars = jars) 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 { 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.TestResultFormats
with sbt.internal.bsp.codec.RunParamsFormats with sbt.internal.bsp.codec.RunParamsFormats
with sbt.internal.bsp.codec.RunResultFormats with sbt.internal.bsp.codec.RunResultFormats
with sbt.internal.bsp.codec.JvmBuildTargetFormats
with sbt.internal.bsp.codec.ScalaBuildTargetFormats with sbt.internal.bsp.codec.ScalaBuildTargetFormats
with sbt.internal.bsp.codec.ScalacOptionsParamsFormats with sbt.internal.bsp.codec.ScalacOptionsParamsFormats
with sbt.internal.bsp.codec.ScalacOptionsItemFormats 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 // DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } 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] { 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 = { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.SbtBuildTarget = {
__jsOpt match { __jsOpt match {

View File

@ -5,7 +5,7 @@
// DO NOT EDIT MANUALLY // DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } 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] { 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 = { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaBuildTarget = {
__jsOpt match { __jsOpt match {
@ -16,8 +16,9 @@ implicit lazy val ScalaBuildTargetFormat: JsonFormat[sbt.internal.bsp.ScalaBuild
val scalaBinaryVersion = unbuilder.readField[String]("scalaBinaryVersion") val scalaBinaryVersion = unbuilder.readField[String]("scalaBinaryVersion")
val platform = unbuilder.readField[Int]("platform") val platform = unbuilder.readField[Int]("platform")
val jars = unbuilder.readField[Vector[String]]("jars") val jars = unbuilder.readField[Vector[String]]("jars")
val jvmBuildTarget = unbuilder.readField[Option[sbt.internal.bsp.JvmBuildTarget]]("jvmBuildTarget")
unbuilder.endObject() unbuilder.endObject()
sbt.internal.bsp.ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars) sbt.internal.bsp.ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, jvmBuildTarget)
case None => case None =>
deserializationError("Expected JsObject but found 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("scalaBinaryVersion", obj.scalaBinaryVersion)
builder.addField("platform", obj.platform) builder.addField("platform", obj.platform)
builder.addField("jars", obj.jars) builder.addField("jars", obj.jars)
builder.addField("jvmBuildTarget", obj.jvmBuildTarget)
builder.endObject() 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 # Scala Extension
## Contains scala-specific metadata for compiling a target containing Scala sources. ## 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. ## A sequence of Scala jars such as scala-library, scala-compiler and scala-reflect.
jars: [String]! jars: [String]!
## The jvm build target describing jdk to be used
jvmBuildTarget: sbt.internal.bsp.JvmBuildTarget
} }
## Scalac options ## Scalac options

67
sbt
View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set +e set +e
declare builtin_sbt_version="1.10.6" declare builtin_sbt_version="1.10.7"
declare -a residual_args declare -a residual_args
declare -a java_args declare -a java_args
declare -a scalac_args declare -a scalac_args
@ -25,6 +25,7 @@ declare use_sbtn=
declare no_server= declare no_server=
declare sbtn_command="$SBTN_CMD" declare sbtn_command="$SBTN_CMD"
declare sbtn_version="1.10.5" declare sbtn_version="1.10.5"
declare use_colors=1
### ------------------------------- ### ### ------------------------------- ###
### Helper methods for BASH scripts ### ### Helper methods for BASH scripts ###
@ -104,6 +105,15 @@ declare -r sbt_home="$(dirname "$sbt_bin_dir")"
echoerr () { echoerr () {
echo 1>&2 "$@" 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 () { vlog () {
[[ $sbt_verbose || $sbt_debug ]] && echoerr "$@" [[ $sbt_verbose || $sbt_debug ]] && echoerr "$@"
} }
@ -486,6 +496,21 @@ copyRt() {
fi 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() { run() {
# Copy preloaded repo to user's preloaded directory # Copy preloaded repo to user's preloaded directory
syncPreloaded syncPreloaded
@ -522,6 +547,7 @@ run() {
done done
echo "shutdown ${#sbt_processes[@]} sbt processes" echo "shutdown ${#sbt_processes[@]} sbt processes"
else else
checkWorkingDirectory
# run sbt # run sbt
execRunner "$java_cmd" \ execRunner "$java_cmd" \
"${java_args[@]}" \ "${java_args[@]}" \
@ -546,7 +572,10 @@ declare -r sbt_opts_file=".sbtopts"
declare -r build_props_file="$(pwd)/project/build.properties" declare -r build_props_file="$(pwd)/project/build.properties"
declare -r etc_sbt_opts_file="/etc/sbt/sbtopts" declare -r etc_sbt_opts_file="/etc/sbt/sbtopts"
# this allows /etc/sbt/sbtopts location to be changed # 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 dist_sbt_opts_file="${sbt_home}/conf/sbtopts"
declare -r win_sbt_opts_file="${sbt_home}/conf/sbtconfig.txt" declare -r win_sbt_opts_file="${sbt_home}/conf/sbtconfig.txt"
declare sbt_jar="$(jar_file)" declare sbt_jar="$(jar_file)"
@ -571,7 +600,7 @@ Usage: `basename "$0"` [options]
enable or disable supershell (sbt 1.3 and above) enable or disable supershell (sbt 1.3 and above)
--traces generate Trace Event report on shutdown (sbt 1.3 and above) --traces generate Trace Event report on shutdown (sbt 1.3 and above)
--timings display task timings report on shutdown --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-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-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) --sbt-cache <path> path to global cache directory (default: operating system specific)
@ -610,7 +639,7 @@ process_my_args () {
case "$1" in case "$1" in
-batch|--batch) exec </dev/null && shift ;; #> -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 ;; new) sbt_new=true && addResidual "$1" && shift ;;
@ -620,23 +649,6 @@ process_my_args () {
# Now, ensure sbt version is used. # Now, ensure sbt version is used.
[[ "${sbt_version}XXX" != "XXX" ]] && addJava "-Dsbt.version=$sbt_version" [[ "${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 ## 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" && export PATH="$2/bin:$PATH" &&
shift 2 ;; shift 2 ;;
-Dsbt.color=never|-Dsbt.log.noformat=true) addJava "$1" && use_colors=0 && shift ;;
"-D*"|-D*) addJava "$1" && shift ;; "-D*"|-D*) addJava "$1" && shift ;;
-J*) addJava "${1:2}" && shift ;; -J*) addJava "${1:2}" && shift ;;
*) addResidual "$1" && shift ;; *) addResidual "$1" && shift ;;
@ -788,11 +801,13 @@ runNativeClient() {
original_args=("$@") original_args=("$@")
# Here we pull in the default settings configuration. # Pull in the machine-wide settings configuration.
[[ -f "$dist_sbt_opts_file" ]] && set -- $(loadConfigFile "$dist_sbt_opts_file") "$@" if [[ -f "$machine_sbt_opts_file" ]]; then
set -- $(loadConfigFile "$machine_sbt_opts_file") "$@"
# Here we pull in the global settings configuration. else
[[ -f "$etc_file" ]] && set -- $(loadConfigFile "$etc_file") "$@" # 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. # Pull in the project-level config file, if it exists.
[[ -f "$sbt_opts_file" ]] && set -- $(loadConfigFile "$sbt_opts_file") "$@" [[ -f "$sbt_opts_file" ]] && set -- $(loadConfigFile "$sbt_opts_file") "$@"

View File

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

View File

@ -4,6 +4,7 @@ libraryDependencies ++= Seq(
"org.slf4j" % "slf4j-api" % "1.7.2", "org.slf4j" % "slf4j-api" % "1.7.2",
"ch.qos.logback" % "logback-classic" % "1.0.7" "ch.qos.logback" % "logback-classic" % "1.0.7"
) )
csrMavenDependencyOverride := false
TaskKey[Unit]("check") := { TaskKey[Unit]("check") := {
val report = updateFull.value 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 # 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 $ copy-file changes/A.scala src/sbt-test/a/b/A.scala
> scripted > scripted

View File

@ -17,7 +17,7 @@ import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import java.nio.file.Files import java.nio.file.{ Files, Paths }
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import scala.concurrent.duration.* import scala.concurrent.duration.*
@ -57,6 +57,16 @@ class BuildServerTest extends AbstractServerTest {
case None => sys.error(s"buildserver-root-build not in ${result.targets}") case None => sys.error(s"buildserver-root-build not in ${result.targets}")
assert(buildServerBuildTarget.id.uri.toString.endsWith("#buildserver-root-build")) assert(buildServerBuildTarget.id.uri.toString.endsWith("#buildserver-root-build"))
assert(!result.targets.exists(_.displayName.contains("badBuildTarget"))) 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") { test("buildTarget/sources") {