diff --git a/.github/ISSUE_TEMPLATE/--feature-request.md b/.github/ISSUE_TEMPLATE/--feature-request.md index 3d76f73b9..bbce6fbc7 100644 --- a/.github/ISSUE_TEMPLATE/--feature-request.md +++ b/.github/ISSUE_TEMPLATE/--feature-request.md @@ -7,4 +7,6 @@ assignees: '' --- -Please use https://github.com/sbt/sbt/discussions including a specific user story instead of posting them to the issue tracker. +If you have new ideas about sbt or an open-ended discussion, please use https://github.com/sbt/sbt/discussions instead of posting them to the issue tracker, which we use to track bugs and (internal) todo list. + +Please note that sbt is mostly maintained by community effort, so one of the questions that we'd ask might be "would like to contribute it if we help you where the relevant code is". diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4c2e80db..57c7f2e17 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,9 @@ on: pull_request: push: +permissions: + contents: read # to fetch code (actions/checkout) + jobs: test: strategy: @@ -41,7 +44,7 @@ jobs: java: 8 distribution: adopt jobtype: 8 - - os: windows-2019 + - os: windows-latest java: 8 distribution: adopt jobtype: 9 @@ -49,7 +52,7 @@ jobs: env: JAVA_OPTS: -Xms800M -Xmx2G -Xss6M -XX:ReservedCodeCacheSize=128M -server -Dsbt.io.virtual=false -Dfile.encoding=UTF-8 JVM_OPTS: -Xms800M -Xmx2G -Xss6M -XX:ReservedCodeCacheSize=128M -server -Dsbt.io.virtual=false -Dfile.encoding=UTF-8 - SCALA_212: 2.12.16 + SCALA_212: 2.12.17 SCALA_213: 2.13.8 SCALA_3: 3.1.0 UTIL_TESTS: "utilCache/test utilControl/test utilInterface/test utilLogging/test utilPosition/test utilRelation/test utilScripted/test utilTracking/test" @@ -97,11 +100,12 @@ jobs: key: ${{ runner.os }}-sbt-cache-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - name: Setup Windows C++ toolchain uses: ilammy/msvc-dev-cmd@v1 - if: ${{ matrix.os == 'windows-2019' }} + if: ${{ matrix.os == 'windows-latest' }} - name: Build and test (1) if: ${{ matrix.jobtype == 1 }} shell: bash run: | + rm -rf "$HOME/.sbt/boot/" || true # ./sbt -v --client mimaReportBinaryIssues ./sbt -v --client javafmtCheck ./sbt -v --client "Test/javafmtCheck" diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml index 7caea8278..0ab49c9b3 100644 --- a/.github/workflows/dependency-graph.yml +++ b/.github/workflows/dependency-graph.yml @@ -3,8 +3,12 @@ name: Submit Dependency Graph on: push: branches: [1.7.x] # default branch of the project +permissions: {} jobs: submit-graph: + permissions: + contents: write # to submit the dependency graph + name: Submit Dependency Graph runs-on: ubuntu-latest # or windows-latest, or macOS-latest steps: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 2897d641f..b7664a12f 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -5,6 +5,9 @@ on: # # 08:00 UTC = 03:00 EST # - cron: '0 8 * * *' +permissions: + contents: read # to fetch code (actions/checkout) + jobs: deploy: strategy: diff --git a/.gitignore b/.gitignore index 4a82989bc..1f81020b9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ npm-debug.log .vscode/ metals.sbt launcher-package/citest/freshly-baked +.vscode diff --git a/DEVELOPING.md b/DEVELOPING.md index 40cbfda3a..909fb566a 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -7,12 +7,15 @@ Create a [fork](https://docs.github.com/en/github/getting-started-with-github/fo ### Branch to work against -sbt uses two branches for development: +sbt uses **two or three** branches for development: +Generally the default branch set on Github is what we recommend as the base line for PRs. -- Development branch: `develop` (this is also called "master") -- Stable branch: `1.$MINOR.x`, where `$MINOR` is current minor version (e.g. `1.1.x` during 1.1.x series) +- Next minor branch: `1.$MINOR.x`, where `$MINOR` is next minor version (e.g. `1.9.x` during 1.8.x series) +- Development branch: `develop` +- Stable branch: `1.$MINOR.x`, where `$MINOR` is current minor version (e.g. `1.8.x` during 1.8.x series) -The `develop` branch represents the next major version of sbt. Only new features are pushed to the `develop` branch. This is the branch that you will branch off of to make your changes. +Currently `develop` branch represents the next major version of sbt, i.e. sbt 2. +Next minor branch is where new features can be added as long as it is binary compatible with sbt 1.0. The `stable` branch represents the current stable sbt release. Only bug fixes are back-ported to the stable branch. ### Instruction to build just sbt diff --git a/build.sbt b/build.sbt index 8ffea7edc..46681bca6 100644 --- a/build.sbt +++ b/build.sbt @@ -10,7 +10,7 @@ import scala.util.Try // ThisBuild settings take lower precedence, // but can be shared across the multi projects. ThisBuild / version := { - val v = "1.7.2-SNAPSHOT" + val v = "1.8.1-SNAPSHOT" nightlyVersion.getOrElse(v) } ThisBuild / version2_13 := "2.0.0-SNAPSHOT" @@ -43,10 +43,11 @@ ThisBuild / scmInfo := Some( ScmInfo(url("https://github.com/sbt/sbt"), "git@github.com:sbt/sbt.git") ) ThisBuild / resolvers += Resolver.mavenLocal +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always Global / semanticdbEnabled := !(Global / insideCI).value // Change main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala too, if you change this. -Global / semanticdbVersion := "4.5.9" +Global / semanticdbVersion := "4.5.13" val excludeLint = SettingKey[Set[Def.KeyedInitialize[_]]]("excludeLintKeys") Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty) Global / excludeLint += componentID diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala index 7cfed50e6..2b1700572 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala @@ -126,11 +126,12 @@ object ConsoleAppender { def out: ConsoleOut } private[sbt] object Properties { - def from(terminal: Terminal): Properties = new Properties { - override def isAnsiSupported: Boolean = terminal.isAnsiSupported - override def isColorEnabled: Boolean = terminal.isColorEnabled - override def out = ConsoleOut.terminalOut(terminal) - } + def from(terminal: Terminal): Properties = + from(ConsoleOut.terminalOut(terminal), terminal.isAnsiSupported, terminal.isColorEnabled) + + def safelyFrom(terminal: Terminal): Properties = + from(ConsoleOut.safeTerminalOut(terminal), terminal.isAnsiSupported, terminal.isColorEnabled) + def from(o: ConsoleOut, ansi: Boolean, color: Boolean): Properties = new Properties { override def isAnsiSupported: Boolean = ansi override def isColorEnabled: Boolean = color @@ -246,6 +247,18 @@ object ConsoleAppender { new ConsoleAppender(name, Properties.from(terminal), noSuppressedMessage) } + /** + * A new `ConsoleAppender` identified by `name`, and that writes to `terminal`. + * Printing to this Appender will not throw if the Terminal has been closed. + * + * @param name An identifier for the `ConsoleAppender`. + * @param terminal The terminal to which this appender corresponds + * @return A new `ConsoleAppender` that writes to `terminal`. + */ + def safe(name: String, terminal: Terminal): Appender = { + new ConsoleAppender(name, Properties.safelyFrom(terminal), noSuppressedMessage) + } + /** * A new `ConsoleAppender` identified by `name`, and that writes to `out`. * diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala index b946a0cc8..0f6a089bf 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala @@ -8,6 +8,7 @@ package sbt.internal.util import java.io.{ BufferedWriter, PrintStream, PrintWriter } +import java.nio.channels.ClosedChannelException import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicReference @@ -90,6 +91,26 @@ object ConsoleOut { override def toString: String = s"TerminalOut" } + /** Same as terminalOut but it catches and ignores the ClosedChannelException + */ + def safeTerminalOut(terminal: Terminal): ConsoleOut = { + val out = terminalOut(terminal) + new ConsoleOut { + override val lockObject: AnyRef = terminal + override def print(s: String): Unit = catchException(out.print(s)) + override def println(s: String): Unit = catchException(out.println(s)) + override def println(): Unit = catchException(out.println()) + override def flush(): Unit = catchException(out.flush) + override def toString: String = s"SafeTerminalOut($terminal)" + private def catchException(f: => Unit): Unit = { + try f + catch { + case _: ClosedChannelException => () + } + } + } + } + private[this] val consoleOutPerTerminal = new ConcurrentHashMap[Terminal, ConsoleOut] def terminalOut(terminal: Terminal): ConsoleOut = consoleOutPerTerminal.get(terminal) match { case null => diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala index 0019b04e9..27b1e29c4 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala @@ -438,6 +438,7 @@ object Terminal { override def toString: String = s"ProxyTerminal(current = $t)" } private[sbt] def get: Terminal = ProxyTerminal + private[sbt] def current: Terminal = activeTerminal.get private[sbt] def withIn[T](in: InputStream)(f: => T): T = { val original = inputStream.get diff --git a/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala b/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala index e040f8bbf..719b9b2ff 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala @@ -103,7 +103,8 @@ object LoggerContext { } } def close(): Unit = { - loggers.forEach((name, l) => l.clearAppenders()) + closed.set(true) + loggers.forEach((_, l) => l.clearAppenders()) loggers.clear() } } diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index f90f91de3..d86fd410e 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1217,7 +1217,7 @@ object Defaults extends BuildCommon { Seq( testFrameworks :== { import sbt.TestFrameworks._ - Seq(ScalaCheck, Specs2, Specs, ScalaTest, JUnit, MUnit) + Seq(ScalaCheck, Specs2, Specs, ScalaTest, JUnit, MUnit, ZIOTest) }, testListeners :== Nil, testOptions :== Nil, diff --git a/main/src/main/scala/sbt/PluginCross.scala b/main/src/main/scala/sbt/PluginCross.scala index 740709b98..dbadb542a 100644 --- a/main/src/main/scala/sbt/PluginCross.scala +++ b/main/src/main/scala/sbt/PluginCross.scala @@ -99,7 +99,7 @@ private[sbt] object PluginCross { VersionNumber(sv) match { case VersionNumber(Seq(0, 12, _*), _, _) => "2.9.2" case VersionNumber(Seq(0, 13, _*), _, _) => "2.10.7" - case VersionNumber(Seq(1, 0, _*), _, _) => "2.12.16" + case VersionNumber(Seq(1, 0, _*), _, _) => "2.12.17" case _ => sys.error(s"Unsupported sbt binary version: $sv") } } diff --git a/main/src/main/scala/sbt/coursierint/LMCoursier.scala b/main/src/main/scala/sbt/coursierint/LMCoursier.scala index 36d7cf524..ebdf02d78 100644 --- a/main/src/main/scala/sbt/coursierint/LMCoursier.scala +++ b/main/src/main/scala/sbt/coursierint/LMCoursier.scala @@ -20,6 +20,7 @@ import lmcoursier.definitions.{ Strict => CStrict, } import lmcoursier._ +import lmcoursier.syntax._ import lmcoursier.credentials.Credentials import Keys._ import sbt.internal.util.Util diff --git a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala index 5eeb3390e..3a69b416b 100644 --- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala +++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala @@ -74,6 +74,7 @@ private[sbt] abstract class AbstractJobHandle extends JobHandle { private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobService { private val nextId = new AtomicLong(1) private val pool = new BackgroundThreadPool() + private val context = LoggerContext() private[sbt] def serviceTempDirBase: File private[sbt] def useLog4J: Boolean @@ -90,7 +91,6 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe // hooks for sending start/stop events protected def onAddJob(@deprecated("unused", "") job: JobHandle): Unit = () protected def onRemoveJob(@deprecated("unused", "") job: JobHandle): Unit = () - private val context = LoggerContext() // this mutable state could conceptually go on State except // that then every task that runs a background job would have @@ -122,12 +122,9 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe def humanReadableName: String = job.humanReadableName job.onStop { () => - // TODO: Fix this - // logger.close() removeJob(this) IO.delete(workingDirectory) context.clearAppenders(logger.name) - context.close() } addJob(this) override final def equals(other: Any): Boolean = other match { @@ -144,15 +141,15 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe override val spawningTask: ScopedKey[_] = unknownTask } - protected def makeContext(id: Long, spawningTask: ScopedKey[_], state: State): ManagedLogger - def doRunInBackground( spawningTask: ScopedKey[_], state: State, start: (Logger, File) => BackgroundJob ): JobHandle = { val id = nextId.getAndIncrement() - val logger = makeContext(id, spawningTask, state) + val extracted = Project.extract(state) + val logger = + LogManager.constructBackgroundLog(extracted.structure.data, state, context)(spawningTask) val workingDir = serviceTempDir / s"job-$id" IO.createDirectory(workingDir) val job = try { @@ -502,10 +499,6 @@ private[sbt] class DefaultBackgroundJobService( ) extends AbstractBackgroundJobService { @deprecated("Use the constructor that specifies the background job temporary directory", "1.4.0") def this() = this(IO.createTemporaryDirectory, false) - override def makeContext(id: Long, spawningTask: ScopedKey[_], state: State): ManagedLogger = { - val extracted = Project.extract(state) - LogManager.constructBackgroundLog(extracted.structure.data, state)(spawningTask) - } } private[sbt] object DefaultBackgroundJobService { diff --git a/main/src/main/scala/sbt/internal/LogManager.scala b/main/src/main/scala/sbt/internal/LogManager.scala index a50f3c8e1..25a3eb2a8 100644 --- a/main/src/main/scala/sbt/internal/LogManager.scala +++ b/main/src/main/scala/sbt/internal/LogManager.scala @@ -72,15 +72,23 @@ object LogManager { manager(data, state, task, to, context) } - @nowarn + @deprecated("Use alternate constructBackgroundLog that provides a LoggerContext", "1.8.0") def constructBackgroundLog( data: Settings[Scope], state: State + ): ScopedKey[_] => ManagedLogger = { + val context = state.get(Keys.loggerContext).getOrElse(LoggerContext.globalContext) + constructBackgroundLog(data, state, context) + } + + def constructBackgroundLog( + data: Settings[Scope], + state: State, + context: LoggerContext ): (ScopedKey[_]) => ManagedLogger = (task: ScopedKey[_]) => { val manager: LogManager = (logManager in task.scope).get(data) getOrElse defaultManager(state.globalLogging.console) - val context = state.get(Keys.loggerContext).getOrElse(LoggerContext.globalContext) manager.backgroundLog(data, state, task, context) } @@ -134,7 +142,7 @@ object LogManager { task: ScopedKey[_], context: LoggerContext ): ManagedLogger = { - val console = screen(task, state) + val console = ConsoleAppender.safe("bg-" + ConsoleAppender.generateName(), ITerminal.current) LogManager.backgroundLog(data, state, task, console, relay(()), context) } } diff --git a/main/src/main/scala/sbt/internal/SysProp.scala b/main/src/main/scala/sbt/internal/SysProp.scala index ce5760edd..b2846a1b7 100644 --- a/main/src/main/scala/sbt/internal/SysProp.scala +++ b/main/src/main/scala/sbt/internal/SysProp.scala @@ -14,6 +14,7 @@ import java.util.Locale import scala.util.control.NonFatal import scala.concurrent.duration._ +import sbt.internal.inc.HashUtil import sbt.internal.util.{ Terminal => ITerminal, Util } import sbt.internal.util.complete.SizeParser import sbt.io.syntax._ @@ -247,7 +248,15 @@ object SysProp { * Windows, and Docker environment. * Mostly these directories will be used as throw-away location to extract * native files etc. + * A deterministic hash is appended in the directory name as "/tmp/.sbt1234ABCD/" + * to avoid collision between multiple users in a shared server environment. */ - private[this] def runtimeDirectory: Path = - Paths.get(sys.env.getOrElse("XDG_RUNTIME_DIR", sys.props("java.io.tmpdir"))).resolve(".sbt") + private[this] def runtimeDirectory: Path = { + val hashValue = + java.lang.Long.toHexString(HashUtil.farmHash(home.toString.getBytes("UTF-8"))) + val halfhash = hashValue.take(8) + Paths + .get(sys.env.getOrElse("XDG_RUNTIME_DIR", sys.props("java.io.tmpdir"))) + .resolve(s".sbt$halfhash") + } } diff --git a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala index 6e04053da..3963da325 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala @@ -201,7 +201,7 @@ final class BuildServerReporterImpl( Diagnostic( range, Option(toDiagnosticSeverity(problem.severity)), - None, + problem.diagnosticCode().toOption.map(_.code), Option("sbt"), problem.message ) diff --git a/main/src/main/scala/sbt/plugins/Giter8TemplatePlugin.scala b/main/src/main/scala/sbt/plugins/Giter8TemplatePlugin.scala index a5cc5efce..26d901d2e 100644 --- a/main/src/main/scala/sbt/plugins/Giter8TemplatePlugin.scala +++ b/main/src/main/scala/sbt/plugins/Giter8TemplatePlugin.scala @@ -26,7 +26,7 @@ object Giter8TemplatePlugin extends AutoPlugin { ModuleID( "org.scala-sbt.sbt-giter8-resolver", "sbt-giter8-resolver", - "0.13.1" + "0.15.0" ) cross CrossVersion.binary, "sbtgiter8resolver.Giter8TemplateResolver" ) diff --git a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala index c1563c60d..b7a558d6d 100644 --- a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala +++ b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala @@ -26,7 +26,7 @@ object SemanticdbPlugin extends AutoPlugin { semanticdbEnabled := SysProp.semanticdb, semanticdbIncludeInJar := false, semanticdbOptions := List(), - semanticdbVersion := "4.5.9" + semanticdbVersion := "4.5.13" ) override lazy val projectSettings: Seq[Def.Setting[_]] = Seq( diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 86df2e8ef..33941806c 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,7 +4,7 @@ import sbt.contraband.ContrabandPlugin.autoImport._ object Dependencies { // WARNING: Please Scala update versions in PluginCross.scala too - val scala212 = "2.12.16" + val scala212 = "2.12.17" val scala213 = "2.13.8" val checkPluginCross = settingKey[Unit]("Make sure scalaVersion match up") val baseScalaVersion = scala212 @@ -12,17 +12,17 @@ object Dependencies { sys.env.get("BUILD_VERSION") orElse sys.props.get("sbt.build.version") // sbt modules - private val ioVersion = nightlyVersion.getOrElse("1.7.0") + private val ioVersion = nightlyVersion.getOrElse("1.8.0") private val lmVersion = - sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.7.0") - val zincVersion = nightlyVersion.getOrElse("1.7.1") + sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.8.0") + val zincVersion = nightlyVersion.getOrElse("1.8.0") private val sbtIO = "org.scala-sbt" %% "io" % ioVersion private val libraryManagementCore = "org.scala-sbt" %% "librarymanagement-core" % lmVersion private val libraryManagementIvy = "org.scala-sbt" %% "librarymanagement-ivy" % lmVersion - val launcherVersion = "1.3.3" + val launcherVersion = "1.4.1" val launcherInterface = "org.scala-sbt" % "launcher-interface" % launcherVersion val rawLauncher = "org.scala-sbt" % "launcher" % launcherVersion val testInterface = "org.scala-sbt" % "test-interface" % "1.0" @@ -77,7 +77,7 @@ object Dependencies { def addSbtZincCompile = addSbtModule(sbtZincPath, "zincCompile", zincCompile) def addSbtZincCompileCore = addSbtModule(sbtZincPath, "zincCompileCore", zincCompileCore) - val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.0.10" + val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.0.13" def sjsonNew(n: String) = Def.setting("com.eed3si9n" %% n % "0.9.1") // contrabandSjsonNewVersion.value @@ -102,9 +102,9 @@ object Dependencies { val scalaXml = Def.setting( if (scalaBinaryVersion.value == "3") { - "org.scala-lang.modules" %% "scala-xml" % "2.0.1" + "org.scala-lang.modules" %% "scala-xml" % "2.1.0" } else { - "org.scala-lang.modules" %% "scala-xml" % "1.3.0" + "org.scala-lang.modules" %% "scala-xml" % "2.1.0" } ) val scalaParsers = Def.setting( diff --git a/project/build.properties b/project/build.properties index 5b12c1dc6..563a014da 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.0 +sbt.version=1.7.2 diff --git a/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala b/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala index bdc850cd2..4317f893d 100644 --- a/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala +++ b/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala @@ -63,8 +63,10 @@ object BuildServerConnection { // For those who use an old sbt script, the -Dsbt.script is not set // As a fallback we try to find the sbt script in $PATH val fileName = if (Properties.isWin) "sbt.bat" else "sbt" - val envPath = sys.env.getOrElse("PATH", "") - val allPaths = envPath.split(File.pathSeparator).map(Paths.get(_)) + val envPath = sys.env.collectFirst { + case (k, v) if k.toUpperCase() == "PATH" => v + } + val allPaths = envPath.map(_.split(File.pathSeparator).map(Paths.get(_))).getOrElse(Array.empty) allPaths .map(_.resolve(fileName)) .find(file => Files.exists(file) && Files.isExecutable(file)) diff --git a/run/src/main/scala/sbt/Fork.scala b/run/src/main/scala/sbt/Fork.scala index c7e74c0f7..7cbf1c7ad 100644 --- a/run/src/main/scala/sbt/Fork.scala +++ b/run/src/main/scala/sbt/Fork.scala @@ -8,6 +8,7 @@ package sbt import java.io.File +import java.io.PrintWriter import java.lang.ProcessBuilder.Redirect import scala.sys.process.Process import OutputStrategy._ @@ -15,6 +16,7 @@ import sbt.internal.util.{ RunningProcesses, Util } import Util.{ AnyOps, none } import java.lang.{ ProcessBuilder => JProcessBuilder } +import java.util.Locale /** * Represents a command that can be forked. @@ -57,7 +59,11 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) { (classpathEnv map { value => Fork.ClasspathEnvKey -> value }) - val jpb = new JProcessBuilder(command.toArray: _*) + val jpb = + if (Fork.shouldUseArgumentsFile(options)) + new JProcessBuilder(executable, Fork.createArgumentsFile(options)) + else + new JProcessBuilder(command.toArray: _*) workingDirectory foreach (jpb directory _) environment foreach { case (k, v) => jpb.environment.put(k, v) } if (connectInput) { @@ -125,4 +131,57 @@ object Fork { val home = javaHome.getOrElse(new File(System.getProperty("java.home"))) new File(new File(home, "bin"), name) } + + /* copied from SysProp.scala for consistency while avoiding + * introducing a circular dependency + */ + private def parseBoolean(value: String): Option[Boolean] = + value.toLowerCase(Locale.ENGLISH) match { + case "1" | "always" | "true" => Some(true) + case "0" | "never" | "false" => Some(false) + case "auto" => None + case _ => None + } + private def booleanOpt(name: String): Option[Boolean] = + sys.props.get(name) match { + case Some(x) => parseBoolean(x) + case _ => + sys.env.get(name.toUpperCase(Locale.ENGLISH).replace('.', '_')) match { + case Some(x) => parseBoolean(x) + case _ => None + } + } + + /** Use an arguments file if: + * - we are on jdk >= 9 + * - sbt.argfile is unset or not falsy + * - the command line length would exceed MaxConcatenatedOptionLength + */ + private def shouldUseArgumentsFile(options: Seq[String]): Boolean = + (sys.props.getOrElse("java.vm.specification.version", "1").toFloat >= 9.0) && + booleanOpt("sbt.argsfile").getOrElse(true) && + (options.mkString.length > MaxConcatenatedOptionLength) + + /** + * Create an arguments file from a sequence of command line arguments + * by quoting each argument to a line with escaped backslashes + * + * @param options command line options to write to the args file + * @return + */ + private def createArgumentsFile(options: Seq[String]): String = { + val file = File.createTempFile(s"sbt-args", ".tmp") + file.deleteOnExit() + + val pw = new PrintWriter(file) + options.foreach { option => + pw.write("\"") + pw.write(option.replace("\\", "\\\\")) + pw.write("\"") + pw.write(System.lineSeparator()) + } + pw.flush() + pw.close() + s"@${file.getAbsolutePath}" + } } diff --git a/sbt b/sbt index a7daac3ea..2702263a3 100755 --- a/sbt +++ b/sbt @@ -1,7 +1,7 @@ #!/usr/bin/env bash set +e -declare builtin_sbt_version="1.7.1" +declare builtin_sbt_version="1.8.0" declare -a residual_args declare -a java_args declare -a scalac_args diff --git a/sbt-app/src/repo-override-test/repo.config b/sbt-app/src/repo-override-test/repo.config index 68dbabf81..49770caeb 100644 --- a/sbt-app/src/repo-override-test/repo.config +++ b/sbt-app/src/repo-override-test/repo.config @@ -1,3 +1,3 @@ [repositories] local - jcenter: https://jcenter.bintray.com/ + sonatype-releases: https://oss.sonatype.org/content/repositories/releases diff --git a/sbt-app/src/sbt-test/actions/compile-time-only/build.sbt b/sbt-app/src/sbt-test/actions/compile-time-only/build.sbt index 5baf27554..f8d308a29 100644 --- a/sbt-app/src/sbt-test/actions/compile-time-only/build.sbt +++ b/sbt-app/src/sbt-test/actions/compile-time-only/build.sbt @@ -1,3 +1,5 @@ +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always + libraryDependencies += "org.scala-sbt" % "sbt" % sbtVersion.value diff --git a/sbt-app/src/sbt-test/actions/cross-advanced/build.sbt b/sbt-app/src/sbt-test/actions/cross-advanced/build.sbt index 91460f0e3..1a8fa296a 100644 --- a/sbt-app/src/sbt-test/actions/cross-advanced/build.sbt +++ b/sbt-app/src/sbt-test/actions/cross-advanced/build.sbt @@ -1,6 +1,6 @@ lazy val check = taskKey[Unit]("") lazy val compile2 = taskKey[Unit]("") -lazy val scala212 = "2.12.16" +lazy val scala212 = "2.12.17" lazy val root = (project in file(".")) .aggregate(foo, bar, client) diff --git a/sbt-app/src/sbt-test/actions/cross-advanced/test b/sbt-app/src/sbt-test/actions/cross-advanced/test index 5ee486310..3b074490d 100644 --- a/sbt-app/src/sbt-test/actions/cross-advanced/test +++ b/sbt-app/src/sbt-test/actions/cross-advanced/test @@ -17,7 +17,7 @@ ## test + with command or alias > clean ## for command cross building you do need crossScalaVerions on root -> set root/crossScalaVersions := Seq("2.12.16", "2.13.1") +> set root/crossScalaVersions := Seq("2.12.17", "2.13.1") > + build $ exists foo/target/scala-2.12 $ exists foo/target/scala-2.13 diff --git a/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/build.sbt b/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/build.sbt index d5cf2f898..aca345c51 100644 --- a/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/build.sbt +++ b/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/build.sbt @@ -1,14 +1,14 @@ -scalaVersion := "2.12.16" +scalaVersion := "2.12.17" lazy val core = project .settings( - crossScalaVersions := Seq("2.12.16", "3.0.2", "3.1.2") + crossScalaVersions := Seq("2.12.17", "3.0.2", "3.1.2") ) lazy val subproj = project .dependsOn(core) .settings( - crossScalaVersions := Seq("2.12.16", "3.1.2"), + crossScalaVersions := Seq("2.12.17", "3.1.2"), // a random library compiled against Scala 3.1 libraryDependencies += "org.http4s" %% "http4s-core" % "0.23.12" ) diff --git a/sbt-app/src/sbt-test/actions/cross-strict-aggregation/build.sbt b/sbt-app/src/sbt-test/actions/cross-strict-aggregation/build.sbt index 1ffbc3b59..f65bc2364 100644 --- a/sbt-app/src/sbt-test/actions/cross-strict-aggregation/build.sbt +++ b/sbt-app/src/sbt-test/actions/cross-strict-aggregation/build.sbt @@ -1,4 +1,4 @@ -lazy val scala212 = "2.12.16" +lazy val scala212 = "2.12.17" lazy val scala213 = "2.13.1" ThisBuild / scalaVersion := scala212 diff --git a/sbt-app/src/sbt-test/compiler-project/run-test/build.sbt b/sbt-app/src/sbt-test/compiler-project/run-test/build.sbt index f424c2d91..1019be51a 100644 --- a/sbt-app/src/sbt-test/compiler-project/run-test/build.sbt +++ b/sbt-app/src/sbt-test/compiler-project/run-test/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.16" +ThisBuild / scalaVersion := "2.12.17" libraryDependencies ++= Seq( "com.novocode" % "junit-interface" % "0.5" % Test, diff --git a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt index f67a2c7ee..5ba1d09ce 100644 --- a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.16" +ThisBuild / scalaVersion := "2.12.17" libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % "1.7.2", diff --git a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt b/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt index b8c0e1620..85fd38f7f 100644 --- a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt @@ -1,5 +1,5 @@ // ThisBuild / useCoursier := false -ThisBuild / scalaVersion := "2.12.16" +ThisBuild / scalaVersion := "2.12.17" ThisBuild / organization := "org.example" ThisBuild / version := "0.1" diff --git a/sbt-app/src/sbt-test/dependency-management/conflict-manager-with-org/test b/sbt-app/src/sbt-test/dependency-management/conflict-manager-with-org/pending similarity index 100% rename from sbt-app/src/sbt-test/dependency-management/conflict-manager-with-org/test rename to sbt-app/src/sbt-test/dependency-management/conflict-manager-with-org/pending diff --git a/sbt-app/src/sbt-test/nio/reload/build.sbt b/sbt-app/src/sbt-test/nio/reload/build.sbt index 910bfa622..509d4f3f8 100644 --- a/sbt-app/src/sbt-test/nio/reload/build.sbt +++ b/sbt-app/src/sbt-test/nio/reload/build.sbt @@ -1,3 +1,5 @@ +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always + import scala.concurrent.duration._ val foo = inputKey[Unit]("working task") diff --git a/sbt-app/src/sbt-test/nio/reload/changes/sub.sbt b/sbt-app/src/sbt-test/nio/reload/changes/sub.sbt index 4797974c4..2a374e9b3 100644 --- a/sbt-app/src/sbt-test/nio/reload/changes/sub.sbt +++ b/sbt-app/src/sbt-test/nio/reload/changes/sub.sbt @@ -1,3 +1,5 @@ +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always + import scala.concurrent.duration._ libraryDependencies += "org.scala-sbt" % "sbt" % "1.3.0" diff --git a/sbt-app/src/sbt-test/nio/reload/project/plugins.sbt b/sbt-app/src/sbt-test/nio/reload/project/plugins.sbt index 99c853f12..1ed25683c 100644 --- a/sbt-app/src/sbt-test/nio/reload/project/plugins.sbt +++ b/sbt-app/src/sbt-test/nio/reload/project/plugins.sbt @@ -1,3 +1,5 @@ +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always + libraryDependencies ++= { if (ScalafmtVersion.value == "2.0.4") { val sbtV = (sbtBinaryVersion in pluginCrossBuild).value diff --git a/sbt-app/src/sbt-test/plugins/play-watch/project/plugins.sbt b/sbt-app/src/sbt-test/plugins/play-watch/project/plugins.sbt index 05e01d6e5..c8882f94b 100644 --- a/sbt-app/src/sbt-test/plugins/play-watch/project/plugins.sbt +++ b/sbt-app/src/sbt-test/plugins/play-watch/project/plugins.sbt @@ -1 +1,2 @@ +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.7.3") \ No newline at end of file diff --git a/sbt-app/src/sbt-test/plugins/sbt-native-packager/project/plugins.sbt b/sbt-app/src/sbt-test/plugins/sbt-native-packager/project/plugins.sbt index 414d83d34..c3e04c903 100644 --- a/sbt-app/src/sbt-test/plugins/sbt-native-packager/project/plugins.sbt +++ b/sbt-app/src/sbt-test/plugins/sbt-native-packager/project/plugins.sbt @@ -1 +1,3 @@ +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always + addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.20") diff --git a/sbt-app/src/sbt-test/project/sbt-plugin/build.sbt b/sbt-app/src/sbt-test/project/sbt-plugin/build.sbt index 1831f255c..d0943ff9d 100644 --- a/sbt-app/src/sbt-test/project/sbt-plugin/build.sbt +++ b/sbt-app/src/sbt-test/project/sbt-plugin/build.sbt @@ -1,6 +1,6 @@ lazy val root = project.in(file(".")) .enablePlugins(SbtPlugin) .settings( - scalaVersion := "2.12.16", + scalaVersion := "2.12.17", scalacOptions ++= Seq("-Xfatal-warnings", "-Xlint") ) diff --git a/sbt-app/src/sbt-test/project/sbt-plugin/changes/oldSbtPlugin.sbt b/sbt-app/src/sbt-test/project/sbt-plugin/changes/oldSbtPlugin.sbt index 03437f9da..61199e931 100644 --- a/sbt-app/src/sbt-test/project/sbt-plugin/changes/oldSbtPlugin.sbt +++ b/sbt-app/src/sbt-test/project/sbt-plugin/changes/oldSbtPlugin.sbt @@ -1,6 +1,6 @@ lazy val root = project.in(file(".")) .settings( - scalaVersion := "2.12.16", + scalaVersion := "2.12.17", sbtPlugin := true, scalacOptions ++= Seq("-Xfatal-warnings", "-Xlint") ) diff --git a/sbt-app/src/sbt-test/project/source-plugins/test b/sbt-app/src/sbt-test/project/source-plugins/pending similarity index 100% rename from sbt-app/src/sbt-test/project/source-plugins/test rename to sbt-app/src/sbt-test/project/source-plugins/pending diff --git a/sbt-app/src/sbt-test/project/src-plugins/project/p.sbt b/sbt-app/src/sbt-test/project/src-plugins/project/p.sbt index 8d6251248..84a62c711 100644 --- a/sbt-app/src/sbt-test/project/src-plugins/project/p.sbt +++ b/sbt-app/src/sbt-test/project/src-plugins/project/p.sbt @@ -1,2 +1,4 @@ +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always + lazy val root = (project in file(".")). dependsOn(RootProject(file("../plugin"))) diff --git a/sbt-app/src/sbt-test/project/unified/build.sbt b/sbt-app/src/sbt-test/project/unified/build.sbt index 8210ad038..fe6b1a94a 100644 --- a/sbt-app/src/sbt-test/project/unified/build.sbt +++ b/sbt-app/src/sbt-test/project/unified/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.16" +ThisBuild / scalaVersion := "2.12.17" import sbt.internal.CommandStrings.{ inspectBrief, inspectDetailed } import sbt.internal.Inspect diff --git a/sbt-app/src/sbt-test/source-dependencies/constants/test b/sbt-app/src/sbt-test/source-dependencies/constants/test index b1e725f26..eeebe3599 100644 --- a/sbt-app/src/sbt-test/source-dependencies/constants/test +++ b/sbt-app/src/sbt-test/source-dependencies/constants/test @@ -1,4 +1,4 @@ -> ++2.12.16! +> ++2.12.17! $ copy-file changes/B.scala B.scala diff --git a/sbt-app/src/sbt-test/tests/scala-instance-classloader/build.sbt b/sbt-app/src/sbt-test/tests/scala-instance-classloader/build.sbt index 1f8431d46..7dcf87b1e 100644 --- a/sbt-app/src/sbt-test/tests/scala-instance-classloader/build.sbt +++ b/sbt-app/src/sbt-test/tests/scala-instance-classloader/build.sbt @@ -3,7 +3,7 @@ import sbt.internal.inc.ScalaInstance lazy val OtherScala = config("other-scala").hide lazy val junitinterface = "com.novocode" % "junit-interface" % "0.11" lazy val akkaActor = "com.typesafe.akka" %% "akka-actor" % "2.5.17" -ThisBuild / scalaVersion := "2.12.16" +ThisBuild / scalaVersion := "2.12.17" lazy val root = (project in file(".")) .configs(OtherScala) diff --git a/sbt-app/src/sbt-test/tests/zio-test/build.sbt b/sbt-app/src/sbt-test/tests/zio-test/build.sbt new file mode 100644 index 000000000..a2ddfa655 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/zio-test/build.sbt @@ -0,0 +1,4 @@ +ThisBuild / scalaVersion := "2.13.10" + +libraryDependencies += "dev.zio" %% "zio-test" % "2.0.2" % Test +libraryDependencies += "dev.zio" %% "zio-test-sbt" % "2.0.2" % Test diff --git a/sbt-app/src/sbt-test/tests/zio-test/src/test/scala/spec/Spec.scala b/sbt-app/src/sbt-test/tests/zio-test/src/test/scala/spec/Spec.scala new file mode 100644 index 000000000..f95610899 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/zio-test/src/test/scala/spec/Spec.scala @@ -0,0 +1,11 @@ +package spec + +import zio.test._ + +object Spec extends ZIOSpecDefault { + def spec = suite("Spec")( + test("test") { + assertTrue(1 == 1) + } + ) +} \ No newline at end of file diff --git a/sbt-app/src/sbt-test/tests/zio-test/test b/sbt-app/src/sbt-test/tests/zio-test/test new file mode 100644 index 000000000..a270b7b48 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/zio-test/test @@ -0,0 +1 @@ +> test \ No newline at end of file diff --git a/server-test/src/server-test/response/build.sbt b/server-test/src/server-test/response/build.sbt index 90f7f9f8d..9bf691e56 100644 --- a/server-test/src/server-test/response/build.sbt +++ b/server-test/src/server-test/response/build.sbt @@ -1,6 +1,6 @@ import sbt.internal.server.{ ServerHandler, ServerIntent } -ThisBuild / scalaVersion := "2.12.16" +ThisBuild / scalaVersion := "2.12.17" Global / serverLog / logLevel := Level.Debug // custom handler diff --git a/testing/src/main/scala/sbt/TestFramework.scala b/testing/src/main/scala/sbt/TestFramework.scala index cb89dd5f4..bd20829ed 100644 --- a/testing/src/main/scala/sbt/TestFramework.scala +++ b/testing/src/main/scala/sbt/TestFramework.scala @@ -27,6 +27,7 @@ object TestFrameworks { TestFramework("org.specs2.runner.Specs2Framework", "org.specs2.runner.SpecsFramework") val JUnit = TestFramework("com.novocode.junit.JUnitFramework") val MUnit = TestFramework("munit.Framework") + val ZIOTest = TestFramework("zio.test.sbt.ZTestFramework") } final class TestFramework(val implClassNames: String*) extends Serializable {