Merge branch 'develop' into fix/1469-junit-xml-fork-stacktrace

This commit is contained in:
volcano303
2026-04-15 21:49:38 +02:00
98 changed files with 865 additions and 250 deletions
+2
View File
@@ -6,3 +6,5 @@ launcher-package/src/windows/sbt text eol=lf
**/contraband-scala/**/* -diff merge=ours
**/contraband-scala/**/* linguist-generated=true
**/contraband-scala/**/* diff
*.contra linguist-language=graphql
+21 -20
View File
@@ -84,19 +84,21 @@ def commonSettings: Seq[Setting[?]] = Def.settings(
Test / testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
Test / testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2"),
compile / javacOptions ++= Seq("-Xlint", "-Xlint:-serial"),
/*
Compile / doc / scalacOptions ++= {
import scala.sys.process._
val devnull = ProcessLogger(_ => ())
val tagOrSha = ("git describe --exact-match" #|| "git rev-parse HEAD").lineStream(devnull).head
Seq(
"-sourcepath",
(LocalRootProject / baseDirectory).value.getAbsolutePath,
"-doc-source-url",
s"https://github.com/sbt/sbt/tree/$tagOrSha€{FILE_PATH}.scala"
)
if (Dependencies.sbtIoPath.isEmpty && Dependencies.sbtZincPath.isEmpty) {
import scala.sys.process.*
val devnull = ProcessLogger(_ => ())
val tagOrSha =
("git describe --exact-match" #|| "git rev-parse HEAD").lineStream(devnull).head
Seq(
"-source-links:github://sbt/sbt",
"-revision",
tagOrSha
)
} else {
Nil
}
},
*/
Compile / javafmtOnCompile := scalafmtOnCompile.value,
Test / javafmtOnCompile := (Test / scalafmtOnCompile).value,
Compile / unmanagedSources / inputFileStamps :=
@@ -186,7 +188,6 @@ lazy val sbtRoot: Project = (project in file("."))
},
Utils.baseScalacOptions,
Docs.settings,
scalacOptions += "-Ymacro-expand:none", // for both sxr and doc
Utils.publishPomSettings,
otherRootSettings,
Utils.noPublish,
@@ -742,8 +743,10 @@ lazy val mainProj = (project in file("main"))
Test / testOptions += Tests
.Argument(TestFrameworks.ScalaCheck, "-minSuccessfulTests", "1000"),
SettingKey[Boolean]("usePipelining") := false,
// TODO: Fix doc
Compile / doc / sources := Nil,
libraryDependencies += {
// https://github.com/scala/scala3/issues/18487
"net.hamnaberg" %% "dataclass-annotation" % dataclassScalafixVersion % Provided
},
mimaSettings,
mimaBinaryIssueFilters ++= Vector(
// Moved to sbt-ivy module (Step 5 of sbt#7640)
@@ -782,8 +785,10 @@ lazy val sbtIvyProj = (project in file("sbt-ivy"))
name := "sbt-ivy",
sbtPlugin := true,
pluginCrossBuild / sbtVersion := version.value,
// TODO: Fix doc
Compile / doc / sources := Nil,
libraryDependencies += {
// https://github.com/scala/scala3/issues/18487
"net.hamnaberg" %% "dataclass-annotation" % dataclassScalafixVersion % Provided
},
mimaPreviousArtifacts := Set.empty, // new module, no previous artifacts
)
.configure(addSbtIO)
@@ -1019,10 +1024,6 @@ lazy val sbtwProj = (project in file("sbtw"))
description := "Windows drop-in launcher for sbt (replaces sbt.bat)",
scalaVersion := "3.8.3",
crossPaths := false,
Compile / scalafix / unmanagedSources := {
// https://github.com/scalameta/scalameta/issues/4531
(Compile / unmanagedSources).value.filterNot(_.getName == "Main.scala")
},
Compile / mainClass := Some("sbtw.Main"),
libraryDependencies += "com.github.scopt" %% "scopt" % "4.1.0",
libraryDependencies += scalaVerify % Test,
@@ -200,6 +200,8 @@ object Tests {
new Group(name, tests, runPolicy, tags)
}
override def hashCode(): Int = (name, tests, runPolicy, tags).##
override def equals(x$1: Any): Boolean = {
this.eq(x$1.asInstanceOf[Object]) || (x$1.isInstanceOf[Group] && ({
val Group$1: Group = x$1.asInstanceOf[Group]
@@ -149,7 +149,9 @@ $HelpCommand <regular expression>
remaining commands with the exception that the JVM is not shut down.
If 'dev' is specified, the current sbt artifacts from the boot directory
(`~/.sbt/boot` by default) are deleted before restarting.
(under the default global base; `sbt.global.base` selects that location) are deleted
before restarting, and the compiler bridge secondary cache at `zinc/org.scala-sbt`
(honoring `sbt.global.base` and `sbt.global.zinc`) is removed.
This forces an update of sbt and Scala, which is useful when working with development
versions of sbt.
If 'full' is specified, the boot directory is wiped out before restarting.
@@ -181,6 +181,7 @@ class NetworkClient(
def success(message: => String): Unit = ()
def log(level: Level.Value, message: => String): Unit = console.appendLog(level, message)
}
private val interactive = arguments.commandArguments.isEmpty
private[sbt] def connectOrStartServerAndConnect(
promptCompleteUsers: Boolean,
@@ -712,8 +713,19 @@ class NetworkClient(
case (`clientJob`, Some(json)) =>
import sbt.internal.worker.codec.JsonProtocol.given
Converter.fromJson[ClientJobParams](json) match {
case Success(params) => clientSideRun(params).get; Vector.empty
case Failure(_) => Vector.empty
case Success(params) =>
clientSideRun(params) match
case Success(_) =>
if interactive then console.success("ok")
else ()
Vector.empty
case Failure(e) =>
if interactive then
Vector(
(Level.Error, e.getMessage)
)
else throw e
case Failure(_) => Vector.empty
}
case (`Shutdown`, Some(_)) => Vector.empty
case (msg, _) if msg.startsWith("build/") => Vector.empty
@@ -921,9 +933,8 @@ class NetworkClient(
withSignalHandler(contHandler, Signals.CONT) {
interactiveThread.set(Thread.currentThread)
val cleaned = arguments.commandArguments
val userCommands = cleaned.takeWhile(_ != TerminateAction)
val interactive = cleaned.isEmpty
val exit = cleaned.nonEmpty && userCommands.isEmpty
val userCommands = arguments.commandArguments.takeWhile(_ != TerminateAction)
val exit = arguments.commandArguments.nonEmpty && userCommands.isEmpty
attachUUID.set(sendJson(attach, s"""{"interactive": $interactive}"""))
val handler: () => Unit = () => {
def exitAbruptly() = {
@@ -30,7 +30,10 @@ object ServerHandler {
lazy val fallback: ServerHandler = ServerHandler({ handler =>
ServerIntent(
onRequest = { case x => handler.log.debug(s"Unhandled request received: ${x.method}: $x") },
onRequest = { case x =>
handler.log.debug(s"Unhandled request received: ${x.method}: $x")
handler.jsonRpcRespondError(Some(x.id), -32601, s"Unknown method: ${x.method}")
},
onResponse = { case x => handler.log.debug(s"Unhandled response received") },
onNotification = { case x =>
handler.log.debug(s"Unhandled notification received: ${x.method}: $x")
+15 -10
View File
@@ -41,7 +41,7 @@ object Cross {
private def switchParser(state: State): Parser[Switch] = {
import DefaultParsers.*
def versionAndCommand(spacePresent: Boolean) = {
def versionAndCommand(commandName: String)(spacePresent: Boolean) = {
val x = Project.extract(state)
import x.*
val knownVersions = crossVersions(x, currentRef)
@@ -56,7 +56,7 @@ object Cross {
ScalaHomeVersion(new File(home), Some(v).filterNot(_.isEmpty), force)
}
}
val spacedVersion = if (spacePresent) version else version & spacedFirst(SwitchCommand)
val spacedVersion = if (spacePresent) version else version & spacedFirst(commandName)
val verboseOpt = Parser.opt(token(Space ~> "-v"))
// Accept valid commands, or project/command patterns that may reference projects
// not yet available after version switch (fixes #7574)
@@ -74,19 +74,24 @@ object Cross {
switch1 | switch2
}
token(SwitchCommand ~> OptSpace) flatMap { sp =>
versionAndCommand(sp.nonEmpty)
}
def parse(commandName: String) =
token(commandName ~> OptSpace) flatMap { sp =>
versionAndCommand(commandName)(sp.nonEmpty)
}
parse(SwitchCommand) | parse(SwitchAlias)
}
private case class CrossArgs(command: String, verbose: Boolean)
private def crossParser(state: State): Parser[CrossArgs] =
token(CrossCommand <~ OptSpace) flatMap { _ =>
(token(Parser.opt("-v" <~ Space)) ~ token(matched(state.combinedParser))).map {
(verbose, command) => CrossArgs(command, verbose.isDefined)
private def crossParser(state: State): Parser[CrossArgs] = {
def parse(commandName: String) =
token(commandName <~ OptSpace) flatMap { _ =>
(token(Parser.opt("-v" <~ Space)) ~ token(matched(state.combinedParser))).map {
(verbose, command) => CrossArgs(command, verbose.isDefined)
}
}
}
parse(CrossCommand) | parse(CrossAlias)
}
private def crossRestoreSessionParser: Parser[String] = token(CrossRestoreSessionCommand)
+3 -1
View File
@@ -4289,7 +4289,9 @@ object Classpaths {
configuration.allRepositories,
)
val processed = configuration.process(pomXml)
scala.xml.XML.save(file.getAbsolutePath, processed, "UTF-8", xmlDecl = true)
val printer = new scala.xml.PrettyPrinter(1000, 4)
val formatted = scala.xml.XML.loadString(printer.format(processed))
scala.xml.XML.save(file.getAbsolutePath, formatted, "UTF-8", xmlDecl = true)
log.info("Wrote " + file.getAbsolutePath)
file
})
+2
View File
@@ -479,6 +479,8 @@ object Keys {
val bspBuildTargetResourcesItem = taskKey[ResourcesItem]("").withRank(DTask)
val bspBuildTargetDependencySources = inputKey[Unit]("").withRank(DTask)
val bspBuildTargetDependencySourcesItem = taskKey[DependencySourcesItem]("").withRank(DTask)
val bspBuildTargetDependencyModules = inputKey[Unit]("").withRank(DTask)
val bspBuildTargetDependencyModulesItem = taskKey[DependencyModulesItem]("").withRank(DTask)
val bspBuildTargetOutputPaths = inputKey[Unit]("").withRank(DTask)
val bspBuildTargetOutputPathsItem = taskKey[OutputPathsItem]("").withRank(DTask)
val bspBuildTargetCompile = inputKey[Unit]("").withRank(DTask)
+9 -1
View File
@@ -1144,12 +1144,20 @@ object BuiltinCommands {
.getOpt(Keys.minForcegcInterval)
.getOrElse(GCUtil.defaultMinForcegcInterval)
val exec: Exec = getExec(s1, minGCInterval)
val isInteractive = exec.source match {
case Some(src) if src.channelName.startsWith("network") =>
exchange.channelForName(src.channelName) match {
case Some(nc: NetworkChannel) => nc.isInteractive
case _ => true
}
case _ => true
}
val newState = s1
.copy(
onFailure = Some(Exec(Shell, None)),
remainingCommands = exec +: Exec(Shell, None) +: s1.remainingCommands
)
.setInteractive(true)
.setInteractive(isInteractive)
val res =
if (exec.commandLine.trim.isEmpty) newState
else newState.clearGlobalLog
+13 -1
View File
@@ -22,6 +22,8 @@ import sbt.internal.util.{
Terminal as ITerminal
}
import sbt.io.{ IO, Using }
import sbt.io.syntax.*
import sbt.librarymanagement.SbtArtifacts
import sbt.protocol.*
import sbt.util.{ Logger, LoggerContext }
@@ -75,6 +77,10 @@ private[sbt] object MainLoop:
case e: RebootCurrent =>
deleteLastLog(logBacking)
deleteCurrentArtifacts(state)
deleteZincBridgeSecondaryCache(
state.log,
BuildPaths.getZincDirectory(state, BuildPaths.getGlobalBase(state)),
)
throw new xsbti.FullReload(e.arguments.toArray, false)
case NonFatal(e) =>
System.err.println(
@@ -89,7 +95,6 @@ private[sbt] object MainLoop:
/** Deletes the current sbt artifacts from boot. */
private[sbt] def deleteCurrentArtifacts(state: State): Unit = {
import sbt.io.syntax.*
val provider = state.configuration.provider
val appId = provider.id
// If we can obtain boot directory more accurately it'd be better.
@@ -109,6 +114,13 @@ private[sbt] object MainLoop:
}
}
/** Removes the Zinc compiler bridge secondary cache (`zincDir/org.scala-sbt`). */
private[sbt] def deleteZincBridgeSecondaryCache(log: Logger, zincDir: File): Unit =
val bridgeCache = zincDir / SbtArtifacts.Organization
if bridgeCache.exists() then
log.info(s"deleting $bridgeCache")
IO.delete(bridgeCache)
/** Runs the next sequence of commands with global logging in place. */
def runWithNewLog(state: State, logBacking: GlobalLogBacking): RunNext =
Using.fileWriter(append = true)(logBacking.file) { writer =>
@@ -17,6 +17,7 @@ import sbt.internal.util.complete.Parser
import sbt.internal.util.complete.Parser.{ failure, seq, success }
import sbt.internal.util.*
import sbt.internal.client.NetworkClient
import sbt.internal.worker.ClientJobParams
import sbt.std.Transform.DummyTaskMap
import sbt.util.{ Logger, Show }
import scala.annotation.tailrec
@@ -87,16 +88,18 @@ object Aggregation {
import complete.*
val log = state.log
val extracted = Project.extract(state)
val success = results match
case Result.Value(_) => true
case Result.Inc(_) => false
// omit success printing for client-side run
val (success, jobParams) = results match
case Result.Value(Seq(KeyValue(_, p: ClientJobParams))) => (true, true)
case Result.Value(_) => (true, false)
case Result.Inc(_) => (false, false)
val isPaused = currentChannel(state) match
case Some(channel) => channel.isPaused
case None => false
results.toEither.foreach { r =>
if show.taskValues then printSettings(r, show.print) else ()
}
if !isPaused && show.success && !state.get(suppressShow).getOrElse(false) then
if !isPaused && show.success && !state.get(suppressShow).getOrElse(false) && !jobParams then
printSuccess(start, stop, extracted, success, cacheSummary, log)
else ()
@@ -226,6 +226,13 @@ private[sbt] object Clean {
case d: DiskActionCacheStore => d.clear()
case _ => ()
IO.delete(outputDirectory.toFile())
IO.delete(
s.configuration
.provider()
.scalaProvider()
.launcher()
.bootDirectory()
)
s
Command.command(CleanFull, h)(expunge andThen clearCachesFun)
}
@@ -331,15 +331,19 @@ defaults
Nil
val CrossCommand = "+"
val CrossAlias = "cross"
val CrossRestoreSessionCommand = "+-"
val SwitchCommand = "++"
val SwitchAlias = "switch"
def crossHelp: Help = Help.more(CrossCommand, CrossDetailed)
def crossHelp: Help =
Help.more(CrossCommand, CrossDetailed) ++ Help.more(CrossAlias, CrossDetailed)
def crossRestoreSessionHelp = Help.more(CrossRestoreSessionCommand, CrossRestoreSessionDetailed)
def switchHelp: Help = Help.more(SwitchCommand, SwitchDetailed)
def switchHelp: Help =
Help.more(SwitchCommand, SwitchDetailed) ++ Help.more(SwitchAlias, SwitchDetailed)
def CrossDetailed =
s"""$CrossCommand [-v] <command>
s"""$CrossCommand (or $CrossAlias) [-v] <command>
Runs <command> for each Scala version specified for cross-building.
For each string in `crossScalaVersions` in each project project, this command sets
@@ -359,7 +363,7 @@ defaults
"""
def SwitchDetailed =
s"""$SwitchCommand <scala-version>[!] [-v] [<command>]
s"""$SwitchCommand (or $SwitchAlias) <scala-version>[!] [-v] [<command>]
Changes the Scala version and runs a command.
<scala-version> may be an actual Scala version such as 3.1.3, or a Semantic Version selector
@@ -23,7 +23,7 @@ object LintUnused {
keyName => includes(keyName)
},
lintExcludeFilter := {
val excludedPrefixes = List("release", "sonatype", "watch", "whitesource")
val excludedPrefixes = List("release", "sonatype", "watch")
val excludes = excludeLintKeys.value.map(_.scopedKey.key.label)
keyName => excludes(keyName) || excludedPrefixes.exists(keyName.startsWith(_))
},
@@ -46,6 +46,8 @@ private[sbt] object PomGenerator:
<modelVersion>4.0.0</modelVersion>
{makeModuleID(crossMid)}
{info.map(i => <name>{i.nameFormal}</name>).getOrElse(NodeSeq.Empty)}
{info.map(makeDescription).getOrElse(NodeSeq.Empty)}
{info.map(makeHomePage).getOrElse(NodeSeq.Empty)}
{info.map(makeStartYear).getOrElse(NodeSeq.Empty)}
{info.map(makeOrganization).getOrElse(NodeSeq.Empty)}
{info.map(makeScmInfo).getOrElse(NodeSeq.Empty)}
@@ -96,6 +98,16 @@ private[sbt] object PomGenerator:
private val IgnoreTypes: Set[String] =
Set(Artifact.SourceType, Artifact.DocType, Artifact.PomType)
private def makeDescription(info: ModuleInfo): NodeSeq =
if info.description != null && info.description.nonEmpty then
<description>{info.description}</description>
else NodeSeq.Empty
private def makeHomePage(info: ModuleInfo): NodeSeq =
info.homepage match
case Some(h) => <url>{h}</url>
case _ => NodeSeq.Empty
private def makeStartYear(info: ModuleInfo): NodeSeq =
info.startYear match
case Some(y) => <inceptionYear>{y}</inceptionYear>
@@ -166,6 +166,21 @@ object BuildServerProtocol {
state.value.respondEvent(result)
}.evaluated,
bspBuildTargetDependencySources / aggregate := false,
bspBuildTargetDependencyModules := bspInputTask { (workspace, filter) =>
val items = bspBuildTargetDependencyModulesItem.result.all(filter).value
val successfulItems = anyOrThrow(items)
val buildItems = workspace.builds
.map { case (targetId, loadedBuildUnit) =>
val projRef = ProjectRef(loadedBuildUnit.unit.uri, loadedBuildUnit.root)
(projRef / updateSbtClassifiers).map(getDependencyModulesItem(targetId, _))
}
.toSeq
.join
.value
val result = DependencyModulesResult((successfulItems ++ buildItems).toVector)
state.value.respondEvent(result)
}.evaluated,
bspBuildTargetDependencyModules / aggregate := false,
bspBuildTargetCompile := bspInputTask { (workspace, filter) =>
val s = state.value
workspace.warnIfBuildsNonEmpty(Method.Compile, s.log)
@@ -306,6 +321,7 @@ object BuildServerProtocol {
ResourcesItem(id, uris)
},
bspBuildTargetDependencySourcesItem := dependencySourcesItemTask.value,
bspBuildTargetDependencyModulesItem := dependencyModulesItemTask.value,
bspBuildTargetOutputPathsItem := {
val id = bspTargetIdentifier.value
OutputPathsItem(id, Vector(OutputPathItem(target.value.toURI, OutputPathItemKind.Directory)))
@@ -374,6 +390,7 @@ object BuildServerProtocol {
final val Resources = "buildTarget/resources"
final val OutputPaths = "buildTarget/outputPaths"
final val DependencySources = "buildTarget/dependencySources"
final val DependencyModules = "buildTarget/dependencyModules"
final val Compile = "buildTarget/compile"
final val Test = "buildTarget/test"
final val Run = "buildTarget/run"
@@ -439,6 +456,12 @@ object BuildServerProtocol {
val command = Keys.bspBuildTargetDependencySources.key
val _ = callback.appendExec(s"$command $targets", Some(r.id))
case r if r.method == Method.DependencyModules =>
val param = Converter.fromJson[DependencyModulesParams](json(r)).get
val targets = param.targets.map(_.uri).mkString(" ")
val command = Keys.bspBuildTargetDependencyModules.key
val _ = callback.appendExec(s"$command $targets", Some(r.id))
case r if r.method == Method.Compile =>
val param = Converter.fromJson[CompileParams](json(r)).get
val targets = param.targets.map(_.uri).mkString(" ")
@@ -856,6 +879,13 @@ object BuildServerProtocol {
)
}
private def dependencyModulesItemTask: Def.Initialize[Task[DependencyModulesItem]] = Def.task {
getDependencyModulesItem(
Keys.bspTargetIdentifier.value,
Keys.updateClassifiers.value
)
}
private def getDependencySourceItem(
targetId: BuildTargetIdentifier,
updateReport: UpdateReport
@@ -869,6 +899,25 @@ object BuildServerProtocol {
DependencySourcesItem(targetId, sources.toVector.distinct)
}
private def getDependencyModulesItem(
targetId: BuildTargetIdentifier,
updateReport: UpdateReport
): DependencyModulesItem = {
val modules = for {
configuration <- updateReport.configurations.view
module <- configuration.modules.view
} yield {
val moduleId = module.module
DependencyModule(
name = s"${moduleId.organization}:${moduleId.name}",
version = moduleId.revision,
dataKind = None,
data = None,
)
}
DependencyModulesItem(targetId, modules.toVector.distinct)
}
private def bspCompileState: Initialize[BuildServerProtocol.BspCompileState] = Def.setting {
new BuildServerProtocol.BspCompileState()
}
@@ -0,0 +1,49 @@
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
import java.io.File
import sbt.internal.util.{ ConsoleOut, GlobalLogging, MainAppender }
import sbt.io.IO
import sbt.io.syntax.*
import sbt.librarymanagement.SbtArtifacts
import sbt.util.Logger
object MainLoopZincCacheTest extends verify.BasicTestSuite:
private def withTestLog[A](f: Logger => A): A =
val logFile = File.createTempFile("sbt-mlz", ".log")
try
val gl = GlobalLogging.initial(
MainAppender.globalDefault(ConsoleOut.globalProxy),
logFile,
ConsoleOut.globalProxy
)
f(gl.full)
finally IO.delete(logFile)
test("deleteZincBridgeSecondaryCache removes org.scala-sbt under zincDir"):
IO.withTemporaryDirectory: tmp =>
val zincRoot = tmp / "zinc"
val bridge = zincRoot / SbtArtifacts.Organization
IO.write(bridge / "marker.txt", "cached")
withTestLog: log =>
MainLoop.deleteZincBridgeSecondaryCache(log, zincRoot)
assert(!bridge.exists(), s"expected $bridge deleted")
test("deleteZincBridgeSecondaryCache is a no-op when org.scala-sbt is absent"):
IO.withTemporaryDirectory: tmp =>
val zincRoot = tmp / "zinc"
IO.createDirectory(zincRoot)
withTestLog: log =>
MainLoop.deleteZincBridgeSecondaryCache(log, zincRoot)
assert(zincRoot.exists())
end MainLoopZincCacheTest
-2
View File
@@ -36,8 +36,6 @@ object Dependencies {
}
lazy val sbtIoPath = getSbtModulePath("sbtio.path")
lazy val sbtUtilPath = getSbtModulePath("sbtutil.path")
lazy val sbtLmPath = getSbtModulePath("sbtlm.path")
lazy val sbtZincPath = getSbtModulePath("sbtzinc.path")
def addSbtModule(
@@ -0,0 +1,57 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* @param name Module name
* @param version Module version
* @param dataKind Kind of data to expect in the `data` field.
* @param data Language-specific metadata about this module.
*/
final class DependencyModule private (
val name: String,
val version: String,
val dataKind: Option[String],
val data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: DependencyModule => (this.name == x.name) && (this.version == x.version) && (this.dataKind == x.dataKind) && (this.data == x.data)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.DependencyModule".##) + name.##) + version.##) + dataKind.##) + data.##)
}
override def toString: String = {
"DependencyModule(" + name + ", " + version + ", " + dataKind + ", " + data + ")"
}
private def copy(name: String = name, version: String = version, dataKind: Option[String] = dataKind, data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue] = data): DependencyModule = {
new DependencyModule(name, version, dataKind, data)
}
def withName(name: String): DependencyModule = {
copy(name = name)
}
def withVersion(version: String): DependencyModule = {
copy(version = version)
}
def withDataKind(dataKind: Option[String]): DependencyModule = {
copy(dataKind = dataKind)
}
def withDataKind(dataKind: String): DependencyModule = {
copy(dataKind = Option(dataKind))
}
def withData(data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]): DependencyModule = {
copy(data = data)
}
def withData(data: sjsonnew.shaded.scalajson.ast.unsafe.JValue): DependencyModule = {
copy(data = Option(data))
}
}
object DependencyModule {
def apply(name: String, version: String, dataKind: Option[String], data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]): DependencyModule = new DependencyModule(name, version, dataKind, data)
def apply(name: String, version: String, dataKind: String, data: sjsonnew.shaded.scalajson.ast.unsafe.JValue): DependencyModule = new DependencyModule(name, version, Option(dataKind), Option(data))
}
@@ -0,0 +1,40 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
final class DependencyModulesItem private (
val target: Option[sbt.internal.bsp.BuildTargetIdentifier],
val modules: Vector[sbt.internal.bsp.DependencyModule]) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: DependencyModulesItem => (this.target == x.target) && (this.modules == x.modules)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.DependencyModulesItem".##) + target.##) + modules.##)
}
override def toString: String = {
"DependencyModulesItem(" + target + ", " + modules + ")"
}
private def copy(target: Option[sbt.internal.bsp.BuildTargetIdentifier] = target, modules: Vector[sbt.internal.bsp.DependencyModule] = modules): DependencyModulesItem = {
new DependencyModulesItem(target, modules)
}
def withTarget(target: Option[sbt.internal.bsp.BuildTargetIdentifier]): DependencyModulesItem = {
copy(target = target)
}
def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): DependencyModulesItem = {
copy(target = Option(target))
}
def withModules(modules: Vector[sbt.internal.bsp.DependencyModule]): DependencyModulesItem = {
copy(modules = modules)
}
}
object DependencyModulesItem {
def apply(target: Option[sbt.internal.bsp.BuildTargetIdentifier], modules: Vector[sbt.internal.bsp.DependencyModule]): DependencyModulesItem = new DependencyModulesItem(target, modules)
def apply(target: sbt.internal.bsp.BuildTargetIdentifier, modules: Vector[sbt.internal.bsp.DependencyModule]): DependencyModulesItem = new DependencyModulesItem(Option(target), modules)
}
@@ -0,0 +1,33 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/** Dependency Modules Request */
final class DependencyModulesParams private (
val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: DependencyModulesParams => (this.targets == x.targets)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (17 + "sbt.internal.bsp.DependencyModulesParams".##) + targets.##)
}
override def toString: String = {
"DependencyModulesParams(" + targets + ")"
}
private def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): DependencyModulesParams = {
new DependencyModulesParams(targets)
}
def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): DependencyModulesParams = {
copy(targets = targets)
}
}
object DependencyModulesParams {
def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): DependencyModulesParams = new DependencyModulesParams(targets)
}
@@ -0,0 +1,33 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/** Dependency Modules Result */
final class DependencyModulesResult private (
val items: Vector[sbt.internal.bsp.DependencyModulesItem]) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: DependencyModulesResult => (this.items == x.items)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (17 + "sbt.internal.bsp.DependencyModulesResult".##) + items.##)
}
override def toString: String = {
"DependencyModulesResult(" + items + ")"
}
private def copy(items: Vector[sbt.internal.bsp.DependencyModulesItem]): DependencyModulesResult = {
new DependencyModulesResult(items)
}
def withItems(items: Vector[sbt.internal.bsp.DependencyModulesItem]): DependencyModulesResult = {
copy(items = items)
}
}
object DependencyModulesResult {
def apply(items: Vector[sbt.internal.bsp.DependencyModulesItem]): DependencyModulesResult = new DependencyModulesResult(items)
}
@@ -0,0 +1,33 @@
/**
* 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 DependencyModuleFormats { self: sbt.internal.util.codec.JValueFormats & sjsonnew.BasicJsonProtocol =>
given DependencyModuleFormat: JsonFormat[sbt.internal.bsp.DependencyModule] = new JsonFormat[sbt.internal.bsp.DependencyModule] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.DependencyModule = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val name = unbuilder.readField[String]("name")
val version = unbuilder.readField[String]("version")
val dataKind = unbuilder.readField[Option[String]]("dataKind")
val data = unbuilder.readField[Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]]("data")
unbuilder.endObject()
sbt.internal.bsp.DependencyModule(name, version, dataKind, data)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.DependencyModule, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("name", obj.name)
builder.addField("version", obj.version)
builder.addField("dataKind", obj.dataKind)
builder.addField("data", obj.data)
builder.endObject()
}
}
}
@@ -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 DependencyModulesItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats & sjsonnew.BasicJsonProtocol & sbt.internal.bsp.codec.DependencyModuleFormats & sbt.internal.util.codec.JValueFormats =>
given DependencyModulesItemFormat: JsonFormat[sbt.internal.bsp.DependencyModulesItem] = new JsonFormat[sbt.internal.bsp.DependencyModulesItem] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.DependencyModulesItem = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val target = unbuilder.readField[Option[sbt.internal.bsp.BuildTargetIdentifier]]("target")
val modules = unbuilder.readField[Vector[sbt.internal.bsp.DependencyModule]]("modules")
unbuilder.endObject()
sbt.internal.bsp.DependencyModulesItem(target, modules)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.DependencyModulesItem, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("target", obj.target)
builder.addField("modules", obj.modules)
builder.endObject()
}
}
}
@@ -0,0 +1,27 @@
/**
* 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 DependencyModulesParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats & sjsonnew.BasicJsonProtocol =>
given DependencyModulesParamsFormat: JsonFormat[sbt.internal.bsp.DependencyModulesParams] = new JsonFormat[sbt.internal.bsp.DependencyModulesParams] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.DependencyModulesParams = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets")
unbuilder.endObject()
sbt.internal.bsp.DependencyModulesParams(targets)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.DependencyModulesParams, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("targets", obj.targets)
builder.endObject()
}
}
}
@@ -0,0 +1,27 @@
/**
* 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 DependencyModulesResultFormats { self: sbt.internal.bsp.codec.DependencyModulesItemFormats & sbt.internal.bsp.codec.BuildTargetIdentifierFormats & sjsonnew.BasicJsonProtocol & sbt.internal.bsp.codec.DependencyModuleFormats & sbt.internal.util.codec.JValueFormats =>
given DependencyModulesResultFormat: JsonFormat[sbt.internal.bsp.DependencyModulesResult] = new JsonFormat[sbt.internal.bsp.DependencyModulesResult] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.DependencyModulesResult = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val items = unbuilder.readField[Vector[sbt.internal.bsp.DependencyModulesItem]]("items")
unbuilder.endObject()
sbt.internal.bsp.DependencyModulesResult(items)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.DependencyModulesResult, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("items", obj.items)
builder.endObject()
}
}
}
@@ -43,6 +43,10 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.bsp.codec.DependencySourcesParamsFormats
with sbt.internal.bsp.codec.DependencySourcesItemFormats
with sbt.internal.bsp.codec.DependencySourcesResultFormats
with sbt.internal.bsp.codec.DependencyModulesParamsFormats
with sbt.internal.bsp.codec.DependencyModuleFormats
with sbt.internal.bsp.codec.DependencyModulesItemFormats
with sbt.internal.bsp.codec.DependencyModulesResultFormats
with sbt.internal.bsp.codec.TaskStartParamsFormats
with sbt.internal.bsp.codec.TaskProgressParamsFormats
with sbt.internal.bsp.codec.TaskFinishParamsFormats
+29
View File
@@ -409,6 +409,35 @@ type DependencySourcesItem {
sources: [java.net.URI]
}
## Dependency Modules Request
type DependencyModulesParams {
targets: [sbt.internal.bsp.BuildTargetIdentifier]
}
## Dependency Modules Result
type DependencyModulesResult {
items: [sbt.internal.bsp.DependencyModulesItem]
}
type DependencyModulesItem {
target: sbt.internal.bsp.BuildTargetIdentifier
modules: [sbt.internal.bsp.DependencyModule]
}
type DependencyModule {
## Module name
name: String!
## Module version
version: String!
## Kind of data to expect in the `data` field.
dataKind: String
## Language-specific metadata about this module.
data: sjsonnew.shaded.scalajson.ast.unsafe.JValue
}
## Task Notifications
type TaskStartParams {
+2 -2
View File
@@ -142,7 +142,7 @@ download_url () {
local jar="$2"
mkdir -p $(dirname "$jar") && {
if command -v curl > /dev/null; then
curl --silent -L "$url" --output "$jar"
curl --fail --silent -L "$url" --output "$jar"
elif command -v wget > /dev/null; then
wget --quiet -O "$jar" "$url"
else
@@ -362,7 +362,7 @@ addSbtScriptProperty () {
}
addJdkWorkaround () {
local is_25="$(expr $java_version "=" 25)"
local is_25="$(expr $java_version ">=" 25)"
if [[ "$hide_jdk_warnings" == "0" ]]; then
:
else
-2
View File
@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration/>
@@ -15,7 +15,7 @@ def completionsParser(state: State) =
{
val notQuoted = (NotQuoted ~ any.*) map { case (nq, s) => (nq +: s).mkString }
val quotedOrUnquotedSingleArgument = Space ~> (StringVerbatim | StringEscapable | notQuoted)
applyEffect(token(quotedOrUnquotedSingleArgument ?? "" examples ("", " ")))(runCompletions(state))
applyEffect(token((quotedOrUnquotedSingleArgument ?? "").examples("", " ")))(runCompletions(state))
}
def runCompletions(state: State)(input: String): State = {
val xs = Parser.completions(state.combinedParser, input, 9).get map {
@@ -5,7 +5,7 @@ lazy val bar = taskKey[Unit]("Runs the bar task")
def makeFoo(config: Configuration): Setting[?] =
config / foo := IO.write(file(s"${config.name}-foo"), "foo")
lazy val PerformanceTest = (config("pt") extend Test)
lazy val PerformanceTest = config("pt").extend(Test)
lazy val root = (project in file("."))
.configs(PerformanceTest)
@@ -0,0 +1,9 @@
lazy val scala212 = "2.12.21"
lazy val scala213 = "2.13.12"
ThisBuild / scalaVersion := scala212
lazy val root = (project in file("."))
.settings(
crossScalaVersions := Seq(scala212, scala213),
)
@@ -0,0 +1,11 @@
# Test that "cross" works as an alias for "+"
> cross clean
# Test that "cross" works with verbose flag
> cross -v compile
# Test that "switch" works as an alias for "++"
> switch 2.13.12!
# Test that "switch" works with a command
> switch 2.12.21! compile
@@ -2,7 +2,7 @@
import sbt.TupleSyntax.*
lazy val root = (project in file(".")).settings(
a := (baseDirectory mapN (b => if ((b / "succeed").exists) () else sys.error("fail"))).value,
a := baseDirectory.mapN(b => if ((b / "succeed").exists) () else sys.error("fail")).value,
// deprecated?
// b := (a.task(at => nop dependsOn(at))).value,
c := (a mapN { _ => () }).value,
@@ -17,7 +17,7 @@ val aResolver = Def.setting {
val bResolver = Def.setting {
val dir = (ThisBuild / baseDirectory).value / "b-repo"
Resolver.file("b-resolver", dir)(Resolver.defaultIvyPatterns)
Resolver.file("b-resolver", dir)(using Resolver.defaultIvyPatterns)
}
val apiBaseSetting = apiURL := Some(apiBase(name.value))
+6 -6
View File
@@ -14,11 +14,11 @@ val check = inputKey[Unit]("")
val sample = AttributeKey[Int]("demo-key")
val dummyKey = taskKey[Unit]("")
def updateDemoInit = state map { s => (s get sample getOrElse 9) + 1 }
def updateDemoInit = state map { s => s.get(sample).getOrElse(9) + 1 }
lazy val root = (project in file(".")).
settings(
updateDemo := (updateDemoInit updateState demoState).value,
updateDemo := updateDemoInit.updateState(demoState).value,
check := checkInit.evaluated,
inMemorySetting,
persistedSetting,
@@ -27,19 +27,19 @@ lazy val root = (project in file(".")).
dummyKey := (),
)
def demoState(s: State, i: Int): State = s put (sample, i + 1)
def demoState(s: State, i: Int): State = s.put(sample, i + 1)
def checkInit: Initialize[InputTask[Unit]] = Def inputTask {
val key = (token(Space ~> IntBasic) ~ token(Space ~> IntBasic).?).parsed
val (curExpected, prevExpected) = key
val value = updateDemo.value
val prev = state.value get sample
val prev = state.value.get(sample)
assert(value == curExpected, s"Expected current value to be $curExpected, got $value")
assert(prev == prevExpected, s"Expected previous value to be $prevExpected, got $prev")
}
def inMemorySetting = keep := (getPrevious(keep) map { case None => 3; case Some(x) => x + 1} keepAs(keep)).value
def persistedSetting = persist := (loadPrevious(persist) map { case None => 17; case Some(x) => x + 1} storeAs(persist)).value
def inMemorySetting = keep := getPrevious(keep) .map { case None => 3; case Some(x) => x + 1}.keepAs(keep).value
def persistedSetting = persist := loadPrevious(persist).map { case None => 17; case Some(x) => x + 1}.storeAs(persist).value
def inMemoryCheck = checkKeep := (inputCheck( (ctx, s) => Space ~> str( getFromContext( keep, ctx, s)) )).evaluated
def persistedCheck = checkPersist := (inputCheck( (ctx, s) => Space ~> str(loadFromContext(persist, ctx, s)) )).evaluated
@@ -0,0 +1,11 @@
lazy val helloWithoutStreams = taskKey[Unit]("")
lazy val helloWithStreams = taskKey[Unit]("")
helloWithoutStreams := {
throw new RuntimeException("boom without streams!")
}
helloWithStreams := {
val log = streams.value.log
throw new RuntimeException("boom with streams!")
}
@@ -0,0 +1,2 @@
-> helloWithoutStreams
-> helloWithStreams
@@ -63,7 +63,7 @@ def org = "test"
def mainArtifact = Artifact(artifactID, tpe, ext, classifier)
// define the IDs to use for publishing and retrieving
def publishedID = org % artifactID % vers artifacts(mainArtifact)
def publishedID = (org % artifactID % vers).artifacts(mainArtifact)
def retrieveID = org % "test-retrieve" % "2.0"
// check that the test class is on the compile classpath, either because it was compiled or because it was properly retrieved
@@ -80,5 +80,5 @@ def checkTask(classpath: TaskKey[Classpath]) =
// use the user local resolver to fetch the SNAPSHOT version of the compiler-bridge
def userLocalFileResolver(appConfig: AppConfiguration): Resolver = {
val ivyHome = appConfig.provider.scalaProvider.launcher.ivyHome
Resolver.file("User Local", ivyHome / "local")(Resolver.defaultIvyPatterns)
Resolver.file("User Local", ivyHome / "local")(using Resolver.defaultIvyPatterns)
}
@@ -33,5 +33,5 @@ val a = project
// use the user local resolver to fetch the SNAPSHOT version of the compiler-bridge
def userLocalFileResolver(appConfig: AppConfiguration): Resolver = {
val ivyHome = appConfig.provider.scalaProvider.launcher.ivyHome
Resolver.file("User Local", ivyHome / "local")(Resolver.defaultIvyPatterns)
Resolver.file("User Local", ivyHome / "local")(using Resolver.defaultIvyPatterns)
}
@@ -2,7 +2,7 @@ lazy val check = taskKey[Unit]("check classifier in update report")
lazy val root = (project in file(".")).settings(
scalaVersion := "2.13.16",
libraryDependencies += "io.netty" % "netty-transport-native-epoll" % "4.1.118.Final" classifier "linux-x86_64",
libraryDependencies += ("io.netty" % "netty-transport-native-epoll" % "4.1.118.Final").classifier("linux-x86_64"),
check := {
val report = update.value
val modules = report.configurations.flatMap(_.modules)
@@ -1,4 +1,4 @@
ivyPaths := IvyPaths(baseDirectory.value.toString, Some(((ThisBuild / baseDirectory).value / "ivy" / "cache").toString))
ThisBuild / csrCacheDirectory := (ThisBuild / baseDirectory).value / "coursier-cache"
libraryDependencies += "org.testng" % "testng" % "5.7" classifier "jdk15"
libraryDependencies += ("org.testng" % "testng" % "5.7").classifier("jdk15")
@@ -1,2 +1,2 @@
ThisBuild / scalaVersion := "2.11.12"
libraryDependencies += "org.jclouds.api" % "nova" % "1.5.9" classifier "tests"
libraryDependencies += ("org.jclouds.api" % "nova" % "1.5.9").classifier("tests")
@@ -10,8 +10,8 @@ libraryDependencies += "bad" % "mvn" % "1.0"
TaskKey[Unit]("check") := {
val cp = (Compile / fullClasspath).value
def isTestJar(n: String): Boolean =
(n contains "scalacheck") ||
(n contains "specs2")
n.contains("scalacheck") ||
n.contains("specs2")
val testLibs = cp map (_.data.name) filter isTestJar
assert(testLibs.isEmpty, s"Compile Classpath has test libs:\n * ${testLibs.mkString("\n * ")}")
}
@@ -1,4 +1,4 @@
resolvers += Resolver.file("buggy", file("repo"))(
resolvers += Resolver.file("buggy", file("repo"))(using
Patterns(
ivyPatterns = Vector("[organization]/[module]/[revision]/ivy.xml"),
artifactPatterns = Vector("[organization]/[module]/[revision]/[artifact].[ext]"),
@@ -8,5 +8,5 @@ resolvers += Resolver.file("buggy", file("repo"))(
)
)
libraryDependencies += "a" % "b" % "1.0.0" % "compile->runtime" artifacts(Artifact("b1", "jar", "jar"))
libraryDependencies += "a" % "b" % "1.0.0" % "test->runtime" artifacts(Artifact("b1", "jar", "jar"))
libraryDependencies += ("a" % "b" % "1.0.0" % "compile->runtime").artifacts(Artifact("b1", "jar", "jar"))
libraryDependencies += ("a" % "b" % "1.0.0" % "test->runtime").artifacts(Artifact("b1", "jar", "jar"))
@@ -61,5 +61,5 @@ val use2 = project
// use the user local resolver to fetch the SNAPSHOT version of the compiler-bridge
def userLocalFileResolver(appConfig: AppConfiguration): Resolver = {
val ivyHome = appConfig.provider.scalaProvider.launcher.ivyHome
Resolver.file("User Local", ivyHome / "local")(Resolver.defaultIvyPatterns)
Resolver.file("User Local", ivyHome / "local")(using Resolver.defaultIvyPatterns)
}
@@ -1,3 +1,3 @@
libraryDependencies += "org.vaadin" % "dontpush-addon-ozonelayer" % "0.4.6" exclude("org.atmosphere", "atmosphere-compat-jetty")
libraryDependencies += ("org.vaadin" % "dontpush-addon-ozonelayer" % "0.4.6").exclude("org.atmosphere", "atmosphere-compat-jetty")
resolvers += "asdf" at "https://maven.vaadin.com/vaadin-addons"
@@ -15,7 +15,7 @@ def check(className: String): Def.Initialize[Task[Unit]] =
import sbt.TupleSyntax.*
(Compile / fullClasspath, fileConverter.toTaskable) mapN { (cp, c) =>
given FileConverter = c
val existing = cp.files.filter(_.toFile.getName contains "scala-library")
val existing = cp.files.filter(_.toFile.getName.contains("scala-library"))
println("Full classpath: " + cp.mkString("\n\t", "\n\t", ""))
println("scala-library.jar: " + existing.mkString("\n\t", "\n\t", ""))
val loader = ClasspathUtilities.toLoader(existing.map(_.toFile()))
@@ -19,7 +19,7 @@ def libraryDeps(base: File) = {
def check(ver: String) =
(Compile / dependencyClasspath) map { jars =>
val log4j = jars map (_.data) collect {
case f if f.name contains "log4j-" => f.name
case f if f.name.contains("log4j-") => f.name
}
if (log4j.size != 1 || !log4j.head.contains(ver))
sys.error("Did not download the correct jar.")
@@ -13,7 +13,7 @@ val publishPort = 3030
// Publish to HTTP server (localhost) - ivyless publish uses PUT
// Resolver.url expects java.net.URL; in build.sbt "url" is sbt.URI, so use java.net.URL explicitly
publishTo := Some(
Resolver.url("test-repo", new java.net.URI(s"http://localhost:$publishPort/").toURL)(Resolver.ivyStylePatterns)
Resolver.url("test-repo", new java.net.URI(s"http://localhost:$publishPort/").toURL)(using Resolver.ivyStylePatterns)
.withAllowInsecureProtocol(true)
)
@@ -9,7 +9,7 @@ scalaVersion := "3.8.3"
val publishRepoBase = settingKey[File]("Base directory for publish repo")
publishRepoBase := baseDirectory.value / "repo"
publishTo := Some(Resolver.file("test-repo", publishRepoBase.value)(Resolver.ivyStylePatterns))
publishTo := Some(Resolver.file("test-repo", publishRepoBase.value)(using Resolver.ivyStylePatterns))
useIvy := false
@@ -12,7 +12,7 @@ version := ( if(stable.value) "1.0" else "1.1-SNAPSHOT" )
publishTo := {
val base = baseDirectory.value / ( if(stable.value) "stable" else "snapshot" )
Some( Resolver.file("local-" + base, base)(Resolver.ivyStylePatterns) )
Some( Resolver.file("local-" + base, base)(using Resolver.ivyStylePatterns) )
}
publishMavenStyle := false
@@ -1,6 +1,6 @@
addSbtPlugin("org.example" % "def" % "latest.integration")
resolvers ++= {
def r(tpe: String) = Resolver.file(s"local-$tpe", baseDirectory.value / ".." / tpe)(Resolver.ivyStylePatterns)
def r(tpe: String) = Resolver.file(s"local-$tpe", baseDirectory.value / ".." / tpe)(using Resolver.ivyStylePatterns)
r("snapshot") :: r("stable") :: Nil
}
@@ -1,11 +1,13 @@
import scala.xml._
lazy val root = (project in file(".")) settings (
lazy val root = (project in file(".")).settings(
readPom := Def.uncached {
val vf = makePom.value
val converter = fileConverter.value
XML.loadFile(converter.toPath(vf).toFile)
},
description := "pom.xml test description",
homepage := Some(url("https://example.com/pom_test_url")),
TaskKey[Unit]("checkPom") := checkPom.value,
TaskKey[Unit]("checkExtra") := checkExtra.value,
TaskKey[Unit]("checkVersionPlusMapping") := checkVersionPlusMapping.value,
@@ -65,13 +67,17 @@ lazy val checkReleaseNotesURL = readPom.map: pomXml =>
lazy val checkPom = Def.task {
val pomXML = readPom.value
checkProject(pomXML)
val urlFromPom = (pomXML \ "url").text
assert(urlFromPom == "https://example.com/pom_test_url", s"Expected homepage url, got: $urlFromPom")
val descriptionFromPom = (pomXML \ "description").text
assert(descriptionFromPom == "pom.xml test description", s"Expected description, got: $descriptionFromPom")
val ivyRepositories = fullResolvers.value
withRepositories(pomXML) { repositoriesElement =>
val repositories = repositoriesElement \ "repository"
val writtenRepositories = repositories.map(read).distinct
val mavenStyleRepositories = (ivyRepositories.collect {
case x: MavenRepository
if (x.name != "public") && (x.name != "jcenter") && !(x.root startsWith "file:") =>
if (x.name != "public") && (x.name != "jcenter") && !(x.root.startsWith("file:")) =>
normalize(x)
}).distinct
@@ -10,7 +10,7 @@ TaskKey[Unit]("checkName") := Def.uncached {
val path = converter.toPath(vf).toAbsolutePath.toString
val module = moduleName.value
val n = name.value
assert(path contains module, s"Path $path did not contain module name $module")
assert(path.contains(module), s"Path $path did not contain module name $module")
assert(!path.contains(n), s"Path $path contained $n")
()
}
@@ -1,5 +1,5 @@
libraryDependencies ++= Seq("natives-windows", "natives-linux", "natives-osx") map ( c =>
"org.lwjgl.lwjgl" % "lwjgl-platform" % "2.8.2" classifier c
("org.lwjgl.lwjgl" % "lwjgl-platform" % "2.8.2").classifier(c)
)
autoScalaLibrary := false
@@ -7,7 +7,7 @@ def commonSettings: Seq[Def.Setting[?]] =
scalaVersion := "2.10.4",
ThisBuild / organization := "org.example",
ThisBuild / version := "1.0-SNAPSHOT",
resolvers += Resolver.file("old-local", file(sys.props("user.home") + "/.ivy2/local"))(Resolver.ivyStylePatterns)
resolvers += Resolver.file("old-local", file(sys.props("user.home") + "/.ivy2/local"))(using Resolver.ivyStylePatterns)
)
lazy val main = project.
@@ -7,7 +7,7 @@ lazy val root = (project in file("."))
dependencyOverrides += "org.webjars.npm" % "is-number" % "5.0.0",
check := {
val cp = (Compile / externalDependencyClasspath).value.map {_.data.name}.sorted
if (!(cp contains "is-number-5.0.0.jar")) {
if (!cp.contains("is-number-5.0.0.jar")) {
sys.error("is-number-5.0.0 not found when it should be included: " + cp.toString)
}
}
@@ -29,7 +29,7 @@ def pomIncludeRepository(base: File, prev: MavenRepository => Boolean): MavenRep
}
def addSlash(s: String): String = s match {
case s if s endsWith "/" => s
case s if s.endsWith("/") => s
case _ => s + "/"
}
@@ -4,7 +4,7 @@ lazy val root = (project in file("."))
.settings(
scalaVersion := "2.13.16",
autoScalaLibrary := false,
libraryDependencies += "org.eclipse.jetty" % "jetty-webapp" % "11.0.15" artifacts (Artifact("jetty-webapp", "war", "war")),
libraryDependencies += ("org.eclipse.jetty" % "jetty-webapp" % "11.0.15").artifacts(Artifact("jetty-webapp", "war", "war")),
libraryDependencies += "com.typesafe" % "config" % "1.4.3",
// classified artifact with non-default type: both <type> and <classifier> must appear
libraryDependencies += ("com.example" % "classified-war" % "1.0")
@@ -1,8 +1,8 @@
val root = project in file(".")
val subJar = project in file("subJar")
def warArtifact = (Compile / packageBin / artifact) ~= (_ withType "war" withExtension "war")
val subWar = project in file("subWar") settings warArtifact
val subParent = project in file("subParent") settings ((Compile / publishArtifact) := false)
def warArtifact = (Compile / packageBin / artifact) ~= (_.withType("war").withExtension("war"))
val subWar = project.in(file("subWar")).settings(warArtifact)
val subParent = project.in(file("subParent")).settings((Compile / publishArtifact) := false)
val checkPom = taskKey[Unit]("")
(ThisBuild / checkPom) := {
@@ -15,8 +15,8 @@ lazy val root = (project in file(".")).
"d" % "d" % "1.0" % "test",
"e" % "e" % "1.0" % Custom,
"f" % "f" % "1.0" % "custom,optional,runtime",
"g" % "g" % "1.0" % "custom,runtime" classifier "foo",
"h" % "h" % "1.0" % "custom,optional,runtime" classifier "foo"
("g" % "g" % "1.0" % "custom,runtime").classifier("foo"),
("h" % "h" % "1.0" % "custom,optional,runtime").classifier("foo")
)
)
@@ -27,7 +27,7 @@ lazy val root = (project in file(".")).
)
def checkServletAPI(paths: Seq[File], shouldBeIncluded: Boolean, label: String) = {
val servletAPI = paths.find(_.getName contains "servlet-api")
val servletAPI = paths.find(_.getName.contains("servlet-api"))
if (shouldBeIncluded) {
if (servletAPI.isEmpty) sys.error(s"Servlet API should have been included in $label.")
} else
@@ -7,7 +7,7 @@ lazy val root = (project in file(".")).
organization := "A",
version := "1.0",
ivyPaths := baseDirectory( dir => IvyPaths(dir, Some(dir / "ivy" / "cache")) ).value,
externalResolvers := (baseDirectory map { base => Resolver.file("local", base / "ivy" / "local" asFile)(Resolver.ivyStylePatterns) :: Nil }).value
externalResolvers := (baseDirectory map { base => Resolver.file("local", base / "ivy" / "local" asFile)(using Resolver.ivyStylePatterns) :: Nil }).value
)),
mavenStyle,
interProject,
@@ -5,7 +5,7 @@ lazy val root = (project in file(".")).
organization := "A",
version := "1.0",
ivyPaths := baseDirectory( dir => IvyPaths(dir, Some(dir / "ivy" / "cache")) ).value,
externalResolvers := (baseDirectory map { base => Resolver.file("local", base / "ivy" / "local" asFile)(Resolver.ivyStylePatterns) :: Nil }).value
externalResolvers := (baseDirectory map { base => Resolver.file("local", base / "ivy" / "local" asFile)(using Resolver.ivyStylePatterns) :: Nil }).value
)),
mavenStyle,
name := "Retrieve Test",
@@ -17,11 +17,11 @@ libraryDependencies += "org.scala-lang" % "scala-actors" % "2.11.12"
lazy val check = taskKey[Unit]("Runs the check")
check := {
val lastLog = BuiltinCommands lastLogFile state.value
val lastLog = BuiltinCommands.lastLogFile(state.value)
val last = IO read lastLog.get
def containsWarn1 = last contains "Binary version (1.1.0) for dependency org.scala-lang#scala-actors-migration_2.11;1.1.0"
def containsWarn2 = last contains "Binary version (0.9.1) for dependency org.scala-lang#scala-pickling_2.11;0.9.1"
def containsWarn3 = last contains "differs from Scala binary version in project (2.11)."
def containsWarn1 = last.contains("Binary version (1.1.0) for dependency org.scala-lang#scala-actors-migration_2.11;1.1.0")
def containsWarn2 = last.contains("Binary version (0.9.1) for dependency org.scala-lang#scala-pickling_2.11;0.9.1")
def containsWarn3 = last.contains("differs from Scala binary version in project (2.11).")
if (containsWarn1 && containsWarn3) sys error "scala-actors-migration isn't exempted from the Scala binary version check"
if (containsWarn2 && containsWarn3) sys error "scala-pickling isn't exempted from the Scala binary version check"
}
@@ -7,7 +7,7 @@ def localCache =
lazy val sharedResolver: Resolver = {
val r = Resolver.defaultShared
r withConfiguration (r.configuration withIsLocal false)
r.withConfiguration(r.configuration.withIsLocal(false))
//MavenRepository("example-shared-repo", "file:///tmp/shared-maven-repo-bad-example")
//Resolver.file("example-shared-repo", repoDir)(Resolver.defaultPatterns)
}
@@ -9,7 +9,7 @@ def localCache =
lazy val sharedResolver: Resolver = {
val r = Resolver.defaultShared
r withConfiguration (r.configuration withIsLocal false)
r.withConfiguration(r.configuration.withIsLocal(false))
//MavenRepository("example-shared-repo", "file:///tmp/shared-maven-repo-bad-example")
//Resolver.file("example-shared-repo", repoDir)(Resolver.defaultPatterns)
}
@@ -5,8 +5,8 @@ lazy val root = (project in file("."))
autoScalaLibrary := false,
managedScalaInstance := false,
transitiveClassifiers := Seq("sources"),
TaskKey[Unit]("checkSources") := (updateClassifiers map checkSources).value,
TaskKey[Unit]("checkBinaries") := (update map checkBinaries).value,
TaskKey[Unit]("checkSources") := updateClassifiers.map(checkSources).value,
TaskKey[Unit]("checkBinaries") := update.map(checkBinaries).value,
)
def getSources(report: UpdateReport) = report.matching(artifactFilter(`classifier` = "sources") )
@@ -3,8 +3,8 @@ ThisBuild / scalaVersion := "2.12.12"
lazy val root = (project in file("."))
.settings(
libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.5.22",
TaskKey[Unit]("checkSources") := (updateClassifiers map checkSources).value,
TaskKey[Unit]("checkBinaries") := (update map checkBinaries).value
TaskKey[Unit]("checkSources") := updateClassifiers.map(checkSources).value,
TaskKey[Unit]("checkBinaries") := update.map(checkBinaries).value
)
def getSources(report: UpdateReport) = report.matching(artifactFilter(`classifier` = "sources") )
@@ -1,3 +1,3 @@
libraryDependencies += "org.example" % "def" % "2.0" classifier("tests")
libraryDependencies += ("org.example" % "def" % "2.0").classifier("tests")
externalResolvers := Seq("example" at (baseDirectory.value / "ivy-repo").toURI.toString)
@@ -9,9 +9,9 @@ libraryDependencies += "exclude.test" % "app" % "1.0.0"
val checkDependencies = taskKey[Unit]("Checks that dependencies are correct.")
checkDependencies := {
val hasBadJar = (Compile / fullClasspath).value.exists { jar => jar.data.name contains "bottom-1.0.0.jar"}
val hasBadJar = (Compile / fullClasspath).value.exists { jar => jar.data.name.contains("bottom-1.0.0.jar")}
val errorJarString = (Compile / fullClasspath).value.map(_.data.name).mkString(" * ", "\n * ", "")
val hasBadMiddleJar = (Compile / fullClasspath).value.exists { jar => jar.data.name contains "middle-1.0.0.jar"}
val hasBadMiddleJar = (Compile / fullClasspath).value.exists { jar => jar.data.name.contains("middle-1.0.0.jar")}
assert(!hasBadMiddleJar, s"Failed to exclude excluded dependency on classpath!\nFound:\n$errorJarString")
assert(!hasBadJar, s"Failed to exclude transitive excluded dependency on classpath!\nFound:\n$errorJarString")
val modules =
@@ -1,3 +1,3 @@
ThisBuild / scalaVersion := "2.11.12"
libraryDependencies += "ccl.northwestern.edu" % "netlogo" % "5.3.1" % "provided" from s"https://github.com/NetLogo/NetLogo/releases/download/5.3.1/NetLogo.jar"
libraryDependencies += ("ccl.northwestern.edu" % "netlogo" % "5.3.1" % "provided").from(s"https://github.com/NetLogo/NetLogo/releases/download/5.3.1/NetLogo.jar")
@@ -8,7 +8,7 @@ def localCache =
lazy val root = (project in file(".")).
settings(
localCache,
libraryDependencies += "org.jsoup" % "jsoup" % "1.9.1" % Test from "https://jsoup.org/packages/jsoup-1.9.1.jar",
libraryDependencies += ("org.jsoup" % "jsoup" % "1.9.1" % Test).from("https://jsoup.org/packages/jsoup-1.9.1.jar"),
ivyLoggingLevel := UpdateLogging.Full,
TaskKey[Unit]("checkInTest") := checkClasspath(Test).value,
TaskKey[Unit]("checkInCompile") := checkClasspath(Compile).value
@@ -14,7 +14,7 @@ val commonSettings = Seq[Def.Setting[?]](
lazy val bippy = project settings (
commonSettings,
resolvers += Resolver
.file("ivy-local", file(sys.props("user.home")) / ".ivy2" / "local")(Resolver.ivyStylePatterns),
.file("ivy-local", file(sys.props("user.home")) / ".ivy2" / "local")(using Resolver.ivyStylePatterns),
publishTo := Some(Resolver.file("local-repo", localRepo.value))
)
@@ -40,6 +40,6 @@ InputKey[Unit]("check") := {
val s = IO readStream jar.getInputStream(jar.getJarEntry("Bippy.scala"))
val expected = s"def release = $n"
assert(s contains expected, s"""Bippy should contain $expected, contents:\n$s""")
assert(s.contains(expected), s"""Bippy should contain $expected, contents:\n$s""")
()
}
@@ -1 +1 @@
resolvers += Resolver.file("ivy-local", file(sys.props("user.home")) / ".ivy2" / "local")(Resolver.ivyStylePatterns)
resolvers += Resolver.file("ivy-local", file(sys.props("user.home")) / ".ivy2" / "local")(using Resolver.ivyStylePatterns)
@@ -31,10 +31,10 @@ lazy val root = (project in file(".")).
val acp = (a / Compile / externalDependencyClasspath).value.sortBy {_.data.name}
val bcp = (b / Compile / externalDependencyClasspath).value.sortBy {_.data.name}
if (acp exists { _.data.name contains "slf4j-api-1.7.5.jar" }) {
if (acp exists { _.data.name.contains("slf4j-api-1.7.5.jar") }) {
sys.error("slf4j-api-1.7.5.jar found when it should NOT be included: " + acp.toString)
}
if (bcp exists { _.data.name contains "dispatch-core_2.11-0.11.1.jar" }) {
if (bcp exists { _.data.name.contains("dispatch-core_2.11-0.11.1.jar") }) {
sys.error("dispatch-core_2.11-0.11.1.jar found when it should NOT be included: " + bcp.toString)
}
@@ -10,7 +10,7 @@ lazy val b = project
lazy val bResolver = Def.setting {
val dir = (ThisBuild / baseDirectory).value / "b-repo"
Resolver.file("b-resolver", dir)(Resolver.defaultIvyPatterns)
Resolver.file("b-resolver", dir)(using Resolver.defaultIvyPatterns)
}
lazy val check = taskKey[Unit]("")
@@ -1,2 +1,2 @@
scalaVersion := "2.12.8"
libraryDependencies += "org.jclouds.api" % "nova" % "1.5.9" classifier "tests"
libraryDependencies += ("org.jclouds.api" % "nova" % "1.5.9").classifier("tests")
@@ -1,3 +1,3 @@
scalaVersion := "2.12.21"
libraryDependencies += "ccl.northwestern.edu" % "netlogo" % "5.3.1" % "provided" from s"https://github.com/NetLogo/NetLogo/releases/download/5.3.1/NetLogo.jar"
libraryDependencies += ("ccl.northwestern.edu" % "netlogo" % "5.3.1" % "provided").from(s"https://github.com/NetLogo/NetLogo/releases/download/5.3.1/NetLogo.jar")
@@ -1,3 +1,3 @@
scalaVersion := "2.12.8"
resolvers += Resolver.file("space-repo", file(raw"/tmp/space the final frontier/repo"))(Resolver.ivyStylePatterns)
resolvers += Resolver.file("space-repo", file(raw"/tmp/space the final frontier/repo"))(using Resolver.ivyStylePatterns)
@@ -10,7 +10,7 @@ mainClass := Some("jartest.Main")
Compile / packageBin / packageOptions := {
def manifestExtra = {
val mf = new Manifest
mf.getMainAttributes.put(Attributes.Name.CLASS_PATH, makeString(scalaInstance.value.libraryJars))
mf.getMainAttributes.put(Attributes.Name.CLASS_PATH, makeString(scalaInstance.value.libraryJars.toSeq))
mf
}
(Compile / packageBin / packageOptions).value :+ Package.JarManifest(manifestExtra)
@@ -9,7 +9,7 @@ packageOptions := {
def manifestExtra = {
import java.util.jar._
val mf = new Manifest
mf.getMainAttributes.put(Attributes.Name.CLASS_PATH, makeString(scalaInstance.value.libraryJars))
mf.getMainAttributes.put(Attributes.Name.CLASS_PATH, makeString(scalaInstance.value.libraryJars.toSeq))
mf
}
Package.JarManifest(manifestExtra) +: packageOptions.value
@@ -1,4 +1,4 @@
val test123 = project in file(".") enablePlugins TestP settings(
val test123 = project.in(file(".")).enablePlugins(TestP).settings(
Compile / resourceGenerators += Def.task {
streams.value.log.info("resource generated in settings")
Seq.empty[File]
@@ -6,9 +6,9 @@ val test123 = project in file(".") enablePlugins TestP settings(
)
TaskKey[Unit]("check") := {
val last = IO read (BuiltinCommands lastLogFile state.value).get
val last = IO.read(BuiltinCommands.lastLogFile(state.value).get)
def assertContains(expectedString: String) =
if (!(last contains expectedString)) sys error s"Expected string $expectedString to be present"
if (!last.contains(expectedString)) sys error s"Expected string $expectedString to be present"
assertContains("resource generated in settings")
assertContains("resource generated in plugin")
}
@@ -2,7 +2,7 @@ val root = (project in file("."))
TaskKey[Unit]("checkScalaVersion", "test") := Def.uncached {
val sv = scalaVersion.value
assert(sv startsWith "3.", s"Found $sv!")
assert(sv.startsWith("3."), s"Found $sv!")
}
TaskKey[Unit]("checkArtifacts", "test") := Def.uncached {
@@ -2,7 +2,7 @@
val loadCount = AttributeKey[Int]("load-count")
val unloadCount = AttributeKey[Int]("unload-count")
def f(key: AttributeKey[Int]) = (s: State) => {
val previous = s get key getOrElse 0
val previous = s.get(key)getOrElse(0)
s.put(key, previous + 1)
}
Seq(
@@ -14,7 +14,7 @@
InputKey[Unit]("checkCount") := {
val s = state.value
val args = Def.spaceDelimited().parsed
def get(label: String) = s get AttributeKey[Int](label) getOrElse 0
def get(label: String) = s.get(AttributeKey[Int](label)).getOrElse(0)
val loadCount = get("load-count")
val unloadCount = get("unload-count")
val expectedLoad = args(0).toInt
@@ -21,7 +21,7 @@ object Common {
val UpdateK3 = Command.command("UpdateK3"): (st: State) =>
val ex = Project extract st
val ex = Project.extract(st)
import ex._
val session2 = BuiltinCommands.setThis(ex, Seq(k3 := {}), """k3 := {
|//
@@ -15,7 +15,7 @@ lazy val commonSettings = Seq(
val ivyHome = Classpaths.bootIvyHome(appConfiguration.value) getOrElse sys.error(
"Launcher did not provide the Ivy home directory."
)
Resolver.file("real-local", ivyHome / "local")(Resolver.ivyStylePatterns)
Resolver.file("real-local", ivyHome / "local")(using Resolver.ivyStylePatterns)
},
resolvers += Resolver.mavenLocal,
resolvers += ("test-repo" at ((ThisBuild / baseDirectory).value / "repo/").asURL.toString)
@@ -1,2 +1,2 @@
lazy val a = project in file(".") dependsOn(b)
lazy val a = project.in(file(".")).dependsOn(b)
lazy val b = project
@@ -4,5 +4,5 @@ lazy val dep = project
lazy val use = project.
settings(
(Compile / unmanagedJars) += ((dep / Compile / packageBin) map Attributed.blank).value
(Compile / unmanagedJars) += (dep / Compile / packageBin).map(Attributed.blank).value
)
+80 -76
View File
@@ -10,8 +10,9 @@ object Main:
val sbtHome = new File(
sys.env
.get("SBT_HOME")
.getOrElse:
.getOrElse(
sys.env.get("SBT_BIN_DIR").map(d => new File(d).getParent).getOrElse(cwd.getAbsolutePath)
)
)
val sbtBinDir = new File(sbtHome, "bin")
@@ -26,86 +27,88 @@ object Main:
System.exit(if exitCode == 0 then 0 else 1)
private def run(cwd: File, sbtHome: File, sbtBinDir: File, opts: LauncherOptions): Int =
if opts.help then return printUsage()
if opts.version || opts.numericVersion || opts.scriptVersion then
return handleVersionCommands(cwd, sbtHome, sbtBinDir, opts)
if opts.shutdownAll then
if opts.help then printUsage()
else if opts.version || opts.numericVersion || opts.scriptVersion then
handleVersionCommands(cwd, sbtHome, sbtBinDir, opts)
else if opts.shutdownAll then
val javaCmd = Runner.findJavaCmd(opts.javaHome)
return Runner.shutdownAll(javaCmd)
if !opts.allowEmpty && !opts.sbtNew && !ConfigLoader.isSbtProjectDir(cwd) then
Runner.shutdownAll(javaCmd)
else if !opts.allowEmpty && !opts.sbtNew && !ConfigLoader.isSbtProjectDir(cwd) then
System.err.println(
"[error] Neither build.sbt nor a 'project' directory in the current directory: " + cwd
)
System.err.println("[error] run 'sbt new', touch build.sbt, or run 'sbt --allow-empty'.")
return 1
1
else
val buildPropsVersion = ConfigLoader.sbtVersionFromBuildProperties(cwd)
val buildPropsVersion = ConfigLoader.sbtVersionFromBuildProperties(cwd)
val javaCmd = Runner.findJavaCmd(opts.javaHome)
val javaVer = Runner.javaVersion(javaCmd)
val minJdk = Runner.minimumJdkVersion(buildPropsVersion)
if javaVer > 0 && javaVer < minJdk then
if minJdk >= 17 then
System.err.println(
"[error] sbt 2.x requires JDK 17 or above, but you have JDK " + javaVer
)
else System.err.println("[error] sbt requires at least JDK 8+, you have " + javaVer)
1
else
val bspMode = opts.residual.exists(a => a == "bsp" || a == "-bsp" || a == "--bsp")
val clientOpt = opts.client || sys.env.get("SBT_NATIVE_CLIENT").contains("true")
val useNativeClient =
if bspMode then false
else shouldRunNativeClient(opts.copy(client = clientOpt), buildPropsVersion)
val javaCmd = Runner.findJavaCmd(opts.javaHome)
val javaVer = Runner.javaVersion(javaCmd)
val minJdk = Runner.minimumJdkVersion(buildPropsVersion)
if javaVer > 0 && javaVer < minJdk then
if minJdk >= 17 then
System.err.println("[error] sbt 2.x requires JDK 17 or above, but you have JDK " + javaVer)
else System.err.println("[error] sbt requires at least JDK 8+, you have " + javaVer)
return 1
if useNativeClient then
val scriptPath = sbtBinDir.getAbsolutePath.replace("\\", "/") + "/sbt.bat"
Runner.runNativeClient(sbtBinDir, scriptPath, opts)
else
val sbtJar = opts.sbtJar
.filter(p => new File(p).isFile)
.getOrElse(new File(sbtBinDir, "sbt-launch.jar").getAbsolutePath)
if !new File(sbtJar).isFile then
System.err.println("[error] Launcher jar not found: " + sbtJar)
1
else
var javaOpts = ConfigLoader.loadJvmOpts(cwd)
if javaOpts.isEmpty then javaOpts = ConfigLoader.defaultJavaOpts
var sbtOpts = Runner.buildSbtOpts(opts)
val bspMode = opts.residual.exists(a => a == "bsp" || a == "-bsp" || a == "--bsp")
val clientOpt = opts.client || sys.env.get("SBT_NATIVE_CLIENT").contains("true")
val useNativeClient =
if bspMode then false
else shouldRunNativeClient(opts.copy(client = clientOpt), buildPropsVersion)
val (residualJava, bootArgs) = Runner.splitResidual(opts.residual)
javaOpts = javaOpts ++ residualJava
if useNativeClient then
val scriptPath = sbtBinDir.getAbsolutePath.replace("\\", "/") + "/sbt.bat"
return Runner.runNativeClient(sbtBinDir, scriptPath, opts)
val (finalJava, finalSbt) = if opts.mem.isDefined then
val evictedJava = Memory.evictMemoryOpts(javaOpts)
val evictedSbt = Memory.evictMemoryOpts(sbtOpts)
val memOpts = Memory.addMemory(opts.mem.get, javaVer)
(evictedJava ++ memOpts, evictedSbt)
else Memory.addDefaultMemory(javaOpts, sbtOpts, javaVer, LauncherOptions.defaultMemMb)
sbtOpts = finalSbt
val sbtJar = opts.sbtJar
.filter(p => new File(p).isFile)
.getOrElse(new File(sbtBinDir, "sbt-launch.jar").getAbsolutePath)
if !new File(sbtJar).isFile then
System.err.println("[error] Launcher jar not found: " + sbtJar)
return 1
if !opts.noHideJdkWarnings && javaVer >= 25 then
sbtOpts = sbtOpts ++ Seq(
"--sun-misc-unsafe-memory-access=allow",
"--enable-native-access=ALL-UNNAMED"
)
val javaOptsWithDebug = opts.jvmDebug.fold(finalJava)(port =>
finalJava :+ s"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$port"
)
var javaOpts = ConfigLoader.loadJvmOpts(cwd)
if javaOpts.isEmpty then javaOpts = ConfigLoader.defaultJavaOpts
var sbtOpts = Runner.buildSbtOpts(opts)
val (residualJava, bootArgs) = Runner.splitResidual(opts.residual)
javaOpts = javaOpts ++ residualJava
val (finalJava, finalSbt) = if opts.mem.isDefined then
val evictedJava = Memory.evictMemoryOpts(javaOpts)
val evictedSbt = Memory.evictMemoryOpts(sbtOpts)
val memOpts = Memory.addMemory(opts.mem.get, javaVer)
(evictedJava ++ memOpts, evictedSbt)
else Memory.addDefaultMemory(javaOpts, sbtOpts, javaVer, LauncherOptions.defaultMemMb)
sbtOpts = finalSbt
if !opts.noHideJdkWarnings && javaVer == 25 then
sbtOpts = sbtOpts ++ Seq(
"--sun-misc-unsafe-memory-access=allow",
"--enable-native-access=ALL-UNNAMED"
)
val javaOptsWithDebug = opts.jvmDebug.fold(finalJava)(port =>
finalJava :+ s"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$port"
)
Runner.runJvm(javaCmd, javaOptsWithDebug, sbtOpts, sbtJar, bootArgs, opts.verbose)
Runner.runJvm(javaCmd, javaOptsWithDebug, sbtOpts, sbtJar, bootArgs, opts.verbose)
private def shouldRunNativeClient(
opts: LauncherOptions,
buildPropsVersion: Option[String]
): Boolean =
if opts.sbtNew then return false
if opts.jvmClient then return false
val version = buildPropsVersion.getOrElse(LauncherOptions.initSbtVersion)
val parts = version.split("[.-]").take(2).flatMap(s => scala.util.Try(s.toInt).toOption)
val (major, minor) = (parts.lift(0).getOrElse(0), parts.lift(1).getOrElse(0))
if major >= 2 then !opts.server
else if major >= 1 && minor >= 4 then opts.client
else false
if opts.sbtNew then false
else if opts.jvmClient then false
else
val version = buildPropsVersion.getOrElse(LauncherOptions.initSbtVersion)
val parts = version.split("[.-]").take(2).flatMap(s => scala.util.Try(s.toInt).toOption)
val (major, minor) = (parts.lift(0).getOrElse(0), parts.lift(1).getOrElse(0))
if major >= 2 then !opts.server
else if major >= 1 && minor >= 4 then opts.client
else false
private def handleVersionCommands(
cwd: File,
@@ -115,8 +118,8 @@ object Main:
): Int =
if opts.scriptVersion then
println(LauncherOptions.initSbtVersion)
return 0
if opts.version then
0
else if opts.version then
if ConfigLoader.isSbtProjectDir(cwd) then
projectSbtVersion(cwd).foreach: version =>
println("sbt version in this project: " + version)
@@ -125,21 +128,22 @@ object Main:
System.err.println(
"[info] Actual version of sbt is declared using project\\build.properties for each build."
)
return 0
if opts.numericVersion then
0
else if opts.numericVersion then
val javaCmd = Runner.findJavaCmd(opts.javaHome)
val sbtJar = opts.sbtJar
.filter(p => new File(p).isFile)
.getOrElse(new File(sbtBinDir, "sbt-launch.jar").getAbsolutePath)
if !new File(sbtJar).isFile then
System.err.println("[error] Launcher jar not found for version check")
return 1
try
val out = Process(Seq(javaCmd, "-jar", sbtJar, "sbtVersion")).!!
println(out.linesIterator.toSeq.lastOption.map(_.trim).getOrElse(""))
return 0
catch { case _: Exception => return 1 }
0
1
else
try
val out = Process(Seq(javaCmd, "-jar", sbtJar, "sbtVersion")).!!
println(out.linesIterator.toSeq.lastOption.map(_.trim).getOrElse(""))
0
catch { case _: Exception => 1 }
else 0
private def projectSbtVersion(cwd: File): Option[String] =
ConfigLoader.sbtVersionFromBuildProperties(cwd).flatMap(normalizeVersion)
@@ -524,6 +524,19 @@ class BuildServerTest extends AbstractServerTest {
)
}
test("buildTarget/dependencyModules") {
val buildTarget = buildTargetUri("runAndTest", "Compile")
val badBuildTarget = buildTargetUri("badBuildTarget", "Compile")
val targets = Vector(buildTarget, badBuildTarget).map(BuildTargetIdentifier.apply)
val id = dependencyModules(targets.map(_.uri))
val res = svr.session.waitForResultInResponseMsg[DependencyModulesResult](10.seconds, id).get
val runAndTestItem = res.items.find(_.target.contains(BuildTargetIdentifier(buildTarget))).get
assert(
runAndTestItem.modules.exists(_.name.contains("jsoniter-scala-core")),
s"dependencyModules should include jsoniter-scala-core, got: ${runAndTestItem.modules.map(_.name)}"
)
}
test("buildTarget/outputPaths") {
val buildTarget = buildTargetUri("util", "Compile")
val badBuildTarget = buildTargetUri("badBuildTarget", "Compile")
@@ -753,6 +766,11 @@ class BuildServerTest extends AbstractServerTest {
sendRequest("buildTarget/sources", SourcesParams(targets))
}
private def dependencyModules(buildTargets: Seq[URI]): String = {
val targets = buildTargets.map(BuildTargetIdentifier.apply).toVector
sendRequest("buildTarget/dependencyModules", DependencyModulesParams(targets))
}
private def sendRequest(method: String): String = {
val id = svr.session.nextId()
svr.session.sendJsonRpc(id, method, "{}").get
@@ -79,6 +79,16 @@ class ResponseTest extends AbstractServerTest {
}
}
test("unknown method returns error") {
val id = svr.session.nextId()
svr.session.sendJsonRpc(id, "build/foo", "{}").get
val response = svr.session.waitForResponseMsg(10.seconds, id).get
assert(
response.error.exists(_.code == -32601),
s"Expected method-not-found error, got: $response"
)
}
private def neverReceiveResponse(
duration: FiniteDuration
)(predicate: sbt.internal.protocol.JsonRpcResponseMessage => Boolean): Unit =
@@ -13,9 +13,9 @@ import java.util.concurrent.atomic.AtomicReference
import testing.{ Event as TEvent, OptionalThrowable, Status as TStatus, TestSelector }
import util.{ AbstractLogger, Level, ControlEvent, LogEvent }
import sbt.io.IO
import sbt.protocol.testing.TestResult
import sbt.internal.worker1.ForkTestMain
import sbt.io.IO
import verify.BasicTestSuite
import scala.xml.XML
@@ -10,7 +10,7 @@ package sbt.util
import java.io.{ File, IOException }
import java.nio.charset.StandardCharsets
import java.nio.file.{ Files, Path, Paths, StandardCopyOption }
import java.nio.file.{ AtomicMoveNotSupportedException, Files, Path, Paths, StandardCopyOption }
import sbt.internal.util.{ ActionCacheEvent, CacheEventLog, StringVirtualFile1 }
import sbt.io.syntax.*
import sbt.io.IO
@@ -82,40 +82,40 @@ object ActionCache:
case e: Exception =>
cacheEventLog.append(ActionCacheEvent.Error)
throw e
val json = Converter.toJsonUnsafe(result)
val normalizedOutputDir = outputDirectory.toAbsolutePath.normalize()
val uncacheableOutputs =
outputs.filter(f =>
f match
case vf if vf.id.endsWith(ActionCache.dirZipExt) =>
false
case _ =>
val outputPath = fileConverter.toPath(f).toAbsolutePath.normalize()
!outputPath.startsWith(normalizedOutputDir)
)
if uncacheableOutputs.nonEmpty then
cacheEventLog.append(ActionCacheEvent.Error)
logger.error(
s"Cannot cache task because its output files are outside the output directory: \n" +
uncacheableOutputs.mkString(" - ", "\n - ", "")
)
result
else
cacheEventLog.append(ActionCacheEvent.OnsiteTask)
val (input, valuePath) = mkInput(key, codeContentHash, extraHash, config.cacheVersion)
val valueFile = StringVirtualFile1(valuePath, CompactPrinter(json))
val newOutputs = Vector(valueFile) ++ outputs.toVector
try
try
val json = Converter.toJsonUnsafe(result)
val normalizedOutputDir = outputDirectory.toAbsolutePath.normalize()
val uncacheableOutputs =
outputs.filter(f =>
f match
case vf if vf.id.endsWith(ActionCache.dirZipExt) =>
false
case _ =>
val outputPath = fileConverter.toPath(f).toAbsolutePath.normalize()
!outputPath.startsWith(normalizedOutputDir)
)
if uncacheableOutputs.nonEmpty then
cacheEventLog.append(ActionCacheEvent.Error)
logger.error(
s"Cannot cache task because its output files are outside the output directory: \n" +
uncacheableOutputs.mkString(" - ", "\n - ", "")
)
result
else
cacheEventLog.append(ActionCacheEvent.OnsiteTask)
val (input, valuePath) = mkInput(key, codeContentHash, extraHash, config.cacheVersion)
val valueFile = StringVirtualFile1(valuePath, CompactPrinter(json))
val newOutputs = Vector(valueFile) ++ outputs.toVector
store.put(UpdateActionResultRequest(input, newOutputs, exitCode = 0)) match
case Right(cachedResult) =>
store.syncBlobs(cachedResult.outputFiles, outputDirectory)
result
case Left(e) => throw e
catch
case e: IOException =>
logger.debug(s"Skipping cache storage due to error: ${e.getMessage}")
cacheEventLog.append(ActionCacheEvent.Error)
result
catch
case e: IOException =>
logger.debug(s"Skipping cache storage due to error: ${e.getMessage}")
cacheEventLog.append(ActionCacheEvent.Error)
result
// Single cache lookup - use exitCode to distinguish success from failure
getWithFailure(key, codeContentHash, extraHash, tags, config) match
@@ -270,6 +270,36 @@ object ActionCache:
private val default2010Timestamp: Long = 1262304000000L
/**
* Publishes `builtZip` as `destZip` by staging next to the destination and renaming into place.
* Avoids races from a direct `Files.copy` into `destZip` under parallel task execution.
*/
private def installPackagedZip(builtZip: Path, destZip: Path, fallbackStagingDir: Path): Unit =
val stagingDir = Option(destZip.getParent) match
case Some(parent) =>
Files.createDirectories(parent)
parent
case None => fallbackStagingDir
val staging = Files.createTempFile(
stagingDir,
destZip.getFileName.toString + ".",
dirZipExt + ".tmp",
)
try
Files.copy(builtZip, staging, StandardCopyOption.REPLACE_EXISTING)
try
Files.move(
staging,
destZip,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.ATOMIC_MOVE,
)
catch
case _: AtomicMoveNotSupportedException =>
Files.move(staging, destZip, StandardCopyOption.REPLACE_EXISTING)
finally Files.deleteIfExists(staging)
def packageDirectory(
dir: VirtualFileRef,
conv: FileConverter,
@@ -311,7 +341,7 @@ object ActionCache:
tempZipPath.toFile(),
Some(default2010Timestamp)
)
Files.copy(tempZipPath, zipPath, StandardCopyOption.REPLACE_EXISTING)
installPackagedZip(tempZipPath, zipPath, tempDir.toPath())
conv.toVirtualFile(zipPath)
@@ -1,5 +1,10 @@
package sbt.util
import java.nio.charset.StandardCharsets
import java.nio.file.{ Files, Path, Paths }
import java.util.Optional
import java.util.concurrent.{ CyclicBarrier, ExecutorService, Executors, TimeUnit }
import sbt.internal.util.CacheEventLog
import sbt.internal.util.StringVirtualFile1
import sbt.io.IO
@@ -7,18 +12,15 @@ import sbt.io.syntax.*
import verify.BasicTestSuite
import xsbti.{
CompileFailed,
FileConverter,
HashedVirtualFileRef,
Problem,
Position,
Severity,
VirtualFile,
FileConverter,
VirtualFileRef
VirtualFileRef,
}
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.Files
import java.util.Optional
import ActionCache.InternalActionResult
object ActionCacheTest extends BasicTestSuite:
@@ -259,6 +261,36 @@ object ActionCacheTest extends BasicTestSuite:
assert(v2 == 42)
assert(called == 2)
test("packageDirectory is safe when many threads package the same directory concurrently"):
IO.withTemporaryDirectory: tmp =>
val root = tmp.toPath
val classesDir = root.resolve("classes")
Files.createDirectories(classesDir)
Files.writeString(classesDir.resolve("A.class"), "compiled")
val classesPathStr = classesDir.toString
val dirRef = VirtualFileRef.of(classesPathStr)
val conv = new FileConverter:
override def toPath(ref: VirtualFileRef): Path = Paths.get(ref.id)
override def toVirtualFile(path: Path): VirtualFile =
val content =
if Files.isRegularFile(path) then
new String(Files.readAllBytes(path), StandardCharsets.UTF_8)
else ""
StringVirtualFile1(path.toString, content)
val threadCount = 64
val barrier = new CyclicBarrier(threadCount)
val pool: ExecutorService = Executors.newFixedThreadPool(threadCount)
try
val tasks =
for _ <- 1 to threadCount yield pool.submit: () =>
barrier.await(30, TimeUnit.SECONDS)
ActionCache.packageDirectory(dirRef, conv, root)
tasks.foreach(_.get(60, TimeUnit.SECONDS))
val zipPath = Paths.get(classesPathStr + ActionCache.dirZipExt)
assert(Files.isRegularFile(zipPath))
assert(Files.size(zipPath) > 0L)
finally pool.shutdown()
test("Changing cacheVersion invalidates the cache"):
withDiskCache(testCacheVersionInvalidation)