diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a..a9173d91a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,10 @@ updates: directory: "/" schedule: interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + target-branch: "develop" + commit-message: + prefix: "[2.x] " diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1c9a644c3..469fbc8a0 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@v4 with: repository: sbt/zinc - ref: develop + ref: 1.10.x path: zinc - name: Setup JDK uses: actions/setup-java@v4 diff --git a/.java-version b/.java-version deleted file mode 100644 index 625934097..000000000 --- a/.java-version +++ /dev/null @@ -1 +0,0 @@ -1.8 diff --git a/build.sbt b/build.sbt index 8975d648a..e2dfb365a 100644 --- a/build.sbt +++ b/build.sbt @@ -47,13 +47,12 @@ ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" Global / semanticdbEnabled := !(Global / insideCI).value // Change main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala too, if you change this. -Global / semanticdbVersion := "4.9.9" -Global / excludeLintKeys += Utils.componentID -Global / excludeLintKeys += scriptedBufferLog -Global / excludeLintKeys += checkPluginCross -Global / excludeLintKeys += nativeImageVersion -Global / excludeLintKeys += nativeImageJvm -ThisBuild / evictionErrorLevel := Level.Info +Global / semanticdbVersion := "4.7.8" +val excludeLint = SettingKey[Set[Def.KeyedInitialize[_]]]("excludeLintKeys") +Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty) +Global / excludeLint += Utils.componentID +Global / excludeLint += scriptedBufferLog +Global / excludeLint += checkPluginCross def commonSettings: Seq[Setting[_]] = Def.settings( headerLicense := Some( @@ -67,6 +66,7 @@ def commonSettings: Seq[Setting[_]] = Def.settings( ) ), scalaVersion := baseScalaVersion, + evictionErrorLevel := Level.Info, Utils.componentID := None, resolvers += Resolver.typesafeIvyRepo("releases").withName("typesafe-sbt-build-ivy-releases"), resolvers ++= Resolver.sonatypeOssRepos("snapshots"), @@ -347,8 +347,8 @@ lazy val utilLogging = project Seq( jline, jline3Terminal, - jline3JNA, - jline3Jansi, + jline3JNI, + jline3Native, log4jApi, log4jCore, disruptor, @@ -1047,6 +1047,10 @@ lazy val sbtClientProj = (project in file("client")) nativeImageOptions ++= Seq( "--no-fallback", s"--initialize-at-run-time=sbt.client", + // "The current machine does not support all of the following CPU features that are required by + // the image: [CX8, CMOV, FXSR, MMX, SSE, SSE2, SSE3, SSSE3, SSE4_1, SSE4_2, POPCNT, LZCNT, AVX, + // AVX2, BMI1, BMI2, FMA, F16C]." + "-march=compatibility", // "--verbose", "-H:IncludeResourceBundles=jline.console.completer.CandidateListCompletionHandler", "-H:+ReportExceptionStackTraces", diff --git a/client/src/main/java/sbt/client/Client.java b/client/src/main/java/sbt/client/Client.java index 909b6b28c..491d725b8 100644 --- a/client/src/main/java/sbt/client/Client.java +++ b/client/src/main/java/sbt/client/Client.java @@ -11,11 +11,15 @@ package sbt.client; import sbt.internal.client.NetworkClient; public class Client { - public static void main(String[] args) { + public static void main(final String[] args) { + boolean hadError = false; try { NetworkClient.main(args); } catch (final Throwable t) { t.printStackTrace(); + hadError = true; + } finally { + if (hadError) System.exit(1); } } } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index 2c0f1fee4..9084b6467 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -130,7 +130,7 @@ object LineReader { } catch { case e: EndOfFileException => - if (terminal == Terminal.console && System.console == null) None + if (terminal == Terminal.console && !Terminal.hasConsole) None else Some("exit") case _: IOError | _: ClosedException => Some("exit") case _: UserInterruptException | _: ClosedByInterruptException | diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala b/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala index 88dbdcb41..87dabca46 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala @@ -19,51 +19,21 @@ import org.jline.terminal.{ Attributes, Size, Terminal => JTerminal } import org.jline.terminal.Attributes.{ InputFlag, LocalFlag } import org.jline.terminal.Terminal.SignalHandler import org.jline.terminal.impl.{ AbstractTerminal, DumbTerminal } -import org.jline.terminal.impl.jansi.JansiTerminalProvider import org.jline.terminal.spi.{ SystemStream, TerminalProvider } -import org.jline.utils.OSUtils +import sbt.internal.util.Terminal.hasConsole import scala.jdk.CollectionConverters.* import scala.util.Try import java.util.concurrent.LinkedBlockingQueue private[sbt] object JLine3 { private[util] val initialAttributes = new AtomicReference[Attributes] - - private val forceWindowsJansiHolder = new AtomicBoolean(false) - private[sbt] def forceWindowsJansi(): Unit = forceWindowsJansiHolder.set(true) - private def windowsJansi(): org.jline.terminal.Terminal = { - val provider = new JansiTerminalProvider - val termType = sys.props.get("org.jline.terminal.type").orElse(sys.env.get("TERM")).orNull - provider.winSysTerminal( - "console", - termType, - OSUtils.IS_CONEMU, - Charset.forName("UTF-8"), - false, - SignalHandler.SIG_DFL, - true, - SystemStream.Output - ) - } - private val jansi = { - val (major, minor) = - (JansiTerminalProvider.getJansiMajorVersion, JansiTerminalProvider.getJansiMinorVersion) - (major > 1 || minor >= 18) && Util.isWindows - } private[util] def system: org.jline.terminal.Terminal = { val term = - if (forceWindowsJansiHolder.get) windowsJansi() - else { - // Only use jna on windows. Both jna and jansi use illegal reflective - // accesses on posix system. - org.jline.terminal.TerminalBuilder - .builder() - .system(System.console != null) - .jna(Util.isWindows && !jansi) - .jansi(jansi) - .paused(true) - .build() - } + org.jline.terminal.TerminalBuilder + .builder() + .system(hasConsole) + .paused(true) + .build() initialAttributes.get match { case null => initialAttributes.set(term.getAttributes) case _ => 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 e581d998f..72de2f4de 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 @@ -289,7 +289,7 @@ object Terminal { * the sbt client to detach from the server it launches. */ def close(): Unit = { - if (System.console == null) { + if (!hasConsole) { originalOut.close() originalIn.close() originalErr.close() @@ -368,7 +368,17 @@ object Terminal { private val isDumb = Some("dumb") == sys.env.get("TERM") private def isDumbTerminal = isDumb || System.getProperty("jline.terminal", "") == "none" - private val hasConsole = Option(java.lang.System.console).isDefined + private[sbt] val hasConsole = { + System.console != null && { + try { + val isTerminal = System.console.getClass.getMethod("isTerminal") + isTerminal.invoke(System.console).asInstanceOf[Boolean] + } catch { + case _: NoSuchMethodException => + true + } + } + } private def useColorDefault: Boolean = { // This approximates that both stdin and stdio are connected, // so by default color will be turned off for pipes and redirects. @@ -717,7 +727,7 @@ object Terminal { inputStream.read match { case -1 => case `NO_BOOT_CLIENTS_CONNECTED` => - if (System.console == null) { + if (!Terminal.hasConsole) { result.put(-1) running.set(false) } diff --git a/launcher-package/build.sbt b/launcher-package/build.sbt index 5563ed2be..becb85242 100755 --- a/launcher-package/build.sbt +++ b/launcher-package/build.sbt @@ -121,7 +121,7 @@ val root = (project in file(".")). file }, // update sbt.sh at root - sbtnVersion := "1.10.3", + sbtnVersion := "1.10.5", sbtnJarsBaseUrl := "https://github.com/sbt/sbtn-dist/releases/download", sbtnJarsMappings := { val baseUrl = sbtnJarsBaseUrl.value diff --git a/launcher-package/src/universal/bin/sbt.bat b/launcher-package/src/universal/bin/sbt.bat index 22956f7ec..093e27323 100755 --- a/launcher-package/src/universal/bin/sbt.bat +++ b/launcher-package/src/universal/bin/sbt.bat @@ -890,27 +890,27 @@ for /F "delims=.-_ tokens=1-2" %%v in ("!sbtV!") do ( set sbtBinaryV_1=%%v set sbtBinaryV_2=%%w ) -set native_client_ready= +rem default to run_native_client=1 for sbt 2.x if !sbtBinaryV_1! geq 2 ( - set native_client_ready=1 + if !sbt_args_client! equ 0 ( + set run_native_client= + ) else ( + set run_native_client=1 + ) ) else ( if !sbtBinaryV_1! geq 1 ( if !sbtBinaryV_2! geq 4 ( - set native_client_ready=1 + if !sbt_args_client! equ 1 ( + set run_native_client=1 + ) ) ) ) -if !native_client_ready! equ 1 ( - if !sbt_args_client! equ 1 ( - set run_native_client=1 - ) -) -set native_client_ready= exit /B 0 :checkjava -set /a required_version=6 +set /a required_version=8 if /I !JAVA_VERSION! GEQ !required_version! ( exit /B 0 ) diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index ae030ae9e..664872743 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -17,6 +17,7 @@ import java.nio.file.Files import java.util.UUID import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference } import java.util.concurrent.{ ConcurrentHashMap, LinkedBlockingQueue, TimeUnit } +import java.text.DateFormat import sbt.BasicCommandStrings.{ DashDashDetachStdio, DashDashServer, Shutdown, TerminateAction } import sbt.internal.langserver.{ LogMessageParams, MessageType, PublishDiagnosticsParams } @@ -534,7 +535,7 @@ class NetworkClient( case null => case (q, startTime, name) => val now = System.currentTimeMillis - val message = timing(startTime, now) + val message = NetworkClient.timing(startTime, now) val ec = exitCode if (batchMode.get || !attached.get) { if (ec == 0) console.success(message) @@ -1008,27 +1009,6 @@ class NetworkClient( RawInputThread.this.interrupt() } } - - // copied from Aggregation - private def timing(startTime: Long, endTime: Long): String = { - import java.text.DateFormat - val format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) - val nowString = format.format(new java.util.Date(endTime)) - val total = math.max(0, (endTime - startTime + 500) / 1000) - val totalString = s"$total s" + - (if (total <= 60) "" - else { - val maybeHours = total / 3600 match { - case 0 => "" - case h => f"$h%02d:" - } - val mins = f"${total % 3600 / 60}%02d" - val secs = f"${total % 60}%02d" - s" ($maybeHours$mins:$secs)" - }) - - s"Total time: $totalString, completed $nowString" - } } object NetworkClient { @@ -1141,6 +1121,25 @@ object NetworkClient { ) } + private[sbt] def timing(format: DateFormat, startTime: Long, endTime: Long): String = + val total = (endTime - startTime + 500) / 1000 + val totalString = s"$total s" + + (if (total <= 60) "" + else { + val maybeHours = total / 3600 match + case 0 => "" + case h => f"$h%02d:" + val mins = f"${total % 3600 / 60}%02d" + val secs = f"${total % 60}%02d" + s" ($maybeHours$mins:$secs)" + }) + s"elapsed time: $totalString" + + private[sbt] def timing(startTime: Long, endTime: Long): String = { + val format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) + timing(format, startTime, endTime) + } + def client( baseDirectory: File, args: Array[String], @@ -1241,7 +1240,6 @@ object NetworkClient { System.out.flush() }) Runtime.getRuntime.addShutdownHook(hook) - if (Util.isNonCygwinWindows) sbt.internal.util.JLine3.forceWindowsJansi() val parsed = parseArgs(restOfArgs) System.exit(Terminal.withStreams(isServer = false, isSubProcess = false) { val term = Terminal.console diff --git a/main-command/src/main/scala/sbt/internal/ui/UITask.scala b/main-command/src/main/scala/sbt/internal/ui/UITask.scala index 399ca6a2e..8145d392e 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UITask.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UITask.scala @@ -17,6 +17,7 @@ import sbt.BasicKeys.{ historyPath, colorShellPrompt } import sbt.State import sbt.internal.CommandChannel import sbt.internal.util.ConsoleAppender.{ ClearPromptLine, ClearScreenAfterCursor, DeleteLine } +import sbt.internal.util.Terminal.hasConsole import sbt.internal.util._ import sbt.internal.util.complete.{ Parser } @@ -70,7 +71,7 @@ private[sbt] object UITask { if (thread.isInterrupted || closed.get) throw interrupted (try reader.readLine(clear + terminal.prompt.mkPrompt()) finally reader.close) match { - case None if terminal == Terminal.console && System.console == null => + case None if terminal == Terminal.console && !hasConsole => // No stdin is attached to the process so just ignore the result and // block until the thread is interrupted. this.synchronized(this.wait()) diff --git a/main/src/main/java/sbt/internal/MetaBuildLoader.java b/main/src/main/java/sbt/internal/MetaBuildLoader.java index 4719131a8..b6008e07e 100644 --- a/main/src/main/java/sbt/internal/MetaBuildLoader.java +++ b/main/src/main/java/sbt/internal/MetaBuildLoader.java @@ -14,6 +14,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.util.LinkedHashSet; import java.util.Set; +import java.util.Stack; import java.util.regex.Pattern; import xsbti.AppProvider; import xsbti.ScalaProvider; @@ -65,21 +66,16 @@ public final class MetaBuildLoader extends URLClassLoader { * library. */ public static MetaBuildLoader makeLoader(final AppProvider appProvider) throws IOException { - final String jlineJars = "jline-?[0-9.]+-sbt-.*|jline-terminal(-(jna|jansi))?-[0-9.]+"; + final String jlineJars = + "jline-?[0-9.]+-sbt-.*|jline-terminal(-(jni))?-[0-9.]+|jline-native-[0-9.]+"; final String testInterfaceJars = "test-interface(-.*)?"; final String compilerInterfaceJars = "compiler-interface(-.*)?"; final String utilInterfaceJars = "util-interface(-.*)?"; final String jansiJars = "jansi-[0-9.]+"; - final String jnaJars = "jna-(platform-)?[0-9.]+"; final String fullPattern = String.format( - "^(%s|%s|%s|%s|%s|%s)\\.jar", - jlineJars, - testInterfaceJars, - compilerInterfaceJars, - utilInterfaceJars, - jansiJars, - jnaJars); + "^(%s|%s|%s|%s|%s)\\.jar", + jlineJars, testInterfaceJars, compilerInterfaceJars, utilInterfaceJars, jansiJars); final Pattern pattern = Pattern.compile(fullPattern); final File[] cp = appProvider.mainClasspath(); final Set interfaceFiles = new LinkedHashSet<>(); diff --git a/main/src/main/scala/sbt/BackgroundJobService.scala b/main/src/main/scala/sbt/BackgroundJobService.scala index 632ac2e9f..9427166d7 100644 --- a/main/src/main/scala/sbt/BackgroundJobService.scala +++ b/main/src/main/scala/sbt/BackgroundJobService.scala @@ -109,6 +109,7 @@ abstract class JobHandle { def id: Long def humanReadableName: String def spawningTask: ScopedKey[?] + def isAutoCancel: Boolean } /** diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 0b28e0a07..755ba45f0 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -123,9 +123,10 @@ object Cross { .map { case uri ~ seg1 ~ cmd => (uri, seg1, cmd) } Parser.parse(command, parser) match { case Right((uri, seg1, cmd)) => - structure.allProjectRefs.find(p => - uri.contains(p.build.toString) && seg1 == p.project - ) match { + structure.allProjectRefs.find { + case p if uri.isDefined => seg1 == p.project && uri.contains(p.build.toString) + case p => seg1 == p.project + } match { case Some(proj) => (Seq(proj), cmd) case _ => (resolveAggregates(extracted), command) } diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index f5f57c957..7965cb06a 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -714,6 +714,20 @@ object Defaults extends BuildCommon { }, crossSbtVersions := Vector((pluginCrossBuild / sbtVersion).value), crossTarget := target.value, + clean := { + try { + val store = AnalysisUtil.staticCachedStore( + analysisFile = (Compile / compileAnalysisFile).value.toPath, + useTextAnalysis = false, + useConsistent = true, + ) + // TODO: Uncomment after Zinc update + // store.clearCache() + } catch { + case NonFatal(_) => () + } + clean.value + }, scalaCompilerBridgeBinaryJar := { val sv = scalaVersion.value val managed = managedScalaInstance.value @@ -1943,6 +1957,10 @@ object Defaults extends BuildCommon { def foregroundRunMainTask: Initialize[InputTask[EmulateForeground]] = Def.inputTask { val handle = bgRunMain.evaluated + handle match + case threadJobHandle: AbstractBackgroundJobService#ThreadJobHandle => + threadJobHandle.isAutoCancel = true + case _ => () EmulateForeground(handle) } @@ -1950,6 +1968,11 @@ object Defaults extends BuildCommon { def foregroundRunTask: Initialize[InputTask[EmulateForeground]] = Def.inputTask { val handle = bgRun.evaluated + handle match { + case threadJobHandle: AbstractBackgroundJobService#ThreadJobHandle => + threadJobHandle.isAutoCancel = true + case _ => + } EmulateForeground(handle) } diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index 69345b326..110cd7967 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -547,6 +547,7 @@ object EvaluateTask { log.warn("Canceling execution...") RunningProcesses.killAll() ConcurrentRestrictions.cancelAll() + DefaultBackgroundJobService.stop() shutdownImpl(true) } } diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index a4bcf71b4..48f5afb51 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -26,6 +26,7 @@ import sbt.internal.inc.ScalaInstance import sbt.internal.io.Retry import sbt.internal.nio.{ CheckBuildSources, FileTreeRepository } import sbt.internal.server.{ BuildServerProtocol, NetworkChannel } +import sbt.internal.util.Terminal.hasConsole import sbt.internal.util.Types.{ const, idFun } import sbt.internal.util.complete.{ Parser, SizeParser } import sbt.internal.util.{ Terminal => ITerminal, _ } @@ -156,8 +157,7 @@ private[sbt] object xMain: try Some(new BootServerSocket(configuration)) -> None catch { - case e: ServerAlreadyBootingException - if System.console != null && !ITerminal.startedByRemoteClient => + case e: ServerAlreadyBootingException if hasConsole && !ITerminal.startedByRemoteClient => printThrowable(e) println("Create a new server? y/n (default y)") val exit = diff --git a/main/src/main/scala/sbt/TemplateCommandUtil.scala b/main/src/main/scala/sbt/TemplateCommandUtil.scala index c9ff55081..5a43c7e7a 100644 --- a/main/src/main/scala/sbt/TemplateCommandUtil.scala +++ b/main/src/main/scala/sbt/TemplateCommandUtil.scala @@ -21,6 +21,7 @@ import sbt.librarymanagement._ import sbt.librarymanagement.ivy.{ IvyConfiguration, IvyDependencyResolution } import sbt.internal.inc.classpath.ClasspathUtil import BasicCommandStrings._, BasicKeys._ +import sbt.internal.util.Terminal.hasConsole import sbt.ProjectExtra.* private[sbt] object TemplateCommandUtil { @@ -84,7 +85,9 @@ private[sbt] object TemplateCommandUtil { hit } match { case Some(_) => // do nothing - case None => System.err.println("Template not found for: " + arguments.mkString(" ")) + case None => + val error = "Template not found for: " + arguments.mkString(" ") + throw new IllegalArgumentException(error) } private def tryTemplate( @@ -183,7 +186,7 @@ private[sbt] object TemplateCommandUtil { "disneystreaming/smithy4s.g8" -> "A Smithy4s project", ) private def fortifyArgs(templates: List[(String, String)]): List[String] = - if (System.console eq null) Nil + if (!hasConsole) Nil else ITerminal.withStreams(true, false) { assert(templates.size <= 20, "template list cannot have more than 20 items") @@ -275,7 +278,8 @@ private[sbt] object TemplateCommandUtil { case TypelevelToolkitSlug :: Nil => typelevelToolkitTemplate() case SbtCrossPlatformSlug :: Nil => sbtCrossPlatformTemplate() case _ => - System.err.println("Local template not found for: " + arguments.mkString(" ")) + val error = "Local template not found for: " + arguments.mkString(" ") + throw new IllegalArgumentException(error) } private final val defaultScalaV = "3.3.4" diff --git a/main/src/main/scala/sbt/internal/Aggregation.scala b/main/src/main/scala/sbt/internal/Aggregation.scala index 84bfb935f..018bfee5d 100644 --- a/main/src/main/scala/sbt/internal/Aggregation.scala +++ b/main/src/main/scala/sbt/internal/Aggregation.scala @@ -18,6 +18,7 @@ import sbt.ScopeAxis.{ Select, Zero } 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.std.Transform.DummyTaskMap import sbt.util.{ Logger, Show } @@ -165,18 +166,7 @@ object Aggregation { } def timing(format: java.text.DateFormat, startTime: Long, endTime: Long): String = - val total = (endTime - startTime + 500) / 1000 - val totalString = s"$total s" + - (if total <= 60 then "" - else { - val maybeHours = total / 3600 match - case 0 => "" - case h => f"$h%02d:" - val mins = f"${total % 3600 / 60}%02d" - val secs = f"${total % 60}%02d" - s" ($maybeHours$mins:$secs)" - }) - s"elapsed time: $totalString" + NetworkClient.timing(format, startTime, endTime) def defaultFormat: DateFormat = { import java.text.DateFormat diff --git a/main/src/main/scala/sbt/internal/AnalysisUtil.scala b/main/src/main/scala/sbt/internal/AnalysisUtil.scala index f7a143d95..0fcc982ea 100644 --- a/main/src/main/scala/sbt/internal/AnalysisUtil.scala +++ b/main/src/main/scala/sbt/internal/AnalysisUtil.scala @@ -11,7 +11,6 @@ package internal import java.nio.file.Path import sbt.internal.inc.MixedAnalyzingCompiler -import scala.concurrent.ExecutionContext import xsbti.compile.{ AnalysisStore => XAnalysisStore } import xsbti.compile.analysis.ReadWriteMappers @@ -34,7 +33,9 @@ private[sbt] object AnalysisUtil { useConsistent = useConsistent, mappers = ReadWriteMappers.getEmptyMappers(), sort = true, - ec = ExecutionContext.global, + ec = scala.concurrent.ExecutionContext.global, + // reproducisble = true will wipe out the timestamp, which we need for sbt 1.x + // reproducible = false, parallelism = parallelism, ) } diff --git a/main/src/main/scala/sbt/internal/Continuous.scala b/main/src/main/scala/sbt/internal/Continuous.scala index 3ce6d4289..d21208e1a 100644 --- a/main/src/main/scala/sbt/internal/Continuous.scala +++ b/main/src/main/scala/sbt/internal/Continuous.scala @@ -1138,7 +1138,7 @@ private[sbt] object Continuous extends DeprecatedContinuous { val callbacks: Callbacks, val dynamicInputs: mutable.Set[DynamicInput], val pending: Boolean, - var failAction: Option[Watch.Action], + var terminationAction: Option[Watch.Action], ) { def this( count: Int, @@ -1171,7 +1171,8 @@ private[sbt] object Continuous extends DeprecatedContinuous { afterWatch, callbacks, dynamicInputs, - p + p, + terminationAction, ) private def withCount(c: Int): ContinuousState = new ContinuousState( @@ -1182,7 +1183,8 @@ private[sbt] object Continuous extends DeprecatedContinuous { afterWatch, callbacks, dynamicInputs, - pending + pending, + terminationAction, ) } } @@ -1350,12 +1352,13 @@ private[sbt] object ContinuousCommands { case Watch.Trigger => Right(s"$runWatch ${channel.name}") case Watch.Reload => val rewatch = s"$ContinuousExecutePrefix ${ws.count} ${cs.commands mkString "; "}" + cs.terminationAction = Some(Watch.Reload) stop.map(_ :: "reload" :: rewatch :: Nil mkString "; ") case Watch.Prompt => stop.map(_ :: s"$PromptChannel ${channel.name}" :: Nil mkString ";") case Watch.Run(commands) => stop.map(_ +: commands.map(_.commandLine).filter(_.nonEmpty) mkString "; ") case a @ Watch.HandleError(_) => - cs.failAction = Some(a) + cs.terminationAction = Some(a) stop.map(_ :: s"$failWatch ${channel.name}" :: Nil mkString "; ") case _ => stop } @@ -1403,7 +1406,7 @@ private[sbt] object ContinuousCommands { } val commands = cs.commands.mkString("; ") val count = cs.count - val action = cs.failAction.getOrElse(Watch.CancelWatch) + val action = cs.terminationAction.getOrElse(Watch.CancelWatch) val st = cs.callbacks.onTermination(action, commands, count, newState) if (error) st.fail else st case _ => if (error) state.fail else state diff --git a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala index babc844cd..844b9bf13 100644 --- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala +++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala @@ -119,7 +119,8 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe override val spawningTask: ScopedKey[?], val logger: ManagedLogger, val workingDirectory: File, - val job: BackgroundJob + val job: BackgroundJob, + @volatile var isAutoCancel: Boolean = false, ) extends AbstractJobHandle { def humanReadableName: String = job.humanReadableName @@ -141,6 +142,7 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe private final class DeadHandle(override val id: Long, override val humanReadableName: String) extends AbstractJobHandle { override val spawningTask: ScopedKey[?] = unknownTask + override val isAutoCancel = false } def doRunInBackground( @@ -537,6 +539,15 @@ private[sbt] object DefaultBackgroundJobService { backgroundJobServices.values.forEach(_.shutdown()) backgroundJobServices.clear() } + + private[sbt] def stop(): Unit = { + backgroundJobServices + .values() + .forEach(jobService => { + jobService.jobs.filter(_.isAutoCancel).foreach(jobService.stop) + }) + } + private[sbt] lazy val backgroundJobServiceSetting: Setting[?] = (GlobalScope / Keys.bgJobService) := { val path = (GlobalScope / sbt.Keys.bgJobServiceDirectory).value diff --git a/main/src/main/scala/sbt/internal/InstallSbtn.scala b/main/src/main/scala/sbt/internal/InstallSbtn.scala index 3e56f6f55..1dc75affc 100644 --- a/main/src/main/scala/sbt/internal/InstallSbtn.scala +++ b/main/src/main/scala/sbt/internal/InstallSbtn.scala @@ -11,6 +11,7 @@ package internal import Def._ import Keys.{ sbtVersion, state, terminal } +import sbt.internal.util.Terminal.hasConsole import java.io.{ File, FileInputStream, FileOutputStream, InputStream, IOException } import java.net.URI @@ -37,7 +38,7 @@ private[sbt] object InstallSbtn { Files.deleteIfExists(tmp) () } - val shell = if (System.console != null) getShell(term) else "none" + val shell = if (hasConsole) getShell(term) else "none" shell match { case "none" => case s => diff --git a/main/src/main/scala/sbt/internal/SysProp.scala b/main/src/main/scala/sbt/internal/SysProp.scala index 6c36266b7..50b62e7df 100644 --- a/main/src/main/scala/sbt/internal/SysProp.scala +++ b/main/src/main/scala/sbt/internal/SysProp.scala @@ -155,6 +155,8 @@ object SysProp { def useLog4J: Boolean = getOrFalse("sbt.log.uselog4j") def turbo: Boolean = getOrFalse("sbt.turbo") def pipelining: Boolean = getOrFalse("sbt.pipelining") + // opt-in or out of Zinc's consistent Analysis format. + def analysis2024: Boolean = getOrFalse("sbt.analysis2024") def taskTimings: Boolean = getOrFalse("sbt.task.timings") def taskTimingsOnShutdown: Boolean = getOrFalse("sbt.task.timings.on.shutdown") diff --git a/main/src/main/scala/sbt/internal/TaskTraceEvent.scala b/main/src/main/scala/sbt/internal/TaskTraceEvent.scala index 6ec2fe0f8..8f4e849e6 100644 --- a/main/src/main/scala/sbt/internal/TaskTraceEvent.scala +++ b/main/src/main/scala/sbt/internal/TaskTraceEvent.scala @@ -59,7 +59,7 @@ private[sbt] final class TaskTraceEvent extends AbstractTaskExecuteProgress with def durationEvent(name: String, cat: String, t: Timer): String = { val sb = new java.lang.StringBuilder(name.length + 2) CompactPrinter.print(new JString(name), sb) - s"""{"name": ${sb.toString}, "cat": "$cat", "ph": "X", "ts": ${(t.startMicros)}, "dur": ${(t.durationMicros)}, "pid": 0, "tname": ${t.threadName}}""" + s"""{"name": ${sb.toString}, "cat": "$cat", "ph": "X", "ts": ${(t.startMicros)}, "dur": ${(t.durationMicros)}, "pid": 0, "tname": "${t.threadName}"}""" } val entryIterator = currentTimings while (entryIterator.hasNext) { diff --git a/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala b/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala index 17d039574..27686d307 100644 --- a/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala +++ b/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala @@ -148,8 +148,9 @@ object DependencyTreeSettings { .asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module), graphWidth) } .mkString("\n") - - streams.value.log.info(output) + synchronized { + streams.value.log.info(output) + } output }, ) ++ @@ -172,7 +173,9 @@ object DependencyTreeSettings { key := { val s = streams.value val str = (key / asString).value - s.log.info(str) + synchronized { + s.log.info(str) + } }, (key / toFile) := { val (targetFile, force) = targetFileAndForceParser.parsed @@ -199,7 +202,7 @@ object DependencyTreeSettings { rendering.DOT.HTMLLabelRendering.AngleBrackets, dependencyDotNodeColors.value ) - val link = DagreHTML.createLink(dotGraph, target.value) + val link = DagreHTML.createLink(dotGraph, dependencyBrowseGraphTarget.value) streams.value.log.info(s"HTML graph written to $link") link } @@ -208,7 +211,7 @@ object DependencyTreeSettings { Def.task { val graph = dependencyTreeModuleGraph0.value val renderedTree = TreeView.createJson(graph) - val link = TreeView.createLink(renderedTree, target.value) + val link = TreeView.createLink(renderedTree, dependencyBrowseTreeTarget.value) streams.value.log.info(s"HTML tree written to $link") link } @@ -246,7 +249,10 @@ object DependencyTreeSettings { Def.task { val uri = uriKey.value streams.value.log.info(s"Opening ${uri} in browser...") - java.awt.Desktop.getDesktop.browse(uri) + val desktop = java.awt.Desktop.getDesktop + desktop.synchronized { + desktop.browse(uri) + } uri } diff --git a/main/src/test/scala/TagsTest.scala b/main/src/test/scala/TagsTest.scala index 1d80e43cf..24eef2c52 100644 --- a/main/src/test/scala/TagsTest.scala +++ b/main/src/test/scala/TagsTest.scala @@ -18,7 +18,7 @@ object TagsTest extends Properties("Tags") { def tagMap: Gen[TagMap] = for (ts <- listOf(tagAndFrequency)) yield ts.toMap def tagAndFrequency: Gen[(Tag, Int)] = - for (t <- tag; count <- Arbitrary.arbitrary[Int]) yield (t, count) + for (t <- tag; count <- Gen.choose(0, Int.MaxValue)) yield (t, count) def tag: Gen[Tag] = for (s <- Gen.alphaStr if !s.isEmpty) yield Tag(s) def size: Gen[Size] = for (i <- Arbitrary.arbitrary[Int] if i != Int.MinValue) yield Size(math.abs(i)) diff --git a/main/src/test/scala/sbt/internal/AggregationSpec.scala b/main/src/test/scala/sbt/internal/AggregationSpec.scala index 0423cb22f..04563c0ca 100644 --- a/main/src/test/scala/sbt/internal/AggregationSpec.scala +++ b/main/src/test/scala/sbt/internal/AggregationSpec.scala @@ -22,4 +22,8 @@ object AggregationSpec extends verify.BasicTestSuite { assert(timing(6003099).startsWith("elapsed time: 6003 s (01:40:03)")) assert(timing(96003099).startsWith("elapsed time: 96003 s (26:40:03)")) } + + test("timing should not emit special space characters") { + assert(!timing(96003099).contains("\u202F")) + } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 70038c8b5..e58289d29 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -80,10 +80,10 @@ object Dependencies { // and the JLine 2 fork version, which uses the same JAnsi val jline = "org.scala-sbt.jline" % "jline" % "2.14.7-sbt-9c3b6aca11c57e339441442bbf58e550cdfecb79" - val jline3Version = "3.27.0" + val jline3Version = "3.27.1" val jline3Terminal = "org.jline" % "jline-terminal" % jline3Version - val jline3Jansi = "org.jline" % "jline-terminal-jansi" % jline3Version - val jline3JNA = "org.jline" % "jline-terminal-jna" % jline3Version + val jline3JNI = "org.jline" % "jline-terminal-jni" % jline3Version + val jline3Native = "org.jline" % "jline-native" % jline3Version val jline3Reader = "org.jline" % "jline-reader" % jline3Version val jline3Builtins = "org.jline" % "jline-builtins" % jline3Version val scalatest = "org.scalatest" %% "scalatest" % "3.2.19" @@ -121,7 +121,7 @@ object Dependencies { // lm-coursier dependencies val dataclassScalafixVersion = "0.1.0" - val coursierVersion = "2.1.13" + val coursierVersion = "2.1.19" val coursier = ("io.get-coursier" %% "coursier" % coursierVersion) .cross(CrossVersion.for3Use2_13) diff --git a/project/build.properties b/project/build.properties index bc7390601..db1723b08 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.3 +sbt.version=1.10.5 diff --git a/sbt b/sbt index 11093ffc4..a2bfeacf8 100755 --- a/sbt +++ b/sbt @@ -1,7 +1,7 @@ #!/usr/bin/env bash set +e -declare builtin_sbt_version="1.10.3" +declare builtin_sbt_version="1.10.6" declare -a residual_args declare -a java_args declare -a scalac_args @@ -24,7 +24,7 @@ declare build_props_sbt_version= declare use_sbtn= declare no_server= declare sbtn_command="$SBTN_CMD" -declare sbtn_version="1.10.3" +declare sbtn_version="1.10.5" ### ------------------------------- ### ### Helper methods for BASH scripts ### @@ -183,7 +183,7 @@ acquire_sbtn () { local archive_target= local url= local arch="x86_64" - if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if [[ "$OSTYPE" == "linux"* ]]; then arch=$(uname -m) if [[ "$arch" == "aarch64" ]] || [[ "$arch" == "x86_64" ]]; then archive_target="$p/sbtn-${arch}-pc-linux-${sbtn_v}.tar.gz" @@ -496,7 +496,7 @@ run() { } # TODO - java check should be configurable... - checkJava "6" + checkJava "8" # Java 9 support copyRt @@ -752,7 +752,14 @@ isRunNativeClient() { [[ "$sbtV" == "" ]] && sbtV="0.0.0" sbtBinaryV_1=$(echo "$sbtV" | sed 's/^\([0-9]*\)\.\([0-9]*\).*$/\1/') sbtBinaryV_2=$(echo "$sbtV" | sed 's/^\([0-9]*\)\.\([0-9]*\).*$/\2/') - if (( $sbtBinaryV_1 >= 2 )) || ( (( $sbtBinaryV_1 >= 1 )) && (( $sbtBinaryV_2 >= 4 )) ); then + # Default to true for sbt 2.x + if (( $sbtBinaryV_1 >= 2 )); then + if [[ "$use_sbtn" == "0" ]]; then + echo "false" + else + echo "true" + fi + elif ( (( $sbtBinaryV_1 >= 1 )) && (( $sbtBinaryV_2 >= 4 )) ); then if [[ "$use_sbtn" == "1" ]]; then echo "true" else diff --git a/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/test b/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/test index a62a34698..59b1e34b3 100644 --- a/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/test +++ b/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/test @@ -1,4 +1,4 @@ > set scalaVersion := "2.13.12" > check213 -> set scalaVersion := "2.12.18" +> set scalaVersion := "2.12.20" > check212 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 4593aca81..fae8cacab 100644 --- a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt @@ -9,6 +9,18 @@ TaskKey[Unit]("check") := { val report = updateFull.value val graph = (Test / dependencyTree / asString).value def sanitize(str: String): String = str.split('\n').drop(1).map(_.trim).mkString("\n") + +/* +Started to return: + +ch.qos.logback:logback-core:1.0.7 +default:sbt_8ae1da13_2.12:0.1.0-SNAPSHOT [S] + +-ch.qos.logback:logback-classic:1.0.7 + | +-org.slf4j:slf4j-api:1.6.6 (evicted by: 1.7.2) + | + +-org.slf4j:slf4j-api:1.7.2 +*/ + val expectedGraph = """default:default-e95e05_2.12:0.1-SNAPSHOT [S] | +-ch.qos.logback:logback-classic:1.0.7 diff --git a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/test b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/pending similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/test rename to sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/pending diff --git a/sbt-app/src/sbt-test/project1/scripted13/test b/sbt-app/src/sbt-test/project1/scripted13/test index 9a7fad1c5..b321a8554 100644 --- a/sbt-app/src/sbt-test/project1/scripted13/test +++ b/sbt-app/src/sbt-test/project1/scripted13/test @@ -1,6 +1,6 @@ # This tests that this sbt scripted plugin can launch the previous one -> ^^1.10.3 +> ^^1.10.5 $ copy-file changes/A.scala src/sbt-test/a/b/A.scala > scripted diff --git a/sbt-app/src/sbt-test/source-dependencies/false-error/test b/sbt-app/src/sbt-test/source-dependencies/false-error/pending similarity index 100% rename from sbt-app/src/sbt-test/source-dependencies/false-error/test rename to sbt-app/src/sbt-test/source-dependencies/false-error/pending diff --git a/sbt-app/src/sbt-test/source-dependencies/pipelining/build.sbt b/sbt-app/src/sbt-test/source-dependencies/pipelining/build.sbt index 96b5b5a17..25dfae396 100644 --- a/sbt-app/src/sbt-test/source-dependencies/pipelining/build.sbt +++ b/sbt-app/src/sbt-test/source-dependencies/pipelining/build.sbt @@ -17,6 +17,6 @@ lazy val use = project val x = (dep / Compile / compile).value val picklePath = (Compile / internalDependencyPicklePath).value assert(picklePath.size == 1 && - picklePath.head.data.name == "dep_2.13-0.1.0-SNAPSHOT.jar", s"picklePath = ${picklePath}") + picklePath.head.data.name == "early.jar", s"picklePath = ${picklePath}") }, )